Jaspers Hub

jaspers / yfinance

pluginOfficialReviewed★ 0↓ 11

yfinance: Free Yahoo Finance market data (quotes, history, fundamentals, options, news, movers) via a bundled MCP server, with five views.

3.6 KB
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
5const DASH = '—'
6
7type Maybe = number | null | undefined
8
9export 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
27const SCALES: [number, string][] = [
28 [1e12, 'T'],
29 [1e9, 'B'],
30 [1e6, 'M'],
31 [1e3, 'K'],
32]
33
34function finite(value: Maybe): value is number {
35 return typeof value === 'number' && Number.isFinite(value)
36}
37
38function grouped(value: number, digits: number): string {
39 return value.toLocaleString('en-US', { minimumFractionDigits: digits, maximumFractionDigits: digits })
40}
41
42function iso(seconds: number): string {
43 return new Date(seconds * 1000).toISOString()
44}
45
46export 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%. */
79export function smallPercent(value: Maybe): string {
80 return finite(value) ? `${Number(value.toPrecision(3))}%` : DASH
81}
82
83export const display = formatter('−')
84export const plain = formatter('-')