yfinance: Free Yahoo Finance market data (quotes, history, fundamentals, options, news, movers) via a bundled MCP server, with five views.
| 1 | // Figures written the way a cell or a line of text shows them: 1,234.56, 4.85T, +0.32%, 2026-09-16. |
| 2 | // A value that is not a finite number shows as a dash. `display` writes a true minus for the views; |
| 3 | // `plain` writes a hyphen, for the text the model reads. |
| 4 | |
| 5 | const DASH = '—' |
| 6 | |
| 7 | type Maybe = number | null | undefined |
| 8 | |
| 9 | export interface Format { |
| 10 | fixed(value: Maybe, digits?: number): string |
| 11 | signed(value: Maybe, digits?: number): string |
| 12 | big(value: Maybe, digits?: number): string |
| 13 | /** A value already in percent: 0.32 is 0.32%. */ |
| 14 | percent(value: Maybe, digits?: number): string |
| 15 | signedPercent(value: Maybe, digits?: number): string |
| 16 | /** A fraction: 0.0452 is 4.52%. */ |
| 17 | fraction(value: Maybe, digits?: number): string |
| 18 | /** Yahoo's `priceHint` decimals (up to six) when given, else two, or four under one. */ |
| 19 | price(value: Maybe, hint?: number | null): string |
| 20 | /** Epoch seconds as the UTC day. */ |
| 21 | day(seconds: Maybe): string |
| 22 | /** Epoch seconds as the day where the market is, `offset` seconds from UTC. */ |
| 23 | localDay(seconds: Maybe, offset: number): string |
| 24 | localTime(seconds: Maybe, offset: number): string |
| 25 | } |
| 26 | |
| 27 | const SCALES: [number, string][] = [ |
| 28 | [1e12, 'T'], |
| 29 | [1e9, 'B'], |
| 30 | [1e6, 'M'], |
| 31 | [1e3, 'K'], |
| 32 | ] |
| 33 | |
| 34 | function finite(value: Maybe): value is number { |
| 35 | return typeof value === 'number' && Number.isFinite(value) |
| 36 | } |
| 37 | |
| 38 | function grouped(value: number, digits: number): string { |
| 39 | return value.toLocaleString('en-US', { minimumFractionDigits: digits, maximumFractionDigits: digits }) |
| 40 | } |
| 41 | |
| 42 | function iso(seconds: number): string { |
| 43 | return new Date(seconds * 1000).toISOString() |
| 44 | } |
| 45 | |
| 46 | export function formatter(minus: string): Format { |
| 47 | const fixed = (value: Maybe, digits = 2): string => { |
| 48 | if (!finite(value)) return DASH |
| 49 | const shown = grouped(Math.abs(value), digits) |
| 50 | return value < 0 && /[1-9]/.test(shown) ? `${minus}${shown}` : shown |
| 51 | } |
| 52 | const signed = (value: Maybe, digits = 2): string => { |
| 53 | const shown = fixed(value, digits) |
| 54 | return finite(value) && value > 0 && /[1-9]/.test(shown) ? `+${shown}` : shown |
| 55 | } |
| 56 | const big = (value: Maybe, digits = 2): string => { |
| 57 | if (!finite(value)) return DASH |
| 58 | const size = Math.abs(value) |
| 59 | const scale = SCALES.find(([at]) => size >= at) |
| 60 | if (!scale) return fixed(value, 0) |
| 61 | return `${fixed(value / scale[0], digits)}${scale[1]}` |
| 62 | } |
| 63 | const percent = (value: Maybe, digits = 2): string => (finite(value) ? `${fixed(value, digits)}%` : DASH) |
| 64 | const signedPercent = (value: Maybe, digits = 2): string => (finite(value) ? `${signed(value, digits)}%` : DASH) |
| 65 | const fraction = (value: Maybe, digits = 2): string => (finite(value) ? percent(value * 100, digits) : DASH) |
| 66 | const price = (value: Maybe, hint?: number | null): string => { |
| 67 | if (!finite(value)) return DASH |
| 68 | if (typeof hint === 'number' && Number.isInteger(hint) && hint >= 0) return fixed(value, Math.min(hint, 6)) |
| 69 | return fixed(value, Math.abs(value) < 1 && value !== 0 ? 4 : 2) |
| 70 | } |
| 71 | const day = (seconds: Maybe): string => (finite(seconds) ? iso(seconds).slice(0, 10) : '') |
| 72 | const localDay = (seconds: Maybe, offset: number): string => (finite(seconds) ? iso(seconds + offset).slice(0, 10) : '') |
| 73 | const localTime = (seconds: Maybe, offset: number): string => |
| 74 | finite(seconds) ? iso(seconds + offset).slice(0, 16).replace('T', ' ') : '' |
| 75 | return { fixed, signed, big, percent, signedPercent, fraction, price, day, localDay, localTime } |
| 76 | } |
| 77 | |
| 78 | /** A small positive percent to three significant figures, as an expense ratio reads: 0.0945%, 0.18%. */ |
| 79 | export function smallPercent(value: Maybe): string { |
| 80 | return finite(value) ? `${Number(value.toPrecision(3))}%` : DASH |
| 81 | } |
| 82 | |
| 83 | export const display = formatter('−') |
| 84 | export const plain = formatter('-') |