Back to Community
How to use Earnings Announcements in your strategies

Earnings announcements happen every quarter every year and tend to correlate to volatility for individual stocks. If the economic hypothesis behind your algorithm doesn’t specifically rely on price movements related to earnings announcements, one possible way to lower volatility is to avoid securities that are close to an earnings announcement.

Thankfully, there are a two ways for you to do this:

  1. Import your own earnings calendar dataset through Fetcher()
  2. See the sample algorithm attached which uses EventVestor's Earnings Calendar dataset

This algorithm, originally published by James Christopher and Delaney, uses two new Pipeline API factors (BusinessDaysUntilNextEarnings and BusinessDaysSincePreviousEarnings) to avoid stocks which have an earnings announcement 7 days ahead and 7 days previous. You can clone the algorithm to see it in action.

When BusinessDaysUntilNextEarnings == 0 it means that the current day has an earnings announcement. Either before market open or after market close.

Disclaimer

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.

30 responses

I know this is just an example, but is it intentional that you are avoiding earnings +/- 3 days, yet only rebalancing once a month? I would have thought that might mean you'd want +/- 22 business days or something like that, to ensure that you don't have earnings for any stocks until your next rebalance. That, or update the rebalancing frequency to daily or weekly.

Also, for whatever reason, the earnings-augmented algo seems to have substantially higher periodic net exposure, which looks like a bug, if the algo is intended to be market neutral.

Good point. Like you mentioned this is an example and the monthly rebalancing frequency was left in to try and preserve the original algorithm as much as possible.

But I see where you're coming from and thanks for bringing up the point. Would definitely encourage others to try playing around with the rebalance frequencies and seeing the results! As for the returns I originally thought that it was because we had avoided a negative earnings surprise but it also may just be a bug like you mentioned.

@Simon: regarding the higher net exposure you can have a look at initialize function, there is a comment:

here are two options: Screen your securities out in initialize() or do it right before you order.  

The backtest shown in this thread uses option 2. That means the screening is done right before ordering stocks, when weights have been already assigned. The problem is that some stocks are not going to be ordered due to earning announcements, making the calculated weights wrong and eventually increasing net exposure. So go for option 1 to fix this (that is uncomment the code for stock screeening in initialize)

Yeah I am trying, working through some bugs.

If you don't mind my little changes, here is a version with slightly better net exposure. Unfortunately it is difficult to get better results with monthly rebalancing

Clone Algorithm
56
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
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: 56a88228c31cd01199572e78
There was a runtime error.

Hey guys,

Thanks for all your comments. Luca, the work you did was awesome! I worked your framework in on a bimonthly basis using a 15 day before/after window.

I found small improvements in returns, volatility, and drawdown compared to the version without the framework - that being said, the overall performance of both algorithms weren't great.

Clone Algorithm
14
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
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: 56a91715d69b4c11900e8d2f
There was a runtime error.

Never mind - I see this is the Option 1 - why do the positions still not total 400 all the time?

EDIT: what I am getting at is that I suspect the differences in statistics is actually caused by the confounding factor/bug of the exposure/leverage oscillating quarterly, rather specifically anything to do with earnings. The genuine effect might be more clear with daily rebalancing.

Good point! I think the appropriate comparison would be to take multiple algorithms made for daily rebalancing and compare the results, similar to how I did with monthly rebalanced algorithms.

@Simon, there is a bug in the pipeline screening. The current filter leaves some securities that are going to be skipped right before ordering them (check_earnings_proximity). Here is a fixed version, that is identical to Seong Lee's latest version with only the pipeline screening fixed.

Please note:
- that there is still a drop in the number of positions hold sometimes , but this is due to the limited number of securities in the pipeline basket.
- The net exposure has improved too.
- The overall performance are terrible

Clone Algorithm
56
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
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: 56a97740708e52110f445a48
There was a runtime error.

So, this is the original algorithm (monthly rebalancing, avoid_earnings_days = 3) with the proper screening in the pipeline.

Bottom line: I cannot see any improvements when accounting for earnings announcements

Edit: well, to be sure that there aren't any improvements I should actually run this exact code disabling earnings announcement logic...but I am too annoyed by the far too many backtest timeouts. I leave this to the community

Clone Algorithm
56
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
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: 56a998e202b20b12aece50dc
There was a runtime error.

Great update!

When will DividendsCalendar be available in pipeline? (and perhaps some of the other EventVestor datasets)

Hi Luca,

Thanks for posting that, I think this original screen that I had used for option 1 and is probably the proper screen to use instead:

pipe.set_screen((ne.isnan() | (ne > context.avoid_earnings_days)) & (pe > context.avoid_earnings_days) & (sma_200 > 5) & (dollar_volume > 10**7))

James,

We're actively working towards that although I don't have a definite timeline for you. I'll let you know as soon as we do - but in the meanwhile, I'd love to get your thoughts on what kinds of factors you'd like to see.

In this case we have BusinessDaysUntilNextEarnings - would you be interested in seeing the same for dividends?

Thanks Seong,

Pretty similar to what you've got here -- Days Before Event, Days After Event.

