Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Momentum strategy with earnings call avoidance

Hi all!

I've recently been turned onto Quantopian by my university professor (thanks Donald). I have begun building a momentum trading algorithm that is meant to pause during a company's quarterly earnings announcement. However, when attempting to build the code for a backtest, the terminal keeps throwing up an error in line 116 (ranks = context.ouput['combined_rank']). I have tried stripping the code back to the sample algorithm I had extrapolated from and building back up but to no avail.

Please any help in identifying a solution to this error would be greatly appreciated (translating my skills in Matlab has only gotten me so far). Many thanks!

import numpy as np  
import pandas as pd

from quantopian.algorithm import attach_pipeline, pipeline_output  
from quantopian.pipeline import Pipeline  
from quantopian.pipeline.classifiers.morningstar import Sector  
from quantopian.pipeline.data import morningstar as mstar  
from quantopian.pipeline.data.builtin import USEquityPricing  
from quantopian.pipeline.factors import AverageDollarVolume, SimpleMovingAverage, Returns, CustomFactor, Latest  
from quantopian.pipeline.factors.eventvestor import BusinessDaysUntilNextEarnings, BusinessDaysSincePreviousEarnings

class CrossSectionalMomentum(CustomFactor):  
    inputs = [USEquityPricing.close]  
    window_length = 252

    def compute(self, today, assets, out, prices):  
        prices = pd.DataFrame(prices)  
        R = (prices / prices.shift(100))  
        out[:] = (R.T - R.T.mean()).T.mean()

def make_pipeline():  
    """  
    Start of Momentum pipe     contents  
    """  
    # Basic momentum metrics  
    cross_momentum = CrossSectionalMomentum()  
    abs_momentum = Returns(inputs = [USEquityPricing.close], window_length = 252)

    # We only want to trade relatively liquid stocks.  
    # Defined as any stock that has $10,000,000 average daily dollar volume over the last 20 days (1 month).  
    dollar_volume = AverageDollarVolume(window_length = 20)  
    is_liquid = (dollar_volume > 1e7)  
    # We also don't wamt to trade penny stocks.  
    # Defined as any stock with an average price of less than $5 over the last 252 days (1 year).  
    sma_252 = SimpleMovingAverage(inputs = [USEquityPricing.close], window_length = 252)  
    non_penny_stock = (sma_252 > 5)

    # We want to discard the bad assets.  
    initial_screen = (is_liquid & non_penny_stock)

    # Rank (combined) stocks for momentum trading.  
    combined_rank = (cross_momentum.rank(mask = initial_screen) + abs_momentum.rank(mask = initial_screen))

    # Construct filters representing the top/bottom 5% of stocks by the combined ranking system.  
    # This forms the trading universe each day.  
    longs = combined_rank.percentile_between(95, 100)  
    shorts = combined_rank.percentile_between(0, 5)  
    """  
    End of Momentum  
    """

    """  
    Start of risk framework for avoiding earnings announcement for Momentum  
    """  
    # Risk framework for avoiding earnings announcement  
    ne = BusinessDaysUntilNextEarnings()  
    pe = BusinessDaysSincePreviousEarnings()  
    # No. of days before/after an announcement to be avoided.  
    avoid_earnings_days = 2  
    does_not_have_earnings = ((ne.isnan() | (ne > avoid_earnings_days)) & (pe > avoid_earnings_days))  
    """  
    End of risk framework  
    """

    # Establishing pipe contents.  
    pipe_screen = (initial_screen)

    pipe_columns = {  
        'longs':longs,  
        'shorts':shorts,  
        'combined_rank':combined_rank,  
        'abs_momentum':abs_momentum,  
        'cross_momentum':cross_momentum,  
        'does_not_have_earnings':does_not_have_earnings,  
        'next_earnings':ne,  
        'prev_earnings':pe  
    }

    # Create pipe.  
    pipe = Pipeline(columns = pipe_columns, screen = pipe_screen)  
    return pipe

def initialize(context):  
    """  
    Trading model parameters  
    """  
    set_slippage(slippage.VolumeShareSlippage(volume_limit = 0.025, price_impact = 0.1))  
    set_commission(commission.PerShare(cost = 0.0075, min_trade_cost = 1))  
    # Benchmark set to S&P500 (symbol = SPY)  
    set_benchmark(sid(8554))  
    """  
    End of Trading model parameters  
    """

    attach_pipeline(make_pipeline(), 'momentum_metrics')

    context.shorts = None  
    context.longs = None  
    context.output = None

    # Schedule rebalance function weekly.  
    schedule_function(func = momentum, date_rule = date_rules.week_start(), time_rule = time_rules.market_open())  
    schedule_function(func = cancel_open_orders, date_rule = date_rules.every_day(), time_rule = time_rules.market_close())

def before_trading_start(context, data):  
    context.output = pipeline_output('momentum_metrics')  
    ranks = context.ouput['combined_rank']

    context.longs = ranks[context.output['longs']]  
    context.shorts = ranks[context.output['shorts']]

    context.active_portfolio = context.longs.index.union(context.shorts.index)  
    update_universe(context.active_portfolio)

    context.results = pipeline_output('')  
    context.stocks_to_trade = [stock for stock in context.stock_selection if stock in context.results.index]

# Will be called on every trade event for the securities specified.  
def handle_data(context, data):  
    pass

def cancel_open_orders(context, data):  
    open_orders = get_open_orders()  
    for security in open_orders:  
        for order in open_orders[security]:  
            cancel_order(order)

def momentum(context, data):  
    # Logic for buying long on selected stocks.  
    for security in context.longs.index:  
        if get_open_orders(security):  
            continue  
        if security in data:  
            order_target_percent(security, 0.5 / len(context.longs.index))

    # Logic for shorting stocks.  
    for security in context.shorts.index:  
        if get_open_orders(security):  
            continue  
        if security in data:  
            order_target_percent(security, -0.5 / len(context.shorts.index))

    # Logic for rebalancing portfolio once a stock fails to meet long or short criteria.  
    for security in context.portfolio.positions:  
        if get_open_orders(security):  
            continue  
        if security in data:  
            if security not in (context.longs.index | context.shorts.index):  
                order_target_percent(security, 0)  
2 responses

forgot the 't'.

ranks = context.ouput['combined_rank']  

should be

ranks = context.output['combined_rank']  

maybe that will fix it?

Thank you Dan!! I never considered that my dyslexia would be the element tripping up my program.