Class Map

Class Map

ArbitrageBot ← main orchestrator | flags: paused_today, _day_closed ├── TelegramCommandHandler ← long-polls getUpdates; syncs Telegram app commands to default + configured chat scopes; dispatches /help /status /safety /positions /risk /explain_signal /audit_positions /confirm_recon /trades /journal /note /pnl /balance /report /logs /ai_market /rerun_ai_market /close_smart /remove_trade /stop_today /resume /kill /override_limit /reset_override plus /golive_status /confidence_report /shadow /promote /revert; backs off 60s on 409 conflict │ ├── start() ← starts command-menu sync and spawns daemon poll thread on bot startup │ ├── stop() ← signals poll loop to exit on bot shutdown │ └── _dispatch() ← routes command string → handler; ignores non-CHAT_ID senders ├── TokenManager ← SQLite-backed Dhan access token lifecycle │ ├── refresh_if_due() ← token_refresher.py renews near-expiry tokens via /v2/RenewToken or PIN+TOTP fallback │ └── reload_from_store() ← bot reloads externally refreshed tokens from SQLite before SDK refresh ├── AutonomousMarketAnalyst ← scheduled 09:05 premarket thesis, 09:20 live confirmation, 15:25 scoring, and OTP-confirmed manual reruns; stores stage-linked rows, token cost, and optional dry-run trade │ ├── run_analysis(manual_rerun=False) ← premarket thesis; skips when disabled, missing OPENAI_API_KEY, non-trading day, or same-day premarket row already exists │ ├── run_confirmation(manual_rerun=False) ← live spot/option-chain confirmation; can create the dry-run trade from the confirmed setup │ └── score_today_forecast() ← closes the daily loop with directional accuracy, Brier score, and excursion stats from intraday snapshots ├── TradeDebriefGenerator ← low-cost OpenAI post-trade debrief for closed trade journal rows; summarizes trade outcome, rejected-strategy delta, AI verdict outcome, and event risk │ └── latest_direction_hint() ← returns same-day BULLISH/BEARISH verdict for DDC weak-trend tie break ├── AIRunner ← OpenAI Responses wrapper; loads autonomous-market and confirmation prompts, requests strict JSON, records input/output tokens and estimated USD/INR cost ├── NSEHolidayCalendar ← cached NSE holiday helper; trading-day gate for AI analysis ├── DhanClient ← SDK wrapper + rate limiting │ ├── get_option_chain() ← respects OPTION_CHAIN_COOLDOWN (3.5 s) │ ├── get_ltp() ← batch LTP via ticker_data │ ├── place_order() ← unwraps v2.0.2 resp["data"], supports validity=IOC │ └── get_order_status() ← handles dict AND list from v2 API ├── BoxSpreadScanner ← finds profitable box opportunities │ └── scan(underlying) ← returns list sorted by net profit desc ├── CalendarSpreadScanner ← tracks near/far NIFTY futures spread │ ├── _resolve_futures_ids() ← downloads scrip CSV, caches to disk │ ├── scan() ← returns signal dict or None │ └── get_current_z_score() ← used by ExecutionEngine for exit checks ├── DoubleCalendarScanner ← scans for ATM straddle double calendar entry (NIFTY weekly/monthly); IV must be 15–25% │ ├── get_expiries() ← returns (near_expiry, far_expiry) tuple when DTE 3–7; None otherwise │ ├── _next_tuesday() ← nearest Tuesday strictly after today (NIFTY weekly expiry) │ ├── _last_tuesday_of_month() ← monthly expiry; rolls to next month if within 7d of near │ └── scan(open_trades) ← fetches both chains, checks IV + premium filters → signal dict or None ├── StretchedDoubleCalendarScanner ← extends DoubleCalendarScanner; 8-leg DCS (ATM DC + OTM wings) │ ├── get_expiries() ← overrides DC: DTE 7–10 window; looks ahead one week if nearest Tuesday is too close │ ├── _calc_wing() ← wing = round(spot × IV/100 × √(DTE/365) × 0.52 / 50) × 50 │ └── scan(open_trades) ← 3-layer guard (count cap ≤ 3, one-per-day, ATM+expiry dedup); IV/straddle filters; → 8-leg signal or None ├── DirectionalDiagonalScanner ← extends DoubleCalendarScanner helpers; 2-leg directional diagonal calendar │ ├── get_expiries() ← near weekly DTE 5–9 plus monthly far expiry │ ├── _capture_day_open() ← stores first valid intraday spot reading as trend anchor │ └── scan(open_trades, ai_direction_hint=None) ← trend threshold, optional AI weak-trend hint, IV, DTE, and OTM short-strike filters → DDC signal or None ├── IronCondorScanner ← extends DoubleCalendarScanner helpers; fallback 4-leg NIFTY weekly credit strategy │ ├── get_expiry() ← near Tuesday only when DTE is in IC_DTE_MIN..IC_DTE_MAX │ ├── scan(open_trades, realized_range) ← blocks when any primary options trade is open, including MDC_*; checks IV, range, IVP-adjusted credit, strikes │ └── _iv_bound() ← normalizes fractional IC config IV to percent convention ├── MasterDCScanner ← meta-strategy orchestrator; classifies regime each cycle and routes to the best DC-family scanner; tags trades MDC_* for persistence, exits, and reporting │ ├── _classify() ← trending / skewed / wide / calm / IC-fallback regime selection │ └── scan(open_trades) ← enforces MDC concurrency, fetches near-week chain, delegates, and prefixes strategy with MDC_ ├── strategies/ ← strategy framework (arb_bot/strategies/) — W5 fully live; all monitor/close/P&L/leg-ID/explain/icon dispatch routes through the registry for every live strategy │ ├── base.py ← Strategy interface: ScannerProtocol, TradeLifecycle, RiskProfile, StrategySpec │ ├── registry.py ← central validated registry; resolve() handles MDC_* prefixes; GREEK_STRATEGIES/OPTION_RISK_STRATEGIES/icon maps auto-derived from registered specs │ ├── dc.py ← DC spec (W3 pilot) │ ├── dcs.py ← DCS + DCS_SKEW specs sharing one 8-leg lifecycle (W4) │ ├── ddc.py ← DDC spec; ddc_pnl is already net → closing_cost 0 (W4) │ ├── ic.py ← IC spec; exit rule converts net P&L ↔ credit value (W4) │ ├── mdc.py ← MDC meta-spec: scan-entry dispatch only; trades resolve to sub-strategy specs; routes to sub-strategies via registry.get(sub) over the eligible set (any registered strategy with a scanner factory) │ └── ai_market.py ← AI_MARKET spec (W5); AIMarketLifecycle + AIMARKET_SPEC; dry-run single-leg NIFTY option entry and exit migrated onto the registry ├── IVSampler ← records one IV surface snapshot per scan cycle: near/mid/far term structure, bounded smile with raw-or-estimated IV, ATM IV, skew, straddles, DTEs, and realized range ├── RiskEnvelope ← pre-scanner gate (Phase 1 autonomous roadmap); enforces six guards before any new trade scan: daily kill switch, weekly/monthly/lifetime DD caps, loss-streak pause, and lot-size lock for first 30 live days │ ├── check_envelope() ← returns {status: HEALTHY|PAUSED|HALTED, reason, until}; called at start of run_scan_cycle() │ ├── update_after_close(pnl) ← increments daily/weekly/monthly/lifetime counters and loss-streak after each trade close │ ├── apply_pause() ← writes pause_until + pause_reason to risk_envelope_state; used by PAUSED triggers │ ├── resume() ← clears pause and loss streak; called by /resume_envelope confirm Telegram command │ └── lot_size_lock() ← returns 1 when <30 live days or lifetime P&L < LOT_UNLOCK_PROFIT; returns 0 to use strategy default ├── ExecutionEngine │ ├── preflight() ← checks balance; triggers reconcile once │ ├── GoLiveManager ← owns DRY_RUN / SHADOW / LIVE state, confidence gate, and 10% margin buffer checks │ ├── reconcile_positions() ← compares open_trades vs Dhan; auto-adopts orphan box spreads; alerts remaining unmatched legs │ ├── can_trade() ← kill switch + balance + daily limit + positions │ ├── _place_legs_ioc() ← anchor to scanner bid/ask + 0.2% drift buffer; retry once at LTP+0.8% │ ├── execute_double_calendar()← reads go-live state; dry-run simulates, shadow/live place real orders with margin check │ ├── execute_stretched_double_calendar()← reads DCS/SDCS state; shadow records SHADOW_DR_* close snapshot │ ├── execute_diagonal_calendar()← reads DDC state; dry-run simulates, shadow/live place far buy + near sell │ ├── execute_iron_condor()← reads IC state; dry-run simulates, shadow/live place hedge buys before short sells │ ├── execute_ai_market()← phase-1 AI_MARKET path; dry-run single NIFTY option trade only, mirrored in ai_market_trades │ ├── _close_double_calendar() ← closes 4 DC legs; dry-run logs + records net P&L │ ├── _close_stretched_double_calendar() ← closes 8 DCS legs; dry-run logs + records net P&L │ ├── _close_diagonal_calendar() ← closes DDC near short and far long; dry-run logs LTPs │ ├── _close_iron_condor() ← closes 4 IC legs; dry-run logs LTPs and records credit-spread P&L │ ├── _close_ai_market() ← closes dry-run AI_MARKET option, updates open_trades and ai_market_trades │ ├── monitor_open_positions() ← exit checks every 30s: BOX + CAL + DC + DCS + DCS_SKEW + DDC + IC + AI_MARKET; same-cycle re-entry blocked by bot.py. All active strategies (DC, DCS, DCS_SKEW, DDC, IC, AI_MARKET, incl. MDC_* tags) dispatch monitor/close/P&L/leg-IDs entirely through the strategy registry (arb_bot/strategies/); the legacy BOX/CALENDAR/RECOVERY/AI_MARKET elif chains were deleted in W5 │ ├── _get_nifty_spot() ← fetches NIFTY 50 IDX_I LTP; handles flat and segment-keyed responses │ ├── check_kill_switch() ← halts if daily P&L ≤ KILL_SWITCH_LOSS │ └── _save_trades() / _load_trades() ← compatibility wrappers around TradeStore ├── execution.persistence.TradeStore ← SQLite-backed open_trades load/save helper ├── execution.reconciliation.ReconciliationService ← scan-cycle auto-healer; removes confirmed-flat trades after 2 cycles, adopts known orphan strategy shapes, blocks strategies with pending qty mismatches, and writes reconciliation_audit rows ├── GoLiveManager ← SQLite-backed go_live_state load/save helper; confidence uses closed_trades table ├── execution.pnl ← pure P&L formulas for BOX, CALENDAR, RECOVERY, DC, DCS, DDC, IC, and AI_MARKET single-leg trades; active strategies (DC family + AI_MARKET) invoke P&L via spec.lifecycle through the registry ├── execution.double_calendar ← DC/DCS trade-record builders and close-leg specifications ├── execution.position_monitor ← DC/DCS/DDC/IC close-decision helpers with tested stop/target ordering ├── PartialFillRecovery ← triggered when _place_legs_ioc() aborts mid-spread ├── execute() ← classify, keep aligned legs, close opposing, arm trailing stop ├── predict_direction() ← two-gate: P2P ≥0.15% (Gate 1) + ≥60% tick consistency (Gate 2) → UP/DOWN/FLAT ├── _delta_sign() ← BUY CE=UP, SELL CE=DOWN, BUY PE=DOWN, SELL PE=UP └── _fresh_atm_order() ← open ATM option in trend direction if no legs kept ├── greeks.py ← pure-Python Black-Scholes Greeks (no scipy); uses math.erf for normal CDF │ ├── net_greeks_dc() ← Δ/Γ/Θ/ν for 4-leg DC at entry; stored as entry_greeks in trade dict │ ├── net_greeks_dcs() ← Δ/Γ/Θ/ν for 8-leg DCS at entry; stored as entry_greeks in trade dict │ └── _compute_live_greeks() ← live Greeks re-computed from current spot + IV for DC, DCS, and DCS_SKEW; shown in /positions command └── MetricsCollector ← collects intraday data + safety events; shared via .metrics on all subsystems ├── record_box_scan() ← funnel: raw pairs → filtered → executed ├── record_cal_scan() ← funnel: signals → filtered → executed ├── record_execution() ← per-trade outcome: filled/aborted, legs filled, expected profit ├── record_close() ← expected vs actual P&L, holding hours, close reason ├── record_safety_event() ← low balance / drift / stale / duplicate / orphan counters ├── save_json() ← writes daily metrics, executions, and closed trades to SQLite └── format_telegram() ← generates EOD Telegram report section ├── market/indices.py ← Performance Dashboard "Market Indices" tile grid (live NSE index LTP + day change% + 5-min sparkline) pushed over /ws/live on a decoupled timer │ ├── MarketIndexService ← batches one Dhan ticker_data per refresh; paces uncached intraday calls, caches candles server-side (MARKET_INDEX_INTRADAY_CACHE_SEC), and emits the index frame independently of trade-monitoring ticks │ └── IndexConfigStore ← persists the enabled-index list as JSON in the Postgres bot_settings table; read/written via GET/PUT /api/market/indices/config

