Overall Statistics
Total Orders
89
Average Win
0.87%
Average Loss
0%
Compounding Annual Return
10.602%
Drawdown
6.400%
Expectancy
-0.087
Start Equity
1000000
End Equity
1528830.15
Net Profit
52.883%
Sharpe Ratio
0.977
Sortino Ratio
0.765
Probabilistic Sharpe Ratio
87.077%
Loss Rate
9%
Win Rate
91%
Profit-Loss Ratio
0
Alpha
0.023
Beta
0.24
Annual Standard Deviation
0.048
Annual Variance
0.002
Information Ratio
-0.465
Tracking Error
0.113
Treynor Ratio
0.195
Total Fees
$915.85
Estimated Strategy Capacity
$6000.00
Lowest Capacity Asset
SPY 32JR182FXC546|SPY R735QTJ8XC9X
Portfolio Turnover
0.28%
# region imports
from AlgorithmImports import *
# endregion


class WheelStrategyAlgorithm(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2020, 6, 1)
        self.set_cash(1_000_000)
        self.set_security_initializer(BrokerageModelSecurityInitializer(self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices)))
        self._equity = self.add_equity("SPY", data_normalization_mode=DataNormalizationMode.Raw)
        self._discount = 0.05 # 5%
        
    def _get_target_contract(self, right, target_price):
        contract_symbols = self.option_chain_provider.get_option_contract_list(self._equity.symbol, self.time)
        expiry = min([symbol.id.date for symbol in contract_symbols if symbol.id.date.date() > self.time.date() + timedelta(30)])
        filtered_symbols = [
            symbol for symbol in contract_symbols 
            if symbol.id.date == expiry and symbol.id.option_right == right and (symbol.id.strike_price <= target_price if right == OptionRight.PUT else symbol.id.strike_price >= target_price)
        ]
        symbol = sorted(filtered_symbols, key=lambda symbol: symbol.id.strike_price, reverse=right == OptionRight.PUT)[0]
        self.add_option_contract(symbol)
        return symbol

    def on_data(self, data):
        if not self.portfolio.invested and self.is_market_open(self._equity.symbol): #self._equity.exchange.hours.is_open(self.time, extended_market_hours=False):
            symbol = self._get_target_contract(OptionRight.PUT, self._equity.price * (1-self._discount))
            self.market_order(symbol, -int(self.portfolio.cash_book['USD'].amount / symbol.id.strike_price / 100))
        elif [self._equity.symbol] == [symbol for symbol, holding in self.portfolio.items() if holding.invested]:
            symbol = self._get_target_contract(OptionRight.CALL, self._equity.price * (1+self._discount))
            self.market_order(symbol, -self._equity.holdings.quantity / 100)