This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
python3.11 -m venv venv && source venv/bin/activate
pip install -e ".[dev]"
pytest -q # 69 tests, ~1sThree console scripts ship:
imbot— Typer CLI (imbot fire,imbot backtest swing,imbot ui,imbot menu, etc.)imbot-daemon— long-running asyncio schedulerimbot-ui— Textual dashboard
State lives in a single DuckDB file at ~/.imbot/state.duckdb (override via IMBOT_HOME).
core/ pure strategy library (no I/O, no Rich, no yfinance)
data/ the only place that touches yfinance + DuckDB
daemon/ asyncio + APScheduler + 8 cron jobs
ui/ Textual TUI, read-only DuckDB, refreshes every 2-10s
cli/commands/ Typer subcommands — presentation + I/O only
optimize/ cartesian-product param sweeps over SwingBacktestConfig
The contract is: core/ must be importable without side effects, no print, no yfinance, no rich. A future pre-commit hook should enforce this (planned in C15).
data/yahoo.py— the only yfinance importer. Sync + async wrappers, 10s timeout, 3 retries with exponential backoff. Fault-injection viaIMBOT_YAHOO_FAULT_INJECT=hang|raise.data/store.py— DuckDB writer (threading.Lock + asyncio.Lock) + read-only opener (DuckDB MVCC makes parallel reads cheap). Schema bootstrap is idempotent.data/market_calendar.py— NSE 09:15–15:30 IST + holiday set.@requires_market_opendecorator gates every decision-making daemon job.data/cache.py— in-memory TTL cache for sync callers; the DuckDBpricestable is the persistent cache.
imbot daemon runs APScheduler.AsyncIOScheduler with 8 jobs (see README "Daemon jobs" table). Single asyncio process. SIGTERM/SIGINT gracefully stops the scheduler, waits 5s, writes a "daemon stopping" event, exits 0.
Seven legacy bugs are fixed in the daemon:
- Monthly SIP refill (no more underinvestment drift)
- NSE-hours gating on decisions (legacy gated display only)
- Atomic writes (DuckDB transactions replace JSON file)
- yfinance timeouts (UI no longer freezes on Yahoo lag)
- Time-stop fires on winners too (no more runaway positions stuck open)
atr_at_entry NOT NULL(no silent skipping of trailing-stop updates)- Retry/backoff on transient Yahoo failures
imbot ui opens a Textual app with 6 widgets in a 3×2 grid. Read-only DuckDB connection per query. textual-plotext for the equity chart with sparkline fallback.
The daemon and UI are intentionally separate processes sharing one DuckDB file (DuckDB MVCC allows unlimited concurrent readers against a single writer). A TUI crash never kills the daemon.
config/default.toml is the source of truth. Override via:
~/.imbot/config.toml(same TOML schema)IMBOT_*env vars with double-underscore for nested keys:IMBOT_RISK__MAX_POSITIONS=5
Universes (which tickers each bot scans) live in src/indianmarket/universe.py — a single source of truth that replaces the 5 duplicated lists in the old code.
- 69 tests at
src/indianmarket/tests/ - Tests use
IMBOT_HOMEoverride +tmp_pathto isolate per-test DuckDB files - One golden value (
test_rsi_golden_value) pins indicator semantics — bump intentionally if changed
core/purity. Addingprint/import rich/import yfinanceto anything undercore/will eventually break the daemon (which expects pure functions) and the tests (which run without network). Hold the line.- DuckDB writer lock. Never
awaitwhile holding a write connection — it serializes the whole daemon. Keep transactions small. - yfinance is flaky.
data.yahooreturnsNoneon terminal failure; callers must handle it. Don't "improve" by raising — silent degradation is intentional. - NSE holidays expire. The hardcoded list in
market_calendar.pycovers 2024-2026; extend yearly. - Survivorship bias in
universe.pyticker lists is a known limitation, deliberately not fixed in v1 (needs point-in-time index membership data). - No broker integration. Bots emit signals; execution is the user's job. Future broker module reads from the
signalstable.