A dark "quant terminal" for backtesting intraday NIFTY/SENSEX options strategies.
This folder is self-contained — the backtesting engine, strategies, FastAPI backend,
and React frontend all live here, so you can commit/clone just dashboard/.
dashboard/
backtest/ the engine (replays a strategy day-by-day off cached candles)
strategies/ strategy modules (example_strategy.py is the committed template)
backend/ FastAPI — strategy discovery, background jobs, analytics, persistence
frontend/ React + Vite + Tailwind v4 (Config → Running → Results → History → Compare)
# from this folder
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
cp .env.example .env # add your Groww keys (only needed to FETCH uncached ranges)
cd frontend && npm install && cd ..
./run.sh # backend :8000 + frontend :5173 → open http://localhost:5173Cached date ranges run fully offline (no creds). Uncached ranges are fetched from
Groww on demand and cached to backtest/cache/ for next time.
The backend has no auto-reload — restart it after editing
backend/*.pyorbacktest/*.py. The frontend hot-reloads.
- New backtest — pick a strategy, underlying, date range, capital, interval, compound; optionally override parameters. A badge shows whether the range is cached (offline) or needs Groww keys.
- Running — the run is a background job; the UI streams progress (per-day) and the engine log, with a Stop button.
- Results — summary cards, equity curve + underwater drawdown, a full risk panel, Yearly/Monthly-heatmap/Daily breakdown, time-of-day edge, and the order tape. A Gross / Net of STT toggle shows P&L before/after the 0.15% options STT.
- History — sortable leaderboard of past runs (running jobs show live; failed runs show their logs + a Retry; Clear-history button). Same Gross/Net-of-STT toggle.
- Compare — overlay two runs' equity curves with a stat-by-stat comparison.
- Pages are URL-addressable (
#/history,#/compare,#/run/<id>) so refresh/back work.
Strategy modules live in strategies/ and must follow the engine's convention:
a module-level UNDERLYING = "NIFTY"|"SENSEX" and a main() the engine calls once per
trading day, talking to the broker only through the openalgo client. UPPERCASE
module-level literals become overridable parameters in the UI.
strategies/example_strategy.pyis a minimal, documented template — copy it.- Add one in-app ("Add a strategy" on the New-backtest screen → paste/upload
.py; validated byPOST /api/strategiesand written tostrategies/), or drop a file instrategies/manually.
Only example_strategy.py is committed — real strategies, the candle cache, results,
tokens, and runtime persistence are gitignored (see .gitignore). Keep your alphas private.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/strategies |
discovered strategies + cache coverage |
| POST | /api/strategies |
validate + save a pasted/uploaded strategy |
| GET | /api/cache-status |
is a range cached (offline) or needs keys |
| POST | /api/backtest |
dispatch a background run → {job_id} |
| GET | /api/jobs/{job_id} |
poll status / log / result |
| POST | /api/jobs/{job_id}/cancel |
request cancellation |
| GET | /api/history |
running jobs + persisted past runs |
| DELETE | /api/history |
clear all history |
| GET | /api/runs/{run_id} |
re-view a persisted run |
See CLAUDE.md for architecture details and the non-obvious gotchas (offline cache
reuse, serialized jobs, lightweight-charts v5, STT model, hash routing).