P
PipsGrowth
← Back to Libraries

Zipline for Algorithmic Trading

A Pythonic algorithmic trading library originally developed by Quantopian. Features a powerful Pipeline API for factor-based strategies and calendar-aware simulation capabilities.

Difficulty: Advanced
Category: Backtesting
📊 Quant-Grade

Installation

Use zipline-reloaded, the maintained fork of Zipline that supports Python 3.8+.

# Install zipline-reloaded
$ pip install zipline-reloaded
# Ingest data bundle
$ zipline ingest -b quandl
# Or use Yahoo data
$ pip install zipline-reloaded[yahoo]

Algorithm Structure

Required Functions

  • initialize()- Called once at start
  • handle_data()- Called every bar

Optional Functions

  • before_trading_start()- Pre-market
  • analyze()- Post-backtest

Key Features

Pipeline API

Powerful factor-based screening system for building quantitative strategies across large universes.

Calendar-Aware

Built-in trading calendar handling with schedule_function for precise timing of trades.

Data Bundles

Efficient data storage and retrieval system with support for multiple data sources.

Pyfolio Integration

Seamless integration with pyfolio for comprehensive performance analysis and tear sheets.

Code Examples

Basic Trading Algorithm

Simple moving average crossover strategy

Python
from zipline.api import order_target_percent, symbol, record
from zipline import run_algorithm
import pandas as pd
def initialize(context):
"""Called once at the start of the algorithm."""
context.asset = symbol('AAPL')
context.short_window = 20
context.long_window = 50
def handle_data(context, data):
"""Called for every bar of data."""
# Get historical prices
short_mavg = data.history(context.asset, 'price', context.short_window, '1d').mean()
long_mavg = data.history(context.asset, 'price', context.long_window, '1d').mean()
# Trading logic
if short_mavg > long_mavg:
# Go long
order_target_percent(context.asset, 1.0)
elif short_mavg < long_mavg:
# Exit position
order_target_percent(context.asset, 0.0)
# Record for analysis
record(
short_mavg=short_mavg,
long_mavg=long_mavg,
price=data.current(context.asset, 'price')
)
# Run the algorithm
start = pd.Timestamp('2020-01-01', tz='utc')
end = pd.Timestamp('2023-12-31', tz='utc')
results = run_algorithm(
start=start,
end=end,
initialize=initialize,
handle_data=handle_data,
capital_base=100000,
bundle='quandl'
)

Pipeline API for Screening

Screen stocks using factor-based criteria

Python
from zipline.api import attach_pipeline, pipeline_output, order_target_percent
from zipline.pipeline import Pipeline
from zipline.pipeline.factors import SimpleMovingAverage, Returns
from zipline.pipeline.filters import QTradableStocksUS
from zipline.pipeline.data import USEquityPricing
def make_pipeline():
"""Create a pipeline for stock screening."""
# Define factors
sma_20 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=20)
sma_50 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=50)
# Momentum factor
momentum = Returns(window_length=252)
# Universe filter
universe = QTradableStocksUS()
# Screen for trend + momentum
longs = (sma_20 > sma_50) & (momentum > 0.1) & universe
shorts = (sma_20 < sma_50) & (momentum < -0.1) & universe
return Pipeline(
columns={
'sma_20': sma_20,
'sma_50': sma_50,
'momentum': momentum,
'longs': longs,
'shorts': shorts
},
screen=universe
)
def initialize(context):
attach_pipeline(make_pipeline(), 'my_pipeline')
context.long_positions = []
def before_trading_start(context, data):
"""Get pipeline results before market opens."""
output = pipeline_output('my_pipeline')
# Select top 10 stocks by momentum
longs = output[output['longs']].nlargest(10, 'momentum')
context.long_positions = longs.index.tolist()
def handle_data(context, data):
# Equal weight portfolio
weight = 1.0 / len(context.long_positions) if context.long_positions else 0
for stock in context.long_positions:
order_target_percent(stock, weight)

Create Custom Factors

Build your own trading factors

Python
from zipline.pipeline import CustomFactor
from zipline.pipeline.data import USEquityPricing
import numpy as np
class Volatility(CustomFactor):
"""Calculate historical volatility."""
inputs = [USEquityPricing.close]
window_length = 20
def compute(self, today, assets, out, close):
returns = np.diff(np.log(close), axis=0)
out[:] = np.nanstd(returns, axis=0) * np.sqrt(252)
class RSI(CustomFactor):
"""Relative Strength Index."""
inputs = [USEquityPricing.close]
window_length = 15
def compute(self, today, assets, out, close):
diff = np.diff(close, axis=0)
gains = np.where(diff > 0, diff, 0)
losses = np.where(diff < 0, -diff, 0)
avg_gain = np.mean(gains[-14:], axis=0)
avg_loss = np.mean(losses[-14:], axis=0)
rs = avg_gain / (avg_loss + 1e-10)
out[:] = 100 - (100 / (1 + rs))
class MACD(CustomFactor):
"""MACD histogram."""
inputs = [USEquityPricing.close]
window_length = 35
def compute(self, today, assets, out, close):
# EMA calculation
def ema(data, span):
alpha = 2 / (span + 1)
result = np.zeros(data.shape[1])
result[:] = data[0]
for i in range(1, len(data)):
result = alpha * data[i] + (1 - alpha) * result
return result
ema_12 = ema(close[-26:], 12)
ema_26 = ema(close, 26)
macd_line = ema_12 - ema_26
out[:] = macd_line
# Use in pipeline
def make_pipeline():
vol = Volatility()
rsi = RSI()
macd = MACD()
return Pipeline(
columns={
'volatility': vol,
'rsi': rsi,
'macd': macd
}
)

