Config Reference
All tunable parameters live in class Config in arb_bot/config.py.
Runtime-Editable Config
A subset of the knobs listed below — everything registered in
arb_bot/settings/registry.py — can be changed at runtime without a
code deploy or bot restart. The database stores only overrides; an
absent row means "use the code default." Changes take effect immediately via
Postgres LISTEN/NOTIFY (the listener thread in the running bot process
receives the notification and clears the in-memory snapshot instantly). For processes
that did not receive the notification, the snapshot is refreshed automatically on a
5-minute TTL. There is no outage window.
There are three ways to edit a runtime-editable knob:
-
Dashboard Settings page — navigate to
/settingsin the React dashboard. Knobs are grouped (Risk, Timing, DC, DCS, MDC, AI, Dry-Run). Each row shows the key, current effective value, code default, and description. Click a row to set a new value; the page validates bounds before saving. -
Telegram
/set KEY VALUE— e.g.,/set DC_PROFIT_TARGET_PCT 0.38. The bot validates the type and bounds, writes to DB, and broadcasts the change. -
Telegram
/config [group]— lists all runtime-editable knobs with their current effective value. Pass a group name (e.g.,/config DC) to filter the output.
arb_bot/settings/registry.py go through
the settings store. Everything else — API tokens, DB URLs, strategy-slot dicts,
enum constants — passes through the normal class attribute path with no DB access.
client_id, access_token, pin,
totp_secret, token expiry) are not Config
knobs and are never runtime-editable. They live Fernet-encrypted in the
brokers.broker_connections Postgres table, separate from both the
bot_settings runtime knobs above and the static .env
infrastructure secrets. They are seeded once via
scripts/seed_broker_connection.py and rotated by the token refresher.
See Brokers.
DC_DRY_RUN, DCS_DRY_RUN, SDCS_DRY_RUN, DDC_DRY_RUN, IC_DRY_RUN, MDC_DRY_RUN, and AI_MARKET_DRY_RUN are first-run or strategy-local safety defaults. Once go-live records exist, runtime state is changed with Telegram commands, not by rereading config flags. AI_MARKET remains dry-run-only in phase 1.
Capital & Risk
| Parameter | Default | Meaning |
|---|---|---|
PHASE1_CAPITAL | ₹8,00,000 | Capital allocated to this bot |
DEPLOYED_CAPITAL | ₹17,00,000 | Operator-deployed capital used by the autonomous roadmap risk envelope. |
KILL_SWITCH_LOSS | −₹10,000 | Daily loss limit before halting new trades. Also the envelope daily cap. |
WEEKLY_DD_CAP | −₹25,000 | Risk envelope rolling weekly drawdown cap. |
MONTHLY_DD_CAP | −₹50,000 | Risk envelope absolute monthly drawdown cap. |
LIFETIME_DD_CAP | −₹1,00,000 | Risk envelope hard cumulative drawdown halt. |
LOSS_STREAK_PAUSE_DAYS | 3 | Consecutive losing days that pause new entries until operator resume. |
LOT_LOCK_DAYS | 30 | Minimum live-trading age before strategies can use their default lot sizing. |
LOT_UNLOCK_PROFIT | ₹50,000 | Cumulative profit required, in addition to LOT_LOCK_DAYS, to unlock default lot sizing. |
CASH_BUFFER_PCT | 15% | Minimum undeployed cash fraction |
MAX_OPEN_POSITIONS | 10 | Max simultaneous positions |
MAX_DAILY_TRADES | 10 | Max orders per day (including reversals) |
PORTFOLIO_GREEKS_GATE_ENABLED | True | Master switch for the portfolio Greeks runtime gate before option entries. |
PORTFOLIO_BLOCK_DRY_RUN | False | When false, dry-run trades that would breach Greek limits still simulate so research data is preserved; SHADOW/LIVE entries are blocked. |
PORTFOLIO_MAX_ABS_DELTA | 130.0 | Maximum absolute portfolio delta after adding a candidate trade. |
PORTFOLIO_MAX_ABS_GAMMA | 0.35 | Maximum absolute portfolio gamma after adding a candidate trade. |
PORTFOLIO_MAX_ABS_THETA | 25,000 | Maximum absolute portfolio theta per day after adding a candidate trade. |
PORTFOLIO_MAX_ABS_VEGA | 25,000 | Maximum absolute portfolio vega per 1% IV move after adding a candidate trade. |
BROKER_HEALTH_ENABLED | True | Master switch for scan-cycle broker health sampling and runtime entry downgrades. |
BROKER_HEALTH_OPTION_CHAIN_LATENCY_MS | 2,500 ms | Option-chain latency threshold that degrades broker health and forces dry-run-only entries. |
BROKER_HEALTH_API_LATENCY_MS | 1,500 ms | Order, position, and margin API latency threshold that degrades broker health. |
BROKER_HEALTH_RECENT_LIMIT | 20 | Recent persisted health samples returned by /api/broker/health. |
IV_SNAPSHOT_RETENTION_DAYS | 180 | Prune cutoff, in days, for the iv_snapshots table. Enforced on every insert. |
BATTLE_LOG_RETENTION_DAYS | 60 | Prune cutoff, in days, for the strategy_battle_log table. Enforced on every insert. |
Event Calendar Guard
| Parameter / Command | Default | Meaning |
|---|---|---|
EVENT_GUARD_ENABLED | True | Master switch for event-risk blocking before DC, DCS, DCS_SKEW, DDC, MDC, and IC scans. |
EVENT_LOOKAHEAD_DAYS | 7 | Default pre-event block window for blocking market events. |
EVENT_CALENDAR_SEED_FILE | data/market_events_seed.json | Manual official/seeded events loaded on bot startup and by the fetch job. |
make fetch-events | manual/operator-run | Loads the seed file, fetches NSE holidays for current/next year, fetches MoSPI CPI/GDP/IIP events, fetches OEA WPI warning events, normalizes rules, and upserts into SQLite. |
Box Spread
| Parameter | Default | Meaning |
|---|---|---|
min_profit_per_lot (per-underlying) | NIFTY ₹300 / BANKNIFTY ₹450 | Minimum net profit after all charges. NIFTY is lower because its premiums are structurally smaller. BANKNIFTY stays at ₹450 to absorb the ₹300–400/lot gap between scan prices and fill prices. |
BOX_MAX_MARGIN_PER_TRADE | ₹80,000 | Skip if margin estimate exceeds this |
BOX_CLOSE_DAYS_BEFORE_EXPIRY | 2 | Close N days before expiry (pin risk) |
BOX_PROFIT_TARGET_PCT | 70% | Close early when this % of max profit is captured |
Calendar Spread
| Parameter | Default | Meaning |
|---|---|---|
CAL_ENTRY_SIGMA | 2.0 | Enter when spread deviates > 2σ from mean |
CAL_EXIT_SIGMA | 0.5 | Exit when spread returns within 0.5σ |
CAL_LOOKBACK_DAYS | 20 | Rolling window for mean / std computation |
CAL_MIN_NET_PROFIT_PER_LOT | ₹50 | Skip if net profit after charges is below this |
NEAR_EXPIRY_TIME_STOP_HOUR | 15 | Hour in IST for DC, DCS/DCS_SKEW, DDC, and IC expiry-day time stops. |
NEAR_EXPIRY_TIME_STOP_MIN | 0 | Minute in IST for expiry-day time stops. With the default, near-expiry option spreads close at/after 15:00 IST on expiry day, or immediately if near expiry has already passed. |
Double Calendar Straddle (DC)
| Parameter | Default | Meaning |
|---|---|---|
DC_LOT_SIZE | 65 | NIFTY lot size |
DC_MIN_DTE | 3 | Minimum DTE to near expiry at entry — below this gamma risk too high |
DC_MAX_DTE | 7 | Maximum DTE to near expiry at entry — beyond this theta advantage is minimal |
DC_MIN_IV | 15% | Minimum near ATM IV — enter only when premium is high enough for the theta edge to materialise |
DC_MAX_IV | 25% | Maximum near ATM IV — skip if event risk (RBI, results); IV could explode and kill the short near leg |
DC_MAX_IV_SPREAD | 5% | Skip if far_iv − near_iv > this — event premium priced into far expiry |
DC_MIN_NEAR_STRADDLE | 80 pts | Minimum near straddle premium — enough theta to collect |
DC_PROFIT_TARGET_PCT | 32% | Close when net P&L ≥ 32% of net debit (after entry + close costs). Theta is front-loaded — bulk arrives in first 2-3 days. |
DC_STOP_PNL_PCT | 20% | Trigger 1: close if net P&L ≤ −20% of net debit. Max-loss guardrail regardless of IV or spot. |
DC_STOP_IV_DROP | 10.0 pts | Trigger 2: close if entry IV − current IV ≥ 10 pts. IV crush kills far legs faster than near theta helps. |
DC_STOP_TENT_DTE | 3 | Trigger 3 only active when DTE ≥ this — inside 3 DTE the near gamma is too volatile to trust the tent shape. |
DC_STOP_TENT_WIDTH | 1.30 | Trigger 3: close if |spot − atm| > near_straddle_entry × 1.30. Widened from 0.90 after live data showed 0.90 exits consistently exited on normal NIFTY noise before theta could accrue. |
DC_STOP_CONFIRM_CYCLES | 2 | Panic stops (P&L stop + tent break) must breach this many consecutive monitor cycles before closing. 1 = fire immediately (legacy). Live data showed every stop fired on a transient intraday tick — many trades were positive at the prior snapshot — so >1 requires the breach to persist. Not applied in EXPIRY_APPROACH (gamma risk demands immediate action). Shipped at 2 (A/B: DC −₹33k → −₹6.5k). |
DCS_STOP_CONFIRM_CYCLES | 1 | Same as DC_STOP_CONFIRM_CYCLES but for DCS / DCS_SKEW. Separate knob so DC can gate without dragging DCS — DCS's gating effect is marginal (its losses trend rather than mean-revert). |
TRAILING_PROFIT_ARM_DEBIT_PCT | 0.15 | Arm the trailing lock once peak P&L ≥ 15% of net debit — a deterministic, reachable anchor. Replaces the old expected_profit-based arm, which live data showed was unreachable (trades peaked ≈13% of debit), leaving the trail dead and peaks given back into losses. |
TRAILING_PROFIT_LOCK_PCT | 0.70 | Deprecated — superseded by TRAILING_PROFIT_ARM_DEBIT_PCT. Kept for back-compat; no longer used by the exit. |
TRAILING_PROFIT_FLOOR_PCT | 0.85 | Once trailing is active, close if live P&L drops below 85% of the peak seen since the lock activated. Locks in most of the profit while allowing normal intraday breathing room. |
DC_MAX_MARGIN | ₹1,50,000 | Skip if net debit × lot exceeds this (capital cap per trade) |
DC_MAX_CONCURRENT | 1 | Max simultaneous DC positions, counting dry-run, shadow, and live rows. |
DC_DRY_RUN | True | First-run seed for DC go-live state. Runtime state is stored in SQLite records. |
DC_REALIZED_RANGE_MAX_PCT | 0.45 | Skip DC if today's intraday spot range > 45% of near ATM straddle — prefer DCS which has OTM wings to absorb the wider range. |
DC_IV_PERCENTILE_GATE_ENABLED | True | Enable the IV percentile quality gate after DC scanner signal generation. |
DC_BLOCK_TOP_IV_PERCENTILE | 90 | Skip new DC entries when mature IV history says current ATM IV percentile is at or above this threshold. |
Stretched Double Calendar (DCS)
| Parameter | Default | Meaning |
|---|---|---|
DCS_MIN_DTE | 7 | Minimum DTE to near expiry — OTM near legs below 5 DTE have negligible theta differential (₹5-15 premium) |
DCS_MAX_DTE | 10 | Maximum DTE — wider than DC to give OTM wings time to contribute theta |
DCS_IV_REGIME_LOW | 22.5 | Near IV below this → low-vol tier; apply DCS_WING_PCT_LOW_IV (tighter wings, less capital). |
DCS_IV_REGIME_HIGH | 24.0 | Near IV above this → high-vol tier; apply DCS_WING_PCT_HIGH_IV (wider wings to stay within likely range). |
DCS_WING_PCT_LOW_IV | 0.45 | Wing = 45% of 1SD when near IV < 22.5%. Tighter tent in low-vol conditions. |
DCS_WING_PCT_MID_IV | 0.52 | Wing = 52% of 1SD when 22.5% ≤ near IV ≤ 24.0%. Standard mid-vol sizing. |
DCS_WING_PCT_HIGH_IV | 0.60 | Wing = 60% of 1SD when near IV > 24.0%. Wider tent to tolerate the larger expected moves. |
DCS_REALIZED_RANGE_MAX_PCT | 0.80 | Skip DCS if today's intraday spot range > 80% of near ATM straddle — market too volatile for an 8-leg structure. |
DCS_SKEW_THRESHOLD | 2.0 | Min IV-pt gap between put skew and call skew to trigger asymmetric wings in DCS_SKEW. Below this, wings are symmetric. |
DCS_SKEW_CE_WING_PCT | 0.45 | CE wing pct used when put skew is dominant (tighter upside tent, wider downside tent). |
DCS_SKEW_PE_WING_PCT | 0.60 | PE wing pct used when put skew is dominant (wider downside tent where risk is concentrated). |
DCS_PROFIT_TARGET_PCT | 42% | Close when net P&L ≥ 42% of net debit. Higher than DC's 32% — wings take 1-2 extra days to contribute. |
DCS_MAX_MARGIN | ₹2,50,000 | Capital cap for 8-leg structure (net debit × lot) |
DCS_DRY_RUN | True | First-run seed for DCS go-live state. Stop triggers same as DC (DC_STOP_*). |
DCS_MAX_CONCURRENT | 3 | Max simultaneous DCS positions, counting dry-run, shadow, and live rows. Each open position must be at a different ATM + near expiry. At most one new DCS entry is allowed per calendar day. |
Skew-Aware DCS (DCS_SKEW)
SkewedDCSScanner runs as a separate strategy alongside DCS for side-by-side comparison.
It uses asymmetric CE/PE wings when put or call skew exceeds the threshold; otherwise identical to DCS.
| Parameter | Default | Meaning |
|---|---|---|
SDCS_DRY_RUN | True | First-run seed for skew-aware DCS go-live state. |
SDCS_MAX_CONCURRENT | 1 | Max simultaneous DCS_SKEW positions, counting dry-run, shadow, and live rows. Kept at 1 during comparison phase to minimize capital commitment. |
DCS_SKEW_DISABLED | False | Re-enabled in dry-run to evaluate performance with AI market context active. Was disabled after 0% win rate in 5 live trades; scanner returns None immediately when True. |
Directional Diagonal Calendar (DDC)
DirectionalDiagonalScanner is a dry-run 2-leg strategy. It buys the far-month ATM option and sells the near-week OTM option on the same side after a strong intraday trend from the captured day open.
| Parameter | Default | Meaning |
|---|---|---|
DDC_DRY_RUN | True | First-run seed for DDC go-live state. |
DDC_DISABLED | False | Re-enabled in dry-run to evaluate performance with AI market context active. Was disabled after 0% win rate in 3 live trades; scanner returns None immediately when True. |
DDC_TREND_THRESHOLD_PCT | 0.5% | Minimum absolute intraday move from captured day open before DDC can signal. |
DDC_MIN_IV | 20% | Minimum ATM IV. Config stores 0.20; scanner normalizes it to the existing percent IV convention. |
DDC_MAX_IV | 25% | Maximum ATM IV. Above this, event risk is too high for the short near-week leg. |
DDC_NEAR_DTE_MIN | 5 | Minimum DTE for the near-week short leg. |
DDC_NEAR_DTE_MAX | 9 | Maximum DTE for the near-week short leg. |
DDC_OTM_DISTANCE_PTS | 200 pts | Fixed OTM distance for the short near-week leg: ATM + 200 for CE, ATM − 200 for PE. |
DDC_PROFIT_TARGET_PCT | 30% | Close when net P&L reaches 30% of entry debit. |
DDC_STOP_LOSS_PCT | 20% | Close when net P&L falls to −20% of entry debit. |
DDC_MAX_MARGIN | ₹80,000 | Skip signal if entry debit × lot size exceeds this cap. |
DDC_MAX_CONCURRENT | 1 | Max simultaneous DDC positions, counting dry-run, shadow, and live rows. |
Iron Condor Fallback (IC)
IronCondorScanner is a dry-run fallback. It only scans when no DC, DCS, DCS_SKEW, or DDC trade is open, and it persists simulated fills instead of placing live orders.
| Parameter | Default | Meaning |
|---|---|---|
IC_DRY_RUN | True | First-run seed for IC go-live state. |
IC_MIN_IV | 18% | Minimum ATM IV. Config stores 0.18; scanner normalizes it to percent. |
IC_MAX_IV | 26% | Maximum ATM IV. Above this, event risk is too high for a short-vol range trade. |
IC_DTE_MIN | 3 | Minimum DTE for near-week IC entry. |
IC_DTE_MAX | 7 | Maximum DTE for near-week IC entry. |
IC_OTM_DISTANCE_PTS | 300 pts | Short CE/PE distance from ATM: CE at ATM + 300, PE at ATM − 300. |
IC_WING_WIDTH_PTS | 200 pts | Long hedge distance beyond each short: CE wing at ATM + 500, PE wing at ATM − 500. |
IC_MIN_CREDIT_PCT | 20% | Skip if net credit is below 20% of wing width × lot size. |
IC_IV_PERCENTILE_CREDIT_ADJUST_ENABLED | True | Enable the low-IV percentile credit adjustment when mature IV history exists. |
IC_LOW_IV_PERCENTILE | 25 | Percentile at or below which IC requires extra credit because short-vol premium is relatively cheap. |
IC_LOW_IV_MIN_CREDIT_MULTIPLIER | 1.25× | Multiplier applied to IC_MIN_CREDIT_PCT × wing width × lot size on low-IV percentile days. |
IC_PROFIT_TARGET_PCT | 50% | Close when credit captured reaches 50% of entry credit. |
IC_STOP_LOSS_CREDIT_X | 1.5× | Close when current loss reaches 1.5× entry credit. |
IC_MAX_RANGE_PCT | 60% | Skip if today's realized spot range exceeds 60% of the near ATM straddle. |
IC_MAX_MARGIN | ₹60,000 | Skip signal if max loss estimate exceeds this cap. |
IC_MAX_CONCURRENT | 1 | Max simultaneous IC positions, counting dry-run, shadow, and live rows. |
AI Market Strategy
All AI calls go through CodexRunner (arb_bot/ai/codex_runner.py) which shells to codex exec — no OpenAI API key, $0 per token. Auth uses the machine's existing free ChatGPT login (mounted at ~/.codex); no API key and no per-token budget. Three flows: Flow 1 = AI_MARKET single-leg trade (premarket + confirmation), Flow 2 = shared market_view prediction every 30 min, Flow 3 = breakout_risk exit overlay every 30 min. News and macro context use SearXNG by default (SEARXNG_URL=http://searxng:8080), with Firecrawl as fallback when SearXNG is unset.
| Parameter | Default | Meaning |
|---|---|---|
CODEX_MODEL | gpt-5.6-terra | Model ID passed to codex exec -m. All three AI flows use this model. |
CODEX_REQUEST_TIMEOUT_SECONDS | 420 | Subprocess timeout for a single codex exec call. Longer than a typical OpenAI request because the Codex sandbox has startup overhead. |
CODEX_HOME | empty | Override path to the codex binary. When empty, the runner searches PATH and then ~/.local/bin/codex. |
CODEX_USE_FREE_CHATGPT_AUTH | True | When true (default), the runner strips any OPENAI_API_KEY from the subprocess environment so the free ChatGPT login under ~/.codex is used untouched. Set to false only if you are paying per-token with an explicit API key. There is no AI_*_BUDGET_* config — Codex is free. |
CODEX_RETRY_ON_429_TIMEOUT | True | When true, a transient codex failure (HTTP 429 rate-limit or a subprocess timeout) is retried up to CODEX_MAX_RETRIES times with backoff before giving up. |
CODEX_MAX_RETRIES | 2 | Number of retry attempts for a transient codex failure. Total attempts = retries + 1. |
CODEX_RETRY_BACKOFF_SEC | 20 | Seconds to sleep between retry attempts. Set to 0 to retry immediately (useful in tests). |
ENRICHER_RETRY_COUNT | 2 | Number of retry attempts for a single data-enricher fetch (yfinance / NSE) before it is considered failed. A failed fetch falls back to the last good cached value when available. |
ENRICHER_CACHE_TTL_MIN | 120 | TTL (minutes) of the in-process enricher cache. On a transient upstream failure, a stale-but-fresh value within this window is returned instead of None so a single provider outage does not force NO_TRADE. |
FIRECRAWL_API_URL | http://localhost:3002 | Fallback self-hosted Firecrawl API base URL. Used by MacroSearchCollector only when SEARXNG_URL is unset. |
FIRECRAWL_API_KEY | local | API key for the self-hosted Firecrawl instance. |
FIRECRAWL_ENABLED | True | Enables the Firecrawl fallback search backend when SEARXNG_URL is unset; macro and news collection still prefer SearXNG when configured. |
SEARXNG_URL | http://searxng:8080 | Primary search backend for daily macro and per-cycle news enrichment. Takes priority over Firecrawl. |
AI_PREDICT_ENABLED | True | Master switch for Flow 2 and Flow 3 (market_view + breakout_risk). Disabling this stops the 30-min scheduled runs; AI hints and breakout overlay are inactive. |
AI_PREDICT_INTERVAL_MIN | 30 | How often (minutes) the market_view and breakout_risk tasks are run during market hours. |
AI_PREDICT_MAX_AGE_MIN | 35 | Maximum age (minutes) of a prediction row before it is considered stale and ignored by the scan cycle. |
AI_PREDICT_DAILY_RUN_CAP | 20 | Maximum prediction rows per calendar day across both tasks combined. Guards against runaway calls. |
AI_HINT_ENABLED | True | When true, market_view predictions are applied to strategy signals via each spec’s ai_hint_consumer. Set to false to run AI predictions but ignore the hints (shadow mode for tuning). |
AI_EXIT_OVERLAY_ENABLED | True | Master switch for the breakout-risk exit overlay (Flow 3). Disabling skips the advisory check during position monitoring. |
AI_EXIT_MIN_CONFIDENCE | 75 | Minimum breakout confidence (%) required before the overlay triggers. Below this threshold no action is taken. |
AI_EXIT_AUTO_CLOSE | True | When true, a high-confidence breakout signal auto-closes the range-bound trade and writes action_taken=AUTO_CLOSE. When false, the advisory is written as ADVISORY_ONLY and a Telegram alert is sent for manual review. |
AI_EXIT_TENT_BUFFER_PCT | 0.5 | Distance from a short strike (as % of spot) within which the overlay considers the tent at risk and eligible for exit. |
AI_DEBRIEF_ENABLED | True | When true, closed-trade journal rows attempt a Codex-backed post-trade debrief after the close snapshot is saved. |
AI_DEBRIEF_DAILY_TOKEN_BUDGET | 200,000 | Daily rolling Codex token cap across all post-trade debriefs. |
AI_DEBRIEF_DAILY_COUNT_CAP | 50 | Maximum number of debriefs generated per calendar day. |
AI_MARKET_ENABLED | True | Master switch for the scheduled 09:05 AI_MARKET analysis (Flow 1). |
AI_MARKET_DRY_RUN | True | Safety default. Dry-run only — refuses live AI_MARKET broker orders. |
AI_MARKET_SHADOW | False | When true, analysis is stored but execution is suppressed for observation-only comparison. |
AI_MARKET_MAX_MARGIN | ₹1,00,000 | Maximum planned exposure for the simulated AI_MARKET trade. |
AI_MARKET_LOTS | 1 | Lots used for simulated AI_MARKET trade sizing. |
AI_MARKET_MIN_CONFIDENCE | 65 | Minimum confidence required before the confirmation stage can create a simulated trade. |
AI_MARKET_CONFIRMATION_TIME | 09:20 | Scheduler time for the live confirmation pass. |
AI_MARKET_MIN_CONFIRM_MOVE_PCT | 0.15 | Minimum confirmation move from the day’s first IV-snapshot spot before the setup is considered live enough to trade. |
AI_MARKET_MAX_CHASE_MOVE_PCT | 0.60 | Maximum move before the setup is too extended to chase. |
AI_MARKET_STOP_PCT | 0.35 | Index-based stop percentage for the confirmed dry-run option trade. |
AI_MARKET_TARGET_PCT | 0.70 | Index-based target percentage for the confirmed dry-run option trade. |
AI_MARKET_TRANSIENT_RETRIES | 1 | Retry count for transient Codex failures. |
AI_MARKET_MAX_OUTPUT_TOKENS | 16000 | Codex output budget for the market analysis prompt. |
AI_GATE_ENABLED | False | Optional gate for blocking MDC entries from the AI verdict. |
RegimeTA Strategy
REGIMETA is a dry-run directional single-leg strategy fed by the shared TA snapshot. Directional AI verdicts still require TA alignment. When AI is non-directional, the dry-run-only fallback can trade from strong TRENDING TA so we collect samples while AI stays conservative.
| Parameter | Default | Meaning |
|---|---|---|
REGIME_TA_ENABLED | True | Master switch for REGIMETA scan-cycle entries. |
REGIME_TA_DRY_RUN | True | REGIMETA is dry-run only; live orders are not implemented. |
REGIME_TA_MIN_STRENGTH | 50 | Minimum TA strength when AI is directional and aligned. |
REGIME_TA_ALLOW_NON_DIRECTIONAL_AI_DRY_RUN | True | Allow dry-run REGIMETA entries from TA when AI says SIDEWAYS, NO_TRADE, or NEUTRAL. |
REGIME_TA_TA_FALLBACK_MIN_STRENGTH | 70 | Minimum TA strength for the non-directional-AI dry-run fallback. |
AI Market Sentiment (deterministic)
Flow 4 — a fully deterministic ~10-min intraday sentiment score (no LLM, no Codex call). SentimentService collects spot/PCR/IV + cached technicals, computes a weighted composite in [−100, +100] that renormalizes over whichever components are present, persists each reading to ai_market_sentiment, and feeds a conservative 4-condition exit overlay for range trades.
| Parameter | Default | Meaning |
|---|---|---|
AI_SENTIMENT_ENABLED | True | Master switch for the scheduled sentiment service. When false, no ai_market_sentiment rows are written and no sentiment job is scheduled. |
AI_SENTIMENT_INTERVAL_MIN | 10 | Minutes between sentiment runs. Runtime-editable knob. |
AI_SENTIMENT_ENRICHER_CACHE_TTL_MIN | 30 | Reserved cache TTL for enricher reuse across sentiment cycles. |
AI_SENTIMENT_MIN_COVERAGE_PCT | 30 | Minimum coverage below which a sentiment reading is considered low-confidence for downstream consumers. |
AI_SENTIMENT_EXIT_ENABLED | True | Master switch for the sentiment-risk exit overlay checked during position monitoring. |
AI_SENTIMENT_EXIT_MIN_STRENGTH | 70 | Minimum strength_pct required before the overlay can trigger an exit. |
AI_SENTIMENT_EXIT_MIN_COVERAGE_PCT | 40 | Minimum coverage_pct required before the overlay can trigger an exit. |
AI_SENTIMENT_EXIT_ADVERSE_MOVE_PCT | 0.6 | Confirmed adverse spot move (as % from prev_close) required to confirm the flip is hurting the trade. |
AI_SENTIMENT_EXIT_AUTO_CLOSE | True | When true, a qualifying signal auto-closes the range trade and writes action_taken=AUTO_CLOSE. When false, an ADVISORY_ONLY row plus a Telegram alert is emitted. |
AI_SENTIMENT_EXIT_SHADOW | False | Forces advisory-only regardless of AI_SENTIMENT_EXIT_AUTO_CLOSE, for observation before trusting the overlay. |
AI_SENTIMENT_W_MOMENTUM / _W_PCR / _W_VOL / _W_TECHNICAL / _W_GLOBAL | 0.35 / 0.25 / 0.15 / 0.15 / 0.10 | Component weights. Their sum need not be 1.0 — the composite renormalizes over whichever components are present, so a missing input is excluded rather than averaged as zero. |
Dashboard AI Assistant
Read-only advisory widget for grounded Q&A on open/closed trades, strategies, and live market state. Answers are grounded in build_context_pack(); suggestions go to Telegram for confirmation.
| Parameter | Default | Meaning |
|---|---|---|
AI_ASSISTANT_ENABLED | True | Master switch for the dashboard floating AI assistant widget (Ask + Refresh). |
AI_ASSISTANT_DAILY_CALL_CAP | 50 | Maximum cumulative assistant API calls per calendar day (both Ask and Refresh combined). |
AI_ASSISTANT_SESSION_CALL_CAP | 20 | Maximum assistant API calls per user session (4-hour TTL cookie). Limits concurrent tab spam. |
IV Surface Tracker
| Parameter | Default | Meaning |
|---|---|---|
IV_SNAPSHOT_RETENTION_DAYS | 180 | Retention window for iv_snapshots; stale rows are pruned on insert. |
BATTLE_LOG_RETENTION_DAYS | 60 | Retention window for strategy_battle_log. |
IV_SURFACE_SMILE_RANGE_PCT | 2.0 | Capture smile rows where strike distance from ATM is within this percent of spot. |
IV_SURFACE_MAX_SMILE_STRIKES | 21 | Maximum smile rows stored per snapshot, centered on ATM. |
Master DC (MDC)
MasterDCScanner is a meta-strategy wrapper. It classifies the current regime each cycle, delegates to the best DC-family sub-scanner, and stores the resulting trade under an MDC_* strategy key so it can coexist with standalone trades.
| Parameter | Default | Meaning |
|---|---|---|
MDC_DRY_RUN | True | First-run seed for MDC go-live state. MDC state is propagated into the selected sub-executor. |
MDC_MAX_CONCURRENT | 2 | Max simultaneous MDC_* positions, counting dry-run, shadow, and live rows. The second real-order position still requires all controlled scale-in gates. |
MDC_MAX_MARGIN | ₹2,50,000 | Per-trade margin cap applied to whichever sub-strategy is selected. |
MDC_SCALE_IN_COOLDOWN_MINUTES | 90 | Minimum elapsed time from the newest remaining MDC anchor before another MDC position is considered. |
MDC_SCALE_IN_MIN_MOVE_PTS | 200 | Absolute minimum NIFTY movement required from the anchor spot. |
MDC_SCALE_IN_STRADDLE_PCT | 0.50 | Dynamic movement threshold as a fraction of the anchor near straddle; effective threshold is the maximum of this value and the fixed-point minimum. |
MDC_SCALE_IN_MAX_ANCHOR_LOSS_PCT | 0.10 | Scale-in is blocked when anchor P&L is at or below −10% of its strategy risk basis. |
MDC_TREND_THRESHOLD_PCT | 0.006 | Intraday spot move ≥ 0.6% from the captured open → trending → DDC. |
MDC_SKEW_THRESHOLD | 2.5 | CE/PE IV gap in points → skewed → DCS_SKEW. |
MDC_CALM_RANGE_PCT | 0.35 | Realized range / near straddle ≤ 35% → calm → DC. |
MDC_WIDE_RANGE_PCT | 0.70 | 35% to 70% → wider → DCS; above 70% → IC fallback or no trade. |
MDC_MAX_IV_FOR_DC | 25.0 | Near IV above this makes DC unattractive; the wrapper falls through to IC. |
MDC_MAX_IV_FOR_IC | 30.0 | Near IV above this is too risky even for IC, so the wrapper returns no signal. |
MAX_CONCURRENT_OPEN_TRADES | 2 | Phase 2: Hard cap on live/shadow open trades across all strategies. Per-strategy *_MAX_CONCURRENT limits separately count dry-run, shadow, and live rows so deployed dry-run validation cannot churn unlimited open positions. The global cap still prevents correlated real exposure explosion during high-IV days. |
STRATEGY_SLOTS | {"dc_family": 2} | Explicit DC-family exposure cap. The second slot is available only after MDC cooldown, movement, anchor-health, margin, Greeks, and exact-leg duplicate gates; a third entry is blocked. |
Order Execution
| Parameter | Default | Meaning |
|---|---|---|
ORDER_SLIPPAGE_PCT | 0.2% | Drift buffer added on top of the scanner bid/ask anchor (first attempt). Previously 0.4% LTP-based — that approach consistently underpriced BUY legs since LTP + 0.4% often sat below the ask on wide BANKNIFTY spreads. |
LEG_RETRY_SLIPPAGE_PCT | 0.8% | Retry uses fresh LTP + 0.8% (not bid/ask anchor) — on retry, the scanner price is too stale to trust so we fall back to aggressive LTP-based crossing. |
REVERSAL_LIMIT_SLIPPAGE_PCT | 0.1% | When reversal IS needed, _auto_reverse() tries IOC LIMIT at LTP ± this much first, then MARKET as fallback. Captures roughly half the bid-ask spread vs a direct MARKET hit (~₹150-200 saved per forced reversal). |
ORDER_FILL_TIMEOUT | 30 s | Max wait for all legs to fill (internal) |
NSE_TICK_SIZE | ₹0.05 | All limit prices rounded to this boundary |
ORDER_DELAY_SEC | 0.3 s | Delay between consecutive order placements |
OPTION_CHAIN_COOLDOWN | 3.5 s | Min gap between option chain API calls |
Partial Fill Recovery
| Parameter | Default | Meaning |
|---|---|---|
RECOVERY_TRAILING_STOP_PCT | 3% | Trailing stop from peak LTP on kept leg |
RECOVERY_TAKE_PROFIT_PCT | 10% | Take profit from entry LTP on kept leg |
RECOVERY_DIRECTION_LOOKBACK | 10 | Compare current spot vs this many readings ago (Gate 1) |
RECOVERY_MIN_TREND_PCT | 0.15% | Minimum point-to-point move to pass Gate 1; below this → FLAT |
RECOVERY_CONSISTENCY_THRESHOLD | 60% | Minimum fraction of tick intervals that must move in the trend direction to pass Gate 2. Filters spikes that fool Gate 1. |
Market Index Dashboard
Live NSE index tiles (LTP + day change% + sparkline) pushed over /ws/live on a decoupled timer. MarketIndexService batches LTP via the shared DhanClient and caches per-index intraday candles. The enabled-index list is persisted as JSON in the Postgres bot_settings table and edited via GET/PUT /api/market/indices/config.
| Parameter | Default | Meaning |
|---|---|---|
MARKET_INDEX_REFRESH_SEC | 60 | Seconds between /ws/live index frames. Runtime-tunable (10–600 s); drives the decoupled index push timer independent of trade-monitoring ticks. |
MARKET_INDEX_INTRADAY_INTERVAL | 5 | Dhan intraday candle interval (minutes) used for the sparkline series. |
MARKET_INDEX_INTRADAY_CACHE_SEC | 240 | Server-side intraday cache TTL — roughly one fetch per 5-min candle to respect DhanHQ rate limits. |
MARKET_INDEX_SPARKLINE_POINTS | 75 | Number of recent points rendered in each index tile's sparkline. |
MARKET_INDEX_DEFAULTS | 7 indices | Default NSE indices (Dhan marketfeed segment IDX_I): NIFTY_50 (13), NIFTY_BANK (25), NIFTY_NEXT50 (38), NIFTY_MIDCAP100 (37), NIFTY_SMLCAP100 (5), NIFTY_500 (19), INDIA_VIX (21). Each tuple is (key, label, segment, security_id). |
Research Strategies
Equity research subsystem configuration. Read-only backtest engine; never touches live trading. See arb_bot/research/ and docs/research_backtest.md.
| Parameter | Default | Meaning |
|---|---|---|
RESEARCH_FUNDAMENTALS_ENABLED | True | Enable fundamentals injection into market_data each rebalance (ROE, D/E, margin, FCF derived from yfinance). |
RESEARCH_FUNDAMENTALS_FILING_LAG_DAYS | 45 | Days after period_end before fundamental data is available (avoids lookahead bias on point-in-time snapshots). |
RESEARCH_MQ_MIN_ROE | 0.12 | momentum_quality: minimum ROE quality gate (12% threshold for financial health). |
RESEARCH_MQ_MAX_DE | 1.0 | momentum_quality: maximum debt-to-equity ratio (leverage cap). |
RESEARCH_MQ_QUALITY_WEIGHT | 0.5 | momentum_quality: blend weight for quality z-score vs momentum z-score (0.5 = equal). |
RESEARCH_QA_MIN_ROE | 0.15 | quality_alpha: minimum return on equity (15% threshold). |
RESEARCH_QA_MIN_ROCE | 0.15 | quality_alpha: minimum return on capital employed; approx. = ROE via yfinance derivation. |
RESEARCH_QA_MAX_DE | 0.6 | quality_alpha: maximum debt-to-equity (stricter than momentum_quality for high-quality screen). |
RESEARCH_QA_MIN_MARGIN | 0.08 | quality_alpha: minimum net profit margin (8% operational efficiency threshold). |
RESEARCH_QA_TREND_FILTER | True | quality_alpha: enable 200-SMA price trend filter (confirm uptrend before entry). |
RESEARCH_QA_TREND_SMA | 200 | quality_alpha: SMA period (days) for trend filter. |
RESEARCH_VT_MIN_PASS | 2 | value_trend: minimum value metrics ≥ universe median to qualify (E/P, B/P, S/P, div yield); requires ≥2 of 4. |
RESEARCH_VT_TREND_SMA | 200 | value_trend: SMA period (days) for trend confirmation filter. |
| pair_statarb strategy (market-neutral pairs trading) | ||
RESEARCH_PAIR_LOOKBACK | 120 | pair_statarb: lookback window (days) for spread z-score calculation. |
RESEARCH_PAIR_CORR_MIN | 0.8 | pair_statarb: minimum closing-return correlation to qualify as a cointegrated pair (same sector). |
RESEARCH_PAIR_Z_IN | 2.0 | pair_statarb: entry z-score threshold (|z| ≥ this triggers long/short placement). |
RESEARCH_PAIR_Z_OUT | 0.5 | pair_statarb: exit z-score threshold (mean reversion exit when |z| ≤ this). |
RESEARCH_PAIR_Z_STOP | 3.5 | pair_statarb: extreme-loss exit when |z| ≥ this (cointegration break protection). |
RESEARCH_PAIR_MAX_PAIRS | 10 | pair_statarb: maximum concurrent pairs to trade (capital split equally per pair). |
RESEARCH_PAIR_MAX_HOLDING_DAYS | 30 | pair_statarb: maximum holding period (days) — force-exit after this regardless of z-score. |
RESEARCH_SHORT_BORROW_BPS_ANNUAL | 50 | pair_statarb: annualized short-borrow cost (basis points); accrued daily on short-leg notional. |
| options_vol_premium strategy (IV-rank iron condor) | ||
RESEARCH_OVP_MIN_IVRANK | 0.5 | Minimum IV-rank to open an OVP iron condor (0–1) |
RESEARCH_OVP_IVRANK_LOOKBACK | 120 | Rolling window (days) for IV-rank calculation |
RESEARCH_OVP_DELTA | 0.16 | Approximate delta target for short strikes (informational) |
RESEARCH_OVP_DTE_MIN | 5 | Minimum DTE to open a new OVP position |
RESEARCH_OVP_DTE_MAX | 10 | Maximum DTE to open a new OVP position |
RESEARCH_OVP_WING_POINTS | 300 | Points from spot to short strike; wings bought same distance further out |
RESEARCH_OVP_PROFIT_TARGET | 0.5 | Close at this fraction of max credit received (0.5 = 50%) |
RESEARCH_OVP_STOP_MULT | 2.0 | Close when loss exceeds this multiple of credit received |
RESEARCH_OVP_LOT_SIZE | 50 | Contract lot size for NIFTY (units per trade) |
RESEARCH_COMBINE_MODE | off | Combination mode: off | composite (Mode A z-blend) | sleeve (Mode B) |
RESEARCH_COMBINE_MEMBERS | Comma-separated member strategy names for combination runs | |
RESEARCH_COMBINE_WEIGHTS | Comma-separated weights aligned to RESEARCH_COMBINE_MEMBERS | |
RESEARCH_EVENTS_ENABLED | True | Inject corporate-events dict into market_data on each rebalance |
RESEARCH_EVENT_LOOKBACK_DAYS | 5 | Rolling window (calendar days) for which events are visible to strategies |
RESEARCH_PEAD_MIN_SURPRISE | 0.05 | Minimum earnings surprise (fraction, e.g. 0.05 = 5 %) to trigger PEAD entry |
RESEARCH_PEAD_HOLDING_DAYS | 40 | Maximum holding period for earnings_pead positions (calendar days) |
RESEARCH_EVENT_TYPES_ENABLED | buyback,index_inclusion,pledge_reduction,block_deal,earnings | Comma-separated event types the event_driven strategy will act on |
RESEARCH_EVENT_HOLDING_DAYS | 30 | Maximum holding period for event_driven positions (calendar days) |
Backtest Config Overrides
The backtesting engine does not add live config parameters — instead it temporarily overrides existing
Config attributes per simulated cycle through a BacktestConfig proxy
(arb_bot/backtest/simulation/context.py).
- Validation up front.
BacktestConfig({...})raisesValueErrorfor any key that is not a realConfigattribute, so a typo in a CLI/API parameter fails before simulation starts. It always forcesOPTION_CHAIN_COOLDOWN = 0so replays do not sleep between option-chain fetches. - Disabled-strategy replay. Standalone DDC/DCS_SKEW runs, and MDC runs that may select them, temporarily set
DDC_DISABLED = Falseand/orDCS_SKEW_DISABLED = Falseinside the isolated backtest worker unless the caller explicitly supplies those keys. Live process values remain unchanged. - Temporary, cycle-local patching. Existing scanners and exit helpers read
Configclass attributes directly. Rather than refactor them, the engine wraps each historical cycle inpatch.object(Config, key, value)for every validated override and restores the original class attributes when the cycle exits. - Restoration guarantee. After every cycle the live
Configvalues are exactly what they were before — a unit test asserts a patched value is reverted on context exit. - Worker-process isolation. Single runs and grid-search combinations execute in separate
ProcessPoolExecutorworker processes. Becausepatch.objectmutates class attributes, process isolation prevents one job's overrides from being observed by another job or by the live bot process. Workers receive the databasedb_path(a string), never a live source or SQLite connection.
Default grid-search sweeps live in arb_bot/backtest/optimizer/param_space.py and reference only real Config keys:
| Strategy | Swept Config keys |
|---|---|
DC | DC_STOP_TENT_WIDTH, DC_PROFIT_TARGET_PCT, DC_STOP_PNL_PCT |
DCS / DCS_SKEW | DCS_STOP_TENT_WIDTH, DCS_PROFIT_TARGET_PCT |
DDC | DDC_STOP_LOSS_PCT, DDC_PROFIT_TARGET_PCT |
IC | IC_PROFIT_TARGET_PCT, IC_STOP_LOSS_CREDIT_X |
MDC | MDC_TREND_THRESHOLD_PCT, MDC_CALM_RANGE_PCT, MDC_WIDE_RANGE_PCT |
Backtest fill model
Two Config attributes exist solely for the backtest fill model — they have no effect on live trading:
| Parameter | Default | Meaning |
|---|---|---|
BACKTEST_SPREAD_PCT | 1.2% | Backtest fill model: full bid-ask spread as a fraction of premium. Every simulated fill pays half of it — buys fill higher, sells fill lower. Matches the Black-Scholes synthesizer's spread model. |
BACKTEST_SPREAD_MIN | ₹0.50 | Minimum full spread per leg in the backtest fill model. Set both spread params to 0 to reproduce pre-fill-model results. |
Portfolio Sync Reliability
Throttle and backoff knobs for the automatic portfolio broker-sync loop run by the
token-refresher container. See
Portfolio Connections & Sync Reliability
for the full auto-refresh model.
| Parameter | Default | Meaning |
|---|---|---|
PORTFOLIO_INTRADAY_SYNC_INTERVAL_MIN | 30 | Minimum minutes between automatic intraday portfolio syncs for DHAN / ZERODHA / MO_RISE connections (read off the dedicated last_auto_sync_at throttle timestamp, not the manual-sync updated_at). |
PORTFOLIO_SYNC_MAX_FAILURES_STALLED | 10 | Consecutive failures after which a connection enters the STALLED terminal state — auto-sync stops retrying it until a manual sync succeeds (which resets failure_count via record_success). |
The escalating failure backoff schedule [1, 5, 15, 60] minutes is the
BrokerSyncStateRepository._BACKOFF_MINUTES constant in
arb_bot/portfolio_app/db/repository.py (capped at 60 min, reached
at the 4th consecutive failure), not a Config value.
Validation Framework (Evidence Spine Phase 2)
| Parameter | Default | Meaning |
|---|---|---|
VALIDATION_FOLD_COUNT | 3 | Number of walk-forward folds for validation. |
VALIDATION_OOS_HOLDOUT_PCT | 0.20 | Fraction of data held out for out-of-sample testing. |
VALIDATION_CAL_TOLERANCE_DAYS | 2 | Max day offset for matching backtest/live trade pairs during calibration. |
VALIDATION_MIN_WIN_RATE | 0.60 | Minimum required win rate to pass validation. |
VALIDATION_MIN_PF | 1.0 | Minimum required profit factor to pass validation. |
VALIDATION_MAX_FOLD_VARIANCE | 0.20 | Max allowed variance across walk-forward folds. |
VALIDATION_CAL_MAX_MEAN_ABS_DELTA | None | If set, max allowed mean absolute P&L delta between backtest and live (None = no gate). |
VALIDATION_MIN_TRADES_PER_FOLD | 2 | Minimum trades required per walk-forward fold. |
VALIDATION_FRESHNESS_WINDOW_DAYS | 30 | Max age of a VALIDATED event before re-validation is required for strategy go-live. |
Portfolio Benchmark (Slice 2)
Benchmark knobs for the Returns page and the evening index-close fetch job. See Portfolio Reports for the returns methodology and benchmark data source.
| Parameter | Default | Meaning |
|---|---|---|
PORTFOLIO_BENCHMARK_DEFAULT | NIFTY_50 | Default benchmark index for the Returns page and the scheduler fetch job. |
PORTFOLIO_BENCHMARK_FETCH_TIME_IST | 18:30 | IST time the daily benchmark-close fetch job runs (post-market close). |
PORTFOLIO_BENCHMARK_INDICES | NIFTY_50 → ^NSEI, SENSEX → ^BSESN | Logical index name to yfinance symbol map used by the reports API and the fetcher. |
PORTFOLIO_BENCHMARK_FETCH_LOOKBACK_DAYS | 5 | Number of daily closes pulled each evening by the benchmark job. |
Portfolio capital-gains reporting
Defaults for the read-only FIFO tax-lot estimate. See Portfolio Tax Lots for the methodology and ledger limitations.
| Parameter | Default | Meaning |
|---|---|---|
CAPITAL_GAINS_LTCG_EQUITY_MONTHS | 12 | Equity holding period must be strictly longer than this many months for LTCG. |
CAPITAL_GAINS_LTCG_DEBT_MONTHS | 36 | Debt/bond holding period threshold for LTCG. |
LTCG_EQUITY_RATE | 10% | Estimated equity LTCG rate after the FY exemption. |
LTCG_EQUITY_EXEMPTION_INR | ₹1,25,000 | Annual equity LTCG exemption used once across the report. |
STCG_EQUITY_RATE | 15% | Estimated equity STCG rate. |
LTCG_DEBT_RATE | 20% | Estimated pre-Apr-2023 debt-MF/bond LTCG rate. |
STCG_DEBT_RATE | 30% | Configurable slab-rate estimate for debt gains that are not eligible for LTCG. |
Diversification & rebalance thresholds
Concentration-warning and currency-normalization knobs for the portfolio Diversification and Rebalance reports. See Portfolio Reports for the report surface.
| Knob | Default | Purpose |
|---|---|---|
PORTFOLIO_CONCENTRATION_SYMBOL_THRESHOLD_PCT | 10.0 | Single-holding % above which a concentration warning fires. |
PORTFOLIO_CONCENTRATION_SECTOR_THRESHOLD_PCT | 40.0 | Sector % above which a concentration warning fires. |
PORTFOLIO_OVERLAP_WARNING_THRESHOLD_PCT | 5.0 | Effective portfolio % at which duplicated direct/fund/ETF stock exposure is reported. |
PORTFOLIO_FUND_HOLDINGS_STALE_DAYS | 7 | Age after which a manual fund or ETF constituent snapshot is marked stale. |
PORTFOLIO_USD_TO_INR_FALLBACK | 83.5 | Fallback USD→INR rate for diversification/rebalance normalization. |
Sector enrichment
A daily PortfolioScheduler job backfills Holding.sector for equity holdings missing one, using yfinance .info (the same source as beta). Only listed equities are enriched; funds, bonds, commodities, and unlisted holdings have no sector and are skipped. The job runs a bounded retry loop when it encounters transient lookup failures, then repeats once per evening (plus a one-shot fetch shortly after each restart) and only touches holdings with an empty sector, so newly-synced assets are classified within a day. Dashboard-only deployments also start the same boot-time enrichment worker at API startup, so sectors do not remain stuck on the fallback `Unknown` value when the trading bot is not running. Precious-metal symbols such as `XAUUSD` and `XAGUSD` are reclassified as commodities during the same boot pass. Operators can also run `/refresh_sectors` in Telegram or use the Portfolio Settings “Refresh Sectors” button to force the same backfill on demand. See Portfolio Reports.
| Knob | Default | Purpose |
|---|---|---|
PORTFOLIO_SECTOR_FETCH_TIME_IST | 18:45 | IST cron time for the daily sector-enrichment job (after the benchmark fetch). |
PORTFOLIO_SECTOR_FETCH_DELAY_S | 0.5 | Seconds to wait between yfinance calls (Yahoo rate-limit throttle). |
PORTFOLIO_SECTOR_BOOT_DELAY_MIN | 2 | Minutes after restart to fire a one-shot sector backfill (in addition to the daily cron). 0 disables the boot fetch. |
Dashboard Live Fallbacks
The dashboard primarily receives live trade ticks and Market Indices through /ws/live. When the WebSocket is reconnecting, it falls back to authenticated REST reads:GET /api/trades/live for current trades and GET /api/market/indices for the latest index snapshot.
If Dhan returns an empty index LTP response, the dashboard reloads any credentials already rotated by the token-refresher and retries once. A continued failure returns the last good values with a stalemarker and applies MARKET_INDEX_FAILURE_BACKOFF_SEC (default15 seconds) before the shared service probes Dhan again. This prevents concurrent browser reconnects from amplifying an upstream market-feed outage. The recovery path is read-only: it never generates a token or places an order.
Portfolio valuation is separate from the index widget. The bot runsPORTFOLIO_PRICE_REFRESH_INTERVAL_MIN every 10 minutes by default, updates holding prices, and persists a matchingportfolio_snapshots record for Networth and each portfolio. Portfolio Overview reads the updated holdings; Reports reads those same persisted valuations. The authenticated refresh endpoint follows the same path for an immediate manual or visible-page refresh.
Option Cockpit (M2)
| Knob | Default | Purpose |
|---|---|---|
Config.WS_TICK_INTERVAL (existing) | 15 | Server-side dashboard tick interval (preserved unchanged). Echoed in the v2 subscribed frame. |
Config.OPTION_COCKPIT_VALUATION_TTL_SECONDS | 1.0 | TTL for the global single-flight valuation cache; must satisfy >= Config.LTP_COOLDOWN. |
Config.LTP_COOLDOWN (existing) | 1.0 | Broker LTP cooldown; the valuation cache TTL must be >= this value. |
CAPABILITY_TIMEOUT_MS | 8000 | Fixed pre-subscribed capability timeout; transitions to UNAVAILABLE with reason server_out_of_date. |
HEARTBEAT_TIMEOUT_MS | derived | max(90000, 3 * tick_interval_seconds * 1000, 3 * heartbeat_interval_seconds * 1000). |
Agent Gateway advisory-run configuration
The Agent Gateway container reads two distinct database DSNs from the runtime secret store. They are NEVER derived from a globalConfig.DATABASE_URL and the container ships without a repository .env.
| Variable | Role | Purpose | Required privileges |
|---|---|---|---|
AGENT_DATABASE_URL | read / gateway | Durable gateway claims, agent-runs persistence, Council evidence reads, decision-observability outbox writes. | SELECT on read tables; ALL onagent_gateway schema. NO INSERT /UPDATE on portfolio,brokers, options_council, ordecision_observability tables. |
AGENT_ADVISORY_WRITE_DATABASE_URL | advisory writer | AI Research run creation, Options Council decision persistence. May be empty when no cost-incurring capability flag is set. | Separately authorized. May differ in role, host, and port from AGENT_DATABASE_URL. |
Capability-aware readiness
The readiness probe (GET /health/ready) returns HTTP 503 when an enabled cost-incurring capability cannot reach its hard dependency:
AGENT_ENABLE_RESEARCH_RUNS=truerequiresAGENT_ADVISORY_WRITE_DATABASE_URLto be configured and reachable; otherwise the probe reportsunavailable_capabilities: ["research"].AGENT_ENABLE_COUNCIL_RUNS=truerequires the writer DSN for Council decision persistence.AGENT_ENABLE_PAPER_PROPOSALS=truerequires the writer DSN for paper-proposal persistence.
Default-off capabilities do not affect readiness. The probe body never exposes the writer DSN, password, or any other secret.
Safe disable and rollback
To disable a cost-incurring capability at runtime:
- Set the corresponding flag to
false(AGENT_ENABLE_RESEARCH_RUNS=false, etc.) and restart the gateway. The flag is read at process start; an in-place change without restart is not honored. - Optional: clear
AGENT_ADVISORY_WRITE_DATABASE_URLif no other capability needs it. A missing writer DSN no longer affects readiness once the flags are off. - Roll back by reversing the flag and confirming
GET /health/readyreturns200with the writer DSN reachable.
Federation destination policy
Remote agent registrations pass through a strict destination-address policy. The following address categories are always rejected — no hostname allowlist can exempt them:
- loopback (127.0.0.0/8, ::1)
- link-local (169.254.0.0/16, fe80::/10)
- multicast (224.0.0.0/4, ff00::/8)
- unspecified (0.0.0.0, ::)
- reserved (per IANA special-purpose registry)
- cloud metadata destinations (169.254.169.254, fd00:ec2::254)
Private (RFC1918 / ULA) addresses are permitted only when the hostname is in AGENT_REMOTE_ALLOWLIST AND the resolved address is in AGENT_INTERNAL_ADDRESSES orAGENT_INTERNAL_CIDRS. A hostname alone is never sufficient.