Python for Algo Trading — A Practical Starting Point
Setting up your first systematic trading script using Python, yfinance, and TA-Lib — from data fetch to basic signal generation.
Why Python for Algo Trading?
Python has become the dominant language for systematic trading for good reasons:
- Rich ecosystem. Libraries like
pandas,numpy,yfinance,TA-Lib, andbacktraderhandle everything from data fetching to backtesting. - Readability. Strategies expressed in Python closely resemble their verbal descriptions, making bugs easier to spot.
- Community. Thousands of open examples exist for options pricing, backtesting, and broker API integration.
- Broker APIs. Zerodha’s Kite Connect, IBKR’s
ib_insync, and Alpaca all have well-maintained Python SDKs.
The downside: Python is not the fastest language. For high-frequency strategies (milliseconds matter), C++ or Java are preferred. For swing trading, daily algos, and overnight position management — Python is more than sufficient.
Setting Up Your Environment
# Create a virtual environment (recommended)
python3 -m venv trading-env
source trading-env/bin/activate # Mac/Linux
# trading-env\Scripts\activate # Windows
# Install core packages
pip install pandas numpy yfinance ta-lib-binary matplotlib jupyter
Note on TA-Lib: The standard
TA-Libpackage requires C library dependencies that can be tricky on some systems.ta-lib-binaryis a pre-compiled alternative that installs with a simplepip install.
Fetching Market Data with yfinance
yfinance pulls historical OHLCV data from Yahoo Finance. It is free, requires no API key, and covers most global markets including NSE/BSE indices (though NSE equity data quality varies).
import yfinance as yf
import pandas as pd
# Download NIFTY 50 daily data for the past 2 years
nifty = yf.download("^NSEI", period="2y", interval="1d")
print(nifty.tail())
# Rename columns for easier use
nifty.columns = ['Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume']
nifty = nifty.dropna()
For US stocks, simply use the ticker symbol: yf.download("AAPL", period="1y").
For ASX: yf.download("BHP.AX", period="1y").
Calculating Indicators with TA-Lib
TA-Lib is the industry standard library for technical indicators. It provides over 150 functions including all major oscillators, moving averages, and candlestick pattern recognition.
import talib
import numpy as np
close = nifty['Close'].values.astype(float)
high = nifty['High'].values.astype(float)
low = nifty['Low'].values.astype(float)
# RSI (14-period)
nifty['RSI'] = talib.RSI(close, timeperiod=14)
# MACD
nifty['MACD'], nifty['MACD_signal'], nifty['MACD_hist'] = talib.MACD(
close, fastperiod=12, slowperiod=26, signalperiod=9
)
# ATR (Average True Range) — for stop placement
nifty['ATR'] = talib.ATR(high, low, close, timeperiod=14)
# Bollinger Bands
nifty['BB_upper'], nifty['BB_mid'], nifty['BB_lower'] = talib.BBANDS(
close, timeperiod=20, nbdevup=2, nbdevdn=2
)
print(nifty[['Close','RSI','MACD','ATR']].tail(10))
A Simple Mean Reversion Signal
The following generates a basic signal: go long when RSI is oversold and price is near the lower Bollinger Band; exit when RSI recovers above 50.
# Signal generation
nifty['signal'] = 0
# Long entry: RSI < 35 AND price near lower Bollinger Band
entry_condition = (nifty['RSI'] < 35) & (nifty['Close'] < nifty['BB_lower'] * 1.01)
# Exit: RSI > 50
exit_condition = nifty['RSI'] > 50
nifty.loc[entry_condition, 'signal'] = 1 # Enter long
nifty.loc[exit_condition, 'signal'] = -1 # Exit
# Forward fill to hold position
nifty['position'] = nifty['signal'].replace(0, np.nan).ffill().fillna(0)
nifty.loc[nifty['signal'] == -1, 'position'] = 0
# Daily returns
nifty['market_return'] = nifty['Close'].pct_change()
nifty['strategy_return'] = nifty['position'].shift(1) * nifty['market_return']
# Cumulative performance
nifty['cum_market'] = (1 + nifty['market_return']).cumprod()
nifty['cum_strategy'] = (1 + nifty['strategy_return']).cumprod()
print(f"Market return: {(nifty['cum_market'].iloc[-1]-1)*100:.1f}%")
print(f"Strategy return: {(nifty['cum_strategy'].iloc[-1]-1)*100:.1f}%")
Important: This is a simplified demonstration. Real backtesting must account for transaction costs, slippage, look-ahead bias, and survivorship bias. Use a proper framework like
backtraderorvectorbtfor robust testing.
Candlestick Pattern Detection with TA-Lib
TA-Lib includes 61 candlestick pattern recognition functions. They return 100 (bullish signal), -100 (bearish signal), or 0 (no pattern).
# Detect Hammer pattern
nifty['hammer'] = talib.CDLHAMMER(
nifty['Open'].values.astype(float),
nifty['High'].values.astype(float),
nifty['Low'].values.astype(float),
nifty['Close'].values.astype(float)
)
# Detect Bullish Engulfing
nifty['engulfing'] = talib.CDLENGULFING(
nifty['Open'].values.astype(float),
nifty['High'].values.astype(float),
nifty['Low'].values.astype(float),
nifty['Close'].values.astype(float)
)
# Show dates where hammer appeared
hammer_days = nifty[nifty['hammer'] == 100]
print(f"Hammer patterns found: {len(hammer_days)}")
print(hammer_days[['Close','RSI','hammer']].tail())
Connecting to a Broker (Paper Trading First)
For Indian markets, Zerodha Kite Connect provides a Python SDK. For US markets, IBKR has ib_insync. Both support paper trading accounts.
Golden rule from this project’s conventions: Always test on paper first. Never send live orders until the strategy has run in paper mode for at least one full market cycle. Always implement a daily kill switch and per-trade loss limits.
# Example: print-only order (never live without explicit flag)
LIVE_TRADING = False # Flip only when you are ready
def place_order(symbol, qty, side):
if not LIVE_TRADING:
print(f"[PAPER] {side} {qty} {symbol}")
return
# broker.order_place(symbol=symbol, qty=qty, side=side)
place_order("NIFTY", 75, "BUY")
# Output: [PAPER] BUY 75 NIFTY
Next Steps
- Learn
vectorbtorbacktraderfor proper backtesting with transaction costs and slippage - Study look-ahead bias — the single most common error in retail backtests (using future data in signal generation)
- Paper trade for 30–60 days before considering live deployment
- Read the IBKR and Kite Connect API docs thoroughly before connecting real capital
The code in this project’s IBKR_Algo/ directory provides a production-grade framework with YAML config, risk management, and paper/live switching. That is the right foundation to build on.
This post is for educational purposes only. Algo trading involves significant risk. Never trade with capital you cannot afford to lose.