Course contents
The stocks that vanished
A backtest run only on the stocks that still exist today quietly excludes every company that failed, inflating results by ignoring the losers, which is survivorship bias. This chapter defines a tradable universe honestly, including delisted and dead stocks, and covers India-specific realities like liquidity filters and the futures-and-options ban period.
- Define a tradable universe and the rules that shape it (liquidity, listing)
- Explain survivorship bias and how it inflates a backtest
- Account for delisted stocks and Indian universe quirks
Suppose you test a strategy on the stocks in the NIFTY 50 today, over the last twenty years. It looks great. It is also a lie, because the NIFTY 50 of today contains only companies that succeeded enough to still be there. Every company that failed, got delisted, or went bust in those twenty years is silently missing from your test. This is survivorship bias, and it is one of the quietest and most damaging ways a backtest flatters itself.
Defining a universe
A universe is the set of instruments you consider trading, and defining it is a real design choice. It might be the largest 200 stocks by size, or every stock above a liquidity threshold, or a sector. Two rules make a universe honest. First, it should filter for tradability: a stock so thinly traded that you could never buy it without moving the price has no business in a backtest that assumes you filled at the market price. Second, and this is the subtle one, the universe must be defined by rules you could have applied at each point in the past, using only what was known then, not chosen with hindsight from what survived.
Survivorship bias
The clearest way to see survivorship bias is to measure it.
# 100 illustrative stocks over 10 years. Some go bust and are delisted. A backtest
# that uses only the stocks still listed today never sees the failures, which
# inflates the measured return. That is survivorship bias, and it is one of the
# quietest ways a backtest lies.
import numpy as np
rng = np.random.default_rng(3)
n_stocks = 100
survivor_returns = rng.normal(0.80, 0.5, n_stocks) # 10-year returns if they survive
died = rng.random(n_stocks) < 0.25 # a quarter go bust
true_returns = np.where(died, -1.0, survivor_returns) # a dead stock loses everything
full_universe_avg = true_returns.mean() # the honest average
survivors_only_avg = true_returns[~died].mean() # only the still-listed stocks
print(f"Stocks: {n_stocks}, of which {int(died.sum())} went bust over 10 years")
print(f"Average 10-year return, FULL universe (with the dead): {full_universe_avg * 100:6.1f}%")
print(f"Average 10-year return, SURVIVORS ONLY: {survivors_only_avg * 100:6.1f}%")
print(f"Survivorship bias inflated the result by "
f"{(survivors_only_avg - full_universe_avg) * 100:.1f} percentage points")
print("\nA backtest on today's listed stocks silently excludes every failure.")
print("Always test on a universe that includes delisted, dead stocks.")Stocks: 100, of which 20 went bust over 10 years Average 10-year return, FULL universe (with the dead): 42.7% Average 10-year return, SURVIVORS ONLY: 78.4% Survivorship bias inflated the result by 35.7 percentage points A backtest on today's listed stocks silently excludes every failure. Always test on a universe that includes delisted, dead stocks.
Of 100 stocks over ten years, 20 went bust and were delisted, losing everything. Include them, as reality would, and the universe averaged a 42.7% ten-year return. Look only at the 80 stocks still listed today, as a careless backtest does, and the average leaps to 78.4%, an inflation of nearly 36 percentage points out of thin air. The strategy did nothing; the bias did all the work, simply by never letting the failures into the sample. A backtest that pulls "the current NIFTY 500" and runs it over history makes exactly this error, and the more turbulent the period, the bigger the lie. The fix is a point-in-time universe that includes delisted and dead stocks, which good data vendors provide and careless pipelines silently omit.
India-specific universe realities
Three Indian realities shape a real universe. Many listed stocks are barely traded, so a sensible universe applies a liquidity filter, and your backtest must respect it or it will assume fills it could never have got. When a stock's derivatives hit their position limit, the exchange places it in a futures-and-options ban period during which new positions are not allowed, so a strategy that would have entered during a ban could not really have traded. And stocks are delisted, merged, and occasionally go to zero, so an honest Indian universe includes the dead, not just the survivors on the screen today.
What to carry forward
Your choice of universe is a design decision that can quietly ruin a backtest. Survivorship bias, testing only on the stocks still listed today, excludes every failure and inflates results out of thin air, as you saw a true 42.7% become a flattering 78.4% simply by dropping the dead. An honest universe is defined point-in-time, includes delisted and bankrupt stocks, filters for genuine tradability, and respects Indian realities like liquidity and the F&O ban period. With clean, adjusted, point-in-time, survivorship-free data in hand, one property of returns still trips up almost every model, and the last chapter of this part confronts it: returns are not a bell curve.