Hello everyone, I'm trying to implement an implied volatility rank which I can use for decision to trade the option. I've been unable to imlement the IV rank for multiple reasons, as the IV is often calculated to be 0, and an error for list index out of range often appears.
What I'm trying to do is to choose the 5 most liquid stocks and then create a dictionary for them that holds their indicators and other values which I might access later. The first indicator I want to hold is a historical volatility indicator that will be used to normalize the implied volatility. Then, I want to store the Normalized IV back into the dictionary so I can rank the dictionary by the stocks with the highest IV and make trades off them.
import pandas as pd
class ParticleCalibratedCoil(QCAlgorithm):
def Initialize(self):
'''
Parameters for adjusting
'''
self.numberOfLiquidStocks = 5 # Controls the number of stocks in play
'''
Backtesting variables
'''
self.SetWarmUp(1)
self.SetStartDate(2019, 11, 18)
self.SetEndDate(2019, 12, 18)
self.SetCash(1000000)
'''
Algorithm variables
'''
self.UniverseSettings.Resolution = Resolution.Minute
self.AddUniverse(self.CoarseSelectionFilter)
self.SetSecurityInitializer(lambda x: x.SetDataNormalizationMode(DataNormalizationMode.Raw))
self.dictOfUnderlyingIndicators = {}
def CoarseSelectionFilter(self, coarse):
'''
1. Sorts each element of the coarse object by dollar volume
2. Returns a list of coarse object, limited to top 100 highest volume
3. Returns a list of symbols we want to initialise into the algorithm
'''
self.sortedByDollarVolume = sorted(coarse, key=lambda c: c.DollarVolume, reverse=True)
self.topHundredMostLiquid = self.sortedByDollarVolume[:self.numberOfLiquidStocks]
return [stock.Symbol for stock in self.topHundredMostLiquid]
def OnSecuritiesChanged (self, changes):
'''
For any new securities added into the universe
If the security is an underlying
Subscribe to the option chains
For any securities we want removed from the universe
Remove the underlying and then remove the options
For each new secury added into the universe
If there is not yet one
Create a standard deviation indicator
'''
for underlying in changes.AddedSecurities:
if underlying.Symbol.SecurityType != SecurityType.Equity: continue
option = self.AddOption(underlying.Symbol.Value, Resolution.Minute)
option.SetFilter(-5, +2, timedelta(30), timedelta(60))
if not underlying.Symbol in self.dictOfUnderlyingIndicators:
self.dictOfUnderlyingIndicators[underlying.Symbol] = \
{"Volatility": self.RSI(underlying.Symbol, 1, Resolution.Minute)}
for underlying in changes.RemovedSecurities:
self.RemoveSecurity(underlying.Symbol)
for symbol in self.Securities.Keys:
if symbol.SecurityType == SecurityType.Option and symbol.Underlying == underlying.Symbol:
self.RemoveSecurity(symbol)
def OnData(self, slice):
'''
For each OptionChain, the key is the underlying symbol object, while the
value is the option chain.
For each chain in OptionChains, each chain represents the entire chain of option contracts
for the underlying security.
'''
for chain in slice.OptionChains.Values:
# Filter for the first ATM contract
atmContract = sorted(chain, key = lambda x: abs(x.UnderlyingLastPrice - x.Strike))[0]
impliedVolatility = atmContract.ImpliedVolatility
underlyingSymbol = atmContract.UnderlyingSymbol
if underlyingSymbol in self.dictOfUnderlyingIndicators and \
"Volatility" in self.dictOfUnderlyingIndicators[underlyingSymbol]:
self.dictOfUnderlyingIndicators[underlyingSymbol] = {'NormalizedIV':
impliedVolatility / self.dictOfUnderlyingIndicators[underlyingSymbol]["Volatility"].Current.Value
}
self.Debug(f"Implied volatility of {underlyingSymbol} is {impliedVolatility}")
self.Debug(f"The normalized implied volatility is {self.dictOfUnderlyingIndicators[underlyingSymbol]['NormalizedIV']}")
Oscar Lee
I managed to solve the implied volatiltiy problem using
option.PriceModel = OptionPriceModels.CrankNicolsonFD()
but still seem unable to rank them by IV.
Oscar Lee
Edit 2: I was using the wrong indicator, should have been STD instead of RSI. But I'm still getting the list index out of range error at for chain in slice.OptionChains.Values:
Oscar Lee
I'm trying a new way to create indicators for above, but I don't seem to be able to get the event handler to work. How do I create an indicator for each option that I'm subscribed to? Issue is at the last two lines.
import pandas as pd from QuantConnect.Securities.Option import OptionPriceModels class ParticleCalibratedCoil(QCAlgorithm): def Initialize(self): ''' Parameters for adjusting ''' self.numberOfLiquidStocks = 5 # Controls the number of stocks in play ''' Backtesting variables ''' self.SetWarmUp(43800) self.SetStartDate(2019, 10, 18) self.SetEndDate(2019, 12, 18) self.SetCash(1000000) ''' Algorithm variables ''' self.UniverseSettings.Resolution = Resolution.Minute self.AddUniverse(self.CoarseSelectionFilter) self.SetSecurityInitializer(lambda x: x.SetDataNormalizationMode(DataNormalizationMode.Raw)) self.indicators = {} def CoarseSelectionFilter(self, coarse): ''' 1. Sorts each element of the coarse object by dollar volume 2. Returns a list of coarse object, limited to top 100 highest volume 3. Returns a list of symbols we want to initialise into the algorithm ''' self.sortedByDollarVolume = sorted(coarse, key=lambda c: c.DollarVolume, reverse=True) self.topHundredMostLiquid = self.sortedByDollarVolume[:self.numberOfLiquidStocks] return [stock.Symbol for stock in self.topHundredMostLiquid] def OnSecuritiesChanged (self,changes): ''' For any new securities added into the universe If the security is an underlying Subscribe to the option chains For any securities we want removed from the universe Remove the underlying and then remove the options For each new secury added into the universe If there is not yet one Create a standard deviation indicator ''' for underlying in changes.AddedSecurities: if underlying.Symbol.SecurityType != SecurityType.Equity: continue option = self.AddOption(underlying.Symbol.Value, Resolution.Minute) option.SetFilter(-5, +2, timedelta(30), timedelta(60)) option.PriceModel = OptionPriceModels.CrankNicolsonFD() if not underlying.Symbol.Value in self.indicators: self.indicators[underlying.Symbol.Value] = \ {"Volatility": self.STD(underlying.Symbol.Value, 30, Resolution.Daily)} self.indicators[underlying.Symbol.Value]["AverageHV"] = \ SimpleMovingAverage(365) self.indicators[underlying.Symbol.Value]["Volatility"].Updated += self.OnVolatility for underlying in changes.RemovedSecurities: self.RemoveSecurity(underlying.Symbol) for symbol in self.Securities.Keys: if symbol.SecurityType == SecurityType.Option and symbol.Underlying == underlying.Symbol: self.RemoveSecurity(symbol) def OnData(self, slice): ''' For each OptionChain, the key is the underlying symbol object, while the value is the option chain. For each chain in OptionChains, each chain represents the entire chain of option contracts for the underlying security. ''' for chain in slice.OptionChains.Values: # Filter for the first ATM contract if chain != None: atmContract = sorted(chain, key = lambda x: abs(x.UnderlyingLastPrice - x.Strike))[0] impliedVolatility = atmContract.ImpliedVolatility underlyingSymbol = atmContract.UnderlyingSymbol def OnVolatility(self, sender, updated): if self.indicators[underlying.Symbol.Value]["Volatility"].IsReady: self.indicators[underlying.Symbol.Value]["AverageHV"].Update(self.Time, updated.Value)
Oscar Lee
This is my latest attempt but still doesn't work. Would appreciate if anyone has a good solution!
import pandas as pd from functools import partial from QuantConnect.Securities.Option import OptionPriceModels class ParticleCalibratedCoil(QCAlgorithm): def Initialize(self): ''' Parameters for adjusting ''' self.numberOfLiquidStocks = 5 # Controls the number of stocks in play ''' Backtesting variables ''' self.SetWarmUp(43800) self.SetStartDate(2019, 10, 18) self.SetEndDate(2019, 12, 18) self.SetCash(1000000) ''' Algorithm variables ''' self.UniverseSettings.Resolution = Resolution.Minute self.AddUniverse(self.CoarseSelectionFilter) self.SetSecurityInitializer(lambda x: x.SetDataNormalizationMode(DataNormalizationMode.Raw)) self.indicators = {} def CoarseSelectionFilter(self, coarse): ''' 1. Sorts each element of the coarse object by dollar volume 2. Returns a list of coarse object, limited to top 100 highest volume 3. Returns a list of symbols we want to initialise into the algorithm ''' self.sortedByDollarVolume = sorted(coarse, key=lambda c: c.DollarVolume, reverse=True) self.topHundredMostLiquid = self.sortedByDollarVolume[:self.numberOfLiquidStocks] return [stock.Symbol for stock in self.topHundredMostLiquid] def OnSecuritiesChanged (self,changes): ''' For any new securities added into the universe If the security is an underlying Subscribe to the option chains For any securities we want removed from the universe Remove the underlying and then remove the options For each new secury added into the universe If there is not yet one Create a standard deviation indicator ''' for underlying in changes.AddedSecurities: if underlying.Symbol.SecurityType != SecurityType.Equity: continue option = self.AddOption(underlying.Symbol.Value, Resolution.Minute) option.SetFilter(-5, +2, timedelta(30), timedelta(60)) option.PriceModel = OptionPriceModels.CrankNicolsonFD() if not underlying.Symbol.Value in self.indicators: self.indicators[underlying.Symbol.Value] = \ {"Volatility": self.STD(underlying.Symbol.Value, 30, Resolution.Daily)} self.indicators[underlying.Symbol.Value]["AverageHV"] = \ SimpleMovingAverage(365) symbol = underlying.Symbol.Value self.indicators[underlying.Symbol.Value]["Volatility"].Updated += partial(self.OnVolatility, symbol=symbol) for underlying in changes.RemovedSecurities: self.RemoveSecurity(underlying.Symbol) for symbol in self.Securities.Keys: if symbol.SecurityType == SecurityType.Option and symbol.Underlying == underlying.Symbol: self.RemoveSecurity(symbol) def OnData(self, slice): ''' For each OptionChain, the key is the underlying symbol object, while the value is the option chain. For each chain in OptionChains, each chain represents the entire chain of option contracts for the underlying security. ''' for chain in slice.OptionChains.Values: # Filter for the first ATM contract if chain != None: atmContract = sorted(chain, key = lambda x: abs(x.UnderlyingLastPrice - x.Strike))[0] impliedVolatility = atmContract.ImpliedVolatility underlyingSymbol = atmContract.UnderlyingSymbol.Value if self.indicators[underlyingSymbol]["AverageHV"].IsReady: historicalVolatility = self.indicators[underlyingSymbol]["AverageHV"].Current.Value self.Debug(f"underlyingSymbol has a implied volatility of {impliedVolatility}") self.Debug(f"underlyingSymbol has a historical volatility of {historicalVolatility}") def OnVolatility(self, sender, updated, symbol): if self.indicators[symbol]["Volatility"].IsReady: self.indicators[symbol]["AverageHV"].Update(self.Time, updated.Value)
Oscar Lee
Runtime Error: IndexError : list index out of range
at OnData in main.py:line 95
IndexError : list index out of range (Open Stacktrace)
This error is very common across all my attempts, despite checking if chain is not none.
Derek Melchin
Hi Oscar,
The event handler is getting called properly. See the attached backtest below which prints a log message on each call. An issue with the code above is that the STD indicator is not warmed up so the handler receives a value of 0 for the first 30 calls. In the attached algorithm, we warm up the indicator after creating it. This process of warming up the indicator can also be extended to warmup the SMA of the STD as well if desired. However, I leave this to be implemented.
To resolve the IndexError issue, we just need to change line 94 in the file above from `if chain != None:` to `if chain.Contracts.Count < 1: continue`. This too is implemented in the attached algorithm.
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.
Oscar Lee
This process of warming up the indicator can also be extended to warmup the SMA of the STD as well if desired. However, I leave this to be implemented.
Apologies if it sounds like a dumb question, but will it work if I just change the history to 365 instead of 30? Since it should provide the SD indicator 30 days to warm up trigger the SMA updates as well?
Derek Melchin
Hi Oscar,
365 days of history would not be quite enough. Instead, we would need 365 + 30 = 395 days of history. The first 30 days warm up the STD indicator, then the following 365 days will update the STD indicator and warm up the SMA. It's important we only warm up the SMA when the STD IsReady.
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.
Jaipal Tuttle
Hello Derek Melchin . I love the piece of code you wrote to provide a solution. Unfortunately, when I run it I get this “Runtime Error: Please register to receive data for symbol 'DELL' using the AddSecurity() function. in Indicators.cs:line 2187 (Open Stack Trace)”
I'm guessing it is a one-line fix but I am still pretty new to QC.
Cheers,
-J
Oscar Lee
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!