Risk Management

Implement position sizing and risk controls

Python
from zipline.api import (
order_target_percent,
get_open_orders,
cancel_order,
symbol,
set_max_position_size,
set_max_order_size,
set_max_leverage
)
import numpy as np
def initialize(context):
context.assets = [symbol('AAPL'), symbol('GOOGL'), symbol('MSFT')]
# Risk limits
set_max_position_size(max_fraction=0.25) # Max 25% per position
set_max_order_size(max_fraction=0.10) # Max 10% per order
set_max_leverage(1.0) # No leverage
context.max_drawdown = -0.15 # 15% max drawdown
context.high_water_mark = 0
def handle_data(context, data):
portfolio_value = context.portfolio.portfolio_value
# Update high water mark
if portfolio_value > context.high_water_mark:
context.high_water_mark = portfolio_value
# Calculate drawdown
drawdown = (portfolio_value - context.high_water_mark) / context.high_water_mark
# Risk-off if drawdown exceeds limit
if drawdown < context.max_drawdown:
# Liquidate all positions
for asset in context.assets:
order_target_percent(asset, 0)
return
# Position sizing based on volatility
for asset in context.assets:
if data.can_trade(asset):
# Calculate volatility
returns = data.history(asset, 'price', 20, '1d').pct_change().dropna()
vol = returns.std() * np.sqrt(252)
# Inverse volatility sizing
target_vol = 0.15
weight = min(target_vol / vol / len(context.assets), 0.25)
order_target_percent(asset, weight)

Schedule Functions

Run code at specific times

Python
from zipline.api import (
schedule_function,
date_rules,
time_rules,
order_target_percent,
symbol,
record
)
def initialize(context):
context.assets = [symbol('AAPL'), symbol('GOOGL')]
# Rebalance weekly on Monday at market open
schedule_function(
rebalance,
date_rules.week_start(days_offset=0),
time_rules.market_open()
)
# Record stats every day at close
schedule_function(
record_stats,
date_rules.every_day(),
time_rules.market_close(minutes=5)
)
# Monthly portfolio review
schedule_function(
monthly_review,
date_rules.month_end(),
time_rules.market_close()
)
def rebalance(context, data):
"""Weekly rebalancing logic."""
for asset in context.assets:
if data.can_trade(asset):
# Equal weight
order_target_percent(asset, 0.5)
def record_stats(context, data):
"""Record daily statistics."""
record(
portfolio_value=context.portfolio.portfolio_value,
positions_count=len(context.portfolio.positions),
cash=context.portfolio.cash
)
def monthly_review(context, data):
"""Monthly performance review."""
print(f"Month End Portfolio Value: {context.portfolio.portfolio_value:,.2f} USD")
def handle_data(context, data):
# Can be empty if using schedule_function for all logic
pass

Analyze Backtest Results

Extract and visualize performance metrics

Python
import pyfolio as pf
import matplotlib.pyplot as plt
import numpy as np
# After running algorithm
results = run_algorithm(...)
# Get returns
returns = results['returns']
positions = results['positions']
transactions = results['transactions']
# Create tear sheet
pf.create_full_tear_sheet(
returns,
positions = positions,
transactions = transactions
)
# Key metrics manually
total_return = (1 + returns).cumprod()[-1] - 1
print(f"Total Return: {total_return:.2%}")
# Sharpe ratio
sharpe = returns.mean() / returns.std() * np.sqrt(252)
print(f"Sharpe Ratio: {sharpe:.2f}")
# Max drawdown
cumulative = (1 + returns).cumprod()
running_max = cumulative.expanding().max()
drawdown = (cumulative - running_max) / running_max
print(f"Max Drawdown: {drawdown.min():.2%}")
# Plot equity curve
fig, axes = plt.subplots(3, 1, figsize = (12, 10))
# Equity curve
cumulative.plot(ax = axes[0], title = 'Equity Curve')
# Drawdown
drawdown.plot(ax = axes[1], title = 'Drawdown', color = 'red')
# Rolling Sharpe
rolling_sharpe = returns.rolling(63).mean() / returns.rolling(63).std() * np.sqrt(252)
rolling_sharpe.plot(ax = axes[2], title = 'Rolling Sharpe (63 days)')
plt.tight_layout()
plt.show()

Common Use Cases

Factor-based investing
Cross-sectional strategies
Quantitative research
Portfolio backtesting
Alpha factor testing
Risk model development
Long-short equity
Statistical arbitrage
Momentum strategies
Academic research

Best Practices & Common Pitfalls

Use schedule_function

Schedule code to run at specific times instead of checking conditions every handle_data call.

Leverage the Pipeline

Use Pipeline API for cross-sectional analysis instead of looping through securities manually.

Check data.can_trade()

Always verify an asset is tradeable before placing orders to handle delistings and halts.

Data Bundle Setup

Zipline requires data bundle ingestion before running. Run zipline ingest -b <bundle> first.

Python Version

Zipline has specific Python version requirements. Use zipline-reloaded for Python 3.8+ support.

Memory Usage

Large universes with many factors can consume significant memory. Filter early in pipeline.

Additional Resources

Related Tools

  • pyfolio - Performance analysis
  • alphalens - Factor analysis
  • empyrical - Risk metrics

Next Steps

Move from backtesting to live trading or explore other frameworks: