How Stock Demon collects data, generates intelligence, and monitors itself.
A personal AI-infrastructure market analysis system built on Cloudflare Workers + D1.
External data source
Compute / workers
Storage
Dashboard pages
Monitoring
📡 Financial Modeling Prep
Company profiles (price, volume, beta, range)
Historical daily prices (5-year backfill)
Fundamentals, market cap, analyst estimates
Earnings dates & EPS/revenue estimates
Price target consensus
REST API · polled on schedule, not real-time
🔍 Web Search
Per-ticker news search
Classified against market expectations
Lean: bullish / bearish / neutral
Horizon: near-term / long-term
The core thesis: judge news vs expectations, not in absolute terms
Trade guidance — model portfolio rebalance (weekly)
Pre-earnings ping — alerts when held names report soon
Weekly digest — narrative market synthesis
Guidance classifier — post-earnings guidance direction UNPROVEN LIVE
Ops health report — daily self-check
Each writes a heartbeat to job_runs on completion
🧠 Analysis Engine
Offline analysis, runs locally against exported data.
Backtester — mechanical signals over 4.5yr history
Earnings-surprise study — miss/beat excess returns
Knowledge repo — extracted lessons + params
Results written back to D1
Point-in-time discipline: 60-day reporting lag, no look-ahead
▼ ▼ ▼
🗄️ Cloudflare D1 — SQLite at the edge
Single database, all components read/write. ~20 tables across four domains:
Market Data
companies (universe)
price_snapshots (daily prices)
fundamentals_annual
market_cap_history
analyst_history
price_targets
Intelligence
news (expectations-aware)
alerts (deterioration flags)
earnings_radar
earnings_surprises
analyst_estimate_snapshots (new)
price_target_snapshots (new)
earnings_revisions (new)
weekly_digests
Portfolio & Backtest
live_positions / live_actions
live_equity (thesis scorecard)
backtest_runs / equity / trades
kb_lessons / kb_params
Operations
job_runs (heartbeats)
add_symbol_status
▼ ▼ ▼
📊 Dashboard — Server-Side Rendered
Every page is rendered from a single D1 read of stored data. The only client-side fetch is the Overview's live-price cell, which polls /api/quotes (see July 2026 additions).
Overview
Regime indicator · alerts panel · inventory by layer · news lean · signal disagreements
Morning (7 AM ET) — Cowork news scan searches the web for each ticker, classifies each headline's lean relative to market expectations (the core thesis), and writes to news. Earnings radar refreshes upcoming report dates.
2
Market close (5:30 PM ET) — Worker cron pulls closing prices from FMP for all active universe members, computes RVOL and 52-week range position, upserts price_snapshots. Supplements pull market cap and price targets.
R
Post-supplement (5:30 PM ET) — After snapshot tables are written, computeEarningsRevisions scans all prints in earnings_surprises, finds the nearest forward fiscal period with matching pre/post snapshots (post ≥ print + 3 days, same fiscal_date only), computes EPS/revenue/PT revision %, and upserts earnings_revisions. Idempotent — refines as better-bracketing snapshots arrive. COMMODITY
3
Post-close (6:01 PM ET) — Deterioration scan reads fresh prices, flags names meeting any of four conditions: ≥4% single-day drop, heavy-volume selling (RVOL ≥1.8 + down), near 52-week low (range_pos ≤0.12), or ≥8% drawdown from prior snapshot. Writes to alerts.
4
Monday evening (6:30 PM ET) — Trade guidance runs the model portfolio rebalance: scores each name mechanically (momentum + valuation + trend), applies separate news and surprise adjustments, logs every action with bull/bear cases. Runs after price pull + deterioration scan — rebalances on Monday's close with alerts in hand.
5
Sunday (5:10 PM ET) — Weekly digest synthesizes the week's movers, alerts, news, earnings, and portfolio actions into a narrative. Fundamentals refresh pulls annual financials and market cap history.
🆕 July 2026 Additions
Three subsystems added after the June revisions/guidance work.
🔀 Split back-adjustment
FMP writes the current post-split price into price_snapshots daily but never rewrites history, so a split leaves an N× discontinuity in the series.
applyRecentSplits() runs at the end of pullPrices, checks the FMP split calendar (14-day lookback), and back-adjusts every pre-split row (price ÷ ratio, volume × ratio)
Idempotent via the splits_applied table — each split is applied exactly once
/add-symbol seeds splits_applied for a new name's recent splits so its already-adjusted backfill isn't re-divided
Motivating case: KLAC 10:1 on 2026-06-12
🔴 Live "right now" prices
The board is otherwise a single D1 read of stored closes; the Live column adds intraday quotes.