Technical Reference
Complete source-level documentation of the Intellect FinLab architecture. Every module, class, interface, database schema, and execution pathway β documented at engineering depth with flow diagrams.
Intellect FinLab is a two-process system: a Python trading engine (main.py) and a FastAPI backend (backend/app/main.py). Both share one async event loop via nest-asyncio. The IBKR API (ib_async) runs on the same loop β zero thread handoff, zero latency penalty on the critical execution path.
Config β backend/data/config.db (5s TTL cache). Trade truth β backend/runtime/intellect_finlab_next.db. Wealth state β runtime/paper/intellect.db or runtime/live/intellect.db. Paths are never hardcoded β always resolved via Settings properties on the Pydantic settings class.
Project Layout
| Path | Purpose |
|---|---|
main.py | Trading engine entry point β constructs all modules, starts single event loop |
backend/app/main.py | FastAPI app factory + build_container() β dependency injection root |
modules/scanners/ | 80+ technical scanners inheriting BaseScanner |
modules/strategy/ | Signal pool, Hunter Loop, rule evaluator, score engine |
modules/execution/ | Order executor, position tracker, watchdog, order state |
modules/risk/ | Risk manager, loss protection, rapid/day shields |
modules/options/ | Option chain loader, greek cache, delta strike picker |
modules/wealth_model/ | Wealth DB, manager, trade diary, finalization |
modules/alerts/ | Email + Telegram alert manager and templates |
backend/app/services/ | Business logic β one service per domain (40+ services) |
backend/app/api/routes/ | FastAPI routers β thin, delegate to services |
backend/app/workers/ | Background threads: sync, flow, scanner, delta ladder |
backend/app/bridges/ | Legacyβmodern integration: LifecycleEventBridge, TradeDiaryBridge |
backend/app/core/ | Settings, enums, interfaces, trade models, state machine |
frontend/src/ | React 19 + TypeScript operator dashboard |
| Layer | Technology | Version | Role |
|---|---|---|---|
| Runtime | Python | 3.11+ (3.13 rec.) | Trading engine, backend API |
| API Framework | FastAPI | β₯ 0.115 | REST + WebSocket, ORJSONResponse default |
| IBKR Bridge | ib_async | β₯ 2.1 | Interactive Brokers async API β event-driven |
| Async Loop | nest-asyncio | latest | Single event loop shared by IBKR + FastAPI + engine |
| ASGI Server | Uvicorn | β | Production-grade async HTTP/WS server |
| Validation | Pydantic v2 + pydantic-settings | v2 | Settings, schema validation, all request/response models |
| Persistence | SQLite | β | Config DB, lifecycle DB, wealth DB (3 separate files) |
| Analytics | pandas / numpy / pyarrow | β | Market data processing, backtest engine, analytics |
| Config | PyYAML | β | YAML fallback configs (DB is runtime source of truth) |
| Frontend | React 19 + TypeScript | React 19 | Operator dashboard β full real-time UI |
| Build | Vite 6 | 6 | Frontend dev server and production build |
| State | Zustand | β | Global frontend live state (WebSocket data) |
| Data Fetching | TanStack Query v5 | v5 | REST data fetching, caching, background refresh |
| Charts | uPlot | β | High-performance financial charting |
| Testing (Python) | pytest + hypothesis | β₯ 8.0 / β₯ 6.0 | Unit + property-based tests |
| Testing (Frontend) | Vitest + fast-check | β | Frontend unit + property-based tests |
This is the foundational performance guarantee: scanner β fill confirmation in ~35β40ms because there are no thread handoffs on the hot path. nest-asyncio allows the IBKR event-driven API and Uvicorn to share the same loop.
import nest_asyncio
nest_asyncio.apply() # allows IBKR + FastAPI to share one loop
# ib_async and Uvicorn run on THE SAME asyncio event loop
# Fill events arrive via ib_async callbacks β dispatched immediately
# No thread.Queue, no sleep() calls on the critical path
# Measured: scanner fire β IBKR order submit β 12ms
# Measured: order submit β fill confirm β 23ms (total β 35ms)SignalPool._publish_event is a threading.Event that fires on every publish(). The Hunter Loop calls wait_for_signal(timeout=1.0) β it wakes instantly when a scanner fires, eliminating the 1-second polling latency of the legacy design. OrderStatusCache provides O(1) order status lookups, replacing slow ib.openTrades() full scans.
Key Async Patterns
RuntimeSyncWorker calls notify_tick() on every IBKR bar event. This wakes the engine from its event wait β no busy polling. The event loop returns to idle between ticks.EventBus dispatches state change events (fills, cancels, position updates) to subscribers without polling. Subscribers register callbacks; the bus fires them synchronously on the event loop.SafeLock is an async re-entrant lock that prevents concurrent entry AND exit operations for the same instrument. Critical for preventing double-entry when scanner fires during an active close.Cooldown Resolution Priority
config_scanner.cooldown β runtime configurable, takes precedence_SCANNER_COOLDOWN_OVERRIDE dict in scanner class_COOLDOWN_MAP: 5mβ300s Β· 15mβ900s Β· 1hrβ3600sMarket Hours Logic per Instrument
| Instrument | Trading Session | Gate Method |
|---|---|---|
| ES / NQ (Futures) | Sun 6PM β Fri 5PM ET (except 5β6PM daily maintenance) | _check_es_session() |
| SPX / VIX | 9:30AM β 4:00PM ET weekdays only | RTH gate |
| SPY / QQQ | 4:00AM β 8:00PM ET weekdays | Extended hours |
Production Scanner Inventory
Scanner Category Map
| Category | Logic Type | Representative Scanners |
|---|---|---|
| Fibonacci | State-based β fires while price is in fib zone | SPX_FIB_1HR_CALL, ES_FIB_CALL_1HR |
| EMA Cross | Cross-based β fires on the actual cross bar | SPX_V3_CALL, ES_CALL_V3, NQ_CALL_V3 |
| MACD | Cross-based β MACD signal line cross | SPX_10MIN_MACD_UP, SPX_1HR_MACD_UP |
| Momentum / SMI | State-based β SMI Ergodic level threshold | SPX_10MIN_MOMENTUM_UP, SPX_4HR_STOCHASTIC_MOMENTUM |
| ADX / Trend | Threshold β ADX strength gate | SPX_ADX_5M, ES_ADX_5M |
| RSI | Threshold β RSI level reversal signal | SPX_15M_RSI_OVERSOLD |
| Zone Breakout | State β price outside consolidation box | SPX_ZONE_BREAKOUT_CALL, ES_ZONE_BREAKOUT |
| Trifecta | Multi-condition AND β EMA21 + MACD + momentum | SPX_EMA21_TRIFECTA_CALL/PUT |
# publish β called from BaseScanner.run() after all 4 gates pass
pool.publish("SPX_FIB_1HR_CALL", {"direction": "CALL", "price": 5800, "score": 87})
# get β strategy engine checks freshness window
entry = pool.get("SPX_FIB_1HR_CALL", max_age_seconds=300)
if entry: # signal is live and within age window
direction = entry.data["direction"]
# wait_for_signal β event-driven wake (REQ-HFT: no polling)
arrived = pool.wait_for_signal(timeout=1.0) # blocks until publish() fires
# _publish_event: threading.Event fires on every publish()
# Hunter Loop wakes INSTANTLY β eliminates 1s polling latency| Field | Type | Purpose |
|---|---|---|
strategy_id | str | Unique identifier, matches DB row primary key |
scanner_patterns_call | list[str] | Scanner names that trigger a CALL entry |
scanner_patterns_put | list[str] | Scanner names that trigger a PUT entry |
scanner_confirmation_window_minutes | int | Multi-scanner confirmation window (default 5 min) |
veera_rules_enabled | bool | Master toggle for Veera Rules market-regime pre-filter |
target_profit_pct | int | Exit target percentage (23 = +23%) |
shield | str|None | "rapid_shield" or "day_shield" or None (global LP) |
wealth_model | str|None | Capital plan override (e.g. "WM235K") |
hunterloop | dict | {"mode":"context_plus_trigger","engine":"..."} |
priority | int | Lower = evaluated first (1 = highest priority) |
enabled | bool | Active in evaluation cycle β toggle without deleting |
| Rule Field | Condition Checked | Timeframe | Default |
|---|---|---|---|
veera_rule_spy_ema_enabled | SPY EMA21 > EMA200 | 5m | OFF |
veera_rule_vix_ema_enabled | VIX EMA5 < EMA12 (vol falling) | 5m | OFF |
veera_rule_macd_enabled | MACD Fast/Slow cross aligned | 10m | OFF |
veera_rule_ema_slope_enabled | EMA21 slope positive | 5m | OFF |
veera_rule_adx_5m_enabled | ADX > 20 (trend strength confirmed) | 5m | OFF |
veera_rule_vix_21_200_enabled | VIX EMA21 vs EMA200 regime | 5m | OFF |
veera_rule_stoch_10m_enabled | Stochastic direction aligned | 10m | OFF |
veera_rule_es_5_12_10m_enabled | ES EMA5 / EMA12 cross | 10m | OFF |
veera_rule_nq_5_12_10m_enabled | NQ EMA5 / EMA12 cross | 10m | OFF |
veera_rule_qqq_5_12_10m_enabled | QQQ EMA5 / EMA12 cross | 10m | OFF |
All Veera Rules default to OFF so strategies work out-of-the-box. They are additive filters β turn on only the rules that match your market regime thesis. Rules are evaluated as AND logic: all enabled rules must pass for the trade to proceed.
| Mode | DB Value | Behaviour |
|---|---|---|
| Standard | "standard" | Immediate entry on strategy pass β no additional confirmation required |
| Context + Trigger | "context_plus_trigger" | Requires a context signal AND a separate trigger signal within the confirmation window. Higher conviction, fewer entries. |
| Module | File | Purpose |
|---|---|---|
DeltaStrikePicker | modules/options/delta_strike_picker.py | Selects strike closest to configured delta target |
GreekCache | modules/options/greek_cache.py | Real-time IV, delta, theta, gamma β cached per tick |
OptionChainLoader | modules/options/option_chain_loader.py | Requests full option chain from IBKR on demand |
LiquidityFilter | modules/execution/liquidity_filter.py | Filters illiquid contracts β bid-ask spread + volume |
ExpiryUtils | modules/options/expiry_utils.py | 0DTE / weekly / monthly expiry calculation |
ESConidResolver | modules/options/es_conid_resolver.py | Resolves ES futures contract IDs for quarterly rolls |
# Step 1: Pre-validate price against IBKR tick rules
# Step 2: Pre-validate position size from PortfolioCache (O(1))
# Step 3: Pre-build new order object β no broker call yet
# Step 4: Cancel old SELL + place new β back-to-back, no sleep
# Step 5: Event-driven cancel confirm via OrderStatusCache (max 100ms)
new_order_id = loss_protection._atomic_replace_sell(
contract, new_price=5.17, label="t1_target_replace"
)
# Total atomic window: <150ms guaranteed by OrderStatusCache event| Service / Module | Purpose |
|---|---|
OrderExecutor | Place, modify, cancel orders via ib_async β single entry point |
OrderStatusCache | O(1) order status lookup β replaces slow ib.openTrades() scans in hot path |
V2FillDispatcher | Routes IBKR fill events β lifecycle bridge β DB write |
V2PositionWatchdog | Daemon thread β monitors open position, breakeven modify, trail stops |
V2OrderStateTracker | Tracks order state transitions in memory for fast lookup |
OrderCancelService | Safe cancel with CancelCause classification (LP / manual / expire) |
OrderCancelRegistry | Immutable record of every cancel with cause and timestamp |
OrderFailureTracker | Classifies and records all order failures β root cause + derived |
StartupReconciliation | Reconciles open positions with lifecycle DB on engine restart |
SafeLock | Async lock preventing concurrent entry/exit for same instrument |
| Rule | Name | Action Taken | Notes |
|---|---|---|---|
| Rule 1 | Profit Target | Limit sell placed at entry; monitored by LP daemon | T1 / T2 / T3 ladder, atomic replace on target adjust |
| Rule 2 | Grace Period Gate | Protects sell order from modification for N minutes | Default 15 min; V2PositionWatchdog handles breakeven after |
| Rule 3 | Unclosed Alert | Fires alert every N minutes if position remains open | Default 30-min interval on CRITICAL alert lane |
| Rule 4 | 3:30PM Handling | Three modes: modify_to_entry, alert_only, force_close | Configurable per environment β default: modify_to_entry |
| Rule 5 | EOD Force Close | Market sell at eod_close_time β non-negotiable | Default 15:55 ET β no exceptions, no overrides |
| Rule 6 | Price Feed Emergency | Market sell after N consecutive zero price readings | Default: 8 consecutive zeros β prevents stale-data hold |
Polling Schedule
| Shield | DB Value | Behaviour | Best For |
|---|---|---|---|
| Rapid Shield | "rapid_shield" | Arms within seconds of fill. Triggers on intraday drawdown threshold (default 22% loss). Immediate market exit. | Breakout strategies β fast adverse moves |
| Day Shield | "day_shield" | Session-level daily loss ceiling. Gates all new entries once daily drawdown is reached. Parks engine in observation mode. | Multi-entry strategies per session |
| None (Global LP) | null | Falls back to global LP rules in stoploss_config table | Default for all strategies |
pool publishes
Veera Rules Β· Hunter
GreekCache
ladder level
market order
IBKR fill event
LP armed
exit fill event
immutable audit
ws/wealth fires
Every state transition writes to the lifecycle DB before proceeding. On restart, StartupReconciliation reads the DB and resumes from the last committed state. No orphaned positions, no missed closes β the engine always knows where each trade is.
| Table | Purpose | Key Columns |
|---|---|---|
trades | One row per trade β full lifecycle | trade_id, symbol, direction, state, buy_fill_price, sell_fill_price, pnl, strategy_id |
trade_events | Audit trail β every state transition | trade_id, event_type, timestamp, data_json |
order_fills | Raw IBKR fill records | order_id, fill_price, fill_time, commission |
position_snapshots | Point-in-time greek snapshots | trade_id, snapshot_time, delta, theta, gamma, iv |
cancel_registry | Every cancel with cause classification | order_id, cancel_cause, timestamp, context |
order_failures | All order failures with root/derived cause | order_id, root_cause, derived_cause, context |
| Model | Starting Capital | Annual Target | Ladder Levels |
|---|---|---|---|
| WM235K | $35,000 | ~$500K | trade_start β T1 β T2 β T3 (weekly milestones) |
| WM2310K | $10,000 | ~$150K | Aggressive compounding for smaller accounts |
| WM2320K | $20,000 | ~$300K | Mid-tier β balance of growth and risk |
@dataclass
class Container:
lifecycle_repo: LifecycleRepository
config_repo: ConfigRepository
trade_lifecycle_svc: TradeLifecycleService
runtime_control_svc: RuntimeControlService
wealth_svc: WealthService
scanner_svc: ScannerService
analytics_svc: AnalyticsService
# ... 35+ more services
def build_container() -> Container:
# Wire all dependencies once at startup
# Never instantiate services directly in routes
...Routes are thin β they receive services via FastAPI DI and delegate immediately. All business logic lives in services. Workers call services on a timer or event. This ensures testability, single responsibility, and no circular imports.
| Route Domain | File | Key Endpoints |
|---|---|---|
| Lifecycle / Trades | routes/lifecycle.py | GET /trades, GET /trades/{id}, GET /trade-story/{id} |
| Config | routes/config.py | GET/PUT /config/:domain β all config domains |
| Wealth | routes/wealth.py | GET /wealth/state, GET /wealth/diary, GET /wealth/plan |
| Scanners | routes/scanners.py | GET /scanners, GET /scanners/status, PUT /scanners/{name}/toggle |
| Analytics | routes/analytics.py | GET /analytics/session, GET /analytics/leaderboard |
| Portfolio | routes/portfolio.py | GET /portfolio/positions, GET /portfolio/risk |
| Backtest | routes/backtest.py | POST /backtest/run, GET /backtest/results, POST /backtest/ai-discover |
| System | routes/system.py | GET /health, POST /system/start, POST /system/stop |
| Alerts | routes/alerts.py | GET /alerts/history, POST /alerts/test |
| Channel | URL | Events Pushed |
|---|---|---|
| Orders / Trades | ws://host/ws/orders | BUY_SUBMITTED, BUY_FILLED, SELL_SUBMITTED, SELL_FILLED, CLOSED |
| Live Flow | ws://host/ws/flow | SCANNER_FIRE, STRATEGY_PASS, RISK_BLOCK, HUNTER_CONFIRMED |
| Trade Center | ws://host/ws/trade-center | Full trade story updates, position mark-to-market |
| Wealth | ws://host/ws/wealth | Capital updates, milestone reached, ladder level change |
| System Events | ws://host/ws/system | Health status, IBKR connection, gate state, engine status |
| Alerts | ws://host/ws/alerts | Telegram/email dispatch events, alert delivery status |
| Worker | Interval | Responsibility |
|---|---|---|
RuntimeSyncWorker | IBKR tick events | Heartbeat β syncs IBKR state to RuntimeStore, fires notify_tick() |
FlowWorker | 500ms | Processes LiveFlow events, dispatches to ws/flow channel |
DeltaLadderWorker | 60s | Pre-activates ES, SPX, NQ symbols for fast greek access |
PromotionSyncWorker | Startup + 5min | Promotes un-promoted legacy diary entries to lifecycle DB |
BrokerTradeReconciler | Startup +8s | Reconciles TWS fills with lifecycle DB on startup |
DBHygieneWorker | Daily | Prunes stale order cache entries, compacts DBs |
Config DB β backend/data/config.db
| Table | Purpose |
|---|---|
config_options | Per-instrument settings (delta targets, entry windows, gamma bounds) |
config_scanner | Per-scanner settings (enabled, cooldown, email, telegram) |
config_scanner_global | Master scanner switches (email_all, telegram_all, es_only_mode) |
config_veera_strategies | All strategy definitions β patterns, rules, hunter mode, shield |
config_meta | System-level config (telegram tokens, environment, market gate) |
stoploss_config | Global LP settings (grace period, EOD time, poll intervals) |
wealth_config | Active wealth model name, current week, capital state |
Lifecycle DB β backend/runtime/intellect_finlab_next.db
| Table | Purpose |
|---|---|
trades | One row per trade β full lifecycle state, fill prices, PnL |
trade_events | Immutable audit trail β every state transition with timestamp |
order_fills | Raw IBKR fill records including commission |
Wealth DB β runtime/paper/intellect.db or runtime/live/intellect.db
| Table | Purpose |
|---|---|
wealth_state | Current capital, week number, ladder level, PnL to date |
trade_diary | Per-trade record with greeks at entry, hold time, close reason |
weekly_summary | Week-over-week capital progression vs plan |
| Lane | Channel | Events |
|---|---|---|
| Main Bot | Telegram Bot 1 | Trade fills (buy/sell), LP events, session summaries, market gate changes |
| Scanner Bot | Telegram Bot 2 | Scanner fire events β separate channel keeps main bot clean |
| SMTP | HTML emails: order_buy, order_sell templates with full greek table | |
| In-App | WebSocket ws/alerts | All events immediately visible in dashboard Live Feed |
| Component | Purpose |
|---|---|
| Scanner Leaderboard | Runs all 80+ scanners against historical data β ranked by TQS (Trade Quality Score) |
| Rule Builder | Custom strategy builder β compose 15+ indicator rules (EMA, RSI, ADX, VWAP, MACD) |
| Equity Curve | Trade-by-trade equity curve with drawdown overlay |
| Wealth Replay | Simulate full WM plan progression against backtest results |
| AI Discovery | Ollama LLM analyses signal combinations for high-conviction patterns |
| Onboard to Paper | Promotes validated backtest config directly to config DB as live strategy |