Hi All,
I have been working on a modified wheel options strategy here where I will be selling puts for premium and am looking to have the system self optimize the best put to buy by using the sorting function to take the bid price and divide it by the number of days left before expiry of the option, in order to find the most premium per day option in the chain. however when i backtest it it comes up with an error that i cant seem to fix with:
Runtime Error: Trying to perform a summation, subtraction, multiplication or division between 'float' and '0, Culture=neutral, PublicKeyToken=null]]' objects throws a TypeError exception. To prevent the exception, ensure that both values share the same type. at OnData in main.py:line 96 :: self.TradePutOption(slice) at TradePutOption in main.py:line 108 at <lambda> in main.py:line 108 TypeError : unsupported operand type(s) for /: 'float' and '0, Culture=neutral, PublicKeyToken=null]]'
now i dont know why this has occured beacause the options chain has been filtered for strikes between 30 and 45 days to there is no way a 0 should be possible in terms of the getbusinessdays function. can someone take a look and help me fix this?
Code below: sorry the backtest cant be found
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(2018, 12, 1)
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 / 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
Derek Melchin
Hi MatteMatto,
To resolve the error above, we need to change
contracts = sorted(puts, key = lambda x: x.BidPrice / self.TradingCalendar.GetDaysByType(TradingDayType.BusinessDay, self.Time, x.Expiry))
to
contracts = sorted(puts, key = lambda x: x.BidPrice / len(list(self.TradingCalendar.GetDaysByType(TradingDayType.BusinessDay, self.Time, x.Expiry))))
See the attached backtest for reference.
Best,
Derek Melchin
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
MatteMatto
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!