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