fix: backfill symbol_demand for sidebar-added symbols + analyst ratings schema fix
- Add await ctx.cache.subscribe() to addSymbol mutation so symbols added via the sidebar get registered in symbol_demand and yfinance jobs are queued immediately - Backfill PEP, WYNN, STZ, CELH into symbol_demand + adapter_queue - Upgrade yahoo-finance2 3.15.3 -> 3.15.4 and pass validateResult:false to quoteSummary() to handle Yahoo schema drift - Add error detail logging for analyst ratings schema failures - Update .gitignore with common ignores
This commit is contained in:
+39
-2
@@ -6,7 +6,7 @@
|
||||
import { DatabaseSync } from 'node:sqlite';
|
||||
import { db as defaultDb } from '../db/client.ts';
|
||||
|
||||
export type SourceKind = 'yfinance' | 'sec' | 'sec-fetch' | 'reddit' | 'x' | 'macro' | 'llm' | 'sec-lint-holders' | 'sec-lint-insiders';
|
||||
export type SourceKind = 'yfinance' | 'sec' | 'sec-fetch' | 'reddit' | 'x' | 'macro' | 'llm' | 'sec-lint-holders' | 'sec-lint-insiders' | 'fred';
|
||||
export type TickerKind = 'equity' | 'crypto' | 'etf' | 'index';
|
||||
export type CacheKey = string; // `${SourceKind}:${kind}:${id}` e.g. 'yfinance:quote:NVDA', 'yfinance:candles:NVDA:1d'
|
||||
export type TtlClass =
|
||||
@@ -18,7 +18,7 @@ export interface Provenance { fetchedAt: string; sourceKind: SourceKind; rawSour
|
||||
|
||||
export interface Quote { symbol: string; price: number; bid?: number | null; ask?: number | null; change?: number | null; changePercent?: number | null; iv?: number | null; }
|
||||
export interface PriceCandle { ts: string; o: number; h: number; l: number; c: number; v: number; adjClose?: number | null; }
|
||||
export interface SymbolMeta { symbol: string; name?: string | null; sector?: string | null; industry?: string | null; exchange?: string | null; tickerKind: TickerKind; peers?: string[] | null; }
|
||||
export interface SymbolMeta { symbol: string; name?: string | null; sector?: string | null; industry?: string | null; exchange?: string | null; tickerKind: TickerKind; peers?: string[] | null; description?: string | null; website?: string | null; marketCap?: number | null; currency?: string | null; employees?: number | null; country?: string | null; city?: string | null; }
|
||||
export interface PriceAdjustment { symbol: string; exDate: string; type: "split" | "dividend"; ratio: number }
|
||||
|
||||
/** Port CacheRepository depends on to schedule background refreshes. SourceAdapter/AdapterQueue satisfy this. */
|
||||
@@ -308,6 +308,8 @@ export interface CacheRepository {
|
||||
unsubscribe(symbol: string): Promise<void>;
|
||||
demandSet(): Promise<string[]>;
|
||||
getMany<T>(keys: CacheKey[]): Promise<Array<{ key: CacheKey; value: T | null; isStale: boolean }>>;
|
||||
/** Delete a cache entry by key (or, for wildcard keys ending in `:*`, all matching entries). */
|
||||
del(key: CacheKey): Promise<void>;
|
||||
}
|
||||
|
||||
export class CacheRepositoryImpl implements CacheRepository {
|
||||
@@ -376,6 +378,41 @@ export class CacheRepositoryImpl implements CacheRepository {
|
||||
return { key, value: e.value, isStale: e.isStale };
|
||||
}));
|
||||
}
|
||||
async del(key: CacheKey): Promise<void> {
|
||||
const { source, kind, id } = parseCacheKey(key);
|
||||
const d = this._db;
|
||||
switch (kind) {
|
||||
case 'quote':
|
||||
d.prepare('DELETE FROM quotes WHERE symbol=?').run(id);
|
||||
break;
|
||||
case 'candles': {
|
||||
const [symbol, tf] = id.split(':');
|
||||
d.prepare('DELETE FROM price_candles WHERE symbol=? AND timeframe=?').run(symbol, tf);
|
||||
break;
|
||||
}
|
||||
case 'symbol':
|
||||
d.prepare('DELETE FROM symbols WHERE symbol=?').run(id);
|
||||
break;
|
||||
case 'adjustments':
|
||||
d.prepare('DELETE FROM price_adjustments WHERE symbol=?').run(id);
|
||||
break;
|
||||
case 'chain':
|
||||
case 'greeks': {
|
||||
const [symbol, expiry] = id.split(':');
|
||||
d.prepare('DELETE FROM options_chains WHERE symbol=? AND expiry=?').run(symbol, expiry);
|
||||
break;
|
||||
}
|
||||
case 'expiry_dates':
|
||||
d.prepare('DELETE FROM kv_cache WHERE key=?').run(`options_expiry:${id}`);
|
||||
break;
|
||||
default: {
|
||||
// Unknown/wildcard kind (e.g. `x:cashtag:*`): best-effort delete from kv_cache via LIKE.
|
||||
const like = key.endsWith(':*') ? `${key.slice(0, -1)}%` : key;
|
||||
d.prepare('DELETE FROM kv_cache WHERE key LIKE ?').run(like);
|
||||
}
|
||||
}
|
||||
void source;
|
||||
}
|
||||
}
|
||||
|
||||
export function createCacheRepository(opts: { db: DatabaseSync; scheduler: CacheScheduler }): CacheRepository {
|
||||
|
||||
Reference in New Issue
Block a user