Greeks and Implied Volatility
Indicators
Parameters
The following table describes the arguments that the automatic Option indicators methods accept:
Argument | Data Type | Description | Default Value |
---|---|---|---|
symbol | Symbol | The contract to use when calculating the indicator values. | |
mirror_option | Symbol | The mirror contract to use in parity type calculations. | None |
risk_free_rate | float | The risk-free interest rate. If you don't provide a value, the default value is the US primary credit rate from the Interest Rate Provider Model. | None |
dividend_yield | float | The dividend yield rate. If you don't provide a value, the default value comes from the Dividend Yield Provider Model. | None |
option_model | OptionPricingModelType |
The Option pricing model that's used to calculate the Greeks.
If you don't provide a value, the default value is OptionPricingModelType.BlackScholes for European-style Options.
| None |
resolution | Resolution | The resolution of the indicator data. If you don't provide a value, the default value is the resolution of the subscription you have for the Option contract(s). | None |
To perform implied volatility (IV) smoothing with a put-call pair, pass one of the contracts as the symbol
argument and pass the other contract in the pair as the mirror_option
argument.
The default IV smoothing method uses the one contract in the pair that's at-the-money or out-of-money to calculate the IV.
To change the smoothing function, call the set_smoothing_function
method of the ImpliedVolatility
class/property.
Several different Option pricing models are supported to calculate the IV and Greeks. The following table describes the OptionPricingModelType
enumeration members:
Implied Volatility
Implied volatility, σ , is the market's expectation for the future volatility of an asset and is implied by the price of the assets's Options contracts. You can't observe it in the market but you can derive it from the price of an Option. For more information about implied volatility, see Implied Volatility .
Automatic Indicators
To create an automatic indicator for implied volatility, call the QCAlgorithm.iv
method with the Option contract symbol
object(s).
class AutomaticImpliedVolatilityIndicatorAlgorithm(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2024, 1, 1) self.set_end_date(2024, 2, 1) # Subscribe to the underlying asset. self._future = self.add_future( Futures.Indices.SP_500_E_MINI, data_mapping_mode=DataMappingMode.OPEN_INTEREST, data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO, contract_depth_offset=0 ) self._future.set_filter(0, 182) # Set up a Scheduled Event to select contracts and create the indicators every day before market open. self.schedule.on( self.date_rules.every_day(self._future.symbol), self.time_rules.at(9, 0), self._update_contracts_and_greeks ) self._options = None def _update_contracts_and_greeks(self) -> None: if self._future.mapped is None: return # Get all the tradable Option contracts. chain = self.option_chain(self._future.mapped, flatten=True).data_frame if chain.empty: return # Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry. chain = chain[chain.expiry > self.time + timedelta(30)] expiry = chain.expiry.min() chain = chain[chain.expiry == expiry] chain.loc[:, 'abs_strike_delta'] = abs(chain['strike'] - chain['underlyinglastprice']) abs_strike_delta = chain['abs_strike_delta'].min() chain = chain[chain['abs_strike_delta'] == abs_strike_delta] # Group the contracts into call/put pairs. contracts_pair_sizes = chain.groupby(['expiry', 'strike']).count()['right'] paired_contracts = contracts_pair_sizes[contracts_pair_sizes == 2].index expiries = [x[0] for x in paired_contracts] strikes = [x[1] for x in paired_contracts] symbols = [ idx[-1] for idx in chain[ chain['expiry'].isin(expiries) & chain['strike'].isin(strikes) ].reset_index().groupby(['expiry', 'strike', 'right', 'symbol']).first().index ] pairs = [(symbols[i], symbols[i+1]) for i in range(0, len(symbols), 2)] for call, put in pairs: # Subscribe to both contracts. contract1 = self.add_future_option_contract(call) contract2 = self.add_future_option_contract(put) # Create and save the automatic ImpliedVolatility indicators. contract1.iv = self.iv(call, put) contract2.iv = self.iv(put, call) self._options = (call, put) def on_data(self, slice: Slice) -> None: # Get the ImpliedVolatility indicator of each contract. for canonical, chain in slice.option_chains.items(): for symbol in chain: option = self.securities[symbol] indicator = option.iv # Sell straddle as an example to trade. if not self.portfolio.invested and self._options: self.sell(self._options[0], 1) self.sell(self._options[1], 1) # Liquidate any assigned positions. if self.portfolio[self._future.mapped].invested: self.liquidate(self._future.mapped)
For more information about the
iv
method, see
Parameters
and
Using IV Indicator
.
Manual Indicators
To create a manual indicator for implied volatility, call the ImpliedVolatility
constructor.
class ManualImpliedVolatilityIndicatorAlgorithm(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2024, 1, 1) self.set_end_date(2024, 2, 1) # Subscribe to the underlying asset. self._future = self.add_future( Futures.Indices.SP_500_E_MINI, data_mapping_mode=DataMappingMode.OPEN_INTEREST, data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO, contract_depth_offset=0 ) self._future.set_filter(0, 182) # Set up the dividend yield provider for the underlying. self._dividend_yield_provider = ConstantDividendYieldModel(0) # Define the Option pricing model. self._option_pricing_model = OptionPricingModelType.FORWARD_TREE # Set up a Scheduled Event to select contract and create the indicators every day before market open. self.schedule.on( self.date_rules.every_day(self._future.symbol), self.time_rules.at(9, 0), self._update_contracts_and_greeks ) self._options = None def _update_contracts_and_greeks(self) -> None: if self._future.mapped is None: return # Get all the tradable Option contracts. chain = self.option_chain(self._future.mapped, flatten=True).data_frame if chain.empty: return # Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry. chain = chain[chain.expiry > self.time + timedelta(30)] expiry = chain.expiry.min() chain = chain[chain.expiry == expiry] chain.loc[:, 'abs_strike_delta'] = abs(chain['strike'] - chain['underlyinglastprice']) abs_strike_delta = chain['abs_strike_delta'].min() chain = chain[chain['abs_strike_delta'] == abs_strike_delta] # Group the contracts into call/put pairs. contracts_pair_sizes = chain.groupby(['expiry', 'strike']).count()['right'] paired_contracts = contracts_pair_sizes[contracts_pair_sizes == 2].index expiries = [x[0] for x in paired_contracts] strikes = [x[1] for x in paired_contracts] symbols = [ idx[-1] for idx in chain[ chain['expiry'].isin(expiries) & chain['strike'].isin(strikes) ].reset_index().groupby(['expiry', 'strike', 'right', 'symbol']).first().index ] pairs = [(symbols[i], symbols[i+1]) for i in range(0, len(symbols), 2)] for call, put in pairs: # Subscribe to both contracts. contract1 = self.add_future_option_contract(call) contract2 = self.add_future_option_contract(put) # Create and save the automatic ImpliedVolatility indicators. for contract_a, contract_b in [(contract1, contract2), (contract2, contract1)]: contract_a.ImpliedVolatility = ImpliedVolatility( contract_a.symbol, self.risk_free_interest_rate_model, self._dividend_yield_provider, contract_b.symbol, self._option_pricing_model ) self._options = (call, put) def on_data(self, slice: Slice) -> None: # Iterate through the indicators. for canonical, chain in slice.option_chains.items(): for option in chain: underlying = option.symbol.underlying indicator = self.securities[option].ImpliedVolatility mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ).value # Check if price data is available for both contracts and the underlying asset. q = slice.quote_bars b = slice.bars if option in q and mirror in q and underlying in b: data_points = [ IndicatorDataPoint(option, q[option].end_time, q[option].close), IndicatorDataPoint(mirror, q[mirror].end_time, q[mirror].close), IndicatorDataPoint( underlying, b[underlying].end_time, b[underlying].close ) ] for data_point in data_points: indicator.update(data_point) # Get the current value of the ImpliedVolatility indicator. value = indicator.current.value # Sell straddle as an example to trade. if not self.portfolio.invested and self._options: self.sell(self._options[0], 1) self.sell(self._options[1], 1) # Liquidate any assigned positions. if self.portfolio[self._future.mapped].invested: self.liquidate(self._future.mapped)
For more information about the ImpliedVolatility
constructor, see Using IV Indicator.
Volatility Smoothing
The default
IV smoothing
method uses the one contract in the pair that's at-the-money or out-of-money to calculate the IV.
To change the smoothing function, pass a
mirror_option
argument to the
iv
method or
ImpliedVolatility
constructor and then call the
set_smoothing_function
method of the resulting
ImpliedVolatility
object.
The follow table describes the arguments of the custom function:
Argument | Data Type | Description |
---|---|---|
iv
|
float
|
The IV of the Option contract. |
mirror_iv
|
float
|
The IV of the mirror Option contract. |
The method must return a
float
as the smoothened IV.
def initialize(self): option = Symbol.create_option("AAPL", Market.USA, OptionStyle.AMERICAN, OptionRight.PUT, 505, datetime(2014, 6, 27)) self.add_option_contract(option) mirror_option = Symbol.create_option("AAPL", Market.USA, OptionStyle.AMERICAN, OptionRight.CALL, 505, datetime(2014, 6, 27)) self.add_option_contract(mirror_option) self._iv = self.iv(option, mirror_option) # Example: The average of the call-put pair. self._iv.set_smoothing_function(lambda iv, mirror_iv: (iv + mirror_iv) * 0.5)
Delta
Delta, Δ, is the rate of change of the Option price with respect to the price of the underlying asset. It measures the first-order sensitivity of the price to a movement in underlying price. For example, an Option delta of 0.4 means that if the underlying asset moves by 1%, then the value of the Option moves by 0.4 × 1% = 0.4%. For more information about delta, see Delta.
Automatic Indicators
To create an automatic indicator for delta, call the QCAlgorithm.d
method with the Option contract symbol
object(s).
class AutomaticDeltaIndicatorAlgorithm(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2024, 1, 1) self.set_end_date(2024, 2, 1) # Subscribe to the underlying asset. self._future = self.add_future( Futures.Indices.SP_500_E_MINI, data_mapping_mode=DataMappingMode.OPEN_INTEREST, data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO, contract_depth_offset=0 ) self._future.set_filter(0, 182) # Set up a Scheduled Event to select contracts and create the indicators every day before market open. self.schedule.on( self.date_rules.every_day(self._future.symbol), self.time_rules.at(9, 0), self._update_contracts_and_greeks ) self._options = None def _update_contracts_and_greeks(self) -> None: if self._future.mapped is None: return # Get all the tradable Option contracts. chain = self.option_chain(self._future.mapped, flatten=True).data_frame if chain.empty: return # Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry. chain = chain[chain.expiry > self.time + timedelta(30)] expiry = chain.expiry.min() chain = chain[chain.expiry == expiry] chain.loc[:, 'abs_strike_delta'] = abs(chain['strike'] - chain['underlyinglastprice']) abs_strike_delta = chain['abs_strike_delta'].min() chain = chain[chain['abs_strike_delta'] == abs_strike_delta] # Group the contracts into call/put pairs. contracts_pair_sizes = chain.groupby(['expiry', 'strike']).count()['right'] paired_contracts = contracts_pair_sizes[contracts_pair_sizes == 2].index expiries = [x[0] for x in paired_contracts] strikes = [x[1] for x in paired_contracts] symbols = [ idx[-1] for idx in chain[ chain['expiry'].isin(expiries) & chain['strike'].isin(strikes) ].reset_index().groupby(['expiry', 'strike', 'right', 'symbol']).first().index ] pairs = [(symbols[i], symbols[i+1]) for i in range(0, len(symbols), 2)] for call, put in pairs: # Subscribe to both contracts. contract1 = self.add_future_option_contract(call) contract2 = self.add_future_option_contract(put) # Create and save the automatic Delta indicators. contract1.d = self.d(call, put) contract2.d = self.d(put, call) self._options = (call, put) def on_data(self, slice: Slice) -> None: # Get the Delta indicator of each contract. for canonical, chain in slice.option_chains.items(): for symbol in chain: option = self.securities[symbol] indicator = option.d # Sell straddle as an example to trade. if not self.portfolio.invested and self._options: self.sell(self._options[0], 1) self.sell(self._options[1], 1) # Liquidate any assigned positions. if self.portfolio[self._future.mapped].invested: self.liquidate(self._future.mapped)
The follow table describes the arguments that the d
method accepts in addition to the standard parameters:
Argument | Data Type | Description | Default Value |
---|---|---|---|
iv_model | OptionPricingModelType |
The Option pricing model to use to estimate the IV when calculating Delta.
If you don't provide a value, the default value is to match the option_model parameter.
| None |
For more information about the d
method, see Using D Indicator.
Manual Indicators
To create a manual indicator for delta, call the Delta
constructor.
class ManualDeltaIndicatorAlgorithm(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2024, 1, 1) self.set_end_date(2024, 2, 1) # Subscribe to the underlying asset. self._future = self.add_future( Futures.Indices.SP_500_E_MINI, data_mapping_mode=DataMappingMode.OPEN_INTEREST, data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO, contract_depth_offset=0 ) self._future.set_filter(0, 182) # Set up the dividend yield provider for the underlying. self._dividend_yield_provider = ConstantDividendYieldModel(0) # Define the Option pricing model. self._option_pricing_model = OptionPricingModelType.FORWARD_TREE # Set up a Scheduled Event to select contract and create the indicators every day before market open. self.schedule.on( self.date_rules.every_day(self._future.symbol), self.time_rules.at(9, 0), self._update_contracts_and_greeks ) self._options = None def _update_contracts_and_greeks(self) -> None: if self._future.mapped is None: return # Get all the tradable Option contracts. chain = self.option_chain(self._future.mapped, flatten=True).data_frame if chain.empty: return # Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry. chain = chain[chain.expiry > self.time + timedelta(30)] expiry = chain.expiry.min() chain = chain[chain.expiry == expiry] chain.loc[:, 'abs_strike_delta'] = abs(chain['strike'] - chain['underlyinglastprice']) abs_strike_delta = chain['abs_strike_delta'].min() chain = chain[chain['abs_strike_delta'] == abs_strike_delta] # Group the contracts into call/put pairs. contracts_pair_sizes = chain.groupby(['expiry', 'strike']).count()['right'] paired_contracts = contracts_pair_sizes[contracts_pair_sizes == 2].index expiries = [x[0] for x in paired_contracts] strikes = [x[1] for x in paired_contracts] symbols = [ idx[-1] for idx in chain[ chain['expiry'].isin(expiries) & chain['strike'].isin(strikes) ].reset_index().groupby(['expiry', 'strike', 'right', 'symbol']).first().index ] pairs = [(symbols[i], symbols[i+1]) for i in range(0, len(symbols), 2)] for call, put in pairs: # Subscribe to both contracts. contract1 = self.add_future_option_contract(call) contract2 = self.add_future_option_contract(put) # Create and save the automatic Delta indicators. for contract_a, contract_b in [(contract1, contract2), (contract2, contract1)]: contract_a.Delta = Delta( contract_a.symbol, self.risk_free_interest_rate_model, self._dividend_yield_provider, contract_b.symbol, self._option_pricing_model ) self._options = (call, put) def on_data(self, slice: Slice) -> None: # Iterate through the indicators. for canonical, chain in slice.option_chains.items(): for option in chain: underlying = option.symbol.underlying indicator = self.securities[option].Delta mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ).value # Check if price data is available for both contracts and the underlying asset. q = slice.quote_bars b = slice.bars if option in q and mirror in q and underlying in b: data_points = [ IndicatorDataPoint(option, q[option].end_time, q[option].close), IndicatorDataPoint(mirror, q[mirror].end_time, q[mirror].close), IndicatorDataPoint( underlying, b[underlying].end_time, b[underlying].close ) ] for data_point in data_points: indicator.update(data_point) # Get the current value of the Delta indicator. value = indicator.current.value # Sell straddle as an example to trade. if not self.portfolio.invested and self._options: self.sell(self._options[0], 1) self.sell(self._options[1], 1) # Liquidate any assigned positions. if self.portfolio[self._future.mapped].invested: self.liquidate(self._future.mapped)
For more information about the Delta
constructor, see Using D Indicator.
Volatility Smoothing
The default IV smoothing method uses the one contract in the pair that's at-the-money or out-of-money to calculate the IV.
To change the smoothing function, pass a mirror_option
argument to the indicator method or constructor and then call the set_smoothing_function
method of the implied_volatility
property of the indicator.
# Example: Average IV of the call-put pair. delta_indicator.implied_volatility.set_smoothing_function(lambda iv, mirror_iv: (iv + mirror_iv) * 0.5)
Gamma
Gamma, Γ, is the rate of change of the portfolio's delta with respect to the underlying asset's price. It represents the second-order sensitivity of the Option to a movement in the underlying asset's price. For more information about Gamma, see Gamma.
Automatic Indicators
To create an automatic indicator for gamma, call the QCAlgorithm.g
method with the Option contract symbol
object(s).
class AutomaticGammaIndicatorAlgorithm(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2024, 1, 1) self.set_end_date(2024, 2, 1) # Subscribe to the underlying asset. self._future = self.add_future( Futures.Indices.SP_500_E_MINI, data_mapping_mode=DataMappingMode.OPEN_INTEREST, data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO, contract_depth_offset=0 ) self._future.set_filter(0, 182) # Set up a Scheduled Event to select contracts and create the indicators every day before market open. self.schedule.on( self.date_rules.every_day(self._future.symbol), self.time_rules.at(9, 0), self._update_contracts_and_greeks ) self._options = None def _update_contracts_and_greeks(self) -> None: if self._future.mapped is None: return # Get all the tradable Option contracts. chain = self.option_chain(self._future.mapped, flatten=True).data_frame if chain.empty: return # Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry. chain = chain[chain.expiry > self.time + timedelta(30)] expiry = chain.expiry.min() chain = chain[chain.expiry == expiry] chain.loc[:, 'abs_strike_delta'] = abs(chain['strike'] - chain['underlyinglastprice']) abs_strike_delta = chain['abs_strike_delta'].min() chain = chain[chain['abs_strike_delta'] == abs_strike_delta] # Group the contracts into call/put pairs. contracts_pair_sizes = chain.groupby(['expiry', 'strike']).count()['right'] paired_contracts = contracts_pair_sizes[contracts_pair_sizes == 2].index expiries = [x[0] for x in paired_contracts] strikes = [x[1] for x in paired_contracts] symbols = [ idx[-1] for idx in chain[ chain['expiry'].isin(expiries) & chain['strike'].isin(strikes) ].reset_index().groupby(['expiry', 'strike', 'right', 'symbol']).first().index ] pairs = [(symbols[i], symbols[i+1]) for i in range(0, len(symbols), 2)] for call, put in pairs: # Subscribe to both contracts. contract1 = self.add_future_option_contract(call) contract2 = self.add_future_option_contract(put) # Create and save the automatic Gamma indicators. contract1.g = self.g(call, put) contract2.g = self.g(put, call) self._options = (call, put) def on_data(self, slice: Slice) -> None: # Get the Gamma indicator of each contract. for canonical, chain in slice.option_chains.items(): for symbol in chain: option = self.securities[symbol] indicator = option.g # Sell straddle as an example to trade. if not self.portfolio.invested and self._options: self.sell(self._options[0], 1) self.sell(self._options[1], 1) # Liquidate any assigned positions. if self.portfolio[self._future.mapped].invested: self.liquidate(self._future.mapped)
The follow table describes the arguments that the g
method accepts in addition to the standard parameters:
Argument | Data Type | Description | Default Value |
---|---|---|---|
iv_model | OptionPricingModelType |
The Option pricing model to use to estimate the IV when calculating Gamma.
If you don't provide a value, the default value is to match the option_model parameter.
| None |
For more information about the g
method, see Using G Indicator.
Manual Indicators
To create a manual indicator for gamma, call the Gamma
constructor.
class ManualGammaIndicatorAlgorithm(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2024, 1, 1) self.set_end_date(2024, 2, 1) # Subscribe to the underlying asset. self._future = self.add_future( Futures.Indices.SP_500_E_MINI, data_mapping_mode=DataMappingMode.OPEN_INTEREST, data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO, contract_depth_offset=0 ) self._future.set_filter(0, 182) # Set up the dividend yield provider for the underlying. self._dividend_yield_provider = ConstantDividendYieldModel(0) # Define the Option pricing model. self._option_pricing_model = OptionPricingModelType.FORWARD_TREE # Set up a Scheduled Event to select contract and create the indicators every day before market open. self.schedule.on( self.date_rules.every_day(self._future.symbol), self.time_rules.at(9, 0), self._update_contracts_and_greeks ) self._options = None def _update_contracts_and_greeks(self) -> None: if self._future.mapped is None: return # Get all the tradable Option contracts. chain = self.option_chain(self._future.mapped, flatten=True).data_frame if chain.empty: return # Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry. chain = chain[chain.expiry > self.time + timedelta(30)] expiry = chain.expiry.min() chain = chain[chain.expiry == expiry] chain.loc[:, 'abs_strike_delta'] = abs(chain['strike'] - chain['underlyinglastprice']) abs_strike_delta = chain['abs_strike_delta'].min() chain = chain[chain['abs_strike_delta'] == abs_strike_delta] # Group the contracts into call/put pairs. contracts_pair_sizes = chain.groupby(['expiry', 'strike']).count()['right'] paired_contracts = contracts_pair_sizes[contracts_pair_sizes == 2].index expiries = [x[0] for x in paired_contracts] strikes = [x[1] for x in paired_contracts] symbols = [ idx[-1] for idx in chain[ chain['expiry'].isin(expiries) & chain['strike'].isin(strikes) ].reset_index().groupby(['expiry', 'strike', 'right', 'symbol']).first().index ] pairs = [(symbols[i], symbols[i+1]) for i in range(0, len(symbols), 2)] for call, put in pairs: # Subscribe to both contracts. contract1 = self.add_future_option_contract(call) contract2 = self.add_future_option_contract(put) # Create and save the automatic Gamma indicators. for contract_a, contract_b in [(contract1, contract2), (contract2, contract1)]: contract_a.Gamma = Gamma( contract_a.symbol, self.risk_free_interest_rate_model, self._dividend_yield_provider, contract_b.symbol, self._option_pricing_model ) self._options = (call, put) def on_data(self, slice: Slice) -> None: # Iterate through the indicators. for canonical, chain in slice.option_chains.items(): for option in chain: underlying = option.symbol.underlying indicator = self.securities[option].Gamma mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ).value # Check if price data is available for both contracts and the underlying asset. q = slice.quote_bars b = slice.bars if option in q and mirror in q and underlying in b: data_points = [ IndicatorDataPoint(option, q[option].end_time, q[option].close), IndicatorDataPoint(mirror, q[mirror].end_time, q[mirror].close), IndicatorDataPoint( underlying, b[underlying].end_time, b[underlying].close ) ] for data_point in data_points: indicator.update(data_point) # Get the current value of the Gamma indicator. value = indicator.current.value # Sell straddle as an example to trade. if not self.portfolio.invested and self._options: self.sell(self._options[0], 1) self.sell(self._options[1], 1) # Liquidate any assigned positions. if self.portfolio[self._future.mapped].invested: self.liquidate(self._future.mapped)
For more information about the Gamma
constructor, see Using G Indicator.
Volatility Smoothing
The default IV smoothing method uses the one contract in the pair that's at-the-money or out-of-money to calculate the IV.
To change the smoothing function, pass a mirror_option
argument to the indicator method or constructor and then call the set_smoothing_function
method of the implied_volatility
property of the indicator.
# Example: Average IV of the call-put pair. gamma_indicator.implied_volatility.set_smoothing_function(lambda iv, mirror_iv: (iv + mirror_iv) * 0.5)
Vega
Vega, ν, is the rate of change in the value of the Option with respect to the volatility of the underlying asset. For more information about vega, see Vega.
Automatic Indicators
To create an automatic indicator for vega, call the QCAlgorithm.v
method with the Option contract symbol
object(s).
class AutomaticVegaIndicatorAlgorithm(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2024, 1, 1) self.set_end_date(2024, 2, 1) # Subscribe to the underlying asset. self._future = self.add_future( Futures.Indices.SP_500_E_MINI, data_mapping_mode=DataMappingMode.OPEN_INTEREST, data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO, contract_depth_offset=0 ) self._future.set_filter(0, 182) # Set up a Scheduled Event to select contracts and create the indicators every day before market open. self.schedule.on( self.date_rules.every_day(self._future.symbol), self.time_rules.at(9, 0), self._update_contracts_and_greeks ) self._options = None def _update_contracts_and_greeks(self) -> None: if self._future.mapped is None: return # Get all the tradable Option contracts. chain = self.option_chain(self._future.mapped, flatten=True).data_frame if chain.empty: return # Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry. chain = chain[chain.expiry > self.time + timedelta(30)] expiry = chain.expiry.min() chain = chain[chain.expiry == expiry] chain.loc[:, 'abs_strike_delta'] = abs(chain['strike'] - chain['underlyinglastprice']) abs_strike_delta = chain['abs_strike_delta'].min() chain = chain[chain['abs_strike_delta'] == abs_strike_delta] # Group the contracts into call/put pairs. contracts_pair_sizes = chain.groupby(['expiry', 'strike']).count()['right'] paired_contracts = contracts_pair_sizes[contracts_pair_sizes == 2].index expiries = [x[0] for x in paired_contracts] strikes = [x[1] for x in paired_contracts] symbols = [ idx[-1] for idx in chain[ chain['expiry'].isin(expiries) & chain['strike'].isin(strikes) ].reset_index().groupby(['expiry', 'strike', 'right', 'symbol']).first().index ] pairs = [(symbols[i], symbols[i+1]) for i in range(0, len(symbols), 2)] for call, put in pairs: # Subscribe to both contracts. contract1 = self.add_future_option_contract(call) contract2 = self.add_future_option_contract(put) # Create and save the automatic Vega indicators. contract1.v = self.v(call, put) contract2.v = self.v(put, call) self._options = (call, put) def on_data(self, slice: Slice) -> None: # Get the Vega indicator of each contract. for canonical, chain in slice.option_chains.items(): for symbol in chain: option = self.securities[symbol] indicator = option.v # Sell straddle as an example to trade. if not self.portfolio.invested and self._options: self.sell(self._options[0], 1) self.sell(self._options[1], 1) # Liquidate any assigned positions. if self.portfolio[self._future.mapped].invested: self.liquidate(self._future.mapped)
The follow table describes the arguments that the v
method accepts in addition to the standard parameters:
Argument | Data Type | Description | Default Value |
---|---|---|---|
iv_model | OptionPricingModelType |
The Option pricing model to use to estimate the IV when calculating Vega.
If you don't provide a value, the default value is to match the option_model parameter.
| None |
For more information about the v
method, see Using V Indicator.
Manual Indicators
To create a manual indicator for vega, call the Vega
constructor.
class ManualVegaIndicatorAlgorithm(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2024, 1, 1) self.set_end_date(2024, 2, 1) # Subscribe to the underlying asset. self._future = self.add_future( Futures.Indices.SP_500_E_MINI, data_mapping_mode=DataMappingMode.OPEN_INTEREST, data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO, contract_depth_offset=0 ) self._future.set_filter(0, 182) # Set up the dividend yield provider for the underlying. self._dividend_yield_provider = ConstantDividendYieldModel(0) # Define the Option pricing model. self._option_pricing_model = OptionPricingModelType.FORWARD_TREE # Set up a Scheduled Event to select contract and create the indicators every day before market open. self.schedule.on( self.date_rules.every_day(self._future.symbol), self.time_rules.at(9, 0), self._update_contracts_and_greeks ) self._options = None def _update_contracts_and_greeks(self) -> None: if self._future.mapped is None: return # Get all the tradable Option contracts. chain = self.option_chain(self._future.mapped, flatten=True).data_frame if chain.empty: return # Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry. chain = chain[chain.expiry > self.time + timedelta(30)] expiry = chain.expiry.min() chain = chain[chain.expiry == expiry] chain.loc[:, 'abs_strike_delta'] = abs(chain['strike'] - chain['underlyinglastprice']) abs_strike_delta = chain['abs_strike_delta'].min() chain = chain[chain['abs_strike_delta'] == abs_strike_delta] # Group the contracts into call/put pairs. contracts_pair_sizes = chain.groupby(['expiry', 'strike']).count()['right'] paired_contracts = contracts_pair_sizes[contracts_pair_sizes == 2].index expiries = [x[0] for x in paired_contracts] strikes = [x[1] for x in paired_contracts] symbols = [ idx[-1] for idx in chain[ chain['expiry'].isin(expiries) & chain['strike'].isin(strikes) ].reset_index().groupby(['expiry', 'strike', 'right', 'symbol']).first().index ] pairs = [(symbols[i], symbols[i+1]) for i in range(0, len(symbols), 2)] for call, put in pairs: # Subscribe to both contracts. contract1 = self.add_future_option_contract(call) contract2 = self.add_future_option_contract(put) # Create and save the automatic Vega indicators. for contract_a, contract_b in [(contract1, contract2), (contract2, contract1)]: contract_a.Vega = Vega( contract_a.symbol, self.risk_free_interest_rate_model, self._dividend_yield_provider, contract_b.symbol, self._option_pricing_model ) self._options = (call, put) def on_data(self, slice: Slice) -> None: # Iterate through the indicators. for canonical, chain in slice.option_chains.items(): for option in chain: underlying = option.symbol.underlying indicator = self.securities[option].Vega mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ).value # Check if price data is available for both contracts and the underlying asset. q = slice.quote_bars b = slice.bars if option in q and mirror in q and underlying in b: data_points = [ IndicatorDataPoint(option, q[option].end_time, q[option].close), IndicatorDataPoint(mirror, q[mirror].end_time, q[mirror].close), IndicatorDataPoint( underlying, b[underlying].end_time, b[underlying].close ) ] for data_point in data_points: indicator.update(data_point) # Get the current value of the Vega indicator. value = indicator.current.value # Sell straddle as an example to trade. if not self.portfolio.invested and self._options: self.sell(self._options[0], 1) self.sell(self._options[1], 1) # Liquidate any assigned positions. if self.portfolio[self._future.mapped].invested: self.liquidate(self._future.mapped)
For more information about the Vega
constructor, see Using V Indicator.
Volatility Smoothing
The default IV smoothing method uses the one contract in the pair that's at-the-money or out-of-money to calculate the IV.
To change the smoothing function, pass a mirror_option
argument to the indicator method or constructor and then call the set_smoothing_function
method of the implied_volatility
property of the indicator.
# Example: Average IV of the call-put pair. vega_indicator.implied_volatility.set_smoothing_function(lambda iv, mirror_iv: (iv + mirror_iv) * 0.5)
Theta
Theta, Θ, is the rate of change of the value of the Option with respect to the passage of time. It is also known to as the time decay of an Option. For more information about theta, see Theta.
Automatic Indicators
To create an automatic indicator for theta, call the QCAlgorithm.t
method with the Option contract symbol
object(s).
class AutomaticThetaIndicatorAlgorithm(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2024, 1, 1) self.set_end_date(2024, 2, 1) # Subscribe to the underlying asset. self._future = self.add_future( Futures.Indices.SP_500_E_MINI, data_mapping_mode=DataMappingMode.OPEN_INTEREST, data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO, contract_depth_offset=0 ) self._future.set_filter(0, 182) # Set up a Scheduled Event to select contracts and create the indicators every day before market open. self.schedule.on( self.date_rules.every_day(self._future.symbol), self.time_rules.at(9, 0), self._update_contracts_and_greeks ) self._options = None def _update_contracts_and_greeks(self) -> None: if self._future.mapped is None: return # Get all the tradable Option contracts. chain = self.option_chain(self._future.mapped, flatten=True).data_frame if chain.empty: return # Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry. chain = chain[chain.expiry > self.time + timedelta(30)] expiry = chain.expiry.min() chain = chain[chain.expiry == expiry] chain.loc[:, 'abs_strike_delta'] = abs(chain['strike'] - chain['underlyinglastprice']) abs_strike_delta = chain['abs_strike_delta'].min() chain = chain[chain['abs_strike_delta'] == abs_strike_delta] # Group the contracts into call/put pairs. contracts_pair_sizes = chain.groupby(['expiry', 'strike']).count()['right'] paired_contracts = contracts_pair_sizes[contracts_pair_sizes == 2].index expiries = [x[0] for x in paired_contracts] strikes = [x[1] for x in paired_contracts] symbols = [ idx[-1] for idx in chain[ chain['expiry'].isin(expiries) & chain['strike'].isin(strikes) ].reset_index().groupby(['expiry', 'strike', 'right', 'symbol']).first().index ] pairs = [(symbols[i], symbols[i+1]) for i in range(0, len(symbols), 2)] for call, put in pairs: # Subscribe to both contracts. contract1 = self.add_future_option_contract(call) contract2 = self.add_future_option_contract(put) # Create and save the automatic Theta indicators. contract1.t = self.t(call, put) contract2.t = self.t(put, call) self._options = (call, put) def on_data(self, slice: Slice) -> None: # Get the Theta indicator of each contract. for canonical, chain in slice.option_chains.items(): for symbol in chain: option = self.securities[symbol] indicator = option.t # Sell straddle as an example to trade. if not self.portfolio.invested and self._options: self.sell(self._options[0], 1) self.sell(self._options[1], 1) # Liquidate any assigned positions. if self.portfolio[self._future.mapped].invested: self.liquidate(self._future.mapped)
The follow table describes the arguments that the t
method accepts in addition to the standard parameters:
Argument | Data Type | Description | Default Value |
---|---|---|---|
iv_model | OptionPricingModelType |
The Option pricing model to use to estimate the IV when calculating theta.
If you don't provide a value, the default value is to match the option_model parameter.
| None |
For more information about the t
method, see Using T Indicator.
Manual Indicators
To create a manual indicator for theta, call the Theta
constructor.
class ManualThetaIndicatorAlgorithm(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2024, 1, 1) self.set_end_date(2024, 2, 1) # Subscribe to the underlying asset. self._future = self.add_future( Futures.Indices.SP_500_E_MINI, data_mapping_mode=DataMappingMode.OPEN_INTEREST, data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO, contract_depth_offset=0 ) self._future.set_filter(0, 182) # Set up the dividend yield provider for the underlying. self._dividend_yield_provider = ConstantDividendYieldModel(0) # Define the Option pricing model. self._option_pricing_model = OptionPricingModelType.FORWARD_TREE # Set up a Scheduled Event to select contract and create the indicators every day before market open. self.schedule.on( self.date_rules.every_day(self._future.symbol), self.time_rules.at(9, 0), self._update_contracts_and_greeks ) self._options = None def _update_contracts_and_greeks(self) -> None: if self._future.mapped is None: return # Get all the tradable Option contracts. chain = self.option_chain(self._future.mapped, flatten=True).data_frame if chain.empty: return # Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry. chain = chain[chain.expiry > self.time + timedelta(30)] expiry = chain.expiry.min() chain = chain[chain.expiry == expiry] chain.loc[:, 'abs_strike_delta'] = abs(chain['strike'] - chain['underlyinglastprice']) abs_strike_delta = chain['abs_strike_delta'].min() chain = chain[chain['abs_strike_delta'] == abs_strike_delta] # Group the contracts into call/put pairs. contracts_pair_sizes = chain.groupby(['expiry', 'strike']).count()['right'] paired_contracts = contracts_pair_sizes[contracts_pair_sizes == 2].index expiries = [x[0] for x in paired_contracts] strikes = [x[1] for x in paired_contracts] symbols = [ idx[-1] for idx in chain[ chain['expiry'].isin(expiries) & chain['strike'].isin(strikes) ].reset_index().groupby(['expiry', 'strike', 'right', 'symbol']).first().index ] pairs = [(symbols[i], symbols[i+1]) for i in range(0, len(symbols), 2)] for call, put in pairs: # Subscribe to both contracts. contract1 = self.add_future_option_contract(call) contract2 = self.add_future_option_contract(put) # Create and save the automatic Theta indicators. for contract_a, contract_b in [(contract1, contract2), (contract2, contract1)]: contract_a.Theta = Theta( contract_a.symbol, self.risk_free_interest_rate_model, self._dividend_yield_provider, contract_b.symbol, self._option_pricing_model ) self._options = (call, put) def on_data(self, slice: Slice) -> None: # Iterate through the indicators. for canonical, chain in slice.option_chains.items(): for option in chain: underlying = option.symbol.underlying indicator = self.securities[option].Theta mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ).value # Check if price data is available for both contracts and the underlying asset. q = slice.quote_bars b = slice.bars if option in q and mirror in q and underlying in b: data_points = [ IndicatorDataPoint(option, q[option].end_time, q[option].close), IndicatorDataPoint(mirror, q[mirror].end_time, q[mirror].close), IndicatorDataPoint( underlying, b[underlying].end_time, b[underlying].close ) ] for data_point in data_points: indicator.update(data_point) # Get the current value of the Theta indicator. value = indicator.current.value # Sell straddle as an example to trade. if not self.portfolio.invested and self._options: self.sell(self._options[0], 1) self.sell(self._options[1], 1) # Liquidate any assigned positions. if self.portfolio[self._future.mapped].invested: self.liquidate(self._future.mapped)
For more information about the Theta
constructor, see Using T Indicator.
Volatility Smoothing
The default IV smoothing method uses the one contract in the pair that's at-the-money or out-of-money to calculate the IV.
To change the smoothing function, pass a mirror_option
argument to the indicator method or constructor and then call the set_smoothing_function
method of the implied_volatility
property of the indicator.
# Example: Average IV of the call-put pair. theta_indicator.implied_volatility.set_smoothing_function(lambda iv, mirror_iv: (iv + mirror_iv) * 0.5)
Rho
Rho, ρ, is the rate of change of the value of a derivative with respect to the interest rate. It is usually small and not a big issue in practice unless the Option is deep in-the-money and has a long horizon. In this case, the interest rate matters because you need to discount a larger cash flow over a longer horizon. For more information about rho, see Rho.
Automatic Indicators
To create an automatic indicator for rho, call the QCAlgorithm.r
method with the Option contract symbol
object(s).
class AutomaticRhoIndicatorAlgorithm(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2024, 1, 1) self.set_end_date(2024, 2, 1) # Subscribe to the underlying asset. self._future = self.add_future( Futures.Indices.SP_500_E_MINI, data_mapping_mode=DataMappingMode.OPEN_INTEREST, data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO, contract_depth_offset=0 ) self._future.set_filter(0, 182) # Set up a Scheduled Event to select contracts and create the indicators every day before market open. self.schedule.on( self.date_rules.every_day(self._future.symbol), self.time_rules.at(9, 0), self._update_contracts_and_greeks ) self._options = None def _update_contracts_and_greeks(self) -> None: if self._future.mapped is None: return # Get all the tradable Option contracts. chain = self.option_chain(self._future.mapped, flatten=True).data_frame if chain.empty: return # Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry. chain = chain[chain.expiry > self.time + timedelta(30)] expiry = chain.expiry.min() chain = chain[chain.expiry == expiry] chain.loc[:, 'abs_strike_delta'] = abs(chain['strike'] - chain['underlyinglastprice']) abs_strike_delta = chain['abs_strike_delta'].min() chain = chain[chain['abs_strike_delta'] == abs_strike_delta] # Group the contracts into call/put pairs. contracts_pair_sizes = chain.groupby(['expiry', 'strike']).count()['right'] paired_contracts = contracts_pair_sizes[contracts_pair_sizes == 2].index expiries = [x[0] for x in paired_contracts] strikes = [x[1] for x in paired_contracts] symbols = [ idx[-1] for idx in chain[ chain['expiry'].isin(expiries) & chain['strike'].isin(strikes) ].reset_index().groupby(['expiry', 'strike', 'right', 'symbol']).first().index ] pairs = [(symbols[i], symbols[i+1]) for i in range(0, len(symbols), 2)] for call, put in pairs: # Subscribe to both contracts. contract1 = self.add_future_option_contract(call) contract2 = self.add_future_option_contract(put) # Create and save the automatic Rho indicators. contract1.r = self.r(call, put) contract2.r = self.r(put, call) self._options = (call, put) def on_data(self, slice: Slice) -> None: # Get the Rho indicator of each contract. for canonical, chain in slice.option_chains.items(): for symbol in chain: option = self.securities[symbol] indicator = option.r # Sell straddle as an example to trade. if not self.portfolio.invested and self._options: self.sell(self._options[0], 1) self.sell(self._options[1], 1) # Liquidate any assigned positions. if self.portfolio[self._future.mapped].invested: self.liquidate(self._future.mapped)
The follow table describes the arguments that the r
method accepts in addition to the standard parameters:
Argument | Data Type | Description | Default Value |
---|---|---|---|
iv_model | OptionPricingModelType |
The Option pricing model to use to estimate the IV when calculating rho
If you don't provide a value, the default value is to match the option_model parameter.
| None |
For more information about the r
method, see Using R Indicator.
Manual Indicators
To create a manual indicator for rho, call the Rho
constructor.
class ManualRhoIndicatorAlgorithm(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2024, 1, 1) self.set_end_date(2024, 2, 1) # Subscribe to the underlying asset. self._future = self.add_future( Futures.Indices.SP_500_E_MINI, data_mapping_mode=DataMappingMode.OPEN_INTEREST, data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO, contract_depth_offset=0 ) self._future.set_filter(0, 182) # Set up the dividend yield provider for the underlying. self._dividend_yield_provider = ConstantDividendYieldModel(0) # Define the Option pricing model. self._option_pricing_model = OptionPricingModelType.FORWARD_TREE # Set up a Scheduled Event to select contract and create the indicators every day before market open. self.schedule.on( self.date_rules.every_day(self._future.symbol), self.time_rules.at(9, 0), self._update_contracts_and_greeks ) self._options = None def _update_contracts_and_greeks(self) -> None: if self._future.mapped is None: return # Get all the tradable Option contracts. chain = self.option_chain(self._future.mapped, flatten=True).data_frame if chain.empty: return # Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry. chain = chain[chain.expiry > self.time + timedelta(30)] expiry = chain.expiry.min() chain = chain[chain.expiry == expiry] chain.loc[:, 'abs_strike_delta'] = abs(chain['strike'] - chain['underlyinglastprice']) abs_strike_delta = chain['abs_strike_delta'].min() chain = chain[chain['abs_strike_delta'] == abs_strike_delta] # Group the contracts into call/put pairs. contracts_pair_sizes = chain.groupby(['expiry', 'strike']).count()['right'] paired_contracts = contracts_pair_sizes[contracts_pair_sizes == 2].index expiries = [x[0] for x in paired_contracts] strikes = [x[1] for x in paired_contracts] symbols = [ idx[-1] for idx in chain[ chain['expiry'].isin(expiries) & chain['strike'].isin(strikes) ].reset_index().groupby(['expiry', 'strike', 'right', 'symbol']).first().index ] pairs = [(symbols[i], symbols[i+1]) for i in range(0, len(symbols), 2)] for call, put in pairs: # Subscribe to both contracts. contract1 = self.add_future_option_contract(call) contract2 = self.add_future_option_contract(put) # Create and save the automatic Rho indicators. for contract_a, contract_b in [(contract1, contract2), (contract2, contract1)]: contract_a.Rho = Rho( contract_a.symbol, self.risk_free_interest_rate_model, self._dividend_yield_provider, contract_b.symbol, self._option_pricing_model ) self._options = (call, put) def on_data(self, slice: Slice) -> None: # Iterate through the indicators. for canonical, chain in slice.option_chains.items(): for option in chain: underlying = option.symbol.underlying indicator = self.securities[option].Rho mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ).value # Check if price data is available for both contracts and the underlying asset. q = slice.quote_bars b = slice.bars if option in q and mirror in q and underlying in b: data_points = [ IndicatorDataPoint(option, q[option].end_time, q[option].close), IndicatorDataPoint(mirror, q[mirror].end_time, q[mirror].close), IndicatorDataPoint( underlying, b[underlying].end_time, b[underlying].close ) ] for data_point in data_points: indicator.update(data_point) # Get the current value of the Rho indicator. value = indicator.current.value # Sell straddle as an example to trade. if not self.portfolio.invested and self._options: self.sell(self._options[0], 1) self.sell(self._options[1], 1) # Liquidate any assigned positions. if self.portfolio[self._future.mapped].invested: self.liquidate(self._future.mapped)
For more information about the Rho
constructor, see Using R Indicator.
Volatility Smoothing
The default IV smoothing method uses the one contract in the pair that's at-the-money or out-of-money to calculate the IV.
To change the smoothing function, pass a mirror_option
argument to the indicator method or constructor and then call the set_smoothing_function
method of the implied_volatility
property of the indicator.
# Example: Average IV of the call-put pair. rho_indicator.implied_volatility.set_smoothing_function(lambda iv, mirror_iv: (iv + mirror_iv) * 0.5)