Backtest = strategy ki time machine. Aap apna logic past data pe chala ke dekhte ho — kya hota agar live chalata. Yahaan Pandas se simple backtest banayenge.
Step 1 — Data nikalo
from kiteconnect import KiteConnect
import pandas as pd
kite = KiteConnect(api_key="key")
kite.set_access_token("token")
candles = kite.historical_data(
instrument_token=738561, # RELIANCE
from_date="2021-01-01",
to_date="2026-04-01",
interval="day"
)
df = pd.DataFrame(candles)
df.set_index('date', inplace=True)
df.to_csv('reliance_5y.csv')
Step 2 — Indicators add karo
df['ema20'] = df['close'].ewm(span=20).mean()
df['ema50'] = df['close'].ewm(span=50).mean()
df['signal'] = 0
df.loc[df['ema20'] > df['ema50'], 'signal'] = 1 # BUY
df.loc[df['ema20'] < df['ema50'], 'signal'] = -1 # SELL
Step 3 — Returns calculate karo
df['returns'] = df['close'].pct_change()
df['strategy_returns'] = df['signal'].shift(1) * df['returns'] # shift to avoid look-ahead bias
df['cum_strategy'] = (1 + df['strategy_returns']).cumprod()
df['cum_buy_hold'] = (1 + df['returns']).cumprod()
Step 4 — Metrics
import numpy as np
# Total return
total_return = df['cum_strategy'].iloc[-1] - 1
print(f"Total return: {total_return*100:.2f}%")
# Annualized return
years = len(df) / 252
ann_return = (df['cum_strategy'].iloc[-1] ** (1/years)) - 1
print(f"Annualized: {ann_return*100:.2f}%")
# Sharpe ratio
sharpe = np.sqrt(252) * df['strategy_returns'].mean() / df['strategy_returns'].std()
print(f"Sharpe: {sharpe:.2f}")
# Max drawdown
cum = df['cum_strategy']
running_max = cum.cummax()
drawdown = (cum - running_max) / running_max
max_dd = drawdown.min()
print(f"Max drawdown: {max_dd*100:.2f}%")
Step 5 — Visualize
import matplotlib.pyplot as plt
plt.figure(figsize=(12, 6))
plt.plot(df.index, df['cum_strategy'], label='Strategy')
plt.plot(df.index, df['cum_buy_hold'], label='Buy & Hold')
plt.legend()
plt.title('Equity Curve')
plt.show()
Common backtest mistakes
- Look-ahead bias — future data leak hota hai signal mein.
shift(1)use karo. - Survivorship bias — sirf successful stocks pe test. Delisted stocks bhi include karo.
- No slippage/brokerage — real life mein 0.05-0.1% nikal jaata hai. Subtract karo.
- Over-fitting — params perfect tune kar diye. Walk-forward validation karo.
- Ignoring market regime — bull mein perfect, bear mein fail.
Realistic backtest with costs
BROKERAGE = 0.0003 # 0.03%
SLIPPAGE = 0.0005 # 0.05%
df['trade'] = df['signal'].diff().abs()
df['costs'] = df['trade'] * (BROKERAGE + SLIPPAGE)
df['net_returns'] = df['strategy_returns'] - df['costs']
df['cum_net'] = (1 + df['net_returns']).cumprod()
print(f"Net total: {(df['cum_net'].iloc[-1]-1)*100:.2f}%")
Walk-forward validation
Better than simple backtest: data ko time-windows mein todho. Pehla 70% pe train (params tune), baki 30% pe test. Repeat for multiple windows.
Pro libraries
- backtrader — full-featured, event-driven
- vectorbt — fastest, vectorized
- zipline-reloaded — Quantopian wala
- bt — simple aur clean
Conclusion
Achha backtest = honest backtest. Costs include karo, look-ahead bias se bachao, multiple regimes pe test karo. Hum custom strategy backtest karke deten hain — full report ke saath.
FAQs
Backtest results live mein different kyu aate hain?
Slippage + brokerage + over-fitting + market regime change. Real market backtest se hamesha different behave karta hai. Past data koi indicator of future behaviour nahi hota.
Kitne saal ka data backtest ke liye chahiye?
Minimum 3-5 saal — alag-alag market conditions cover ho.
Need a custom solution?
Instacode builds production-grade software — algo trading, ecommerce, web apps. Let's talk.
Get in Touch
💬 Comments (0)
Be the first to comment 🚀