Back to Community
Long/short events based trading strategy (share buybacks and earnings reports)

As both buybacks and earnings calendars are now available in pipeline, this example algorithm is the first I’ve written to trade on more than one event. If you’d like to learn more about trading on buyback announcements or earnings surprises, please view the original posts for share buybacks and earnings surprises.

I’m trying something new by providing a much more in-depth descriptions of the data feeds and custom factors used. Let me know if it’s helpful. You'll find dataset descriptions, custom factor descriptions, and strategy description as you read through.

Dataset and Custom factor descriptions

Strategy Details:

  • Universe filter: We look at only the top 1,000 most liquid securities.
  • Event filter: Each event (earnings surprise & buyback) must have an article sentiment greater than .45 for long positions and less than -45 for short positions.
  • Long Positions: Positive earnings surprises and new buyback authorizations are included in long positions.
  • Short Positions: Negative earnings surprises make up all the short positions that we hold in this algorithm.
  • Weights: The weight for each security is determined by the total number of longs and shorts we have in that current day. So if we have 2 longs and 2 shorts, the weight for each long will be 50% (1.0/number of securities) and the weight for each short will be -50%. This is a rolling rebalance at the beginning of each day according to the number of securities currently held and to order.
  • Capital base: $1,000,000
  • Profit and Loss limits are set to 6%
  • Days held: Positions are currently held for 4 days but are easily changeable by modifying 'context.days_to_hold'
  • Surprise Percent threshold: Only earnings surprises between 0% and 6% in absolute magnitude will be considered as a trading signal. These are adjustable using the minimum and maximum threshold variables in context.
  • Trade dates: All trades are made 1 business day AFTER an event regardless of whether it was a Before Market Open or After Market announcement

For all examples using data, visit the data pipeline factor library.


The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.

4 responses

OOS. Drawdown is a little high once you run it on bigger timescale.

Clone Algorithm
Backtest from to with initial capital
Total Returns
Max Drawdown
Benchmark Returns
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
# Backtest ID: 574cbe25e6b2450f84b5baf6
There was a runtime error.

Dataset Descriptions:

Earnings Calendar (Sample data date range: 01 Jan 2007 - 10 Feb 2014)

from quantopian.pipeline.factors.eventvestor import (  
BusinessDaysUntilNextEarnings, # The number of business days until the next earnings report (0 == day of)  
BusinessDaysSincePreviousEarnings # The number of business days since the previous earnings report (0 == day of)  

from import EarningsCalendar  
# Available Fields:  
# previous_announcement - datetime64[ns]  
# next_announcement - datetime64[ns]  

Buyback Authorizations (Sample data date range: 01 Jun 2007 - 30 May 2014)

# Number of business days since a buyback announcement (0 == day of)  
from quantopian.pipeline.factors.eventvestor import BusinessDaysSinceBuybackAuth

from import BuybackAuthorizations  
# Fields:  
# previous_date - datetime64[ns]  
# previous_type - object  
# previous_amount - float64  
# previous_unit - object  

Crowdsourced Earnings Estimates from Estimize (Sample data date range: 18 Oct 2010 - 30 May 2015)

from import (  

# Fields:  
# previous_mean - float64  
# next_count - float64  
# previous_fiscal_year - float64  
# next_fiscal_quarter - float64  
# previous_fiscal_quarter - float64  
# previous_high - float64  
# next_standard_deviation - float64  
# previous_actual_value - float64  
# next_low - float64  
# next_fiscal_year - float64  
# previous_low - float64  
# previous_release_date - datetime64[ns]  
# previous_standard_deviation - float64  
# next_mean - float64  
# next_high - float64  
# next_release_date - datetime64[ns]  
# previous_count - float64  

Accern's News Sentiment (Sample data date range: 26 Aug 2012 - 30 May 2014)

from import alphaone  
# Fields:  
# article_sentiment - float64  
# impact_score - float64  

Custom Factor Descriptions:

# Calculate the percent surprise for each earnings announcement  
class PercentSurprise(CustomFactor):  
    window_length = 1  
    inputs = [ConsensusEstimizeEPS.previous_actual_value,  

    def compute(self, today, assets, out, actual_eps, estimize_eps):  
        out[:] = (actual_eps[-1] - estimize_eps[-1])/(estimize_eps[-1] + 0)

# Calculates the number of days between an Estimize's earnings release  
# date and the current earnings announcement. This is to insure that we  
# are only getting the previous quarter's data and not more than 1 quarter  
# behind.  
class DaysSinceRelease(CustomFactor):  
    # Only getting the previous quarter's estimize surprise  
    window_length = 1  
    inputs = [EarningsCalendar.previous_announcement,  
    def compute(self, today, assets, out,  
                earnings_announcement, estimize_release):  
        days = estimize_release - earnings_announcement  
        out[:] = abs(days.astype('timedelta64[D]').astype(int))  

With ConsensusEstimizeEPS, how do I get hold of the current Estimize consensus estimate? I can see fields above marked "previous_" and "next_", which suggest to me the previous and upcoming quarter. Is the "next_" the current live consensus estimate, or is it looking ahead to the next quarterly announcement?

I note the Research dataset has a field called "mean", which is current.


We've found an issue with the Estimize dataset and are shutting down access to it starting July 18th, 2016 (full details here). I'm happy to discuss the question with you over email slee @