ingest_live_minute_bars() +
ingest_daily_history()
ResearchWatchlistProvider.get_candidates()
Watcher.tick()
evaluate_setup()
Watcher emits signal_ready
TradingExecutionService.tick()
Dashboard or --auto-approve
AlpacaPaperExecutor.submit()
AlpacaPaperSync.sync_account()
Dashboard (SSE stream)
| Module | Responsibility | Key Files |
|---|---|---|
| research/ingestion | Ingest market data (bars, daily history), discover candidates, classify market sessions | market_data.py, watcher_task.py, scheduler.py |
| research/ | Query and project research databases (minute/daily bars, gappers, RSS news) | query.py, schema.py, multi_schema.py |
| runtime/watcher | Symbol state machine: discovered → watching → ready | blocked. Emit all watcher events. | watcher.py |
| strategy/evaluation | Pure setup evaluation: 9 criteria (gap%, RVOL, bull-flag, structure, quality, etc.) | setup_evaluator.py |
| strategy/risk | Position sizing, risk per trade, reward multiple, daily loss circuit breaker | position_sizing.py |
| strategy/backtest | Intraday backtest engine: replay bars, simulate entry/exit, calculate P&L | engine.py |
| storage/event_store | Append-only event log (canonical source of truth). Correlate by session_id. | event_store.py, event_schema.py |
| storage/projections | Query event store for current state: approval queue, open positions, P&L, etc. | projections.py |
| alpaca_paper/ | Alpaca paper broker integration: order submission, position sync, account state | client.py, execution.py, sync.py |
| schwab/ | Schwab live integration (OAuth, market data, positions, orders, health) | oauth.py, market_data.py, positions.py, health.py |
| api/ | Dashboard JSON API + SSE streaming. Read-only projections served as REST + events. | main.py, static/ |
| trading_execution.py | Signal → sizing → approval → order pipeline. Manages armed entries & timeouts. | trading_execution.py |
| Event | Emitted By | Purpose |
|---|---|---|
symbol_discovered |
Watcher | New symbol added to watchlist |
symbol_state_changed |
Watcher | Symbol transitions: discovered → watching → ready | blocked |
criteria_evaluated |
Watcher (via evaluate_setup) | 9 setup criteria evaluated for symbol (pass/fail/not-evaluated) |
signal_ready |
Watcher | Symbol ready: ≥60% criteria passed. Debounced once per session. |
signal_blocked |
Watcher | Symbol failed criteria check. Resets debounce. |
order_approval_requested |
TradingExecutionService | Ready signal → sizing + risk checks passed. Awaiting approval or auto-submit. |
order_approved |
Dashboard (manual) or TradingExecutionService (auto) | Approval given. Next: submit to broker. |
order_rejected |
Dashboard (manual) or TradingExecutionService (risk rule) | Approval denied or risk rule triggered. Signal dequeued. |
order_submitted |
AlpacaPaperExecutor | Bracket order sent to Alpaca (entry + target + stop). |
account_positions_updated |
AlpacaPaperSync | Open positions snapshot from Alpaca. Includes entry, qty, P&L. |
account_orders_updated |
AlpacaPaperSync | Pending orders snapshot from Alpaca. Tracks fills, cancellations, expirations. |
account_summary_updated |
AlpacaPaperSync | Account balance, buying power, margin, NAV, realized P&L. |
| Symptom | Likely Cause | Check |
|---|---|---|
| No symbols appearing in Watcher | Watchlist provider not returning candidates | ResearchWatchlistProvider.get_candidates() → is research DB populated? Check data/research/market.duckdb |
| Symbols discovered but never go ready | Criteria evaluation failing or threshold too high | Watcher → criteria_evaluated events. Check setup_evaluator.py scores. Lower ready_score_pct or min_quality config. |
| Signal ready but no approval request | TradingExecutionService.tick() not running or risk rule triggered | Check trading_execution.py: max_concurrent_positions, max_daily_loss_pct. Emit risk_rule_triggered? Check event store. |
| Approval request but order never submitted | Dashboard approval not clicked OR auto-approve not set | Dashboard approval queue visible? Or set TRADING_AUTO_APPROVE=1 in .env |
| Order submitted but no fills from Alpaca | API keys missing or Alpaca API down | Check ALPACA_API_KEY / ALPACA_SECRET_KEY in .env. Run --once to test without loop. |
| Dashboard not updating (SSE fails) | Network drop or event stream blocked | Dashboard falls back to polling. Check http://127.0.0.1:8010/api/events (should return JSON). Check browser console. |
| Backtests showing different P&L than live | Bar data mismatch or backtest engine using different fills | Backtest engine simulates limit fills at exact prices. Live uses actual Alpaca fills. Compare bars in research DB vs Alpaca API. |
data/momentum_events.duckdb — append-only events, indexed by timestamp + session_id
data/research/market.duckdb — minute bars + daily bars ingested from Alpaca
data/schwab_tokens.json — OAuth tokens (git-ignored, 0600 perms)
.env — API keys, trading settings (git-ignored)