Sensex Backtesting for Free in Python (^BSESN Walkthrough)
By Shakti Tiwari — Nifty Option Trader, Research Analyst & XGBoost Expert
The BSE Sensex is India's oldest benchmark, and you can backtest strategies on it for free — no Bloomberg terminal, no paid data vendor. Yahoo Finance carries the index as ^BSESN, and with yfinance plus pandas you can build an honest backtest in under 60 lines. This walkthrough is code-first and India-specific, and it flags the traps that make Sensex backtests lie.
Step 1: Pull Free Sensex Data with the Right Ticker
pip install yfinance pandas numpy
The correct symbol for the S&P BSE Sensex on Yahoo is ^BSESN (not SENSEX). Get it wrong and you'll download nothing or the wrong series.
import yfinance as yf
import pandas as pd
import numpy as np
df = yf.download("^BSESN", start="2010-01-01", end="2024-12-31", auto_adjust=True)
df = df.dropna()
print(df.head(), df.tail(), sep="\n\n")
print(f"Total trading days: {len(df)}")
Sensex history on Yahoo goes back well over a decade — enough to cover multiple market regimes (2013 taper tantrum, 2020 COVID crash, 2021 bull run), which is exactly what you want a strategy to survive.
Step 2: Define a Strategy You Can Verify by Hand
We'll use RSI mean-reversion — buy when the index is oversold, exit when it recovers. Simple enough to sanity-check, and it behaves differently from a trend strategy, so it's a good second tool in your kit.
def rsi(series, period=14):
delta = series.diff()
gain = delta.clip(lower=0).rolling(period).mean()
loss = (-delta.clip(upper=0)).rolling(period).mean()
rs = gain / (loss + 1e-9)
return 100 - (100 / (1 + rs))
df["rsi"] = rsi(df["Close"], 14)
df["signal"] = np.where(df["rsi"] < 30, 1, np.where(df["rsi"] > 55, 0, np.nan))
df["signal"] = df["signal"].ffill().fillna(0)
The ffill holds the position between an entry (RSI < 30) and an exit (RSI > 55) — a common, realistic way to model a mean-reversion trade.
Step 3: Prevent Lookahead — Shift the Position
This is where most free backtests go wrong. You act on a signal only on the next bar, never the same bar it was generated.
df["position"] = df["signal"].shift(1)
df["mkt_ret"] = df["Close"].pct_change()
df["strat_ret"] = df["position"] * df["mkt_ret"]
df = df.dropna()
If you skip the .shift(1), your RSI strategy will look like it perfectly bought every bottom — because it's cheating with future data.
Step 4: Subtract Real Costs
Every entry and exit costs money: brokerage, STT, exchange fees, and slippage. Charge them on each position change.
COST = 0.0005 # ~5 bps round-trip proxy; set to your Zerodha/Angel reality
df["trade"] = df["position"].diff().abs()
df["strat_net"] = df["strat_ret"] - df["trade"] * COST
Step 5: Equity Curve and Honest Metrics
df["equity"] = (1 + df["strat_net"]).cumprod()
df["buyhold"] = (1 + df["mkt_ret"]).cumprod()
def metrics(r, periods=252):
r = r.dropna()
cagr = (1 + r).prod() ** (periods / len(r)) - 1
sharpe = np.sqrt(periods) * r.mean() / (r.std() + 1e-9)
eq = (1 + r).cumprod()
maxdd = (eq / eq.cummax() - 1).min()
return {"CAGR": round(cagr,4), "Sharpe": round(sharpe,2), "MaxDD": round(maxdd,4)}
print("Sensex RSI strategy:", metrics(df["strat_net"]))
print("Sensex buy & hold: ", metrics(df["mkt_ret"]))
I won't quote a specific CAGR — your window and cost assumptions change it, and a fixed number would be a fabricated stat. Run the code and read your output.
Sensex vs Nifty: Practical Differences
- The Sensex has 30 stocks; the Nifty 50 has 50. Sensex is more concentrated in large-cap heavyweights.
- Yahoo's
^BSESNand^NSEIare highly correlated — a strategy that works on one usually behaves similarly on the other. Test both to check robustness. - There is no liquid Sensex options market comparable to Nifty. Sensex derivatives exist but Nifty F&O is far deeper, so most Indian derivative traders execute on Nifty even if they research on Sensex.
Where Free Data Ends
- Yahoo
^BSESNis end-of-day and can have occasional missing sessions. Alwaysdropna()and check date continuity. - Intraday Sensex history on Yahoo is very limited. For minute-level BSE data use Angel One SmartAPI (free, generous history) or Zerodha Kite Connect (paid). Both give exchange-grade candles.
Pitfalls Checklist
- Wrong ticker (
SENSEXinstead of^BSESN) → empty download. - Missing
.shift(1)→ lookahead, inflated results. - Zero-cost assumption → mean-reversion strategies especially over-trade and die once costs are real.
- Testing a single date range → validate across regimes or use walk-forward analysis.
Takeaway
- Backtest the Sensex for free using
^BSESNwithyfinance+pandas. - Get the ticker right,
dropna(), and always.shift(1)your position. - Net out realistic Indian costs; RSI mean-reversion is cost-sensitive.
- Judge with Sharpe and max drawdown, never final return alone.
- For intraday BSE data beyond Yahoo's limits, use Angel SmartAPI or Zerodha Kite.
Related: My book Option Trading with AI: XGBoost, Transformers & Quantized Models for the Retail Nifty Trader shows retail traders how to research and validate strategies with free tools.
Disclaimer: This article is for education only and is not financial, investment, or trading advice. SEBI-registered research rules apply — verify everything before acting.
🔗 Get the book on Amazon: https://www.amazon.in/dp/B0H9ZNTBPK
📢 Daily Nifty analysis on Telegram: https://t.me/shaktitrade
📧 Email: shaktitiwari715@gmail.com
💬 Need Help? (Free Support)
If you have any trouble building your own AI trading model, or questions about any article — I can help you for free.
📧 Email: shaktitiwari715@gmail.com
📢 Telegram: https://t.me/shaktitrade
No course, no upsell — just genuine help for retail traders.

Top comments (0)