@Seong: my bad, I erroneously changed the screen. But I fixed it again in the last two backtests I posted here and the risk metrics are not better than the original algorithm (the one that doesn't take into account the earning announcements).

The only algorithm that shows improvements in the risk metrics in the one that filters out stocks right before ordering, when weights have been already assigned. Because that creates an unbalanced (long/short exposure) portfolio we cannot say for sure the improvements are due to the filtering or due to the unbalanced portfolio.

Hi Luca,

Thanks for your comments, I appreciate them and think you have some pretty good points.

Here's another example using the risk framework taken from the Quantopian Lectures Long-Short Equity Strategy but modified for a weekly rebalance period.

Clone Algorithm
105
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
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: 56aa17e2ccb8eb12b037500f
There was a runtime error.

Thanks for everyone who joined during the webinar on 2/2. If you missed it, we recorded it here.

I've also tried creating a much simpler algorithm that rebalances a few large market cap tech stocks while avoiding earnings.

Clone Algorithm
61
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
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: 56b3c9e2e0b29a129801595f
There was a runtime error.

The second-to-last algorithm makes a pretty strong case for market efficiency!

Maybe I'm wrong, but I think, there is a timing inconsistency in the factors.
I mean the EBIT in the Value factor is the quarterly one, while the Net Income in the ROE of the Quality factor is TTM (trailing twelve months), isn't it?
Can you please confirm it?

There are TTM metrics exposed in our fundamentals data currently, to the best of my knowledge.

Disclaimer

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.

Yes, the ratios (i.e. Roe, PS, etc...) are TTM but according to the documentation "Quantopian currently exposes the most recent period's data."... Or is it only for get_fundamentals true but not for the pipeline API?
Quantopian is a great platform... Anyway I hope that Quantopian will soon introduce a convenient way to handle historical fundamental data in the Pipeline API... I'm trying to implement the Piotroski score as pipeline factor, but without that is really difficult

Hi Constantino,

Have you seen this thread? (https://www.quantopian.com/posts/piotroski-long-slash-short-using-pipeline) Another member attempted to implement the piotroski score using pipeline. Thought you might find it useful.

Seong

@Josh: There are some metrics that uses TTM data, like for example some ratios, but the basic fundamentals data (for example Revenues, Costs, etc..) are quarterly based, making quite difficult so build some custom historical ratios. I wrote a small algorithm to check it.

@Seong: I'd seen that thread. The author Giuseppe Moriconi overcomes the time frame problem using TTM based ratios, but now always like in the case of the Operating Cash Flow: He uses the Cash Flow from the last quarter, while the original Piotroski score requires the data in the last 12 months. To keep it short, the limitation with the historical fundamental data remains, even if Giuseppe Moriconi found a smart workaround.

Just looking at using these for my mean reversion algo, and it seems to help. Am I right in understanding that zero days to next earnings means day of earnings report. This may be before market open or after market close (and very rarely during market hours). Since the pipeline is calculated at the end of the day, when I actually trade on the info, it's within a schedule function at market open the next day at the earliest?

Hi Dan,

I'll do my best to answer your question.

Am I right in understanding that zero days to next earnings means day of earnings report. This may be before market open or after market close (and very rarely during market hours).

This is correct.

Since the pipeline is calculated at the end of the day, when I actually trade on the info, it's within a schedule function at market open the next day at the earliest?

I'm not quite sure what you're asking, so I'll do my best to answer but let me know if this is not clear. Pipeline is calculated at the beginning of each day before trading starts. So when next == 0, for the current day you know that there was an earnings announcement either before market open or sometime later after market close.

When prev == 1, you know that yesterday there was an earnings announcement

Ah OK, that's what confused me. I thought pipeline was calculated at market close, i.e. the date of reckoning is the day before you can actually trade with the information it produces. But what you're saying is that it's calculated before market open, so you can then trade that same day.

My point was to understand what "next == 0" means, and I get that now, even though my brain aches like Marty McFly in Back To The Future 3.

An observation. The following two pieces of code produce the same results:

    has_earnings = ne.notnan() & (ne == 1)  
    pipe.set_screen(universe & ~has_earnings)  
    has_earnings = ne.notnan() & (ne == 0)  
    pipe.set_screen(universe & ~has_earnings)  

It took me a while to figure out, but I think it's because pipeline factors are floats, so exact equality comparison to integers isn't going to work. This is counter-intuitive, as days to earnings is always a whole number.

My work around is something horrible like:

    has_earnings = ne.notnan() & (ne > -1) & (ne < 1)  
    pipe.set_screen(universe & ~has_earnings)  

Any suggestions?

Ah yes, you're correct. I'm rereading Scott's post here https://www.quantopian.com/posts/pipeline-classifiers-are-here#57040f9fb58c6d60d00000c4 to refresh my memory.

It looks like this might work for you:

    has_earnings = (ne.notnan() & ne.eq(0))  
    pipe.set_screen(universe & ~has_earnings)  

Hello, is there any update regarding the issues related to the availability of these data in June 2015, thanks!

Thanks Francesco, we're working on a solution now and will update this thread once that's available.

Hello folks,

The data availability issue starting in June 2015 has since been resolved.

Let me know if you have any other questions,

Seong