A backtesting research framework for Indian equities (NSE, daily bars), built to answer one question honestly: which trading claims survive out-of-sample testing after real Indian transaction costs?
This is a research framework, not a trading system. There is no live execution, no broker integration, and none is planned.
| Layer | Location | Purpose |
|---|---|---|
| Data | quant/data/ |
NSE bhavcopy ingestion, corporate-action adjustment, point-in-time universe |
| Engine | quant/engine/ |
Vectorized backtest with next-bar execution, full Indian cost stack, risk limits |
| Strategies | quant/strategies/ |
Momentum, MA crossover, mean reversion — one shared interface |
| Signals | quant/signals/ |
Free alt-data signals (Google Trends, news sentiment) behind the same interface |
| Analytics | quant/analytics/ |
Metrics, walk-forward harness, run export |
| Dashboard | dashboard/ |
Next.js app that renders exported runs from results/ |
| Research | research/ |
Notebooks and the final writeup — they use the library, never contain logic |
- No lookahead: signals computed on day T's close execute at day T+1's open; tested, not assumed.
- No survivorship bias: the universe is the index membership as of each date, not today's list.
- Full cost model: brokerage, STT, exchange charges, GST, stamp duty, plus slippage. All results shown gross vs net.
- Out-of-sample only: every strategy is tuned and reported through a walk-forward harness.
Only official, freely published channels: NSE daily reports (bhavcopy, corporate actions), niftyindices.com (index levels, constituent history), manual CSV exports from Google Trends, and public RSS feeds (headlines scored locally with the Loughran-McDonald dictionary; only derived scores are stored). Raw downloads are gitignored and never redistributed through this repository.
Deliberately excluded: yfinance and pytrends (unofficial scraping), any LLM or paid API, satellite/card-panel data (institutional-only versions of the alt-data idea).
| Strategy | CAGR net | vs gross | Verdict |
|---|---|---|---|
| Buy & hold NIFTYBEES | +11.9% | −0.1 pt | the bar to beat (also the engine-validation run) |
| Momentum 12-1, monthly | +26.8% | −2.2 pt | survives costs |
| MA crossover, monthly | +6.3% | −1.5 pt | loses to the index |
| Mean reversion, weekly | −17.7% | −15.6 pt | destroyed by 41× turnover costs |
Exactly one canonical strategy survives its own contract note.
pip install -e ".[dev]" # Python engine
pytest # run the test suite (integration tests need local data)
cd dashboard && pnpm install && pnpm dev # results dashboardWorks on Python 3.9+; a 3.11+ virtualenv is recommended (3.9 is past end-of-life).
# 1. pull official NSE history (rate-limited, idempotent; ~15 min for 5 years)
python -m quant.data.bhavcopy --start 2021-01-01 --end 2026-07-07
# 2. (optional, improves adjustment coverage) download the CF-CA corporate
# actions CSV from nseindia.com -> Corporate Filings -> Corporate Actions
# into data/raw/corporate_actions/
# 3. run and export backtests
python -m quant.runner buy-and-hold --symbol NIFTYBEES
python -m quant.runner walk-forward --strategy momentum
python -m quant.runner walk-forward --strategy ma-crossover
python -m quant.runner walk-forward --strategy mean-reversion
# 4. view them
cd dashboard && pnpm devFor the alt-data signals: export Google Trends CSVs (region India, 5 years)
into data/raw/trends/ with a mapping.csv (term,symbol), and run the news
collector periodically — python -m quant.signals.sentiment (RSS has no
archive, so sentiment history only exists from the day collection starts).