Introduction
Retailers typically see an increase in sales volume leading into a holiday. The increase in sales can be from shoppers taking advantage of holiday sales or spending extra cash to buy gifts for friends and family. In this research post, we analyze trading opportunities for Amazon around Black Friday and Prime Day.
Background
Black Friday is the day following Thanksgiving Day in the United States, which is celebrated on the fourth Thursday of November. Prime Day is an annual shopping event hosted on Amazon. Both of these holidays feature large discounts for Amazon members, which typically increases the amount of attention Amazon receives from the market.
Research
To investigate if there are any trading opportunities surrounding these holidays, we created a notebook in the Research Environment. We started by gathering the daily returns of Amazon and the S&P 500 leading into each holiday. From the sets of daily returns, we calculated the median price path 10 days before Black Friday and Prime Day for each asset. The following image shows the median price paths for Black Friday:
The following image shows the median price paths for Prime Day:
As shown in the visuals above, AMZN typically outperforms SPY leading into each type of holiday. However, to determine if the median AMZN performance during these periods is statistically significant, we ran some one-sample t-tests. The null hypothesis of these tests is that the mean 2-week return leading into the holiday equals the usual 2-week return of the asset. The p-value of both tests was below 0.01, so we rejected the null hypothesis.
Strategy Implementation
To implement this strategy, we start by defining the dates of every Black Friday and Prime Day.
_black_fridays = [
datetime(1998, 11, 27),
datetime(1999, 11, 26),
datetime(2000, 11, 24),
# ...
]
_prime_days = [
datetime(2015, 7, 15),
datetime(2016, 7, 12),
datetime(2017, 7, 11),
# ...
]
Next, in the initialize method, we add the AMZN and SPY Equities.
amzn = self.add_equity('AMZN')
spy = self.add_equity('SPY')
Lastly, we iterate through each holiday and create Scheduled Events to rotate the portfolio between AMZN and SPY. We hold AMZN 14 calendar days before the holiday and then go back to holding SPY after the holiday.
for holidays in [self._black_fridays, self._prime_days]:
for holiday in holidays:
# Hold AMZN before the holiday.
self.schedule.on(
self.date_rules.on(spy.exchange.hours.get_next_market_close(holiday - timedelta(14), False)),
self.time_rules.before_market_close(spy.symbol, 1),
lambda: self.set_holdings([PortfolioTarget(amzn.symbol, 1)], True)
)
# Hold SPY after the holiday.
self.schedule.on(
self.date_rules.on(spy.exchange.hours.get_next_market_close(holiday + timedelta(1), False)),
self.time_rules.before_market_close(spy.symbol, 1),
lambda: self.set_holdings([PortfolioTarget(spy.symbol, 1)], True)
)
Other Monetization Methods
The underlying signal can be monetized with other investment vehicles, like Equity Options, to improve the strategy's performance. As a Black Friday or Prime Day sales event approaches, we open AMZN Option positions to capture the pre-holiday rally and then close the position when the holiday has passed.
Equity Option contract premiums increase when the volatility of the underlying Equity increases. To capture the premium and gain exposure to AMZN, we can sell at-the-money or out-of-the-money put Option contracts that expire after the holiday. To adjust the algorithm so that it trades Options, we start by changing the data normalization mode for AMZN to raw and adding a member to track the Option contract Symbol.
self._amzn = self.add_equity('AMZN', data_normalization_mode=DataNormalizationMode.RAW)
self._contract_symbol = None
Next, we adjust the Scheduled Events to trade Options instead of AMZN Equity.
# Sell an AMZN put contract before the holiday.
self.schedule.on(
self.date_rules.on(self._spy.exchange.hours.get_next_market_close(holiday - timedelta(self._holding_period), False)),
self.time_rules.before_market_close(self._spy.symbol, 1),
self._sell_put
)
# Liquidate the put contract after the holiday.
self.schedule.on(
self.date_rules.on(self._spy.exchange.hours.get_next_market_close(holiday + timedelta(1), False)),
self.time_rules.before_market_close(self._spy.symbol, 1),
lambda: self.liquidate(self._contract_symbol) if self._contract_symbol else None
)
We define the _sell_put method to select the contract, add it to the algorithm, and then sell it.
def _sell_put(self):
chain = self.option_chain(self._amzn.symbol).data_frame
if chain.empty:
return
expiry_threshold = self._amzn.exchange.hours.get_next_market_close(self.time + timedelta(self._holding_period), False)
expiry = chain[chain.expiry > expiry_threshold].expiry.min()
self._contract_symbol = chain[
(chain.expiry == expiry) &
(chain.right == OptionRight.PUT) &
(chain.strike <= chain.underlyinglastprice)
].sort_values('openinterest').index[-1]
self.add_option_contract(self._contract_symbol)
self.set_holdings(self._contract_symbol, -0.2)
To ensure that we can add the contract and trade it during the same time step, we set a security initializer that seeds the price of each contract we add to the algorithm.
self.set_security_initializer(BrokerageModelSecurityInitializer(self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices)))
The last step is to add an on_data method that liquidates the underlying AMZN position if the Option buyer ever exercises their contract.
def on_data(self, data):
if self._amzn.holdings.invested:
self.liquidate(self._amzn.symbol)
self._contract_symbol = None
Alternatively, to hold SPY while trading the AMZN put Options, we just need to add two extra lines to the on_data method.
def on_data(self, data):
if not self._spy.holdings.invested:
self.set_holdings(self._spy.symbol, 1)
if self._amzn.holdings.invested:
self.liquidate(self._amzn.symbol)
self._contract_symbol = None
Results
Let's review the performance of each monetization method.
Version 1: Rotating Between AMZN and SPY Equities
This version of the strategy rotates between AMZN and SPY. We backtested this version from 1998 to 2024. The results show that the strategy achieves a 0.51 Sharpe ratio. In contrast, buying and holding the SPY achieves a 0.303 Sharpe ratio. Therefore, the strategy outperforms the benchmark.
To test the robustness of the strategy, we ran an optimization job. We varied the number of calendar days that the algorithm held AMZN shares before each holiday. We tested 3 calendar days to 31 calendar days in steps of one day. Of the 29 strategy variants, 29/29 (100%) outperformed buying and holding the SPY.
Version 2: Collecting Put Option Premiums
This version of the strategy sells AMZN put Option contracts before each Black Friday and Prime Day. We backtested this version from 2012 to 2024, which is the longest period of time our Options dataset supports. The strategy produces a 100% win rate. It generates a profit every Black Friday and Prime Day.
Version 3: Holding SPY + Collecting Put Option Premiums
This version of the strategy is the same as Version 2, but it also buys and holds SPY. We also backtested this version from 2012 to 2024. The results show that the strategy achieves a 0.722 Sharpe ratio. In contrast, buying and holding the SPY over the same time period without trading AMZN put Options results in a 0.657 Sharpe ratio. Therefore, this monetization method of the strategy signal also outperforms the benchmark.
Decebal mihailescu
Where is the code for version 1 and 2?
Anastasia ruzmaikina
Derek,
I tried to do what you did with Amazon, but used 30% eBay + 70% Etsy for 30 days up to December 26 (as you did from 2012-2024 with $1,000,000 initial cash.) The Sharpe ratio is 0.675.
Anastasia ruzmaikina
Derek,
Thank you for the recognition! I shall try with put options. I must say, I am a big fan of your algorithm. I tried it again with five of magnificent seven earnings quarterly reports (I figured that they would hedge each other) and it worked. Sharpe ratio is 0.721. You may want to streamline the algorithm to be used for any short-term wins and then back to SPY, because your algorithm literally seems to work with everything.
Anastasia ruzmaikina
Derek,
I tried adding put options to the “five out of magnificent seven earnings announcement” algorithm. I added 30-day put options. It did increase Sharpe ratio to 0.762 and increased total earnings by almost 20%. What I found surprising is that it significantly reduced win rate to 28% instead of 67%, I believe, for the algorithm without put options. Do you know why that is?
Derek Melchin
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 QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect 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. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!