Skip to content
All projects
Tools & Dashboards

23-Indicator Combination Backtester on the NSE Universe

indicator-backtest

Computes 23 technical indicators (SMA, EMA, MACD, Parabolic SAR, RSI, Stochastic, CCI, ROC, Bollinger Bands, ATR, SuperTrend, OBV, Chaikin Money Flow, Volume RSI, Ichimoku Cloud and others), reduces each to a -1/0/1 signal, and backtests every 1–4 indicator combination — 10,902 in total — against roughly 330 NSE stocks under both strict-AND and majority-vote logic, since 2009.

engine.py does the heavy compute and writes a parquet file hosted externally on Hugging Face Spaces rather than committed to GitHub — a deliberate architecture choice. app.py is a Streamlit dashboard with Global Overview, Stock Analysis and Strategy Explorer views, reading only from that parquet so it deploys without any of the heavy backtest dependencies.

Independently verified correct

Wilder RSI/ATR smoothing, the AND/majority vectorized combination logic, the 1-bar execution lag (no look-ahead bias), Parabolic SAR's flip and AF-reset logic, and Ichimoku's forward-shift direction. Confirmed by a real end-to-end engine run producing 130,830 result rows with zero infinities, plus Streamlit's AppTest harness exercising every tab, stock and metric.

Fixed in review

  • RSI (and Volume-RSI, which reuses the same helper) went NaN during zero-down-day rallies instead of correctly saturating at 100, because a NaN guard on the denominator caught the valid all-gains case too.
  • A single flat (High == Low) trading day silently NaN'd an entire 20-day rolling Chaikin Money Flow window — pandas propagates one NaN across a whole rolling-sum window, so one bad day was killing about 19 days of output.
  • The dashboard could crash on real data: AND and MAJORITY logic are mathematically identical for 2-indicator combos, so the same combo string can legitimately rank twice in one top-N table, producing a duplicate DataFrame index that broke Streamlit's cell styling. Reproduced live and fixed.
  • The .gitignore did not match the actual cache directory the code writes to, so the large cache files it was meant to exclude would have been silently re-committed.
  • A stale "15 indicators / 1,940 combinations" claim in the README and UI, left from an earlier engine, corrected to the real 23-indicator / 10,902-combination figure.
BacktestingParquetStreamlitVectorized
Next projectnifty-beta-clustering