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