Overall Statistics
Total Orders
41
Average Win
2.05%
Average Loss
0%
Compounding Annual Return
15.634%
Drawdown
32.300%
Expectancy
0
Start Equity
1000000
End Equity
5626242.72
Net Profit
462.624%
Sharpe Ratio
0.722
Sortino Ratio
0.73
Probabilistic Sharpe Ratio
22.643%
Loss Rate
0%
Win Rate
100%
Profit-Loss Ratio
0
Alpha
0.009
Beta
0.956
Annual Standard Deviation
0.134
Annual Variance
0.018
Information Ratio
0.305
Tracking Error
0.016
Treynor Ratio
0.101
Total Fees
$2748.07
Estimated Strategy Capacity
$48000000.00
Lowest Capacity Asset
SPY R735QTJ8XC9X
Portfolio Turnover
0.03%
# region imports
from AlgorithmImports import *
# endregion

class DeterminedOrangeArmadillo(QCAlgorithm):

    _black_fridays = [
        datetime(1998, 11, 27),
        datetime(1999, 11, 26),
        datetime(2000, 11, 24),
        datetime(2001, 11, 23),
        datetime(2002, 11, 29),
        datetime(2003, 11, 28),
        datetime(2004, 11, 26),
        datetime(2005, 11, 25),
        datetime(2006, 11, 24),
        datetime(2007, 11, 23),
        datetime(2008, 11, 28),
        datetime(2009, 11, 27),
        datetime(2010, 11, 26),
        datetime(2011, 11, 25),
        datetime(2012, 11, 23),
        datetime(2013, 11, 29),
        datetime(2014, 11, 28),
        datetime(2015, 11, 27),
        datetime(2016, 11, 25),
        datetime(2017, 11, 24),
        datetime(2018, 11, 23),
        datetime(2019, 11, 29),
        datetime(2020, 11, 27),
        datetime(2021, 11, 26),
        datetime(2022, 11, 25),
        datetime(2023, 11, 24),
    ]

    _prime_days = [
        datetime(2015, 7, 15),
        datetime(2016, 7, 12),
        datetime(2017, 7, 11),
        datetime(2018, 7, 17),
        datetime(2019, 7, 15),
        datetime(2020, 10, 13),
        datetime(2021, 7, 21),
        datetime(2022, 7, 12),
        datetime(2023, 7, 11),
    ]
    _contract_symbol = None

    def initialize(self):
        self.set_start_date(2012, 11, 13)
        self.set_end_date(2024, 10, 1)
        self.set_cash(1_000_000)
        self.set_security_initializer(BrokerageModelSecurityInitializer(self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices)))
        self._amzn = self.add_equity('AMZN', data_normalization_mode=DataNormalizationMode.RAW)
        self._spy = self.add_equity('SPY')
        self._holding_period = self.get_parameter('holding_period', 14)
        for holidays in [self._black_fridays, self._prime_days]:
            for holiday in holidays:
                # 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
                )

    def on_data(self, data):
        # Buy and hold the SPY.
        if not self._spy.holdings.invested:
            self.set_holdings(self._spy.symbol, 1)
        # If the buyer exercises the Option, liquidate the underlying Equity position.
        if self._amzn.holdings.invested:
            self.liquidate(self._amzn.symbol)
            self._contract_symbol = None

    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)