Token & API Management
token_refresher.py ← standalone systemd service, checks every 5 minutes
│
├── TokenManager.refresh_if_due() ← renew when missing, expired, ≥23h old, or <4h from expiry
├── Try POST /v2/RenewToken → extends active token (happy path)
├── Retry failures every 5 minutes ← after 3 consecutive failures, Telegram asks operator to check Dhan
│
└── If expired → full regeneration
├── TOTP via pyotp.TOTP(DHAN_TOTP_SECRET).now()
├── POST login with PIN + TOTP
├── Write token health to runtime_state:dhan_token
├── Update .env and os.environ as compatibility fallback
└── Telegram confirmation includes method and expiry
ArbitrageBot scheduler ← every 5 minutes
└── TokenManager.reload_from_store()
└── DhanClient.refresh_client() ← only when SQLite token changed
The dashboard reads /api/token/health for expiry, source, last update, and status. The API never returns the token value.
Rate Limits
Option chain:
OPTION_CHAIN_COOLDOWN = 3.5 s between calls.
Order placement: ORDER_DELAY_SEC = 0.3 s between orders (< 10 OPS limit).
LTP batch: LTP_COOLDOWN = 1.0 s minimum gap between serialized ticker_data calls. A host-wide file lock coordinates the bot and dashboard processes. Dashboard WebSocket connections share one cached LTP snapshot and refresh it every WS_TICK_INTERVAL seconds (default 15) so multiple open tabs do not create concurrent marketfeed bursts.