slice 1a-1d: DB + CacheRepository + YFinance adapter + AdapterQueue

Node 26 + node:sqlite backend (zero native deps; runtime glue adapted from
Bun-spec design, deep-module architecture unchanged). 29 tests green across
schema/CacheRepository/YFinance-parse/AdapterQueue-dedupe.
This commit is contained in:
Investor Flow Build
2026-06-29 17:28:57 -04:00
commit 1962ecc740
17 changed files with 3584 additions and 0 deletions
+42
View File
@@ -0,0 +1,42 @@
// Investor Flow — SourceAdapter contract (DESIGN.md §3a Module 2 + §5).
//
// Implementation split (vs the design's single SourceAdapter interface):
// - SourceFetch (here): the per-source fetch+parse contract. One concrete adapter per source.
// - AdapterQueue (slice 1d): the shared queue/dedupe/backoff/drain orchestrator that calls
// SourceFetch.fetchOne during drain and writes results to CacheRepository.
// This preserves the design's intent (one seam per source; shared dedupe) while keeping each
// micro-dispatch focused. The SPA never calls either directly — only CacheRepository.get →
// AdapterQueue.queue → (drain) → SourceFetch.fetchOne → CacheRepository.set.
import type { CacheKey, SourceKind, TtlClass, Provenance } from '../cache/CacheRepository.ts';
export interface FetchOpts { force?: boolean; } // force = bypass dedupe (admin.resetBackoff)
export interface AdapterHealth { queued: number; in_flight: number; backoff_until?: string | null; last_error?: string; }
export interface FetchResult { value: unknown; ttlClass: TtlClass; provenance: Provenance; }
/** Per-source fetch+parse. The AdapterQueue calls fetchOne(key) during drain. */
export interface SourceFetch {
readonly sourceKind: SourceKind;
fetchOne(key: CacheKey): Promise<FetchResult>;
}
/**
* Test double: deterministic canned responses per key. Implements SourceFetch.
* `calls` records every fetchOne invocation — used by dedupe tests
* ("two concurrent snapshot calls collapse into one fetch" => calls.length === 1).
*/
export class FakeSourceAdapter implements SourceFetch {
readonly sourceKind: SourceKind;
private readonly canned = new Map<CacheKey, FetchResult>();
public calls: CacheKey[] = [];
constructor(sourceKind: SourceKind = 'yfinance') { this.sourceKind = sourceKind; }
set(key: CacheKey, value: unknown, ttlClass: TtlClass, provenance?: Provenance): this {
this.canned.set(key, { value, ttlClass, provenance: provenance ?? { fetchedAt: new Date().toISOString(), sourceKind: this.sourceKind } });
return this;
}
async fetchOne(key: CacheKey): Promise<FetchResult> {
this.calls.push(key);
const r = this.canned.get(key);
if (!r) throw new Error(`FakeSourceAdapter: no canned response for ${key}`);
return r;
}
}
+112
View File
@@ -0,0 +1,112 @@
// Investor Flow — YFinanceAdapter (DESIGN.md §3a Module 2 + §5 yfinance policy).
// One concrete SourceFetch for yfinance: quote / candles / symbol(info+sector).
// yahoo-finance2 v3 is dynamically imported inside fetchOne so the module loads without the
// dependency and tests exercise only the pure parse functions (fixtures, no network).
import type { CacheKey, TtlClass, Provenance, Quote, PriceCandle, SymbolMeta, TickerKind } from '../cache/CacheRepository.ts';
import { parseCacheKey } from '../cache/CacheRepository.ts';
import type { SourceFetch, FetchResult } from './SourceAdapter.ts';
// Polite UA including operator contact (yfinance community norm; EDGAR requires similar).
const OPERATOR_EMAIL = process.env.YF_OPERATOR_EMAIL ?? 'operator@example.com';
export class YFinanceAdapter implements SourceFetch {
readonly sourceKind = 'yfinance' as const;
private _yf: unknown = null;
private async yf(): Promise<YFinanceLike> {
if (!this._yf) {
const mod = await import('yahoo-finance2');
this._yf = new mod.default({ agent: undefined } as never);
}
return this._yf as YFinanceLike;
}
async fetchOne(key: CacheKey): Promise<FetchResult> {
const { kind, id } = parseCacheKey(key);
const fetchedAt = new Date().toISOString();
const yf = await this.yf();
switch (kind) {
case 'quote': {
const raw = await yf.quote(id);
return { value: parseQuote(raw, id), ttlClass: 'live_quote', provenance: { fetchedAt, sourceKind: 'yfinance', rawSourceId: id } };
}
case 'candles': {
const [symbol, timeframe] = id.split(':');
const interval = timeframeToInterval(timeframe);
const days = timeframe === '1wk' ? 365 : 95; // slice 1: recent range for sparkline; slice 4 backfills `max`
const raw = await yf.chart(symbol, { period1: new Date(Date.now() - days * 86_400_000), interval });
return { value: parseCandles(raw), ttlClass: 'daily_permanent', provenance: { fetchedAt, sourceKind: 'yfinance', rawSourceId: `${symbol}:${timeframe}` } };
}
case 'symbol': {
const raw = await yf.quoteSummary(id, { modules: ['summaryProfile'] });
return { value: parseSymbol(raw, id), ttlClass: 'symbol_meta', provenance: { fetchedAt, sourceKind: 'yfinance', rawSourceId: id } };
}
default: throw new Error(`YFinanceAdapter: unknown kind '${kind}'`);
}
}
}
interface YFinanceLike { quote(s: string): Promise<Record<string, unknown>>; chart(s: string, o: { period1: Date; interval: string }): Promise<Record<string, unknown>>; quoteSummary(s: string, o: { modules: string[] }): Promise<Record<string, unknown>>; }
// ----- Pure parse helpers (tested with recorded fixtures; no network) -----
export function parseQuote(raw: Record<string, unknown>, symbol: string): Quote {
const price = num(raw.regularMarketPrice);
if (price === null) throw new Error(`quote ${symbol}: missing regularMarketPrice`);
return {
symbol,
price,
bid: num(raw.bid),
ask: num(raw.ask),
change: num(raw.regularMarketChange),
changePercent: num(raw.regularMarketChangePercent),
iv: null, // IV comes from the options chain kind, not the quote
};
}
export function parseCandles(raw: Record<string, unknown>): PriceCandle[] {
const quotes = raw.quotes;
if (!Array.isArray(quotes)) return [];
const out: PriceCandle[] = [];
for (const q of quotes as Array<Record<string, unknown> | null>) {
if (!q) continue;
const close = num(q.close);
if (close === null) continue; // skip null candles (non-trading days / gaps)
out.push({
ts: str(q.date) ?? '',
o: num(q.open) ?? 0,
h: num(q.high) ?? 0,
l: num(q.low) ?? 0,
c: close,
v: num(q.volume) ?? 0,
adjClose: num(q.adjclose),
});
}
return out;
}
export function parseSymbol(raw: Record<string, unknown>, symbol: string): SymbolMeta {
const sp = (raw.summaryProfile ?? {}) as Record<string, unknown>;
const price = (raw.price ?? {}) as Record<string, unknown>;
return {
symbol,
name: str(sp.longName ?? price.longName ?? sp.shortName),
sector: str(sp.sector),
industry: str(sp.industry),
exchange: str(sp.exchange ?? price.exchange),
tickerKind: inferTickerKind(symbol),
peers: null,
};
}
const CRYPTO = new Set(['BTC', 'ETH', 'BNB', 'SOL', 'XRP', 'ADA', 'DOGE', 'DOT']);
const ETF = new Set(['SPY', 'QQQ', 'IWM', 'DIA', 'VTI', 'VOO', 'EEM']);
export function inferTickerKind(symbol: string): TickerKind {
if (CRYPTO.has(symbol)) return 'crypto';
if (ETF.has(symbol)) return 'etf';
if (symbol.startsWith('^')) return 'index';
return 'equity';
}
function timeframeToInterval(tf: string): string { return tf; } // '1d'->'1d', '1wk'->'1wk', '1h'->'1h'
function num(v: unknown): number | null { return typeof v === 'number' && Number.isFinite(v) ? v : null; }
function str(v: unknown): string | null { return typeof v === 'string' && v.length > 0 ? v : null; }
@@ -0,0 +1,80 @@
import { test } from 'node:test';
import { strict as assert } from 'node:assert';
import { readFileSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { parseQuote, parseCandles, parseSymbol, inferTickerKind } from '../YFinanceAdapter.ts';
import { FakeSourceAdapter } from '../SourceAdapter.ts';
const __dirname = dirname(fileURLToPath(import.meta.url));
const FIX = join(__dirname, 'fixtures');
const readFixture = (n: string) => JSON.parse(readFileSync(join(FIX, n), 'utf8'));
test('parseQuote extracts the live-quote fields from a recorded yf2 response', () => {
const raw = readFixture('nvda.quote.json');
const q = parseQuote(raw, 'NVDA');
assert.equal(q.symbol, 'NVDA');
assert.equal(q.price, raw.regularMarketPrice);
assert.equal(q.change, raw.regularMarketChange);
assert.equal(q.changePercent, raw.regularMarketChangePercent);
assert.equal(q.bid, raw.bid);
assert.equal(q.ask, raw.ask);
assert.equal(q.iv, null);
});
test('parseQuote throws when regularMarketPrice is missing', () => {
assert.throws(() => parseQuote({ regularMarketPrice: undefined }, 'NVDA'));
});
test('parseCandles maps chart.quotes[] to typed candles, skipping nulls', () => {
const raw = readFixture('nvda.chart.json');
const candles = parseCandles(raw);
const expectedLen = (raw.quotes as Array<Record<string, unknown> | null>).filter((q) => q && q.close != null).length;
assert.equal(candles.length, expectedLen);
const first = candles[0];
assert.equal(first.ts, (raw.quotes[0] as { date: string }).date);
assert.equal(first.c, (raw.quotes[0] as { close: number }).close);
assert.equal(first.adjClose, (raw.quotes[0] as { adjclose: number }).adjclose);
for (const c of candles) {
assert.equal(typeof c.o, 'number'); assert.equal(typeof c.h, 'number');
assert.equal(typeof c.l, 'number'); assert.equal(typeof c.c, 'number');
}
});
test('parseCandles returns [] when quotes is absent/empty', () => {
assert.deepEqual(parseCandles({}), []);
assert.deepEqual(parseCandles({ quotes: [] }), []);
assert.deepEqual(parseCandles({ quotes: [null, { close: null }] }), []);
});
test('parseSymbol extracts sector/industry from summaryProfile', () => {
const raw = readFixture('nvda.summary.json');
const s = parseSymbol(raw, 'NVDA');
assert.equal(s.symbol, 'NVDA');
assert.equal(s.sector, (raw.summaryProfile as { sector: string }).sector);
assert.equal(s.industry, (raw.summaryProfile as { industry: string }).industry);
assert.equal(s.tickerKind, 'equity');
assert.equal(s.peers, null);
});
test('inferTickerKind: crypto/etf/index/equity', () => {
assert.equal(inferTickerKind('BTC'), 'crypto');
assert.equal(inferTickerKind('SPY'), 'etf');
assert.equal(inferTickerKind('^GSPC'), 'index');
assert.equal(inferTickerKind('NVDA'), 'equity');
});
test('FakeSourceAdapter returns canned responses and records calls', async () => {
const fake = new FakeSourceAdapter('yfinance');
fake.set('yfinance:quote:NVDA', { symbol: 'NVDA', price: 194.97 }, 'live_quote');
const r = await fake.fetchOne('yfinance:quote:NVDA');
assert.deepEqual(r.value, { symbol: 'NVDA', price: 194.97 });
assert.equal(r.ttlClass, 'live_quote');
assert.equal(r.provenance.sourceKind, 'yfinance');
assert.deepEqual(fake.calls, ['yfinance:quote:NVDA']);
});
test('FakeSourceAdapter throws on missing canned key', async () => {
const fake = new FakeSourceAdapter();
await assert.rejects(() => fake.fetchOne('yfinance:quote:UNKNOWN'));
});
@@ -0,0 +1,655 @@
{
"meta": {
"currency": "USD",
"symbol": "NVDA",
"exchangeName": "NMS",
"fullExchangeName": "NasdaqGS",
"instrumentType": "EQUITY",
"firstTradeDate": "1999-01-22T14:30:00.000Z",
"regularMarketTime": "2026-06-29T20:00:00.000Z",
"hasPrePostMarketData": true,
"gmtoffset": -14400,
"timezone": "EDT",
"exchangeTimezoneName": "America/New_York",
"regularMarketPrice": 194.97,
"fiftyTwoWeekHigh": 236.54,
"fiftyTwoWeekLow": 151.49,
"regularMarketDayHigh": 196.17,
"regularMarketDayLow": 189.8,
"regularMarketVolume": 146600013,
"longName": "NVIDIA Corporation",
"shortName": "NVIDIA Corporation",
"chartPreviousClose": 171.24,
"priceHint": 2,
"currentTradingPeriod": {
"pre": {
"timezone": "EDT",
"start": "2026-06-29T08:00:00.000Z",
"end": "2026-06-29T13:30:00.000Z",
"gmtoffset": -14400
},
"regular": {
"timezone": "EDT",
"start": "2026-06-29T13:30:00.000Z",
"end": "2026-06-29T20:00:00.000Z",
"gmtoffset": -14400
},
"post": {
"timezone": "EDT",
"start": "2026-06-29T20:00:00.000Z",
"end": "2026-06-30T00:00:00.000Z",
"gmtoffset": -14400
}
},
"dataGranularity": "1d",
"range": "",
"validRanges": [
"1d",
"5d",
"1mo",
"3mo",
"6mo",
"1y",
"2y",
"5y",
"10y",
"ytd",
"max"
]
},
"quotes": [
{
"date": "2026-03-26T13:30:00.000Z",
"high": 176.50999450683594,
"volume": 186152200,
"open": 176.07000732421875,
"low": 171.13999938964844,
"close": 171.24000549316406,
"adjclose": 171.04066467285156
},
{
"date": "2026-03-27T13:30:00.000Z",
"high": 170.97000122070312,
"volume": 196212700,
"open": 170,
"low": 167.00999450683594,
"close": 167.52000427246094,
"adjclose": 167.32498168945312
},
{
"date": "2026-03-30T13:30:00.000Z",
"high": 169.4499969482422,
"volume": 185627000,
"open": 168.77999877929688,
"low": 164.27000427246094,
"close": 165.1699981689453,
"adjclose": 164.97772216796875
},
{
"date": "2026-03-31T13:30:00.000Z",
"high": 174.6199951171875,
"volume": 226181300,
"open": 166.97000122070312,
"low": 166.9600067138672,
"close": 174.39999389648438,
"adjclose": 174.1969757080078
},
{
"date": "2026-04-01T13:30:00.000Z",
"high": 177.3699951171875,
"volume": 168132000,
"open": 176,
"low": 174.75,
"close": 175.75,
"adjclose": 175.54541015625
},
{
"date": "2026-04-02T13:30:00.000Z",
"high": 177.49000549316406,
"volume": 143143200,
"open": 172.17999267578125,
"low": 171.3699951171875,
"close": 177.38999938964844,
"adjclose": 177.18348693847656
},
{
"date": "2026-04-06T13:30:00.000Z",
"high": 177.7899932861328,
"volume": 107564300,
"open": 177.16000366210938,
"low": 175.75999450683594,
"close": 177.63999938964844,
"adjclose": 177.43319702148438
},
{
"date": "2026-04-07T13:30:00.000Z",
"high": 178.22999572753906,
"volume": 132534900,
"open": 175.72999572753906,
"low": 173.66000366210938,
"close": 178.10000610351562,
"adjclose": 177.89266967773438
},
{
"date": "2026-04-08T13:30:00.000Z",
"high": 185.25999450683594,
"volume": 147732700,
"open": 184.5,
"low": 180.3000030517578,
"close": 182.0800018310547,
"adjclose": 181.8680419921875
},
{
"date": "2026-04-09T13:30:00.000Z",
"high": 184.0800018310547,
"volume": 116428500,
"open": 181.83999633789062,
"low": 180.6199951171875,
"close": 183.91000366210938,
"adjclose": 183.69590759277344
},
{
"date": "2026-04-10T13:30:00.000Z",
"high": 190,
"volume": 160459500,
"open": 184.30999755859375,
"low": 184.3000030517578,
"close": 188.6300048828125,
"adjclose": 188.41041564941406
},
{
"date": "2026-04-13T13:30:00.000Z",
"high": 189.66000366210938,
"volume": 133648200,
"open": 186.02999877929688,
"low": 185.74000549316406,
"close": 189.30999755859375,
"adjclose": 189.08961486816406
},
{
"date": "2026-04-14T13:30:00.000Z",
"high": 196.50999450683594,
"volume": 161307000,
"open": 190.83999633789062,
"low": 190.77000427246094,
"close": 196.50999450683594,
"adjclose": 196.28123474121094
},
{
"date": "2026-04-15T13:30:00.000Z",
"high": 200.39999389648438,
"volume": 185338400,
"open": 196.5399932861328,
"low": 195.74000549316406,
"close": 198.8699951171875,
"adjclose": 198.63848876953125
},
{
"date": "2026-04-16T13:30:00.000Z",
"high": 199.85000610351562,
"volume": 134012900,
"open": 197.42999267578125,
"low": 195.80999755859375,
"close": 198.35000610351562,
"adjclose": 198.1190948486328
},
{
"date": "2026-04-17T13:30:00.000Z",
"high": 201.6999969482422,
"volume": 160324400,
"open": 199.89999389648438,
"low": 199.27000427246094,
"close": 201.67999267578125,
"adjclose": 201.44520568847656
},
{
"date": "2026-04-20T13:30:00.000Z",
"high": 202.1699981689453,
"volume": 119381400,
"open": 199.97999572753906,
"low": 197.83999633789062,
"close": 202.05999755859375,
"adjclose": 201.82476806640625
},
{
"date": "2026-04-21T13:30:00.000Z",
"high": 202.75,
"volume": 107945300,
"open": 202.1300048828125,
"low": 199,
"close": 199.8800048828125,
"adjclose": 199.64732360839844
},
{
"date": "2026-04-22T13:30:00.000Z",
"high": 202.5,
"volume": 107501000,
"open": 200.99000549316406,
"low": 199,
"close": 202.5,
"adjclose": 202.26426696777344
},
{
"date": "2026-04-23T13:30:00.000Z",
"high": 203.8300018310547,
"volume": 113561800,
"open": 202.4600067138672,
"low": 197.22000122070312,
"close": 199.63999938964844,
"adjclose": 199.4075927734375
},
{
"date": "2026-04-24T13:30:00.000Z",
"high": 210.9499969482422,
"volume": 214134400,
"open": 199.9600067138672,
"low": 199.80999755859375,
"close": 208.27000427246094,
"adjclose": 208.0275421142578
},
{
"date": "2026-04-27T13:30:00.000Z",
"high": 216.8300018310547,
"volume": 187172400,
"open": 209.64999389648438,
"low": 207.3800048828125,
"close": 216.61000061035156,
"adjclose": 216.3578338623047
},
{
"date": "2026-04-28T13:30:00.000Z",
"high": 214.72999572753906,
"volume": 180275400,
"open": 209.49000549316406,
"low": 208.1999969482422,
"close": 213.1699981689453,
"adjclose": 212.92184448242188
},
{
"date": "2026-04-29T13:30:00.000Z",
"high": 212.72000122070312,
"volume": 123711800,
"open": 212.6999969482422,
"low": 207.5800018310547,
"close": 209.25,
"adjclose": 209.00640869140625
},
{
"date": "2026-04-30T13:30:00.000Z",
"high": 210.3000030517578,
"volume": 225239200,
"open": 209.92999267578125,
"low": 198.6999969482422,
"close": 199.57000732421875,
"adjclose": 199.33767700195312
},
{
"date": "2026-05-01T13:30:00.000Z",
"high": 203,
"volume": 128647000,
"open": 201.27999877929688,
"low": 197.1199951171875,
"close": 198.4499969482422,
"adjclose": 198.21897888183594
},
{
"date": "2026-05-04T13:30:00.000Z",
"high": 201.72999572753906,
"volume": 125368100,
"open": 199.5,
"low": 194.74000549316406,
"close": 198.47999572753906,
"adjclose": 198.24893188476562
},
{
"date": "2026-05-05T13:30:00.000Z",
"high": 200.24000549316406,
"volume": 113406600,
"open": 199.3000030517578,
"low": 196.02999877929688,
"close": 196.5,
"adjclose": 196.271240234375
},
{
"date": "2026-05-06T13:30:00.000Z",
"high": 208.27000427246094,
"volume": 188362800,
"open": 199.88999938964844,
"low": 198.61000061035156,
"close": 207.8300018310547,
"adjclose": 207.5880584716797
},
{
"date": "2026-05-07T13:30:00.000Z",
"high": 214.1999969482422,
"volume": 168307900,
"open": 208.33999633789062,
"low": 206.5,
"close": 211.5,
"adjclose": 211.2537841796875
},
{
"date": "2026-05-08T13:30:00.000Z",
"high": 217.8000030517578,
"volume": 136421400,
"open": 213.02999877929688,
"low": 212.88999938964844,
"close": 215.1999969482422,
"adjclose": 214.94947814941406
},
{
"date": "2026-05-11T13:30:00.000Z",
"high": 222.3000030517578,
"volume": 160685800,
"open": 214.0399932861328,
"low": 213.88999938964844,
"close": 219.44000244140625,
"adjclose": 219.18453979492188
},
{
"date": "2026-05-12T13:30:00.000Z",
"high": 223.75,
"volume": 159176600,
"open": 218.5500030517578,
"low": 214.9199981689453,
"close": 220.77999877929688,
"adjclose": 220.52297973632812
},
{
"date": "2026-05-13T13:30:00.000Z",
"high": 227.83999633789062,
"volume": 150405400,
"open": 224.92999267578125,
"low": 221.57000732421875,
"close": 225.8300018310547,
"adjclose": 225.56710815429688
},
{
"date": "2026-05-14T13:30:00.000Z",
"high": 236.5399932861328,
"volume": 180782900,
"open": 229.85000610351562,
"low": 229.3000030517578,
"close": 235.74000549316406,
"adjclose": 235.465576171875
},
{
"date": "2026-05-15T13:30:00.000Z",
"high": 231.5,
"volume": 180977600,
"open": 229.75999450683594,
"low": 224.24000549316406,
"close": 225.32000732421875,
"adjclose": 225.05770874023438
},
{
"date": "2026-05-18T13:30:00.000Z",
"high": 230,
"volume": 146280900,
"open": 229.8699951171875,
"low": 218.3699951171875,
"close": 222.32000732421875,
"adjclose": 222.0612030029297
},
{
"date": "2026-05-19T13:30:00.000Z",
"high": 224.47999572753906,
"volume": 140948200,
"open": 219.6199951171875,
"low": 217.91000366210938,
"close": 220.61000061035156,
"adjclose": 220.35317993164062
},
{
"date": "2026-05-20T13:30:00.000Z",
"high": 226.1300048828125,
"volume": 184201600,
"open": 223.17999267578125,
"low": 220.5,
"close": 223.47000122070312,
"adjclose": 223.20985412597656
},
{
"date": "2026-05-21T13:30:00.000Z",
"high": 227.39999389648438,
"volume": 203381800,
"open": 222.2899932861328,
"low": 217.92999267578125,
"close": 219.50999450683594,
"adjclose": 219.25445556640625
},
{
"date": "2026-05-22T13:30:00.000Z",
"high": 221.00999450683594,
"volume": 169275700,
"open": 220.89999389648438,
"low": 214.8000030517578,
"close": 215.3300018310547,
"adjclose": 215.07933044433594
},
{
"date": "2026-05-26T13:30:00.000Z",
"high": 218.17999267578125,
"volume": 187202600,
"open": 216.5399932861328,
"low": 212,
"close": 214.86000061035156,
"adjclose": 214.60987854003906
},
{
"date": "2026-05-27T13:30:00.000Z",
"high": 214.14999389648438,
"volume": 167601200,
"open": 214.1199951171875,
"low": 208.77999877929688,
"close": 212.60000610351562,
"adjclose": 212.35250854492188
},
{
"date": "2026-05-28T13:30:00.000Z",
"high": 215.52000427246094,
"volume": 143996000,
"open": 211.27999877929688,
"low": 211.22000122070312,
"close": 214.25,
"adjclose": 214.00057983398438
},
{
"date": "2026-05-29T13:30:00.000Z",
"high": 217.86000061035156,
"volume": 289410600,
"open": 214.5800018310547,
"low": 211.1300048828125,
"close": 211.13999938964844,
"adjclose": 210.8942108154297
},
{
"date": "2026-06-01T13:30:00.000Z",
"high": 224.8699951171875,
"volume": 212850700,
"open": 215.72999572753906,
"low": 215.6999969482422,
"close": 224.36000061035156,
"adjclose": 224.09881591796875
},
{
"date": "2026-06-02T13:30:00.000Z",
"high": 232.27999877929688,
"volume": 193362900,
"open": 227.17999267578125,
"low": 221.35000610351562,
"close": 222.82000732421875,
"adjclose": 222.56060791015625
},
{
"date": "2026-06-03T13:30:00.000Z",
"high": 222.82000732421875,
"volume": 160907000,
"open": 221.72000122070312,
"low": 214.50999450683594,
"close": 214.75,
"adjclose": 214.5
},
{
"date": "2026-06-04T13:30:00.000Z",
"high": 221.60000610351562,
"volume": 169022200,
"open": 213.91000366210938,
"low": 210.97000122070312,
"close": 218.66000366210938,
"adjclose": 218.66000366210938
},
{
"date": "2026-06-05T13:30:00.000Z",
"high": 214.8699951171875,
"volume": 219655500,
"open": 214.52999877929688,
"low": 204.3300018310547,
"close": 205.10000610351562,
"adjclose": 205.10000610351562
},
{
"date": "2026-06-08T13:30:00.000Z",
"high": 210.47000122070312,
"volume": 138372800,
"open": 210.17999267578125,
"low": 206,
"close": 208.63999938964844,
"adjclose": 208.63999938964844
},
{
"date": "2026-06-09T13:30:00.000Z",
"high": 211.39999389648438,
"volume": 180962500,
"open": 210.6199951171875,
"low": 199.33999633789062,
"close": 208.19000244140625,
"adjclose": 208.19000244140625
},
{
"date": "2026-06-10T13:30:00.000Z",
"high": 207.22000122070312,
"volume": 161746600,
"open": 204.42999267578125,
"low": 199.9199981689453,
"close": 200.4199981689453,
"adjclose": 200.4199981689453
},
{
"date": "2026-06-11T13:30:00.000Z",
"high": 205.66000366210938,
"volume": 158643200,
"open": 201.49000549316406,
"low": 199.5399932861328,
"close": 204.8699951171875,
"adjclose": 204.8699951171875
},
{
"date": "2026-06-12T13:30:00.000Z",
"high": 207.07000732421875,
"volume": 112001800,
"open": 204.86000061035156,
"low": 203.44000244140625,
"close": 205.19000244140625,
"adjclose": 205.19000244140625
},
{
"date": "2026-06-15T13:30:00.000Z",
"high": 212.7100067138672,
"volume": 149936700,
"open": 208.9199981689453,
"low": 208.33999633789062,
"close": 212.4499969482422,
"adjclose": 212.4499969482422
},
{
"date": "2026-06-16T13:30:00.000Z",
"high": 211.49000549316406,
"volume": 125694100,
"open": 211.17999267578125,
"low": 207.2899932861328,
"close": 207.41000366210938,
"adjclose": 207.41000366210938
},
{
"date": "2026-06-17T13:30:00.000Z",
"high": 209.2100067138672,
"volume": 128363500,
"open": 208.52999877929688,
"low": 203.0800018310547,
"close": 204.64999389648438,
"adjclose": 204.64999389648438
},
{
"date": "2026-06-18T13:30:00.000Z",
"high": 211.38999938964844,
"volume": 241272000,
"open": 207.3300018310547,
"low": 206.5,
"close": 210.69000244140625,
"adjclose": 210.69000244140625
},
{
"date": "2026-06-22T13:30:00.000Z",
"high": 213.99000549316406,
"volume": 122041400,
"open": 211.44000244140625,
"low": 207.72000122070312,
"close": 208.64999389648438,
"adjclose": 208.64999389648438
},
{
"date": "2026-06-23T13:30:00.000Z",
"high": 203.77000427246094,
"volume": 153496200,
"open": 202.1699981689453,
"low": 200,
"close": 200.0399932861328,
"adjclose": 200.0399932861328
},
{
"date": "2026-06-24T13:30:00.000Z",
"high": 201.6699981689453,
"volume": 151810700,
"open": 200.1199951171875,
"low": 196.5800018310547,
"close": 199,
"adjclose": 199
},
{
"date": "2026-06-25T13:30:00.000Z",
"high": 200.8000030517578,
"volume": 149550000,
"open": 200.0800018310547,
"low": 192.1300048828125,
"close": 195.74000549316406,
"adjclose": 195.74000549316406
},
{
"date": "2026-06-26T13:30:00.000Z",
"high": 195.5500030517578,
"volume": 178906300,
"open": 193.1199951171875,
"low": 191.22000122070312,
"close": 192.52999877929688,
"adjclose": 192.52999877929688
},
{
"date": "2026-06-29T13:30:00.000Z",
"high": 196.1699981689453,
"volume": 146600013,
"open": 193.78500366210938,
"low": 189.8000030517578,
"close": 194.97000122070312,
"adjclose": 194.97000122070312
}
],
"events": {
"dividends": [
{
"amount": 0.25,
"date": "2026-06-04T13:30:00.000Z"
}
]
}
}
@@ -0,0 +1,97 @@
{
"language": "en-US",
"region": "US",
"quoteType": "EQUITY",
"typeDisp": "Equity",
"quoteSourceName": "Nasdaq Real Time Price",
"triggerable": true,
"customPriceAlertConfidence": "HIGH",
"currency": "USD",
"corporateActions": [],
"postMarketTime": "2026-06-29T21:04:54.000Z",
"regularMarketTime": "2026-06-29T20:00:00.000Z",
"exchange": "NMS",
"messageBoardId": "finmb_32307",
"exchangeTimezoneName": "America/New_York",
"exchangeTimezoneShortName": "EDT",
"gmtOffSetMilliseconds": -14400000,
"market": "us_market",
"esgPopulated": false,
"regularMarketChangePercent": 1.2673362,
"regularMarketPrice": 194.97,
"marketState": "POST",
"shortName": "NVIDIA Corporation",
"longName": "NVIDIA Corporation",
"epsTrailingTwelveMonths": 6.53,
"epsForward": 12.76443,
"epsCurrentYear": 8.96197,
"priceEpsCurrentYear": 21.75526,
"sharesOutstanding": 24221000000,
"impliedSharesOutstanding": 24221000000,
"bookValue": 8.07,
"fiftyDayAverage": 210.086,
"fiftyDayAverageChange": -15.115997,
"fiftyDayAverageChangePercent": -0.07195147,
"twoHundredDayAverage": 190.6402,
"twoHundredDayAverageChange": 4.3298035,
"twoHundredDayAverageChangePercent": 0.022711912,
"marketCap": 4722368446464,
"forwardPE": 15.274478,
"priceToBook": 24.159853,
"sourceInterval": 15,
"exchangeDataDelayedBy": 0,
"prevName": "Usual Stablecoin",
"nameChangeDate": "2026-06-29T00:00:00.000Z",
"averageAnalystRating": "1.3 - Strong Buy",
"tradeable": false,
"cryptoTradeable": false,
"hasPrePostMarketData": true,
"firstTradeDateMilliseconds": "1999-01-22T14:30:00.000Z",
"priceHint": 2,
"postMarketChangePercent": -0.025646536,
"postMarketPrice": 194.92,
"postMarketChange": -0.05000305,
"regularMarketChange": 2.4400024,
"regularMarketDayHigh": 196.17,
"regularMarketDayRange": {
"low": 189.8,
"high": 196.17
},
"regularMarketDayLow": 189.8,
"regularMarketVolume": 146600013,
"regularMarketPreviousClose": 192.53,
"bid": 194.26,
"ask": 195.95,
"bidSize": 40,
"askSize": 40,
"fullExchangeName": "NasdaqGS",
"financialCurrency": "USD",
"regularMarketOpen": 193.85,
"averageDailyVolume3Month": 160883770,
"averageDailyVolume10Day": 151307270,
"fiftyTwoWeekLowChange": 43.479996,
"fiftyTwoWeekLowChangePercent": 0.28701562,
"fiftyTwoWeekRange": {
"low": 151.49,
"high": 236.54
},
"fiftyTwoWeekHighChange": -41.569992,
"fiftyTwoWeekHighChangePercent": -0.17574191,
"fiftyTwoWeekLow": 151.49,
"fiftyTwoWeekHigh": 236.54,
"fiftyTwoWeekChangePercent": 21.862137,
"dividendDate": "2026-06-26T00:00:00.000Z",
"earningsTimestamp": "2026-08-26T20:00:00.000Z",
"earningsTimestampStart": "2026-08-26T20:00:00.000Z",
"earningsTimestampEnd": "2026-08-26T20:00:00.000Z",
"earningsCallTimestampStart": "2026-05-20T21:00:00.000Z",
"earningsCallTimestampEnd": "2026-05-20T21:00:00.000Z",
"isEarningsDateEstimate": false,
"trailingAnnualDividendRate": 0.04,
"trailingPE": 29.85758,
"dividendRate": 1,
"trailingAnnualDividendYield": 0.00020775983,
"dividendYield": 0.52,
"displayName": "NVIDIA",
"symbol": "NVDA"
}
@@ -0,0 +1,23 @@
{
"summaryProfile": {
"address1": "2788 San Tomas Expressway",
"city": "Santa Clara",
"state": "CA",
"zip": "95051",
"country": "United States",
"phone": "408 486 2000",
"website": "https://www.nvidia.com",
"industry": "Semiconductors",
"industryKey": "semiconductors",
"industryDisp": "Semiconductors",
"sector": "Technology",
"sectorKey": "technology",
"sectorDisp": "Technology",
"longBusinessSummary": "NVIDIA Corporation operates as a data center scale AI infrastructure company in the United States, Taiwan, China, Hong Kong, Europe, and internationally. It operates through Compute & Networking, and Graphics segments. The Compute & Networking segment provides data center accelerated computing and networking platforms and artificial intelligence solutions and software, and automotive platforms and autonomous and electric vehicle solutions, including software. The Graphics segment offers GeForce GPUs for gaming and PCs; Quadro/NVIDIA RTX GPUs for enterprise workstation graphics. The company's products are used in gaming, professional visualization, data center, and automotive markets. It sells its products to original equipment manufacturers, original device manufacturers, system integrators and distributors, independent software vendors, cloud service providers, add-in board manufacturers, distributors, automotive manufacturers and tier-1 automotive suppliers, and other ecosystem participants. NVIDIA Corporation was incorporated in 1993 and is headquartered in Santa Clara, California.",
"fullTimeEmployees": 42000,
"companyOfficers": [],
"irWebsite": "http://phx.corporate-ir.net/phoenix.zhtml?c=116466&p=irol-IRHome",
"executiveTeam": [],
"maxAge": 86400
}
}