Research subsystem (isolated)

arb_bot/research/strategies/ ← backtestable strategies; each ResearchStrategy spec exposes scan(market_data) → signal and lifecycle for entry/exit/P&L ├── momentum_quality ← Blends 12M−1M price momentum z-score with quality fundamentals z-score (ROE gate, D/E gate); balances trending strength with financial health ├── quality_alpha ← Screens on ROE/ROCE/D/E/margin/FCF thresholds, ranks by composite quality z-score, confirms uptrend with 200-SMA filter ├── value_trend ← Value stocks (E/P, B/P, S/P, dividend yield ≥ universe median; min 2 of 4 metrics) confirmed by 200-SMA price trend ├── pair_statarb ← Market-neutral pairs engine (PairBacktestEngine); trades same-sector cointegrated spreads via z-score entry/exit, dollar-neutral sizing, daily short-borrow cost, and daily rebalance cadence ├── options_vol_premium ← Strategy #14 — sells defined-risk iron condors when IV-rank is elevated; uses OptionsVolEngine with EOD bhavcopy pricing and Black-Scholes fallback (arb_bot/research/strategies/options_vol_premium.py) ├── CombinedStrategy ← (arb_bot/research/strategies/combined.py) Blends N member strategies by z-scoring each member's composite score cross-sectionally and weighting the blend; no new factor logic — composes existing strategies for diversified factor exposure (Mode A composite) ├── earnings_pead ← Strategy #8 — enters on positive earnings surprise (surprise_pct ≥ RESEARCH_PEAD_MIN_SURPRISE); holds up to RESEARCH_PEAD_HOLDING_DAYS via carry-forward signals; exits via engine's "sell anything not targeted" path when aged out (arb_bot/research/strategies/earnings_pead.py) └── event_driven ← Strategy #12 — enters on enabled corporate events (buyback, index_inclusion, pledge_reduction, block_deal); score = type_weight × value_num; holds up to RESEARCH_EVENT_HOLDING_DAYS; depends on manual event ingestion for non-earnings types (arb_bot/research/strategies/event_driven.py) arb_bot/research/engine/options_vol_engine.py: OptionsVolEngine ← Options vol-premium backtest engine — iron condor entry/exit, IV-rank signal, EOD bhavcopy pricing; isolated from live trading; lazy-imports arb_bot.backtest.simulation.option_pricer only for Black-Scholes fallback arb_bot/research/providers/ ← read-only data providers; never import arb_bot.client or place orders (enforced by tests/research/test_import_isolation.py + test_provider_safety.py) ├── dhan_data.py ← read-only Dhan historical-data accessor + instrument master for RESEARCH_DATA_PROVIDER=dhan; resolves symbols to security_ids and fetches OHLCV with no order surface │ ├── DhanHistoricalData ← wraps the dhanhq SDK exposing ONLY read methods; builds its own SDK client from TokenManager credentials when none is injected │ │ ├── historical_daily() ← Dhan /charts/historical daily OHLCV → date-indexed DataFrame (open/high/low/close/volume); provider callers normalize dates to YYYY-MM-DD before SDK calls │ │ ├── intraday_minute() ← intraday minute OHLCV (default 5-min interval) │ │ ├── ticker_data() ← batch last-traded-price read; grouped one call per segment; backs DhanProvider.get_ltp() │ │ └── ohlc_data() ← snapshot OHLC read │ └── InstrumentMaster ← lazily fetches Dhan's public compact security-list CSV; resolve(symbol) → (security_id, segment, instrument); accepts compact equity segment E while returning SDK segment NSE_EQ; benchmark aliases (^NSEI / ^NSEBANK / ^NSMIDCAP / ^CNXSML) map to IDX_I security_ids ├── dhan_provider.py: DhanProvider MarketDataProvider impl registered as "dhan"; resolves symbols via InstrumentMaster, fetches OHLCV via DhanHistoricalData, reuses the shared DB-backed cache (key includes the adjusted flag), and applies the AdjustmentProvider seam when adjusted=True ├── events_store.py upsert_events(rows) writes to research_events via ON CONFLICT … DO UPDATE; get_window(symbols, as_of_date, lookback_days) returns a dict of per-symbol event lists with no-lookahead gate (announce_date ≤ as_of_date) └── adjustments.py ← corporate-action adjustment seam for raw Dhan OHLC; default NoOpAdjustmentProvider returns raw-equivalent prices until a real NSE corp-action source is wired ├── AdjustmentProvider ← ABC: get_adjustments(symbol, start, end) → list[Adjustment] ├── NoOpAdjustmentProvider ← default; returns [] (raw passthrough) └── apply_adjustments(df, adjustments) ← back-adjusts pre-ex-date OHLC by each ratio (split+bonus combined); volume inverse-scaled

Interactive Architecture Explorer

Click any class to inspect its key methods. Press Trace Scan Cycle to animate the execution path.