Overall Statistics |
Total Orders 1 Average Win 0% Average Loss 0% Compounding Annual Return 38.353% Drawdown 31.000% Expectancy 0 Start Equity 100000 End Equity 621972.64 Net Profit 521.973% Sharpe Ratio 1.05 Sortino Ratio 1.235 Probabilistic Sharpe Ratio 51.013% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0.142 Beta 1.191 Annual Standard Deviation 0.254 Annual Variance 0.065 Information Ratio 1.009 Tracking Error 0.16 Treynor Ratio 0.224 Total Fees $3.36 Estimated Strategy Capacity $0 Lowest Capacity Asset AAPL R735QTJ8XC9X Portfolio Turnover 0.05% |
from AlgorithmImports import * from datetime import timedelta class CoveredCallWithSMAStrategy(QCAlgorithm): def Initialize(self): self.SetStartDate(2019, 1, 1) # Set Start Date #self.SetEndDate(2029, 12, 31) # Set End Date self.SetCash(100000) # Set Strategy Cash self.symbol = self.AddEquity("AAPL", Resolution.Daily).Symbol self.option_symbol = self.AddOption(self.symbol, Resolution.Daily).Symbol self.contract = None self.sma = self.SMA(self.symbol, 200, Resolution.Daily, Field.Close) # 200-day Simple Moving Average self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday), self.TimeRules.AfterMarketOpen(self.symbol, 10), self.CheckSMAAndTrade) self.SetWarmUp(200) def CheckSMAAndTrade(self): if self.IsWarmingUp: return current_price = self.Securities[self.symbol].Price if not self.Portfolio[self.symbol].Invested: self.SetHoldings(self.symbol, 1.0) # Buy shares # Fetch the option chain option_chain = self.OptionChainProvider.GetOptionContractList(self.option_symbol, self.Time) if not option_chain: self.Log("No option chain available.") return # Filter for weekly options expiring next Friday expiry = self.Time + timedelta((4 - self.Time.weekday()) % 7) options = [x for x in option_chain if x.ID.Date == expiry] if not options: self.Log("No options available for the required expiry.") return # Determine if we are above or below the SMA if current_price > self.sma.Current.Value: self.Log("Price is above the 200-day SMA. Selling OTM covered call.") # Sell an OTM covered call with a delta of 0.30 options = sorted(options, key=lambda x: x.ID.StrikePrice) calls = [x for x in options if x.ID.OptionRight == OptionRight.Call] otm_calls = sorted(calls, key=lambda x: abs(self.CalculateDelta(x) - 0.30)) if otm_calls: self.contract = otm_calls[0] else: self.Log("Price is below the 200-day SMA. Selling ITM covered call.") # Sell an ITM covered call with a delta of 0.70 options = sorted(options, key=lambda x: x.ID.StrikePrice, reverse=True) calls = [x for x in options if x.ID.OptionRight == OptionRight.Call] itm_calls = sorted(calls, key=lambda x: abs(self.CalculateDelta(x) - 0.70)) if itm_calls: self.contract = itm_calls[0] if self.contract and not self.Portfolio[self.contract.Symbol].Invested: self.Sell(self.contract.Symbol, 1) # Sell the covered call def CalculateDelta(self, option_contract): # Option Greeks are calculated and provided by QuantConnect's underlying system chain = self.Securities[self.option_symbol].OptionChainProvider.GetOptionContractList(self.option_symbol, self.Time) for option in chain: if option.Symbol == option_contract.Symbol: return option.Greeks.Delta return None def OnOrderEvent(self, orderEvent): self.Log(f"OrderEvent: {orderEvent}") def OnEndOfAlgorithm(self): self.Log(f"Final Portfolio Value: {self.Portfolio.TotalPortfolioValue}")