| Overall Statistics |
|
Total Orders 352 Average Win 0.02% Average Loss -1.15% Compounding Annual Return 2.443% Drawdown 1.300% Expectancy 0.014 Start Equity 1000000 End Equity 1029320 Net Profit 2.932% Sharpe Ratio -7.008 Sortino Ratio -3.34 Probabilistic Sharpe Ratio 98.429% Loss Rate 1% Win Rate 99% Profit-Loss Ratio 0.02 Alpha -0.038 Beta 0 Annual Standard Deviation 0.005 Annual Variance 0 Information Ratio -0.882 Tracking Error 0.109 Treynor Ratio -261.257 Total Fees $0.00 Estimated Strategy Capacity $4000.00 Lowest Capacity Asset SPXW YPZ4T6Y6SOKU|SPX 31 Portfolio Turnover 0.01% |
# region imports
from AlgorithmImports import *
# endregion
class OneDTEIndexOptionUniverseAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2024, 1, 1)
self.set_cash(1_000_000)
self.settings.automatic_indicator_warm_up = True
# Add 1DTE SPY contracts.
self._index = self.add_index('SPX')
self._index.std = IndicatorExtensions.Of(StandardDeviation(21), self.roc(self._index.symbol, 1, Resolution.DAILY))
self._option = self.add_index_option(self._index.symbol, 'SPXW')
self._option.set_filter(lambda u: u.include_weeklys().expiration(1, 1).strikes(-40, 40))
# Create a member to track when the algorithm should trade.
self._can_trade = False
date_rule = self.date_rules.every_day(self._option.symbol)
self.schedule.on(date_rule, self.time_rules.at(15, 45), lambda: setattr(self, '_can_trade', True))
self.schedule.on(date_rule, self.time_rules.at(16, 15), lambda: setattr(self, '_can_trade', False))
self.set_warm_up(timedelta(31))
def on_data(self, data):
if self.is_warming_up or self.portfolio.invested:
return
# Only process data between 3:45 PM and 4:15 PM on Thursdays.
if not self._can_trade:
return
# Get the Option chain.
chain = data.option_chains.get(self._option.symbol)
if not chain:
return
upper_threshold = self._index.price * (1 + 4*self._index.std.current.value)
lower_threshold = self._index.price * (1 - 4*self._index.std.current.value)
self.plot('STD', "Price", self._index.price)
self.plot('STD', "upper_threshold", upper_threshold)
self.plot('STD', "lower_threshold", lower_threshold)
# Order the OTM calls by strike to find the nearest to ATM
call_contracts = sorted(
[contract for contract in chain if contract.right == OptionRight.CALL and contract.strike > upper_threshold],
key=lambda x: x.strike
)
if not call_contracts:
return
# Order the OTM puts by strike to find the nearest to ATM
put_contracts = sorted(
[contract for contract in chain if contract.right == OptionRight.PUT and contract.strike < lower_threshold],
key=lambda x: x.strike, reverse=True
)
if not put_contracts:
return
# Select the call and put contracts.
call = call_contracts[0]
put = put_contracts[0]
# Place the trade.
self.buy(OptionStrategies.short_strangle(self._option.symbol, call.strike, put.strike, call.expiry), 10)