July cohort is now open: How to secure your spot:

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.

import pandas as pd

from zipline import run_algorithm
from zipline.pipeline import Pipeline, CustomFactor
from zipline.pipeline.data import USEquityPricing
from zipline.api import (
    attach_pipeline,
    calendars,
    pipeline_output,
    date_rules,
    time_rules,
    set_commission,
    set_slippage,
    order_target_percent,
    get_open_orders,
    schedule_function
)

import warnings
warnings.filterwarnings("ignore")

N_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.

%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.

class Momentum(CustomFactor):
    # Default inputs
    inputs = [USEquityPricing.close]

    # Compute momentum
    def compute(self, today, assets, out, close):
        out[:] = close[-1] / close[0]


def make_pipeline():

    twenty_day_momentum = Momentum(window_length=20)
    thirty_day_momentum = Momentum(window_length=30)

    positive_momentum = (
        (twenty_day_momentum &gt; 1) &amp; 
        (thirty_day_momentum &gt; 1)
    )

    return Pipeline(
        columns={
            'longs': thirty_day_momentum.top(N_LONGS),
        },
        screen=positive_momentum
    )

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.

def before_trading_start(context, data):
    context.factor_data = pipeline_output("factor_pipeline")
    assets = context.factor_data.index

def initialize(context):
    attach_pipeline(make_pipeline(), "factor_pipeline")
    schedule_function(
        rebalance,
        date_rules.week_start(),
        time_rules.market_open(),
        calendar=calendars.US_EQUITIES,
    )

def rebalance(context, data):
    factor_data = context.factor_data
    assets = factor_data.index

    longs = assets[factor_data.longs]
    divest = context.portfolio.positions.keys() - longs

    exec_trades(data, assets=divest, target_percent=0)
    exec_trades(data, assets=longs, target_percent=1 / N_LONGS if N_LONGS else 0)

def exec_trades(data, assets, target_percent):
    # Loop through every asset...
    for asset in assets:
        # ...if the asset is tradeable and there are no open orders...
        if data.can_trade(asset) and not get_open_orders(asset):
            # ...execute the order against the target percent
            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.

start = pd.Timestamp('2015')
end = pd.Timestamp('2018')

perf = run_algorithm(
    start=start,
    end=end,
    initialize=initialize,
    before_trading_start=before_trading_start,
    capital_base=100_000,
    bundle="quandl",
)

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.