Overall Statistics |
Total Orders 2933 Average Win 0.12% Average Loss -0.08% Compounding Annual Return 42.300% Drawdown 4.700% Expectancy 0.456 Start Equity 100000 End Equity 142163 Net Profit 42.163% Sharpe Ratio 2.314 Sortino Ratio 3.228 Probabilistic Sharpe Ratio 95.680% Loss Rate 42% Win Rate 58% Profit-Loss Ratio 1.52 Alpha 0 Beta 0 Annual Standard Deviation 0.115 Annual Variance 0.013 Information Ratio 2.476 Tracking Error 0.115 Treynor Ratio 0 Total Fees $1635.00 Estimated Strategy Capacity $2000.00 Lowest Capacity Asset QQQ 323F7C7O1OBC6|QQQ RIWIV7K5Z9LX Portfolio Turnover 57.36% |
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.closing_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*1.01) self.cover_orders[self.contracts[1].Symbol] = self.StopMarketOrder(self.symbol, -100, otm_puts[0].ID.StrikePrice*0.99) self.closing_orders[self.contracts[0].Symbol] = self.StopMarketOrder(self.symbol, -100, otm_calls[0].ID.StrikePrice*0.995) self.closing_orders[self.contracts[1].Symbol] = self.StopMarketOrder(self.symbol, 100, otm_puts[0].ID.StrikePrice*1.005) self.Log(f"Opened strangle with contracts: {self.contracts[0].Symbol} (Call) and {self.contracts[1].Symbol} (Put)") 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 OnOrderEvent(self, orderEvent): self.Log(f"OrderEvent: {orderEvent}") if orderEvent.Status == OrderStatus.Filled: order = self.Transactions.GetOrderById(orderEvent.OrderId) if order.Type == OrderType.StopMarket: if order.Symbol in self.cover_orders.values(): if order.Quantity > 0: self.MarketOrder(order.Symbol, 100) else: self.MarketOrder(order.Symbol, -100) self.cover_orders = {k: v for k, v in self.cover_orders.items() if v.OrderId != orderEvent.OrderId} elif order.Symbol in self.closing_orders.values(): if order.Quantity > 0: self.MarketOrder(order.Symbol, -100) else: self.MarketOrder(order.Symbol, 100) self.closing_orders = {k: v for k, v in self.closing_orders.items() if v.OrderId != orderEvent.OrderId} def OnEndOfAlgorithm(self): self.Log(f"Final Portfolio Value: {self.Portfolio.TotalPortfolioValue}")