Files
InvestmentsVisualizer/scripts/fetch-sp500.mjs
Dasemu 1dae56415c Initial commit: investment dashboard with contributions panel and forecast chart
React + Vite frontend with a local-JSON-backed API (Vite middleware) for
tracking portfolio holdings, contributions/withdrawals, recurring
contribution plans, and a forecast chart comparing the portfolio's real
value against a same-contributions S&P 500 benchmark.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 19:18:46 +02:00

34 lines
1.3 KiB
JavaScript

// Refreshes data/sp500.json with daily S&P 500 close prices from Yahoo Finance's
// public chart endpoint (no API key required). Run with: node scripts/fetch-sp500.mjs
import { writeFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
const __dirname = dirname(fileURLToPath(import.meta.url));
const OUT_PATH = join(__dirname, "..", "data", "sp500.json");
const URL =
"https://query1.finance.yahoo.com/v8/finance/chart/%5EGSPC?range=10y&interval=1d";
const res = await fetch(URL, { headers: { "User-Agent": "Mozilla/5.0" } });
if (!res.ok) {
throw new Error(`Yahoo Finance request failed: ${res.status} ${res.statusText}`);
}
const data = await res.json();
const result = data?.chart?.result?.[0];
if (!result) {
throw new Error("Unexpected response shape from Yahoo Finance");
}
const { timestamp } = result;
const closes = result.indicators.quote[0].close;
const series = [];
for (let i = 0; i < timestamp.length; i++) {
if (closes[i] == null) continue;
const date = new Date(timestamp[i] * 1000).toISOString().slice(0, 10);
series.push({ date, close: Number(closes[i].toFixed(2)) });
}
writeFileSync(OUT_PATH, JSON.stringify({ source: "yahoo-finance:^GSPC", updatedAt: new Date().toISOString(), series }, null, 2));
console.log(`Wrote ${series.length} points to ${OUT_PATH}`);