Files
investor-flow/app/server/scripts/backfill-stock-float.ts
T
Investor Flow Build ac94acf9e3
CI / Test & Type-Check (push) Canceled after 0s
feat: dealer flow, mirror portfolio (M21), options convexity, FINRA short interest, alert producers, vendor gate
Snapshot of in-progress module work across multiple slices:

- Dealer Flow: dealerExposureEngine, dealerMapService, dealerMapExplain,
  dealerMapIntegrity, dealerMapReplay, dealerStudyEngine, hanStyleLevels
- Mirror Portfolio (M21): fundRepository, captureIngest, mirrorAlertProducers,
  fund holdings strip, live book, position capture ingest
- Options: BSM, NormalizedOptionSurface types, OptionsChainRouter,
  ConvexityGate, option legs panel
- Alert producers: vixLevel, rotation, thesis, unlock, portfolioRisk,
  mirror (fund_capture, fund_13f, mirror_diff)
- FINRA short interest adapter + queue integration
- SEC company tickers adapter + ingest (symbol search index seed)
- Vendor gate (rate-limit-first data plane, ADR-0009)
- CUSIP registry, reverse 13F refresh, stock float service
- LRU cache, portfolio backtest engine
- Frontend: dealer-flow, funds, journal, lab, monitor, plan, portfolio,
  reports, screener, strategies, theses, guided-start, exits, more pages
- Volume profile, workspace profile, visibility-aware poll
- ADRs 0010 (mirror math not advice), 0011 (symbol search index)
- VENDOR_INTEGRATIONS.md, END_USER_TEST.md
- .gitignore: exclude DBs, .DS_Store, local config, agent scratch
2026-08-10 13:36:26 -04:00

78 lines
3.0 KiB
TypeScript

#!/usr/bin/env npx tsx
/**
* CLI: backfill stock_float table with sharesOutstanding / floatShares from Yahoo Finance.
*
* Usage (from app/server):
* npx tsx scripts/backfill-stock-float.ts
* npx tsx scripts/backfill-stock-float.ts --symbol IREN # single symbol
* npx tsx scripts/backfill-stock-float.ts --all # all symbols in DB
*
* Reads tracked fund positions and 13F filings to find symbols needing float data.
* Respects vendor rate limits; skips entries fetched within the last 24h.
*/
import { DatabaseSync } from 'node:sqlite';
import { resolve, dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { fetchAndPersistFloat, refreshAllStockFloats } from '../src/services/stockFloatService.ts';
const __dirname = dirname(fileURLToPath(import.meta.url));
const SERVER_ROOT = join(__dirname, '..');
const DB_PATH = resolve(SERVER_ROOT, 'data', 'investor-flow.db');
function arg(name: string): string | undefined {
const i = process.argv.indexOf(name);
if (i < 0 || i + 1 >= process.argv.length) return undefined;
return process.argv[i + 1];
}
async function main() {
const symbolArg = arg('--symbol');
const forceFlag = arg('--force') === 'true' || arg('--force') === '';
console.log('Backfilling stock float data from Yahoo Finance...');
console.log(`DB: ${DB_PATH}`);
if (symbolArg) console.log(`Symbol filter: ${symbolArg.toUpperCase()}`);
if (forceFlag && !symbolArg) console.log('Mode: force refresh all symbols');
const db = new DatabaseSync(DB_PATH);
try {
let result;
if (symbolArg) {
// Single symbol backfill.
console.log(`Fetching float for ${symbolArg.toUpperCase()}...`);
await fetchAndPersistFloat(db, symbolArg.toUpperCase());
const row = db.prepare(
`SELECT shares_outstanding, float_shares FROM stock_float WHERE symbol = ? ORDER BY as_of DESC LIMIT 1`
).get(symbolArg.toUpperCase()) as { shares_outstanding: number | null; float_shares: number | null } | undefined;
console.log(` sharesOutstanding: ${row?.shares_outstanding ?? 'null'}`);
console.log(` floatShares: ${row?.float_shares ?? 'null'}`);
result = { refreshed: 1, skipped: 0, errors: [] };
} else if (forceFlag) {
// Force refresh all symbols regardless of TTL.
result = await refreshAllStockFloats(db, { forceRefresh: true });
} else {
// Default: refresh stale entries only.
result = await refreshAllStockFloats(db);
}
console.log('\nDone.');
console.log(` Refreshed: ${result.refreshed}`);
console.log(` Skipped (fresh): ${result.skipped}`);
if (result.errors.length > 0) {
console.log(`\nErrors (${result.errors.length}):`);
for (const err of result.errors.slice(0, 10)) console.log(` - ${err}`);
if (result.errors.length > 10) console.log(` ... and ${result.errors.length - 10} more`);
}
} finally {
db.close();
}
}
main().catch((err) => {
console.error('Backfill failed:', err);
process.exit(1);
});