Hi all,
I'm trying to implement an implied volatility rank which I can use for decision to trade options off a basket of stocks filtered based on their underlying volume. Following tips I've found in the forums, I'm updating the options universe at the OnSecuritiesChanged rather than in the initialize.
I've been unable to implement the IV rank for multiple reasons, the most common of which is an error for list index out of range often appears, and the second is being unable to create indicators for all the underlying stocks.
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
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.
'''
# This is where the index out of range happens
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}")
# I'm unable to pipe an indicator into another (Trying to get the AverageHV into the dict)
def OnVolatility(self, sender, updated, symbol):
if self.indicators[symbol]["Volatility"].IsReady:
self.indicators[symbol]["AverageHV"].Update(self.Time, updated.Value)
Oscar Lee
I've solved for the error for list index out of range by explicitly ensuring the size of the list is greater than 0. However, I've not yet been able to figure out how to implement an indicator for all the underlying symbols that requires another indicator to be piped into them.
# Filter for the first ATM contract if chain != None and len(list(chain)) > 0: atmContract = sorted(chain, key = lambda x: abs(x.UnderlyingLastPrice - x.Strike))[0] impliedVolatility = atmContract.ImpliedVolatility underlyingSymbol = atmContract.UnderlyingSymbol.Value self.Debug(f"{underlyingSymbol} has a implied volatility of {impliedVolatility}")
Oscar Lee
Edit: I've realised that not even my 30 day historical indicator is working
Trying to get it to print its values return 0.
if not underlying.Symbol.Value in self.indicators: self.indicators[underlying.Symbol.Value] = \ {"Volatility": self.STD(underlying.Symbol.Value, 30, Resolution.Daily)}
Derek Melchin
Hi Oscar,
Refer to our previous thread to resolve the 0 indicator value error. Please avoid creating duplicate threads going forward.
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.
David Gordon
Hi Derek,
The link you posted is broken.
Jared Broad
Fixed thanks David
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
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!