| 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}")