Overall Statistics |
Total Orders 2166 Average Win 0.10% Average Loss -0.73% Compounding Annual Return 8.597% Drawdown 6.500% Expectancy -0.024 Start Equity 100000 End Equity 108572.5 Net Profit 8.572% Sharpe Ratio 0.596 Sortino Ratio 0.308 Probabilistic Sharpe Ratio 42.853% Loss Rate 14% Win Rate 86% Profit-Loss Ratio 0.14 Alpha 0 Beta 0 Annual Standard Deviation 0.072 Annual Variance 0.005 Information Ratio 0.854 Tracking Error 0.072 Treynor Ratio 0 Total Fees $988.50 Estimated Strategy Capacity $0 Lowest Capacity Asset QQQ 323H68DU1INL2|QQQ RIWIV7K5Z9LX Portfolio Turnover 22.25% |
from AlgorithmImports import * import datetime class ShortStrangleWithStockCoverAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2022, 1, 1) # Set Start Date self.SetEndDate(2022, 12, 31) # Set End Date self.SetCash(100000) # Set Strategy Cash self.symbol = self.AddEquity("QQQ", Resolution.Daily).Symbol self.option_symbol = self.AddOption(self.symbol).Symbol self.contracts = [] self.cover_orders = {} self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Cash) self.SetWarmUp(40) self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x))) #self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday), self.on_data) self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.At(15, 45), self.CheckStopOrders) option = self.add_option(self.symbol) #, self.Time) #option_chain.set_filter(min_expiry=timedelta(days=30), max_expiry=timedelta(days=40)) option.set_filter(self.universe_func) self.symbolo = option.symbol def universe_func(self, universe: OptionFilterUniverse) -> OptionFilterUniverse: return universe.include_weeklys().strikes(-15, 15).expiration(timedelta(30), timedelta(40)) def on_data(self, slice: Slice) -> None: if self.Time.hour == 10 and self.Time.minute == 0: option_chain = self.OptionChainProvider.GetOptionContractList(self.symbolo, self.Time) if not option_chain: self.Log("No option chain available.") return #target_expiry = self.Time + timedelta(days=30) #expiry_dates = sorted(list(set([x.ID.Date for x in option_chain]))) #expiry = min([date for date in expiry_dates if date >= target_expiry], key=lambda x: abs((x - target_expiry).days), default=None) call_otm_level = 1.02 * self.Securities[self.symbol].Price put_otm_level = 0.98 * self.Securities[self.symbol].Price #expiry = sorted([x.ID.Date for x in option_chain], key=lambda x: abs((x - self.Time).days - 30)) otm_calls = sorted([x for x in option_chain if x.ID.OptionRight == OptionRight.Call and x.ID.StrikePrice > call_otm_level]) otm_puts = sorted([x for x in option_chain if x.ID.OptionRight == OptionRight.Put and x.ID.StrikePrice < put_otm_level]) if not otm_calls or not otm_puts: return self.contracts = [self.AddOptionContract(otm_calls[0], Resolution.Daily), self.AddOptionContract(otm_puts[0], Resolution.Daily)] self.Sell(self.contracts[0].Symbol, 1) self.Sell(self.contracts[1].Symbol, 1) self.cover_orders[self.contracts[0].Symbol] = self.StopMarketOrder(self.symbol, 100, otm_calls[0].ID.StrikePrice) self.cover_orders[self.contracts[1].Symbol] = self.StopMarketOrder(self.symbol, -100, otm_puts[0].ID.StrikePrice) self.Log(f"Opened strangle with contracts: {self.contracts[0].Symbol} (Call) and {self.contracts[1].Symbol} (Put)") def CheckStopOrders(self): if not self.contracts: return for contract in self.contracts: option = self.Securities[contract.Symbol] underlying_price = self.Securities[self.symbol].Price if underlying_price < self.contracts[0].StrikePrice and underlying_price > self.contracts[1].StrikePrice: self.LiquidateStockCover(contract) def LiquidateStockCover(self, contract): stock_quantity = self.Portfolio[self.symbol].Quantity if (contract.Symbol in self.cover_orders) and stock_quantity!=0: self.Liquidate(self.symbol) #del self.cover_orders[contract.Symbol] #self.contracts.remove(contract) #self.Buy(contract.Symbol, 1) # Close the short option position self.Log(f"Liquidated stock cover for contract: {contract.Symbol}") def OnEndOfAlgorithm(self): self.Log(f"Final Portfolio Value: {self.Portfolio.TotalPortfolioValue}")