Backtest a custom momentum strategy with Zipline
Backtest a custom momentum strategy with Zipline
When I first started trading, I thought success meant find the perfect trading strategy.
I spent countless hours backtesting, optimizing, and data mining.
Even trying to build custom momentum strategies.
I thought I had found the holy grail of trading.
But when I traded my strategy live, I lost money.
Over and over again.
It wasn’t until I met an old-time options trader that I learned my mistake.
He was a trader in Chicago’s options pits at the CBOE for 20 years.
He told me something I still remember to this day:
“I make money when others are panicking because they push the prices too far.”
I realized then that trying to brute force optimize backtests was always going to lose me money.
The real answer was to trade inefficiencies.
Inefficiencies are market anomalies that arise from market participants’ irrational behavior.
I use Python to analyze market data and find inefficiencies.
Then I trade them.
And I make money.
Backtest a custom momentum strategy with Zipline
Zipline is a Python library that allows you to backtest trading algorithms using historical data.
It was developed by Quantopian in 2012 and used to manage their $100,000,000 crowdfunded hedge fund.
By using Zipline’s Pipeline API, you can model market inefficiencies and build strategies that trade them. Momentum (or trend) is a strategy used by hedge funds and institutional investors.
Using Zipline’s Pipeline API, you can quickly test different custom momentum strategies.
And the best part?
You can use Zipline like the professionals with Python.
Today, you will implement a momentum trading strategy using the Zipline library in Python. The strategy will buy stocks with strong positive momentum and rebalance the portfolio weekly.
Here's how to get started.
Imports and set up
First, import pandas
to create timestamps and zipline
for implementing the trading strategy. We also set up the number of long positions we'll take and ignore any warnings to keep the output clean.
1import pandas as pd
2
3from zipline import run_algorithm
4from zipline.pipeline import Pipeline, CustomFactor
5from zipline.pipeline.data import USEquityPricing
6from zipline.api import (
7 attach_pipeline,
8 calendars,
9 pipeline_output,
10 date_rules,
11 time_rules,
12 set_commission,
13 set_slippage,
14 order_target_percent,
15 get_open_orders,
16 schedule_function
17)
18
19import warnings
20warnings.filterwarnings("ignore")
21
22N_LONGS = 10
After the imports, load the Zipline extension and download data. You'll need a free API from Nasdaq Data Link, which you can get here.
1%load_ext zipline<br><br>! zipline ingest -b quandl<br>
Create a custom momentum factor and build the pipeline
Now, create a custom momentum to calculate the momentum and define the pipeline for our trading algorithm.
1class Momentum(CustomFactor):
2 # Default inputs
3 inputs = [USEquityPricing.close]
4
5 # Compute momentum
6 def compute(self, today, assets, out, close):
7 out[:] = close[-1] / close[0]
8
9
10def make_pipeline():
11
12 twenty_day_momentum = Momentum(window_length=20)
13 thirty_day_momentum = Momentum(window_length=30)
14
15 positive_momentum = (
16 (twenty_day_momentum > 1) &
17 (thirty_day_momentum > 1)
18 )
19
20 return Pipeline(
21 columns={
22 'longs': thirty_day_momentum.top(N_LONGS),
23 },
24 screen=positive_momentum
25 )
Create a custom momentum factor called Momentum which takes the closing prices of stocks and computes the momentum using their price ratio.
Then define a make_pipeline function that creates a pipeline with two momentum calculations: one for 20-day momentum and another for 30-day momentum.
Consider stocks with positive momentum (greater than 1) in both calculations and select the top N_LONGS stocks with the highest 30-day momentum.
Implement the trading strategy
Implement the trading strategy by initializing the pipeline, scheduling the rebalancing function, and executing the trades.
1def before_trading_start(context, data):
2 context.factor_data = pipeline_output("factor_pipeline")
3 assets = context.factor_data.index
4
5def initialize(context):
6 attach_pipeline(make_pipeline(), "factor_pipeline")
7 schedule_function(
8 rebalance,
9 date_rules.week_start(),
10 time_rules.market_open(),
11 calendar=calendars.US_EQUITIES,
12 )
13
14def rebalance(context, data):
15 factor_data = context.factor_data
16 assets = factor_data.index
17
18 longs = assets[factor_data.longs]
19 divest = context.portfolio.positions.keys() - longs
20
21 exec_trades(data, assets=divest, target_percent=0)
22 exec_trades(data, assets=longs, target_percent=1 / N_LONGS if N_LONGS else 0)
23
24def exec_trades(data, assets, target_percent):
25 # Loop through every asset...
26 for asset in assets:
27 # ...if the asset is tradeable and there are no open orders...
28 if data.can_trade(asset) and not get_open_orders(asset):
29 # ...execute the order against the target percent
30 order_target_percent(asset, target_percent)
Define a before_trading_start
function that gets the pipeline output. The initialize
function attaches the pipeline and schedules a rebalancing function to run at the start of each week, right when the market opens.
The rebalance
function gets the pipeline output, filters the assets we want to go long, and calculates the assets we need to divest. Then execute the trades for both long and divest positions using the exec_trades
function.
In the exec_trades
function, loop through the assets and check if they are tradable and if there are no open orders for them. If both conditions are met, we place an order targeting the specified percentage of our portfolio.
And finally, run the algorithm.
1start = pd.Timestamp('2015')
2end = pd.Timestamp('2018')
3
4perf = run_algorithm(
5 start=start,
6 end=end,
7 initialize=initialize,
8 before_trading_start=before_trading_start,
9 capital_base=100_000,
10 bundle="quandl",
11)
Momentum trading is a market inefficiency used by many professional traders. Building trading algorithms like this can help investors automate their trading decisions and potentially improve their returns. Even non-professionals should use Python to build and test their trading strategies.