Spaces:
Running
Running
| // src/lib/prices.ts | |
| type PriceMap = Record<string, number> | |
| // Map your asset code -> provider + symbol | |
| const MAP: Record<string, { p: 'binance'|'stooq'; s: string }> = { | |
| // crypto (USDT pairs) | |
| BTC: { p: 'binance', s: 'BTCUSDT' }, | |
| ETH: { p: 'binance', s: 'ETHUSDT' }, | |
| SOL: { p: 'binance', s: 'SOLUSDT' }, | |
| BNB: { p: 'binance', s: 'BNBUSDT' }, | |
| DOGE:{ p: 'binance', s: 'DOGEUSDT' }, | |
| XRP: { p: 'binance', s: 'XRPUSDT' }, | |
| // equities (stooq wants .us) | |
| AAPL:{ p: 'stooq', s: 'aapl.us' }, | |
| MSFT:{ p: 'stooq', s: 'msft.us' }, | |
| BMRN:{ p: 'stooq', s: 'bmrn.us' }, | |
| MRNA:{ p: 'stooq', s: 'mrna.us' }, | |
| TSLA:{ p: 'stooq', s: 'tsla.us' }, | |
| }; | |
| // --- simple cache + pub/sub | |
| let latest: PriceMap = {}; | |
| const subs = new Set<(m: PriceMap)=>void>(); | |
| const push = () => subs.forEach(fn => fn({ ...latest })); | |
| export function subscribePrices(fn: (m: PriceMap)=>void) { | |
| subs.add(fn); fn({ ...latest }); | |
| return () => subs.delete(fn); | |
| } | |
| async function fetchBinance(symbol: string) { | |
| const r = await fetch(`https://api.binance.com/api/v3/ticker/price?symbol=${encodeURIComponent(symbol)}`); | |
| const j = await r.json(); const n = Number(j?.price); | |
| return Number.isFinite(n) ? n : undefined; | |
| } | |
| async function fetchStooq(symbol: string) { | |
| const url = `https://stooq.com/q/l/?s=${encodeURIComponent(symbol)}&f=sd2t2ohlcv&h&e=csv`; | |
| const r = await fetch(url); | |
| const txt = await r.text(); // first line is header; second line has data, column 'Close' | |
| const line = txt.split('\n')[1] || ''; | |
| const cols = line.split(','); // Symbol,Date,Time,Open,High,Low,Close,Volume | |
| const n = Number(cols[6]); | |
| return Number.isFinite(n) ? n : undefined; | |
| } | |
| export async function pollPrices(codes: string[], intervalMs = 30000) { | |
| const uniq = [...new Set(codes)].filter(c => MAP[c]); | |
| async function tick() { | |
| await Promise.all(uniq.map(async code => { | |
| const { p, s } = MAP[code]; | |
| const px = p === 'binance' ? await fetchBinance(s) : await fetchStooq(s); | |
| if (px !== undefined) latest[code] = px; | |
| })); | |
| push(); | |
| } | |
| await tick(); | |
| setInterval(tick, intervalMs); | |
| } | |
| export const fmtUSD = (n?: number) => | |
| typeof n === 'number' | |
| ? n.toLocaleString(undefined, { style: 'currency', currency: 'USD', maximumFractionDigits: 2 }) | |
| : 'β'; | |