Hello,
I am attempting to write a simple algo that uses derivatives to hedge existing positions. I saw an example of this being used and created by Jing Wu. I've attempted to try to use her two methods for Selling a Call and Buying a Put as a form of hedging.
Seem to get an error when I run it (please see below for runtime exception error message), that says the method does not exist?
Wanted to see if anyone might have a fix or if there is any documentation on how to use the GetOptionContractList() method specifically? From what I can tell this is the right syntax but I'm still learning so I could be wrong.
self.OptionChainProvider.GetOptionContractList(self.Symbol, self.Time.date())
Runtime Error: Trying to dynamically access a method that does not exist throws a TypeError exception. To prevent the exception, ensure each parameter type matches those required by the 'datetime.date'>) method. Please checkout the API documentation.
at SellCall
contracts = self.OptionChainProvider.GetOptionContractList(self.Symbol in main.py:line 109
TypeError : No method matches given arguments for GetOptionContractList: (, )Trying to dynamically access a method that does not exist throws a TypeError exception. To prevent the exception, ensure each parameter type matches those required by the 'datetime.date'>) method. Please checkout the API documentation.
at SellCall
contracts = self.OptionChainProvider.GetOptionContractList(self.Symbol in main.py:line 109
Derek Melchin
Hi Miguel,
The second argument passed to `GetOptionContractList` should be a datetime object. This is documented here. To resolve the issue, we can use
self.OptionChainProvider.GetOptionContractList(self.Symbol, self.Time)
See the attached backtest for an example.
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.
Miguel Palanca
Hi Derek,
Thank you for the quick response. That worked but I now get the below issue.
Runtime Error: Trying to dynamically access a method that does not exist throws a TypeError exception. To prevent the exception, ensure each parameter type matches those required by the 'datetime.datetime'>) method. Please checkout the API documentation. at SellCall contracts = self.OptionChainProvider.GetOptionContractList(self.Symbol in main.py:line 110 TypeError : No method matches given arguments for GetOptionContractList: (, )Trying to dynamically access a method that does not exist throws a TypeError exception. To prevent the exception, ensure each parameter type matches those required by the 'datetime.datetime'>) method. Please checkout the API documentation. at SellCall contracts = self.OptionChainProvider.GetOptionContractList(self.Symbol in main.py:line 110#Calling the two below methods in the OnSecuritiesChanged self.SellCall() self.BuyPut() def SellCall(self): contracts = self.OptionChainProvider.GetOptionContractList(self.Symbol, self.Time) #contracts = self.OptionChainProvider.GetOptionChains(self.Symbol, self.Time.date()) if len(contracts) == 0: return min_expiry = 0 max_expiry = 40 filtered_contracts = [i for i in contracts if min_expiry <= (i.ID.Date.date() - self.date()).days <= max_expiry] call = [x for x in filtered_contracts if x.ID.OptionRight == 0] if len(call) == 0: return # sorted the contracts according to their expiration dates and choose the ATM options self.contract = sorted(sorted(call, key = lambda x: abs(symbol.Price - x.ID.StrikePrice)), key = lambda x: x.ID.Date, reverse=True)[0] self.AddOptionContract(self.contract, Resolution.Minute) self.MarketOrder(self.contract, -1) def BuyPut(self): contracts = self.OptionChainProvider.GetOptionContractList(symbol, self.Time()) #contracts = self.OptionChainProvider.GetOptionChains(self.Symbol, self.Time.date()) if len(contracts) == 0: return min_expiry = 0 max_expiry = 40 filtered_contracts = [i for i in contracts if min_expiry <= (i.ID.Date.date() - self.Time.date()).days <= max_expiry] put = [x for x in filtered_contracts if x.ID.OptionRight == 1] if len(put) == 0: return # sorted the contracts according to their expiration dates and choose the ATM options self.contract = sorted(sorted(put, key = lambda x: abs(symbol.Price - x.ID.StrikePrice)), key = lambda x: x.ID.Date, reverse=True)[0] self.AddOptionContract(self.contract, Resolution.Minute) self.MarketOrder(self.contract, 1)
Derek Melchin
Hi Miguel,
To resolve the issue, we need to remove the parentheses on `self.Time()`. We also need to rename `self.Symbol` to `self.symbol`. Lastly, if we are calling `AddOptionContract` in the same time step where we order the option contract, we need to add a security initializer.
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.
Miguel Palanca
Hi Derek,
Thank you! That seems to work in one of my projects. However on the EMA project I'm working on, the security initializer seems to fail to fire. It throws down the below error. Could this be because I'm attempting to fire it through a for loop or would it be loop agnostic?
Runtime Error: ArgumentException : The underlying equity asset (BAC) is set to Adjusted, please change this to DataNormalizationMode.Raw with the SetDataNormalization() method at QuantConnect.Algorithm.QCAlgorithm.AddOptionContract (QuantConnect.Symbol symbol, System.Nullable`1[T] resolution, System.Boolean fillDataForward, System.Decimal leverage) [0x000f2] in :0 at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke(System.Reflection.MonoMethod,object,object[],System.Exception&) at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00032] in :0 at BuyPut self.AddOptionContract(self.contract in main.py:line 168 ArgumentException : The underlying equity asset (BAC) is set to Adjusted, please change this to DataNormalizationMode.Raw with the SetDataNormalization() method at QuantConnect.Algorithm.QCAlgorithm.AddOptionContract (QuantConnect.Symbol symbol, System.Nullable`1[T] resolution, System.Boolean fillDataForward, System.Decimal leverage) [0x000f2] in :0 at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke(System.Reflection.MonoMethod,object,object[],System.Exception&) at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00032] in :0 To try and amend this, I tried to add it within the OnSecuritiesChanged method, although my use of this could be wrong?I've attached a code snippet below, unfortunately couldnt attach the exact backtest since it had a runtime error.from riskManagement import * from datetime import timedelta class EMAMomentumUniverse(QCAlgorithm): def Initialize(self): self.SetStartDate(2015, 1, 1) #self.SetEndDate(2019, 4, 1) self.SetCash(100000) self.SetBenchmark("SPY") self.UniverseSettings.Resolution = Resolution.Minute #setting the coarse filter for investment universe self.AddUniverse(self.CoarseSelectionFunction) #self.AddUniverseSelection(LiquidValueUniverseSelectionModel()) #self.AddRiskManagement( ProtecOptions() ) #declaring dictionary averages self.SetRiskManagement(MaximumDrawdownPercentPerSecurityCustom(0.10)) self.SetExecution(ImmediateExecutionModel()) self.averages = { } # self.underlyingsymbol = equity.Symbol self.hist = RollingWindow[float](390*22) self.contract = None self.SetSecurityInitializer(self.security_initializer) def security_initializer(self, security): if security.Type == SecurityType.Equity: security.SetDataNormalizationMode(DataNormalizationMode.Raw) elif security.Type == SecurityType.Option: security.SetMarketPrice(self.GetLastKnownPrice(security)) def CoarseSelectionFunction(self, universe): #Main output, creating a list where the below applies selected = [] #Sort by dollar volume using lambda function, declare universe as EQTY > $10 universe = sorted(universe, key=lambda c: c.DollarVolume, reverse=True) universe = [c for c in universe if c.Price > 10][:100] #loop for all stocks in universe, uses all coarse data for coarse in universe: symbol = coarse.Symbol #Check for instance of SelectionData for this symbol in averages dictionary if symbol not in self.averages: # 1. Call history to get an array of 200 days of history data history = self.History(symbol, 200, Resolution.Daily) #2. Create new instance of SelectionData with the 'history' result self.averages[symbol] = SelectionData(history) #Update symbol with latest coarse.AdjustedPrice data \\ accesing method and pass params self.averages[symbol].update(self.Time, coarse.AdjustedPrice) #Check if indicators are ready, and that the 50 day EMA is > the 200 day EMA; then add to list 'selected' #Access property of class as dictionary item if self.averages[symbol].is_ready() and self.averages[symbol].fast > self.averages[symbol].slow: selected.append(symbol) #update the selected list with the top 10 results return selected[:10] #def OnData(self, data): #self.security_initializer(data) #Method for monitoring if universe has changed def OnSecuritiesChanged(self, changes): #liquidate securities leaving the universe for security in changes.RemovedSecurities: self.Liquidate(security.Symbol) #Allocate 10% holdings to each asset added to universe for security in changes.AddedSecurities: self.symbol = security.Symbol self.security_initializer(security) self.SetHoldings(security.Symbol, 0.10) #self.log(security.Symbol + " Symbol Log") if security.Type == SecurityType.Equity: #self.SellCall(security.Symbol) self.security_initializer(security) self.BuyPut() #def SellCall(self, sec): '''def SellCall(self): #self.symbol = sec contracts = self.OptionChainProvider.GetOptionContractList(self.symbol, self.Time) self.Debug(f"SellCall: {len(contracts)}") if len(contracts) == 0: return min_expiry = 0 max_expiry = 40 filtered_contracts = [i for i in contracts if min_expiry <= (i.ID.Date.date() - self.Time.date()).days <= max_expiry] call = [x for x in filtered_contracts if x.ID.OptionRight == 0] if len(call) == 0: return # sorted the contracts according to their expiration dates and choose the ATM options price = self.Securities[self.symbol].Price self.contract = sorted(sorted(call, key = lambda x: abs(price - x.ID.StrikePrice)), key = lambda x: x.ID.Date, reverse=True)[0] self.AddOptionContract(self.contract, Resolution.Minute) self.MarketOrder(self.contract, -1) ''' #def BuyPut(self, sec): def BuyPut(self): contracts = self.OptionChainProvider.GetOptionContractList(self.symbol, self.Time) self.Debug(f"BuyPut: {len(contracts)}") #contracts = self.OptionChainProvider.GetOptionChains(self.Symbol, self.Time.date()) if len(contracts) == 0: return min_expiry = 0 max_expiry = 40 filtered_contracts = [i for i in contracts if min_expiry <= (i.ID.Date.date() - self.Time.date()).days <= max_expiry] put = [x for x in filtered_contracts if x.ID.OptionRight == 1] if len(put) == 0: return price = self.Securities[self.symbol].Price # sorted the contracts according to their expiration dates and choose the ATM options self.contract = sorted(sorted(put, key = lambda x: abs(price - x.ID.StrikePrice)), key = lambda x: x.ID.Date, reverse=True)[0] self.AddOptionContract(self.contract, Resolution.Minute) self.MarketOrder(self.contract, 1)
Derek Melchin
Hi Miguel,
The security initializer is called automatically when securities subscriptions are added or removed. We don't need to explicitly call it.
The algorithm above places orders during the OnSecuritiesChanged method. We should adjust this because the algorithm is using minute resolution data, so there is no data at the current time when OnSecuritiesChanged is called since it's called at midnight.
In the attached backtest, the ordering logic has been moved to the OnData method. In addition, a SelectionData class has been added and the weight given to the SetHoldings method has been reduced. 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.
Miguel Palanca
Hi Derek,
Thank you for the fix and the response! I appreciate it. The info on the OnSecuritiesChanged running at midnight was definitely a huge help. I had thought this would automatically run throughout the day as new securities met the criteria. Definitely will be switching it to the OnData even for my other live algo!
As for the self.Liquidate being necessary, my understanding of it is that when the criteria is no longer met, calling that would liquidate the position from my portfolio. Is this wrong? Always open to suggestions.
I cloned tried running the above backtest with a longer date (until 2016.01.01), but it seems to throw the same error, albeit for a different ticker? Is there a way I can confirm the security initializer is actually firing? I think the error is that it is trying to create an option contract but the data type for the equity is wrong. If I put at the start of the BuyPut() method a line to set the data normalization to raw, do you think this would be a sufficient band-aid fix, or does setting the data normalization twice not work?
Runtime Error: ArgumentException : The underlying equity asset (IVV) is set to Adjusted, please change this to DataNormalizationMode.Raw with the SetDataNormalization() method at QuantConnect.Algorithm.QCAlgorithm.AddOptionContract (QuantConnect.Symbol symbol, System.Nullable`1[T] resolution, System.Boolean fillDataForward, System.Decimal leverage) [0x000f2] in :0 at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke(System.Reflection.MonoMethod,object,object[],System.Exception&) at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00032] in :0 at BuyPut self.AddOptionContract(self.contract in main.py:line 180 ArgumentException : The underlying equity asset (IVV) is set to Adjusted, please change this to DataNormalizationMode.Raw with the SetDataNormalization() method at QuantConnect.Algorithm.QCAlgorithm.AddOptionContract (QuantConnect.Symbol symbol, System.Nullable`1[T] resolution, System.Boolean fillDataForward, System.Decimal leverage) [0x000f2] in :0 at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke(System.Reflection.MonoMethod,object,object[],System.Exception&) at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00032] in :0Miguel Palanca
The very strange part is that it seems to only affect some tickers, everything else seems to be in order, with contracts sold for a variety of tickers except GILD, TLT and im sure a few others. Could this be a data source issue?
Derek Melchin
Hi Miguel,
The "Is this necessary?" comment was just accidentally left behind during development of the backtest above. The comment can be removed. Calling Liquidate does sell the positions in the portfolio.
In regards to the data normalization error, the underlying issue is that the `security_initializer` is only called once, when a security is created. When a security is added/removed to/from the universe, we don't delete the security, we only add/remove data subscriptions, which are created by using the `UniverseSettings` properties. Therefore, to resolve the data normalization error, we should remove
if security.Type == SecurityType.Equity: security.SetDataNormalizationMode(DataNormalizationMode.Raw)
from the `security_initializer` and instead add
self.UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw
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.
Miguel Palanca
Hi Derek,
Thank you very much! This seems to have fixed the error. With the new addition, does that mean the security initializer is no longer required? Is it best practice to still keep it there?
Derek Melchin
Hi Miguel,
In this case, we should keep the security initializer. Setting
security.SetMarketPrice(self.GetLastKnownPrice(security))
enables us to call `AddOptionContract` and purchase the contract within the same time step.
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.
Miguel Palanca
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!