This is a simple strategy that rebalances a few large market cap tech stocks and hedges its positions with the SPY. It also exits out of these positions 1 day before an earnings announcement and re-enters the position 1 day afterwards.
I've written about the subject of earnings announcements and volatility. While you can read the full research based off a Stanford study, the quick summary is that stock price volatility on the day of an earnings announcement is significantly higher than its average.
To run the algorithm, get the free sample version of EventVestor's Earnings Calendar dataset and clone the algorithm below.
Would you like to see something like a tech-sector algorithm with Pipeline? Post in the comments.
|Returns||1 Month||3 Month||6 Month||12 Month|
|Alpha||1 Month||3 Month||6 Month||12 Month|
|Beta||1 Month||3 Month||6 Month||12 Month|
|Sharpe||1 Month||3 Month||6 Month||12 Month|
|Sortino||1 Month||3 Month||6 Month||12 Month|
|Volatility||1 Month||3 Month||6 Month||12 Month|
|Max Drawdown||1 Month||3 Month||6 Month||12 Month|
from quantopian.algorithm import attach_pipeline, pipeline_output from quantopian.pipeline import Pipeline from quantopian.pipeline.factors import CustomFactor, SimpleMovingAverage from quantopian.pipeline.data.builtin import USEquityPricing from quantopian.pipeline.data import morningstar from quantopian.pipeline.factors.eventvestor import BusinessDaysUntilNextEarnings, BusinessDaysSincePreviousEarnings def make_pipeline(): """ Create and return our pipeline. We break this piece of logic out into its own function to make it easier to test and modify in isolation. In particular, this function can be copy/pasted into research and run by itself. """ pipe = Pipeline() """ Risk Framework """ # EarningsCalendar.X is the actual date of the announcement # E.g. 9/12/2015 # pipe.add(EarningsCalendar.next_announcement.latest, 'next') # pipe.add(EarningsCalendar.previous_announcement.latest, 'prev') # BusinessDaysX is the integer days until or after the closest # announcement. So if AAPL had an earnings announcement yesterday, # prev_earnings would be 1. If it's the day of, it will be 0. # For BusinessDaysUntilNextEarnings(), it is common that the value # is NaaN because we typically don't know the precise date of an # earnings announcement until about 15 days before ne = BusinessDaysUntilNextEarnings() pe = BusinessDaysSincePreviousEarnings() pipe.add(ne, 'next_earnings') pipe.add(pe, 'prev_earnings') # The number of days before/after an announcement that you want to # avoid an earnings for. avoid_earnings_days = 1 does_not_have_earnings = ((ne.isnan() | (ne > avoid_earnings_days)) & (pe > avoid_earnings_days)) # Comment in/out to toggle risk framework pipe.set_screen(does_not_have_earnings) """ End of Risk Framework """ return pipe def before_trading_start(context, data): context.results = pipeline_output('tech_stocks') context.stocks_to_trade = [stock for stock in context.tech_stocks if stock in context.results.index] # Put any initialization logic here. The context object will be passed to # the other methods in your algorithm. def initialize(context): set_symbol_lookup_date('2015-01-01') context.spy = sid(8554) context.tech_stocks = [ symbol('AAPL'), symbol('NFLX'), symbol('AMZN'), symbol('GOOG_L'), symbol('YHOO'), symbol('TSLA'), symbol('MSFT'), symbol('IBM'),] attach_pipeline(make_pipeline(), 'tech_stocks') # Schedule my rebalance function weekly schedule_function(func=rebalance, date_rule=date_rules.every_day(), time_rule=time_rules.market_open(hours=0,minutes=30), half_days=True) def rebalance(context, data): for stock in context.stocks_to_trade: order_target_percent(stock, .5*1.0/len(context.stocks_to_trade)) for stock in context.portfolio.positions: if stock not in context.stocks_to_trade and stock != context.spy: order_target_percent(stock, 0) log.info("Earnings Release too close for %s" % stock.symbol) # SPY Hedge only the amount that we're ordering order_target_percent( context.spy, -.5*len(context.stocks_to_trade)/len(context.tech_stocks)) # Will be called on every trade event for the securities you specify. def handle_data(context, data): record(positions=len(context.stocks_to_trade), leverage=context.account.leverage)