Overall Statistics |
Total Trades 1 Average Win 0% Average Loss 0% Compounding Annual Return 3.177% Drawdown 0.400% Expectancy 0 Net Profit 0.060% Sharpe Ratio 1.933 Probabilistic Sharpe Ratio 54.217% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0.016 Annual Variance 0 Information Ratio 1.933 Tracking Error 0.016 Treynor Ratio 0 Total Fees $1.00 |
from QuantConnect.Securities.Option import OptionPriceModels from datetime import timedelta import decimal as d class CoveredCallAlgorithm(QCAlgorithm): def Initialize(self): self._no_K = 20 # no of strikes around ATM => for uni selection self.MIN_EXPIRY = 30 # min num of days to expiration => for uni selection self.MAX_EXPIRY = 60 # max num of days to expiration => for uni selection self.MAX_DELTA = d.Decimal(0.3) self.MIN_PREMIUM = d.Decimal(0.3) self.ticker = "AMD" self.benchmarkTicker = "SPX" self.SetStartDate(2017, 1, 1) self.SetEndDate(2017, 1, 7) self.SetCash(5000) self.resolution = Resolution.Minute self.call, self.put, self.takeProfitTicket = None, None, None equity = self.AddEquity(self.ticker, self.resolution) option = self.AddOption(self.ticker, self.resolution) self.symbol = option.Symbol # set strike/expiry filter for this option chain option.SetFilter(timedelta(30), timedelta(45)) # set our strike/expiry filter for this option chain option.SetFilter(self.UniverseFunc) # for greeks and pricer (needs some warmup) - https://github.com/QuantConnect/Lean/blob/21cd972e99f70f007ce689bdaeeafe3cb4ea9c77/Common/Securities/Option/OptionPriceModels.cs#L81 option.PriceModel = OptionPriceModels.CrankNicolsonFD() # both European & American, automatically # this is needed for Greeks calcs self.SetWarmUp(TimeSpan.FromDays(60)) # timedelta(7) # use the underlying equity as the benchmark # self.SetBenchmark(self.benchmarkTicker) self.SetBenchmark(self.benchmarkTicker) def OnData(self,slice): if (self.IsWarmingUp): return option_invested = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.Option] if len(option_invested) == 1: return # If we already have underlying - check if we need to sell covered call if self.Portfolio[self.ticker].Invested: self.TradeCallOption(slice) else: self.TradePutOption(slice) def TradePutOption(self,slice): for i in slice.OptionChains: if i.Key != self.symbol: continue chain = i.Value # filter the put options contracts puts = [x for x in chain if x.Right == OptionRight.Put and abs(x.Greeks.Delta) > 0 and abs(x.Greeks.Delta) < self.MAX_DELTA and x.BidPrice > self.MIN_PREMIUM] # sorted the contracts according to their bid price divided by the number of days left in the contract to find premium per day contracts = sorted(puts, key = lambda x: x.BidPrice / len(list(self.TradingCalendar.GetDaysByType(TradingDayType.BusinessDay, self.Time, x.Expiry)))) if len(contracts) == 0: continue self.put = contracts[0].Symbol # Sell Put ticket = self.MarketOrder(self.put, -1, asynchronous = False) # set Take Profit order self.takeProfitTicket = self.LimitOrder(self.put, 1, round(ticket.AverageFillPrice * d.Decimal(0.1), 2)) def TradeCallOption(self,slice): for i in slice.OptionChains: if i.Key != self.symbol: continue chain = i.Value # filter the put options contracts calls = [x for x in chain if x.Right == OptionRight.Call and abs(x.Greeks.Delta) > 0 and abs(x.Greeks.Delta) < self.MAX_DELTA and x.BidPrice > self.MIN_PREMIUM] # sorted the contracts according to their expiration dates and choose the ATM options contracts = sorted(sorted(calls, key = lambda x: x.BidPrice, reverse=True), key = lambda x: x.Expiry) if len(contracts) == 0: continue self.call = contracts[0].Symbol # short the call options ticket = self.MarketOrder(self.call, -1, asynchronous = False) # set Take Profit order self.takeProfitTicket = self.LimitOrder(self.call, 1, round(ticket.AverageFillPrice * d.Decimal(0.1), 2)) def OnOrderEvent(self, orderEvent): self.Log(str(orderEvent)) def OnAssignmentOrderEvent(self, assignmentEvent): if self.takeProfitTicket != None: self.takeProfitTicket.cancel(); self.takeProfitTicket = None def UniverseFunc(self, universe): return universe.IncludeWeeklys()\ .Strikes(-self._no_K, self._no_K)\ .Expiration(timedelta(self.MIN_EXPIRY), timedelta(self.MAX_EXPIRY)) def OnFrameworkData(self): return