Greeks and Implied Volatility

Indicators

Introduction

Option indicators enable you to calculate daily and intraday values for the Greeks and implied volatility of Option contracts. This page explains the most popular Option indicators and how to use them with LEAN.

Parameters

The following table describes the arguments that the automatic Option indicators methods accept:

ArgumentData TypeDescriptionDefault Value
symbolSymbolThe contract to use when calculating the indicator values.
mirrorOptionmirror_optionSymbolThe mirror contract to use in parity type calculations.nullNone
riskFreeRaterisk_free_ratedecimalfloat 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. nullNone
dividendYielddividend_yielddecimalfloat The dividend yield rate. If you don't provide a value, the default value comes from the Dividend Yield Provider Model. nullNone
optionModeloption_modelOptionPricingModelType 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 Index Options. nullNone
resolutionResolution 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). nullNone

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 mirrorOptionmirror_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 SetSmoothingFunctionset_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.IVQCAlgorithm.iv method with the Option contract Symbolsymbol object(s).

public class AutomaticImpliedVolatilityIndicatorAlgorithm : QCAlgorithm
{
    private Symbol _underlying;
    private (Symbol option1, Symbol option2) _options;

    public override void Initialize()
    {
        SetStartDate(2024, 1, 1);
        // Subscribe to the underlying asset.
        _underlying = AddIndex("SPX").Symbol;
    
        // Set up a Scheduled Event to select contracts and create the indicators every day before market open.
        Schedule.On(
            DateRules.EveryDay(_underlying),
            TimeRules.At(9, 0),
            UpdateContractsAndGreeks
        );
    }

    private void UpdateContractsAndGreeks()
    {
        // Get all the tradable Option contracts.
        var chain = OptionChain(_underlying);
        
        // Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry.
        var filteredChain = chain.Where(contract => contract.Expiry > Time.AddDays(30));
        if (filteredChain.Count() == 0)
        {
            return;
        }
        var expiry = filteredChain.Min(contract => contract.Expiry);
        filteredChain = filteredChain
            .Where(contract => contract.Expiry == expiry)
            .OrderBy(contract => Math.Abs(contract.Strike - contract.UnderlyingLastPrice))
            .Take(4);

        // Group the contracts into call/put pairs.
        foreach (var group in filteredChain.GroupBy(contract => contract.Strike))
        {
            var contracts = group.ToList();
            if (contracts.Count > 1)
            {
                // Subscribe to both contracts.
                var contract1 = AddIndexOptionContract(contracts[0]) as dynamic;
                var contract2 = AddIndexOptionContract(contracts[1]) as dynamic;

                // Create and save the automatic ImpliedVolatility indicators.
                contract1.IV = IV(contracts[0], contracts[1]);
                contract2.IV = IV(contracts[1], contracts[0]);

                _options = (contracts[0], contracts[1]);
            }
        }
    }

    public override void OnData(Slice slice)
    {
        // Get the ImpliedVolatility indicator of each contract.
        foreach (var (canonical, chain) in slice.OptionChain)
        {
            foreach (var symbol in chain)
            {
                var option = Securities[symbol] as dynamic;
                var indicator = option.IV;
            }
        }

        // Sell straddle as an example to trade.
        if (!Portfolio.Invested)
        {
            Sell(_options.option1, 1);
            Sell(_options.option2, 1);
        }
        // Liquidate any assigned positions.
        if (Portfolio[_underlying].Invested)
        {
            Liquidate(_underlying);
        }
    }
}
class AutomaticImpliedVolatilityIndicatorAlgorithm(QCAlgorithm):
    
    def initialize(self) -> None:
        self.set_start_date(2024, 1, 1)
        # Subscribe to the underlying asset.
        self._underlying = self.add_index('SPX').symbol

        # 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._underlying),
            self.time_rules.at(9, 0),
            self._update_contracts_and_greeks
        )

    def _update_contracts_and_greeks(self) -> None:
        # Get all the tradable Option contracts.
        chain = self.option_chain(self._underlying, 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_index_option_contract(call)
            contract2 = self.add_index_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_chain.items():
            for symbol in chain:
                option = self.securities[symbol]
                indicator = option.iv        
        # Sell straddle as an example to trade.
        if not self.portfolio.invested:
            self.sell(self._options[0], 1)
            self.sell(self._options[1], 1)
        # Liquidate any assigned positions.
        if self.portfolio[self._underlying].invested:
            self.liquidate(self._underlying)

For more information about the IViv method, see Parameters and Using IV Indicator.

Manual Indicators

To create a manual indicator for implied volatility, call the ImpliedVolatility constructor.

public class ManualImpliedVolatilityIndicatorAlgorithm : QCAlgorithm
{
    private Symbol _underlying;
    private DividendYieldProvider _dividendYieldProvider;
    // Define the Option pricing model.
    private readonly OptionPricingModelType _optionPricingModel = OptionPricingModelType.ForwardTree;
    private (Symbol option1, Symbol option2) _options;

    public override void Initialize()
    {
        SetStartDate(2024, 1, 1);
        // Subscribe to the underlying asset.
        _underlying = AddIndex("SPX").Symbol;

        // Set up dividend yield provider for the underlying
        _dividendYieldProvider = new(_underlying);
        
        // Set up a Scheduled Event to select contract and create the indicators every day before market open.
        Schedule.On(
            DateRules.EveryDay(_underlying),
            TimeRules.At(9, 0),
            UpdateContractsAndGreeks
        );
    }

    private void UpdateContractsAndGreeks()
    {
        // Get all the tradable Option contracts.
        var chain = OptionChain(_underlying);
        
        // Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry.
        var filteredChain = chain.Where(contract => contract.Expiry > Time.AddDays(30));
        if (filteredChain.Count() == 0)
        {
            return;
        }
        var expiry = filteredChain.Min(contract => contract.Expiry);
        filteredChain = filteredChain
            .Where(contract => contract.Expiry == expiry)
            .OrderBy(contract => Math.Abs(contract.Strike - contract.UnderlyingLastPrice))
            .Take(4);

        // Group the contracts into call/put pairs.
        foreach (var group in filteredChain.GroupBy(contract => contract.Strike))
        {
            var contracts = group.ToList();
            if (contracts.Count > 1)
            {
                // Subscribe to both contracts.
                var contract1 = AddIndexOptionContract(contracts[0]);
                var contract2 = AddIndexOptionContract(contracts[1]);

                // Create and save the manual ImpliedVolatility indicators.
                foreach (var (contractA, contractB) in new[] { (contract1, contract2), (contract2, contract1) })
                {
                    var option = contractA as dynamic;
                    option.ImpliedVolatility = new ImpliedVolatility(
                            contractA.Symbol, RiskFreeInterestRateModel, _dividendYieldProvider, 
                            contractB.Symbol, _optionPricingModel
                        );
                }

                _options = (contracts[0], contracts[1]);
            }
        }
    }

    public override void OnData(Slice slice)
    {
        foreach (var (canonical, chain) in slice.OptionChain)
        {
            foreach (var option in chain)
            {
                var indicator = (Securities[option] as dynamic).ImpliedVolatility;
                var mirror = QuantConnect.Symbol.CreateOption(
                    option.Underlying.Value, option.ID.Market, option.ID.OptionStyle, 
                    option.ID.OptionRight == OptionRight.Call ? OptionRight.Put : OptionRight.Call, 
                    option.ID.StrikePrice, option.ID.Date
                ).Value;
                
                // Check if price data is available for both contracts and the underlying asset.
                var q = slice.QuoteBars;
                var b = slice.Bars;
                if (q.ContainsKey(option) && q.ContainsKey(mirror) && b.ContainsKey(option.Underlying))
                {
                    var dataPoints = new List<IndicatorDataPoint>
                    {
                        new IndicatorDataPoint(option, q[option].EndTime, q[option].Close),
                        new IndicatorDataPoint(mirror, q[mirror].EndTime, q[mirror].Close),
                        new IndicatorDataPoint(
                            option.Underlying, b[option.Underlying].EndTime, b[option.Underlying].Close
                        )
                    };
                    foreach (var dataPoint in dataPoints)
                    {
                        indicator.Update(dataPoint);
                    }

                    // Get the current value of the ImpliedVolatility indicator.
                    var value = indicator.Current.Value;
                }
            }
        }

        // Sell straddle as an example to trade.
        if (!Portfolio.Invested)
        {
            Sell(_options.option1, 1);
            Sell(_options.option2, 1);
        }
        // Liquidate any assigned positions.
        if (Portfolio[_underlying].Invested)
        {
            Liquidate(_underlying);
        }
    }
}
class ManualImpliedVolatilityIndicatorAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 1, 1)
        # Subscribe to the underlying asset.
        self._underlying = self.add_index('SPX').symbol
        # Set up the dividend yield provider for the underlying.
        self._dividend_yield_provider = DividendYieldProvider(self._underlying)
        # 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._underlying),
            self.time_rules.at(9, 0),
            self._update_contracts_and_greeks
        )
        
    def _update_contracts_and_greeks(self) -> None:
        # Get all the tradable Option contracts.
        chain = self.option_chain(self._underlying, 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_index_option_contract(call)
            contract2 = self.add_index_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_chain.items():
            for option in chain:
                indicator = self.securities[option].ImpliedVolatility                mirror = Symbol.create_option(
                    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
                )
                # 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 option.underlying in b:
                    data_points = [
                        IndicatorDataPoint(option, q[option].end_time, q[option].close),
                        IndicatorDataPoint(mirror, q[mirror].end_time, q[mirror].close),
                        IndicatorDataPoint(
                            option.underlying, b[option.underlying].end_time, b[option.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:
            self.sell(self._options[0], 1)
            self.sell(self._options[1], 1)
        # Liquidate any assigned positions.
        if self.portfolio[self._underlying].invested:
            self.liquidate(self._underlying)

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 mirrorOptionmirror_option argument to the IViv method or ImpliedVolatility constructor and then call the SetSmoothingFunctionset_smoothing_function method of the resulting ImpliedVolatility object. The follow table describes the arguments of the custom function:

ArgumentData TypeDescription
ivdecimalfloatThe IV of the Option contract.
mirrorIvmirror_ivdecimalfloatThe IV of the mirror Option contract.

The method must return a decimalfloat as the smoothened IV.

private ImpliedVolatility _iv;

public override void Initialize()
{
    var option = QuantConnect.Symbol.CreateOption("AAPL", Market.USA, OptionStyle.American, OptionRight.Put, 505m, new DateTime(2014, 6, 27));
    AddOptionContract(option);

    var mirrorOption = QuantConnect.Symbol.CreateOption("AAPL", Market.USA, OptionStyle.American, OptionRight.Call, 505m, new DateTime(2014, 6, 27));
    AddOptionContract(mirrorOption);

    _iv = IV(option, mirrorOption);
    // example: take average of the call-put pair
    _iv.SetSmoothingFunction((iv, mirrorIv) => (iv + mirrorIv) * 0.5m);
}
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.DQCAlgorithm.d method with the Option contract Symbolsymbol object(s).

public class AutomaticDeltaIndicatorAlgorithm : QCAlgorithm
{
    private Symbol _underlying;
    private (Symbol option1, Symbol option2) _options;

    public override void Initialize()
    {
        SetStartDate(2024, 1, 1);
        // Subscribe to the underlying asset.
        _underlying = AddIndex("SPX").Symbol;
    
        // Set up a Scheduled Event to select contracts and create the indicators every day before market open.
        Schedule.On(
            DateRules.EveryDay(_underlying),
            TimeRules.At(9, 0),
            UpdateContractsAndGreeks
        );
    }

    private void UpdateContractsAndGreeks()
    {
        // Get all the tradable Option contracts.
        var chain = OptionChain(_underlying);
        
        // Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry.
        var filteredChain = chain.Where(contract => contract.Expiry > Time.AddDays(30));
        if (filteredChain.Count() == 0)
        {
            return;
        }
        var expiry = filteredChain.Min(contract => contract.Expiry);
        filteredChain = filteredChain
            .Where(contract => contract.Expiry == expiry)
            .OrderBy(contract => Math.Abs(contract.Strike - contract.UnderlyingLastPrice))
            .Take(4);

        // Group the contracts into call/put pairs.
        foreach (var group in filteredChain.GroupBy(contract => contract.Strike))
        {
            var contracts = group.ToList();
            if (contracts.Count > 1)
            {
                // Subscribe to both contracts.
                var contract1 = AddIndexOptionContract(contracts[0]) as dynamic;
                var contract2 = AddIndexOptionContract(contracts[1]) as dynamic;

                // Create and save the automatic Delta indicators.
                contract1.D = D(contracts[0], contracts[1]);
                contract2.D = D(contracts[1], contracts[0]);

                _options = (contracts[0], contracts[1]);
            }
        }
    }

    public override void OnData(Slice slice)
    {
        // Get the Delta indicator of each contract.
        foreach (var (canonical, chain) in slice.OptionChain)
        {
            foreach (var symbol in chain)
            {
                var option = Securities[symbol] as dynamic;
                var indicator = option.D;
            }
        }

        // Sell straddle as an example to trade.
        if (!Portfolio.Invested)
        {
            Sell(_options.option1, 1);
            Sell(_options.option2, 1);
        }
        // Liquidate any assigned positions.
        if (Portfolio[_underlying].Invested)
        {
            Liquidate(_underlying);
        }
    }
}
class AutomaticDeltaIndicatorAlgorithm(QCAlgorithm):
    
    def initialize(self) -> None:
        self.set_start_date(2024, 1, 1)
        # Subscribe to the underlying asset.
        self._underlying = self.add_index('SPX').symbol

        # 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._underlying),
            self.time_rules.at(9, 0),
            self._update_contracts_and_greeks
        )

    def _update_contracts_and_greeks(self) -> None:
        # Get all the tradable Option contracts.
        chain = self.option_chain(self._underlying, 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_index_option_contract(call)
            contract2 = self.add_index_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_chain.items():
            for symbol in chain:
                option = self.securities[symbol]
                indicator = option.d        
        # Sell straddle as an example to trade.
        if not self.portfolio.invested:
            self.sell(self._options[0], 1)
            self.sell(self._options[1], 1)
        # Liquidate any assigned positions.
        if self.portfolio[self._underlying].invested:
            self.liquidate(self._underlying)

The follow table describes the arguments that the Dd method accepts in addition to the standard parameters:

ArgumentData TypeDescriptionDefault Value
ivModeliv_modelOptionPricingModelType 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 optionModeloption_model parameter. nullNone

For more information about the Dd method, see Using D Indicator.

Manual Indicators

To create a manual indicator for delta, call the Delta constructor.

public class ManualDeltaIndicatorAlgorithm : QCAlgorithm
{
    private Symbol _underlying;
    private DividendYieldProvider _dividendYieldProvider;
    // Define the Option pricing model.
    private readonly OptionPricingModelType _optionPricingModel = OptionPricingModelType.ForwardTree;
    private (Symbol option1, Symbol option2) _options;

    public override void Initialize()
    {
        SetStartDate(2024, 1, 1);
        // Subscribe to the underlying asset.
        _underlying = AddIndex("SPX").Symbol;

        // Set up dividend yield provider for the underlying
        _dividendYieldProvider = new(_underlying);
        
        // Set up a Scheduled Event to select contract and create the indicators every day before market open.
        Schedule.On(
            DateRules.EveryDay(_underlying),
            TimeRules.At(9, 0),
            UpdateContractsAndGreeks
        );
    }

    private void UpdateContractsAndGreeks()
    {
        // Get all the tradable Option contracts.
        var chain = OptionChain(_underlying);
        
        // Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry.
        var filteredChain = chain.Where(contract => contract.Expiry > Time.AddDays(30));
        if (filteredChain.Count() == 0)
        {
            return;
        }
        var expiry = filteredChain.Min(contract => contract.Expiry);
        filteredChain = filteredChain
            .Where(contract => contract.Expiry == expiry)
            .OrderBy(contract => Math.Abs(contract.Strike - contract.UnderlyingLastPrice))
            .Take(4);

        // Group the contracts into call/put pairs.
        foreach (var group in filteredChain.GroupBy(contract => contract.Strike))
        {
            var contracts = group.ToList();
            if (contracts.Count > 1)
            {
                // Subscribe to both contracts.
                var contract1 = AddIndexOptionContract(contracts[0]);
                var contract2 = AddIndexOptionContract(contracts[1]);

                // Create and save the manual Delta indicators.
                foreach (var (contractA, contractB) in new[] { (contract1, contract2), (contract2, contract1) })
                {
                    var option = contractA as dynamic;
                    option.Delta = new Delta(
                            contractA.Symbol, RiskFreeInterestRateModel, _dividendYieldProvider, 
                            contractB.Symbol, _optionPricingModel
                        );
                }

                _options = (contracts[0], contracts[1]);
            }
        }
    }

    public override void OnData(Slice slice)
    {
        foreach (var (canonical, chain) in slice.OptionChain)
        {
            foreach (var option in chain)
            {
                var indicator = (Securities[option] as dynamic).Delta;
                var mirror = QuantConnect.Symbol.CreateOption(
                    option.Underlying.Value, option.ID.Market, option.ID.OptionStyle, 
                    option.ID.OptionRight == OptionRight.Call ? OptionRight.Put : OptionRight.Call, 
                    option.ID.StrikePrice, option.ID.Date
                ).Value;
                
                // Check if price data is available for both contracts and the underlying asset.
                var q = slice.QuoteBars;
                var b = slice.Bars;
                if (q.ContainsKey(option) && q.ContainsKey(mirror) && b.ContainsKey(option.Underlying))
                {
                    var dataPoints = new List<IndicatorDataPoint>
                    {
                        new IndicatorDataPoint(option, q[option].EndTime, q[option].Close),
                        new IndicatorDataPoint(mirror, q[mirror].EndTime, q[mirror].Close),
                        new IndicatorDataPoint(
                            option.Underlying, b[option.Underlying].EndTime, b[option.Underlying].Close
                        )
                    };
                    foreach (var dataPoint in dataPoints)
                    {
                        indicator.Update(dataPoint);
                    }

                    // Get the current value of the Delta indicator.
                    var value = indicator.Current.Value;
                }
            }
        }

        // Sell straddle as an example to trade.
        if (!Portfolio.Invested)
        {
            Sell(_options.option1, 1);
            Sell(_options.option2, 1);
        }
        // Liquidate any assigned positions.
        if (Portfolio[_underlying].Invested)
        {
            Liquidate(_underlying);
        }
    }
}
class ManualDeltaIndicatorAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 1, 1)
        # Subscribe to the underlying asset.
        self._underlying = self.add_index('SPX').symbol
        # Set up the dividend yield provider for the underlying.
        self._dividend_yield_provider = DividendYieldProvider(self._underlying)
        # 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._underlying),
            self.time_rules.at(9, 0),
            self._update_contracts_and_greeks
        )
        
    def _update_contracts_and_greeks(self) -> None:
        # Get all the tradable Option contracts.
        chain = self.option_chain(self._underlying, 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_index_option_contract(call)
            contract2 = self.add_index_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_chain.items():
            for option in chain:
                indicator = self.securities[option].Delta                mirror = Symbol.create_option(
                    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
                )
                # 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 option.underlying in b:
                    data_points = [
                        IndicatorDataPoint(option, q[option].end_time, q[option].close),
                        IndicatorDataPoint(mirror, q[mirror].end_time, q[mirror].close),
                        IndicatorDataPoint(
                            option.underlying, b[option.underlying].end_time, b[option.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:
            self.sell(self._options[0], 1)
            self.sell(self._options[1], 1)
        # Liquidate any assigned positions.
        if self.portfolio[self._underlying].invested:
            self.liquidate(self._underlying)

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 mirrorOptionmirror_option argument to the indicator method or constructor and then call the SetSmoothingFunctionset_smoothing_function method of the ImpliedVolatilityimplied_volatility property of the indicator.

// Example: Average IV of the call-put pair.
deltaIndicator.ImpliedVolatility.SetSmoothingFunction((iv, mirrorIv) => (iv + mirrorIv) * 0.5m);
# 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.GQCAlgorithm.g method with the Option contract Symbolsymbol object(s).

public class AutomaticGammaIndicatorAlgorithm : QCAlgorithm
{
    private Symbol _underlying;
    private (Symbol option1, Symbol option2) _options;

    public override void Initialize()
    {
        SetStartDate(2024, 1, 1);
        // Subscribe to the underlying asset.
        _underlying = AddIndex("SPX").Symbol;
    
        // Set up a Scheduled Event to select contracts and create the indicators every day before market open.
        Schedule.On(
            DateRules.EveryDay(_underlying),
            TimeRules.At(9, 0),
            UpdateContractsAndGreeks
        );
    }

    private void UpdateContractsAndGreeks()
    {
        // Get all the tradable Option contracts.
        var chain = OptionChain(_underlying);
        
        // Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry.
        var filteredChain = chain.Where(contract => contract.Expiry > Time.AddDays(30));
        if (filteredChain.Count() == 0)
        {
            return;
        }
        var expiry = filteredChain.Min(contract => contract.Expiry);
        filteredChain = filteredChain
            .Where(contract => contract.Expiry == expiry)
            .OrderBy(contract => Math.Abs(contract.Strike - contract.UnderlyingLastPrice))
            .Take(4);

        // Group the contracts into call/put pairs.
        foreach (var group in filteredChain.GroupBy(contract => contract.Strike))
        {
            var contracts = group.ToList();
            if (contracts.Count > 1)
            {
                // Subscribe to both contracts.
                var contract1 = AddIndexOptionContract(contracts[0]) as dynamic;
                var contract2 = AddIndexOptionContract(contracts[1]) as dynamic;

                // Create and save the automatic Gamma indicators.
                contract1.G = G(contracts[0], contracts[1]);
                contract2.G = G(contracts[1], contracts[0]);

                _options = (contracts[0], contracts[1]);
            }
        }
    }

    public override void OnData(Slice slice)
    {
        // Get the Gamma indicator of each contract.
        foreach (var (canonical, chain) in slice.OptionChain)
        {
            foreach (var symbol in chain)
            {
                var option = Securities[symbol] as dynamic;
                var indicator = option.G;
            }
        }

        // Sell straddle as an example to trade.
        if (!Portfolio.Invested)
        {
            Sell(_options.option1, 1);
            Sell(_options.option2, 1);
        }
        // Liquidate any assigned positions.
        if (Portfolio[_underlying].Invested)
        {
            Liquidate(_underlying);
        }
    }
}
class AutomaticGammaIndicatorAlgorithm(QCAlgorithm):
    
    def initialize(self) -> None:
        self.set_start_date(2024, 1, 1)
        # Subscribe to the underlying asset.
        self._underlying = self.add_index('SPX').symbol

        # 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._underlying),
            self.time_rules.at(9, 0),
            self._update_contracts_and_greeks
        )

    def _update_contracts_and_greeks(self) -> None:
        # Get all the tradable Option contracts.
        chain = self.option_chain(self._underlying, 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_index_option_contract(call)
            contract2 = self.add_index_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_chain.items():
            for symbol in chain:
                option = self.securities[symbol]
                indicator = option.g        
        # Sell straddle as an example to trade.
        if not self.portfolio.invested:
            self.sell(self._options[0], 1)
            self.sell(self._options[1], 1)
        # Liquidate any assigned positions.
        if self.portfolio[self._underlying].invested:
            self.liquidate(self._underlying)

The follow table describes the arguments that the Gg method accepts in addition to the standard parameters:

ArgumentData TypeDescriptionDefault Value
ivModeliv_modelOptionPricingModelType 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 optionModeloption_model parameter. nullNone

For more information about the Gg method, see Using G Indicator.

Manual Indicators

To create a manual indicator for gamma, call the Gamma constructor.

public class ManualGammaIndicatorAlgorithm : QCAlgorithm
{
    private Symbol _underlying;
    private DividendYieldProvider _dividendYieldProvider;
    // Define the Option pricing model.
    private readonly OptionPricingModelType _optionPricingModel = OptionPricingModelType.ForwardTree;
    private (Symbol option1, Symbol option2) _options;

    public override void Initialize()
    {
        SetStartDate(2024, 1, 1);
        // Subscribe to the underlying asset.
        _underlying = AddIndex("SPX").Symbol;

        // Set up dividend yield provider for the underlying
        _dividendYieldProvider = new(_underlying);
        
        // Set up a Scheduled Event to select contract and create the indicators every day before market open.
        Schedule.On(
            DateRules.EveryDay(_underlying),
            TimeRules.At(9, 0),
            UpdateContractsAndGreeks
        );
    }

    private void UpdateContractsAndGreeks()
    {
        // Get all the tradable Option contracts.
        var chain = OptionChain(_underlying);
        
        // Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry.
        var filteredChain = chain.Where(contract => contract.Expiry > Time.AddDays(30));
        if (filteredChain.Count() == 0)
        {
            return;
        }
        var expiry = filteredChain.Min(contract => contract.Expiry);
        filteredChain = filteredChain
            .Where(contract => contract.Expiry == expiry)
            .OrderBy(contract => Math.Abs(contract.Strike - contract.UnderlyingLastPrice))
            .Take(4);

        // Group the contracts into call/put pairs.
        foreach (var group in filteredChain.GroupBy(contract => contract.Strike))
        {
            var contracts = group.ToList();
            if (contracts.Count > 1)
            {
                // Subscribe to both contracts.
                var contract1 = AddIndexOptionContract(contracts[0]);
                var contract2 = AddIndexOptionContract(contracts[1]);

                // Create and save the manual Gamma indicators.
                foreach (var (contractA, contractB) in new[] { (contract1, contract2), (contract2, contract1) })
                {
                    var option = contractA as dynamic;
                    option.Gamma = new Gamma(
                            contractA.Symbol, RiskFreeInterestRateModel, _dividendYieldProvider, 
                            contractB.Symbol, _optionPricingModel
                        );
                }

                _options = (contracts[0], contracts[1]);
            }
        }
    }

    public override void OnData(Slice slice)
    {
        foreach (var (canonical, chain) in slice.OptionChain)
        {
            foreach (var option in chain)
            {
                var indicator = (Securities[option] as dynamic).Gamma;
                var mirror = QuantConnect.Symbol.CreateOption(
                    option.Underlying.Value, option.ID.Market, option.ID.OptionStyle, 
                    option.ID.OptionRight == OptionRight.Call ? OptionRight.Put : OptionRight.Call, 
                    option.ID.StrikePrice, option.ID.Date
                ).Value;
                
                // Check if price data is available for both contracts and the underlying asset.
                var q = slice.QuoteBars;
                var b = slice.Bars;
                if (q.ContainsKey(option) && q.ContainsKey(mirror) && b.ContainsKey(option.Underlying))
                {
                    var dataPoints = new List<IndicatorDataPoint>
                    {
                        new IndicatorDataPoint(option, q[option].EndTime, q[option].Close),
                        new IndicatorDataPoint(mirror, q[mirror].EndTime, q[mirror].Close),
                        new IndicatorDataPoint(
                            option.Underlying, b[option.Underlying].EndTime, b[option.Underlying].Close
                        )
                    };
                    foreach (var dataPoint in dataPoints)
                    {
                        indicator.Update(dataPoint);
                    }

                    // Get the current value of the Gamma indicator.
                    var value = indicator.Current.Value;
                }
            }
        }

        // Sell straddle as an example to trade.
        if (!Portfolio.Invested)
        {
            Sell(_options.option1, 1);
            Sell(_options.option2, 1);
        }
        // Liquidate any assigned positions.
        if (Portfolio[_underlying].Invested)
        {
            Liquidate(_underlying);
        }
    }
}
class ManualGammaIndicatorAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 1, 1)
        # Subscribe to the underlying asset.
        self._underlying = self.add_index('SPX').symbol
        # Set up the dividend yield provider for the underlying.
        self._dividend_yield_provider = DividendYieldProvider(self._underlying)
        # 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._underlying),
            self.time_rules.at(9, 0),
            self._update_contracts_and_greeks
        )
        
    def _update_contracts_and_greeks(self) -> None:
        # Get all the tradable Option contracts.
        chain = self.option_chain(self._underlying, 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_index_option_contract(call)
            contract2 = self.add_index_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_chain.items():
            for option in chain:
                indicator = self.securities[option].Gamma                mirror = Symbol.create_option(
                    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
                )
                # 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 option.underlying in b:
                    data_points = [
                        IndicatorDataPoint(option, q[option].end_time, q[option].close),
                        IndicatorDataPoint(mirror, q[mirror].end_time, q[mirror].close),
                        IndicatorDataPoint(
                            option.underlying, b[option.underlying].end_time, b[option.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:
            self.sell(self._options[0], 1)
            self.sell(self._options[1], 1)
        # Liquidate any assigned positions.
        if self.portfolio[self._underlying].invested:
            self.liquidate(self._underlying)

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 mirrorOptionmirror_option argument to the indicator method or constructor and then call the SetSmoothingFunctionset_smoothing_function method of the ImpliedVolatilityimplied_volatility property of the indicator.

// Example: Average IV of the call-put pair.
gammaIndicator.ImpliedVolatility.SetSmoothingFunction((iv, mirrorIv) => (iv + mirrorIv) * 0.5m);
# 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.VQCAlgorithm.v method with the Option contract Symbolsymbol object(s).

public class AutomaticVegaIndicatorAlgorithm : QCAlgorithm
{
    private Symbol _underlying;
    private (Symbol option1, Symbol option2) _options;

    public override void Initialize()
    {
        SetStartDate(2024, 1, 1);
        // Subscribe to the underlying asset.
        _underlying = AddIndex("SPX").Symbol;
    
        // Set up a Scheduled Event to select contracts and create the indicators every day before market open.
        Schedule.On(
            DateRules.EveryDay(_underlying),
            TimeRules.At(9, 0),
            UpdateContractsAndGreeks
        );
    }

    private void UpdateContractsAndGreeks()
    {
        // Get all the tradable Option contracts.
        var chain = OptionChain(_underlying);
        
        // Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry.
        var filteredChain = chain.Where(contract => contract.Expiry > Time.AddDays(30));
        if (filteredChain.Count() == 0)
        {
            return;
        }
        var expiry = filteredChain.Min(contract => contract.Expiry);
        filteredChain = filteredChain
            .Where(contract => contract.Expiry == expiry)
            .OrderBy(contract => Math.Abs(contract.Strike - contract.UnderlyingLastPrice))
            .Take(4);

        // Group the contracts into call/put pairs.
        foreach (var group in filteredChain.GroupBy(contract => contract.Strike))
        {
            var contracts = group.ToList();
            if (contracts.Count > 1)
            {
                // Subscribe to both contracts.
                var contract1 = AddIndexOptionContract(contracts[0]) as dynamic;
                var contract2 = AddIndexOptionContract(contracts[1]) as dynamic;

                // Create and save the automatic Vega indicators.
                contract1.V = V(contracts[0], contracts[1]);
                contract2.V = V(contracts[1], contracts[0]);

                _options = (contracts[0], contracts[1]);
            }
        }
    }

    public override void OnData(Slice slice)
    {
        // Get the Vega indicator of each contract.
        foreach (var (canonical, chain) in slice.OptionChain)
        {
            foreach (var symbol in chain)
            {
                var option = Securities[symbol] as dynamic;
                var indicator = option.V;
            }
        }

        // Sell straddle as an example to trade.
        if (!Portfolio.Invested)
        {
            Sell(_options.option1, 1);
            Sell(_options.option2, 1);
        }
        // Liquidate any assigned positions.
        if (Portfolio[_underlying].Invested)
        {
            Liquidate(_underlying);
        }
    }
}
class AutomaticVegaIndicatorAlgorithm(QCAlgorithm):
    
    def initialize(self) -> None:
        self.set_start_date(2024, 1, 1)
        # Subscribe to the underlying asset.
        self._underlying = self.add_index('SPX').symbol

        # 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._underlying),
            self.time_rules.at(9, 0),
            self._update_contracts_and_greeks
        )

    def _update_contracts_and_greeks(self) -> None:
        # Get all the tradable Option contracts.
        chain = self.option_chain(self._underlying, 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_index_option_contract(call)
            contract2 = self.add_index_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_chain.items():
            for symbol in chain:
                option = self.securities[symbol]
                indicator = option.v        
        # Sell straddle as an example to trade.
        if not self.portfolio.invested:
            self.sell(self._options[0], 1)
            self.sell(self._options[1], 1)
        # Liquidate any assigned positions.
        if self.portfolio[self._underlying].invested:
            self.liquidate(self._underlying)

The follow table describes the arguments that the Vv method accepts in addition to the standard parameters:

ArgumentData TypeDescriptionDefault Value
ivModeliv_modelOptionPricingModelType 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 optionModeloption_model parameter. nullNone

For more information about the Vv method, see Using V Indicator.

Manual Indicators

To create a manual indicator for vega, call the Vega constructor.

public class ManualVegaIndicatorAlgorithm : QCAlgorithm
{
    private Symbol _underlying;
    private DividendYieldProvider _dividendYieldProvider;
    // Define the Option pricing model.
    private readonly OptionPricingModelType _optionPricingModel = OptionPricingModelType.ForwardTree;
    private (Symbol option1, Symbol option2) _options;

    public override void Initialize()
    {
        SetStartDate(2024, 1, 1);
        // Subscribe to the underlying asset.
        _underlying = AddIndex("SPX").Symbol;

        // Set up dividend yield provider for the underlying
        _dividendYieldProvider = new(_underlying);
        
        // Set up a Scheduled Event to select contract and create the indicators every day before market open.
        Schedule.On(
            DateRules.EveryDay(_underlying),
            TimeRules.At(9, 0),
            UpdateContractsAndGreeks
        );
    }

    private void UpdateContractsAndGreeks()
    {
        // Get all the tradable Option contracts.
        var chain = OptionChain(_underlying);
        
        // Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry.
        var filteredChain = chain.Where(contract => contract.Expiry > Time.AddDays(30));
        if (filteredChain.Count() == 0)
        {
            return;
        }
        var expiry = filteredChain.Min(contract => contract.Expiry);
        filteredChain = filteredChain
            .Where(contract => contract.Expiry == expiry)
            .OrderBy(contract => Math.Abs(contract.Strike - contract.UnderlyingLastPrice))
            .Take(4);

        // Group the contracts into call/put pairs.
        foreach (var group in filteredChain.GroupBy(contract => contract.Strike))
        {
            var contracts = group.ToList();
            if (contracts.Count > 1)
            {
                // Subscribe to both contracts.
                var contract1 = AddIndexOptionContract(contracts[0]);
                var contract2 = AddIndexOptionContract(contracts[1]);

                // Create and save the manual Vega indicators.
                foreach (var (contractA, contractB) in new[] { (contract1, contract2), (contract2, contract1) })
                {
                    var option = contractA as dynamic;
                    option.Vega = new Vega(
                            contractA.Symbol, RiskFreeInterestRateModel, _dividendYieldProvider, 
                            contractB.Symbol, _optionPricingModel
                        );
                }

                _options = (contracts[0], contracts[1]);
            }
        }
    }

    public override void OnData(Slice slice)
    {
        foreach (var (canonical, chain) in slice.OptionChain)
        {
            foreach (var option in chain)
            {
                var indicator = (Securities[option] as dynamic).Vega;
                var mirror = QuantConnect.Symbol.CreateOption(
                    option.Underlying.Value, option.ID.Market, option.ID.OptionStyle, 
                    option.ID.OptionRight == OptionRight.Call ? OptionRight.Put : OptionRight.Call, 
                    option.ID.StrikePrice, option.ID.Date
                ).Value;
                
                // Check if price data is available for both contracts and the underlying asset.
                var q = slice.QuoteBars;
                var b = slice.Bars;
                if (q.ContainsKey(option) && q.ContainsKey(mirror) && b.ContainsKey(option.Underlying))
                {
                    var dataPoints = new List<IndicatorDataPoint>
                    {
                        new IndicatorDataPoint(option, q[option].EndTime, q[option].Close),
                        new IndicatorDataPoint(mirror, q[mirror].EndTime, q[mirror].Close),
                        new IndicatorDataPoint(
                            option.Underlying, b[option.Underlying].EndTime, b[option.Underlying].Close
                        )
                    };
                    foreach (var dataPoint in dataPoints)
                    {
                        indicator.Update(dataPoint);
                    }

                    // Get the current value of the Vega indicator.
                    var value = indicator.Current.Value;
                }
            }
        }

        // Sell straddle as an example to trade.
        if (!Portfolio.Invested)
        {
            Sell(_options.option1, 1);
            Sell(_options.option2, 1);
        }
        // Liquidate any assigned positions.
        if (Portfolio[_underlying].Invested)
        {
            Liquidate(_underlying);
        }
    }
}
class ManualVegaIndicatorAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 1, 1)
        # Subscribe to the underlying asset.
        self._underlying = self.add_index('SPX').symbol
        # Set up the dividend yield provider for the underlying.
        self._dividend_yield_provider = DividendYieldProvider(self._underlying)
        # 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._underlying),
            self.time_rules.at(9, 0),
            self._update_contracts_and_greeks
        )
        
    def _update_contracts_and_greeks(self) -> None:
        # Get all the tradable Option contracts.
        chain = self.option_chain(self._underlying, 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_index_option_contract(call)
            contract2 = self.add_index_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_chain.items():
            for option in chain:
                indicator = self.securities[option].Vega                mirror = Symbol.create_option(
                    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
                )
                # 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 option.underlying in b:
                    data_points = [
                        IndicatorDataPoint(option, q[option].end_time, q[option].close),
                        IndicatorDataPoint(mirror, q[mirror].end_time, q[mirror].close),
                        IndicatorDataPoint(
                            option.underlying, b[option.underlying].end_time, b[option.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:
            self.sell(self._options[0], 1)
            self.sell(self._options[1], 1)
        # Liquidate any assigned positions.
        if self.portfolio[self._underlying].invested:
            self.liquidate(self._underlying)

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 mirrorOptionmirror_option argument to the indicator method or constructor and then call the SetSmoothingFunctionset_smoothing_function method of the ImpliedVolatilityimplied_volatility property of the indicator.

// Example: Average IV of the call-put pair.
vegaIndicator.ImpliedVolatility.SetSmoothingFunction((iv, mirrorIv) => (iv + mirrorIv) * 0.5m);
# 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.TQCAlgorithm.t method with the Option contract Symbolsymbol object(s).

public class AutomaticThetaIndicatorAlgorithm : QCAlgorithm
{
    private Symbol _underlying;
    private (Symbol option1, Symbol option2) _options;

    public override void Initialize()
    {
        SetStartDate(2024, 1, 1);
        // Subscribe to the underlying asset.
        _underlying = AddIndex("SPX").Symbol;
    
        // Set up a Scheduled Event to select contracts and create the indicators every day before market open.
        Schedule.On(
            DateRules.EveryDay(_underlying),
            TimeRules.At(9, 0),
            UpdateContractsAndGreeks
        );
    }

    private void UpdateContractsAndGreeks()
    {
        // Get all the tradable Option contracts.
        var chain = OptionChain(_underlying);
        
        // Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry.
        var filteredChain = chain.Where(contract => contract.Expiry > Time.AddDays(30));
        if (filteredChain.Count() == 0)
        {
            return;
        }
        var expiry = filteredChain.Min(contract => contract.Expiry);
        filteredChain = filteredChain
            .Where(contract => contract.Expiry == expiry)
            .OrderBy(contract => Math.Abs(contract.Strike - contract.UnderlyingLastPrice))
            .Take(4);

        // Group the contracts into call/put pairs.
        foreach (var group in filteredChain.GroupBy(contract => contract.Strike))
        {
            var contracts = group.ToList();
            if (contracts.Count > 1)
            {
                // Subscribe to both contracts.
                var contract1 = AddIndexOptionContract(contracts[0]) as dynamic;
                var contract2 = AddIndexOptionContract(contracts[1]) as dynamic;

                // Create and save the automatic Theta indicators.
                contract1.T = T(contracts[0], contracts[1]);
                contract2.T = T(contracts[1], contracts[0]);

                _options = (contracts[0], contracts[1]);
            }
        }
    }

    public override void OnData(Slice slice)
    {
        // Get the Theta indicator of each contract.
        foreach (var (canonical, chain) in slice.OptionChain)
        {
            foreach (var symbol in chain)
            {
                var option = Securities[symbol] as dynamic;
                var indicator = option.T;
            }
        }

        // Sell straddle as an example to trade.
        if (!Portfolio.Invested)
        {
            Sell(_options.option1, 1);
            Sell(_options.option2, 1);
        }
        // Liquidate any assigned positions.
        if (Portfolio[_underlying].Invested)
        {
            Liquidate(_underlying);
        }
    }
}
class AutomaticThetaIndicatorAlgorithm(QCAlgorithm):
    
    def initialize(self) -> None:
        self.set_start_date(2024, 1, 1)
        # Subscribe to the underlying asset.
        self._underlying = self.add_index('SPX').symbol

        # 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._underlying),
            self.time_rules.at(9, 0),
            self._update_contracts_and_greeks
        )

    def _update_contracts_and_greeks(self) -> None:
        # Get all the tradable Option contracts.
        chain = self.option_chain(self._underlying, 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_index_option_contract(call)
            contract2 = self.add_index_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_chain.items():
            for symbol in chain:
                option = self.securities[symbol]
                indicator = option.t        
        # Sell straddle as an example to trade.
        if not self.portfolio.invested:
            self.sell(self._options[0], 1)
            self.sell(self._options[1], 1)
        # Liquidate any assigned positions.
        if self.portfolio[self._underlying].invested:
            self.liquidate(self._underlying)

The follow table describes the arguments that the Tt method accepts in addition to the standard parameters:

ArgumentData TypeDescriptionDefault Value
ivModeliv_modelOptionPricingModelType 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 optionModeloption_model parameter. nullNone

For more information about the Tt method, see Using T Indicator.

Manual Indicators

To create a manual indicator for theta, call the Theta constructor.

public class ManualThetaIndicatorAlgorithm : QCAlgorithm
{
    private Symbol _underlying;
    private DividendYieldProvider _dividendYieldProvider;
    // Define the Option pricing model.
    private readonly OptionPricingModelType _optionPricingModel = OptionPricingModelType.ForwardTree;
    private (Symbol option1, Symbol option2) _options;

    public override void Initialize()
    {
        SetStartDate(2024, 1, 1);
        // Subscribe to the underlying asset.
        _underlying = AddIndex("SPX").Symbol;

        // Set up dividend yield provider for the underlying
        _dividendYieldProvider = new(_underlying);
        
        // Set up a Scheduled Event to select contract and create the indicators every day before market open.
        Schedule.On(
            DateRules.EveryDay(_underlying),
            TimeRules.At(9, 0),
            UpdateContractsAndGreeks
        );
    }

    private void UpdateContractsAndGreeks()
    {
        // Get all the tradable Option contracts.
        var chain = OptionChain(_underlying);
        
        // Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry.
        var filteredChain = chain.Where(contract => contract.Expiry > Time.AddDays(30));
        if (filteredChain.Count() == 0)
        {
            return;
        }
        var expiry = filteredChain.Min(contract => contract.Expiry);
        filteredChain = filteredChain
            .Where(contract => contract.Expiry == expiry)
            .OrderBy(contract => Math.Abs(contract.Strike - contract.UnderlyingLastPrice))
            .Take(4);

        // Group the contracts into call/put pairs.
        foreach (var group in filteredChain.GroupBy(contract => contract.Strike))
        {
            var contracts = group.ToList();
            if (contracts.Count > 1)
            {
                // Subscribe to both contracts.
                var contract1 = AddIndexOptionContract(contracts[0]);
                var contract2 = AddIndexOptionContract(contracts[1]);

                // Create and save the manual Theta indicators.
                foreach (var (contractA, contractB) in new[] { (contract1, contract2), (contract2, contract1) })
                {
                    var option = contractA as dynamic;
                    option.Theta = new Theta(
                            contractA.Symbol, RiskFreeInterestRateModel, _dividendYieldProvider, 
                            contractB.Symbol, _optionPricingModel
                        );
                }

                _options = (contracts[0], contracts[1]);
            }
        }
    }

    public override void OnData(Slice slice)
    {
        foreach (var (canonical, chain) in slice.OptionChain)
        {
            foreach (var option in chain)
            {
                var indicator = (Securities[option] as dynamic).Theta;
                var mirror = QuantConnect.Symbol.CreateOption(
                    option.Underlying.Value, option.ID.Market, option.ID.OptionStyle, 
                    option.ID.OptionRight == OptionRight.Call ? OptionRight.Put : OptionRight.Call, 
                    option.ID.StrikePrice, option.ID.Date
                ).Value;
                
                // Check if price data is available for both contracts and the underlying asset.
                var q = slice.QuoteBars;
                var b = slice.Bars;
                if (q.ContainsKey(option) && q.ContainsKey(mirror) && b.ContainsKey(option.Underlying))
                {
                    var dataPoints = new List<IndicatorDataPoint>
                    {
                        new IndicatorDataPoint(option, q[option].EndTime, q[option].Close),
                        new IndicatorDataPoint(mirror, q[mirror].EndTime, q[mirror].Close),
                        new IndicatorDataPoint(
                            option.Underlying, b[option.Underlying].EndTime, b[option.Underlying].Close
                        )
                    };
                    foreach (var dataPoint in dataPoints)
                    {
                        indicator.Update(dataPoint);
                    }

                    // Get the current value of the Theta indicator.
                    var value = indicator.Current.Value;
                }
            }
        }

        // Sell straddle as an example to trade.
        if (!Portfolio.Invested)
        {
            Sell(_options.option1, 1);
            Sell(_options.option2, 1);
        }
        // Liquidate any assigned positions.
        if (Portfolio[_underlying].Invested)
        {
            Liquidate(_underlying);
        }
    }
}
class ManualThetaIndicatorAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 1, 1)
        # Subscribe to the underlying asset.
        self._underlying = self.add_index('SPX').symbol
        # Set up the dividend yield provider for the underlying.
        self._dividend_yield_provider = DividendYieldProvider(self._underlying)
        # 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._underlying),
            self.time_rules.at(9, 0),
            self._update_contracts_and_greeks
        )
        
    def _update_contracts_and_greeks(self) -> None:
        # Get all the tradable Option contracts.
        chain = self.option_chain(self._underlying, 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_index_option_contract(call)
            contract2 = self.add_index_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_chain.items():
            for option in chain:
                indicator = self.securities[option].Theta                mirror = Symbol.create_option(
                    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
                )
                # 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 option.underlying in b:
                    data_points = [
                        IndicatorDataPoint(option, q[option].end_time, q[option].close),
                        IndicatorDataPoint(mirror, q[mirror].end_time, q[mirror].close),
                        IndicatorDataPoint(
                            option.underlying, b[option.underlying].end_time, b[option.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:
            self.sell(self._options[0], 1)
            self.sell(self._options[1], 1)
        # Liquidate any assigned positions.
        if self.portfolio[self._underlying].invested:
            self.liquidate(self._underlying)

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 mirrorOptionmirror_option argument to the indicator method or constructor and then call the SetSmoothingFunctionset_smoothing_function method of the ImpliedVolatilityimplied_volatility property of the indicator.

// Example: Average IV of the call-put pair.
thetaIndicator.ImpliedVolatility.SetSmoothingFunction((iv, mirrorIv) => (iv + mirrorIv) * 0.5m);
# 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.RQCAlgorithm.r method with the Option contract Symbolsymbol object(s).

public class AutomaticRhoIndicatorAlgorithm : QCAlgorithm
{
    private Symbol _underlying;
    private (Symbol option1, Symbol option2) _options;

    public override void Initialize()
    {
        SetStartDate(2024, 1, 1);
        // Subscribe to the underlying asset.
        _underlying = AddIndex("SPX").Symbol;
    
        // Set up a Scheduled Event to select contracts and create the indicators every day before market open.
        Schedule.On(
            DateRules.EveryDay(_underlying),
            TimeRules.At(9, 0),
            UpdateContractsAndGreeks
        );
    }

    private void UpdateContractsAndGreeks()
    {
        // Get all the tradable Option contracts.
        var chain = OptionChain(_underlying);
        
        // Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry.
        var filteredChain = chain.Where(contract => contract.Expiry > Time.AddDays(30));
        if (filteredChain.Count() == 0)
        {
            return;
        }
        var expiry = filteredChain.Min(contract => contract.Expiry);
        filteredChain = filteredChain
            .Where(contract => contract.Expiry == expiry)
            .OrderBy(contract => Math.Abs(contract.Strike - contract.UnderlyingLastPrice))
            .Take(4);

        // Group the contracts into call/put pairs.
        foreach (var group in filteredChain.GroupBy(contract => contract.Strike))
        {
            var contracts = group.ToList();
            if (contracts.Count > 1)
            {
                // Subscribe to both contracts.
                var contract1 = AddIndexOptionContract(contracts[0]) as dynamic;
                var contract2 = AddIndexOptionContract(contracts[1]) as dynamic;

                // Create and save the automatic Rho indicators.
                contract1.R = R(contracts[0], contracts[1]);
                contract2.R = R(contracts[1], contracts[0]);

                _options = (contracts[0], contracts[1]);
            }
        }
    }

    public override void OnData(Slice slice)
    {
        // Get the Rho indicator of each contract.
        foreach (var (canonical, chain) in slice.OptionChain)
        {
            foreach (var symbol in chain)
            {
                var option = Securities[symbol] as dynamic;
                var indicator = option.R;
            }
        }

        // Sell straddle as an example to trade.
        if (!Portfolio.Invested)
        {
            Sell(_options.option1, 1);
            Sell(_options.option2, 1);
        }
        // Liquidate any assigned positions.
        if (Portfolio[_underlying].Invested)
        {
            Liquidate(_underlying);
        }
    }
}
class AutomaticRhoIndicatorAlgorithm(QCAlgorithm):
    
    def initialize(self) -> None:
        self.set_start_date(2024, 1, 1)
        # Subscribe to the underlying asset.
        self._underlying = self.add_index('SPX').symbol

        # 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._underlying),
            self.time_rules.at(9, 0),
            self._update_contracts_and_greeks
        )

    def _update_contracts_and_greeks(self) -> None:
        # Get all the tradable Option contracts.
        chain = self.option_chain(self._underlying, 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_index_option_contract(call)
            contract2 = self.add_index_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_chain.items():
            for symbol in chain:
                option = self.securities[symbol]
                indicator = option.r        
        # Sell straddle as an example to trade.
        if not self.portfolio.invested:
            self.sell(self._options[0], 1)
            self.sell(self._options[1], 1)
        # Liquidate any assigned positions.
        if self.portfolio[self._underlying].invested:
            self.liquidate(self._underlying)

The follow table describes the arguments that the Rr method accepts in addition to the standard parameters:

ArgumentData TypeDescriptionDefault Value
ivModeliv_modelOptionPricingModelType 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 optionModeloption_model parameter. nullNone

For more information about the Rr method, see Using R Indicator.

Manual Indicators

To create a manual indicator for rho, call the Rho constructor.

public class ManualRhoIndicatorAlgorithm : QCAlgorithm
{
    private Symbol _underlying;
    private DividendYieldProvider _dividendYieldProvider;
    // Define the Option pricing model.
    private readonly OptionPricingModelType _optionPricingModel = OptionPricingModelType.ForwardTree;
    private (Symbol option1, Symbol option2) _options;

    public override void Initialize()
    {
        SetStartDate(2024, 1, 1);
        // Subscribe to the underlying asset.
        _underlying = AddIndex("SPX").Symbol;

        // Set up dividend yield provider for the underlying
        _dividendYieldProvider = new(_underlying);
        
        // Set up a Scheduled Event to select contract and create the indicators every day before market open.
        Schedule.On(
            DateRules.EveryDay(_underlying),
            TimeRules.At(9, 0),
            UpdateContractsAndGreeks
        );
    }

    private void UpdateContractsAndGreeks()
    {
        // Get all the tradable Option contracts.
        var chain = OptionChain(_underlying);
        
        // Filter the contracts down. For example, ATM contracts with atleast 1 month until expiry.
        var filteredChain = chain.Where(contract => contract.Expiry > Time.AddDays(30));
        if (filteredChain.Count() == 0)
        {
            return;
        }
        var expiry = filteredChain.Min(contract => contract.Expiry);
        filteredChain = filteredChain
            .Where(contract => contract.Expiry == expiry)
            .OrderBy(contract => Math.Abs(contract.Strike - contract.UnderlyingLastPrice))
            .Take(4);

        // Group the contracts into call/put pairs.
        foreach (var group in filteredChain.GroupBy(contract => contract.Strike))
        {
            var contracts = group.ToList();
            if (contracts.Count > 1)
            {
                // Subscribe to both contracts.
                var contract1 = AddIndexOptionContract(contracts[0]);
                var contract2 = AddIndexOptionContract(contracts[1]);

                // Create and save the manual Rho indicators.
                foreach (var (contractA, contractB) in new[] { (contract1, contract2), (contract2, contract1) })
                {
                    var option = contractA as dynamic;
                    option.Rho = new Rho(
                            contractA.Symbol, RiskFreeInterestRateModel, _dividendYieldProvider, 
                            contractB.Symbol, _optionPricingModel
                        );
                }

                _options = (contracts[0], contracts[1]);
            }
        }
    }

    public override void OnData(Slice slice)
    {
        foreach (var (canonical, chain) in slice.OptionChain)
        {
            foreach (var option in chain)
            {
                var indicator = (Securities[option] as dynamic).Rho;
                var mirror = QuantConnect.Symbol.CreateOption(
                    option.Underlying.Value, option.ID.Market, option.ID.OptionStyle, 
                    option.ID.OptionRight == OptionRight.Call ? OptionRight.Put : OptionRight.Call, 
                    option.ID.StrikePrice, option.ID.Date
                ).Value;
                
                // Check if price data is available for both contracts and the underlying asset.
                var q = slice.QuoteBars;
                var b = slice.Bars;
                if (q.ContainsKey(option) && q.ContainsKey(mirror) && b.ContainsKey(option.Underlying))
                {
                    var dataPoints = new List<IndicatorDataPoint>
                    {
                        new IndicatorDataPoint(option, q[option].EndTime, q[option].Close),
                        new IndicatorDataPoint(mirror, q[mirror].EndTime, q[mirror].Close),
                        new IndicatorDataPoint(
                            option.Underlying, b[option.Underlying].EndTime, b[option.Underlying].Close
                        )
                    };
                    foreach (var dataPoint in dataPoints)
                    {
                        indicator.Update(dataPoint);
                    }

                    // Get the current value of the Rho indicator.
                    var value = indicator.Current.Value;
                }
            }
        }

        // Sell straddle as an example to trade.
        if (!Portfolio.Invested)
        {
            Sell(_options.option1, 1);
            Sell(_options.option2, 1);
        }
        // Liquidate any assigned positions.
        if (Portfolio[_underlying].Invested)
        {
            Liquidate(_underlying);
        }
    }
}
class ManualRhoIndicatorAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 1, 1)
        # Subscribe to the underlying asset.
        self._underlying = self.add_index('SPX').symbol
        # Set up the dividend yield provider for the underlying.
        self._dividend_yield_provider = DividendYieldProvider(self._underlying)
        # 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._underlying),
            self.time_rules.at(9, 0),
            self._update_contracts_and_greeks
        )
        
    def _update_contracts_and_greeks(self) -> None:
        # Get all the tradable Option contracts.
        chain = self.option_chain(self._underlying, 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_index_option_contract(call)
            contract2 = self.add_index_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_chain.items():
            for option in chain:
                indicator = self.securities[option].Rho                mirror = Symbol.create_option(
                    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
                )
                # 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 option.underlying in b:
                    data_points = [
                        IndicatorDataPoint(option, q[option].end_time, q[option].close),
                        IndicatorDataPoint(mirror, q[mirror].end_time, q[mirror].close),
                        IndicatorDataPoint(
                            option.underlying, b[option.underlying].end_time, b[option.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:
            self.sell(self._options[0], 1)
            self.sell(self._options[1], 1)
        # Liquidate any assigned positions.
        if self.portfolio[self._underlying].invested:
            self.liquidate(self._underlying)

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 mirrorOptionmirror_option argument to the indicator method or constructor and then call the SetSmoothingFunctionset_smoothing_function method of the ImpliedVolatilityimplied_volatility property of the indicator.

// Example: Average IV of the call-put pair.
rhoIndicator.ImpliedVolatility.SetSmoothingFunction((iv, mirrorIv) => (iv + mirrorIv) * 0.5m);
# Example: Average IV of the call-put pair.
rho_indicator.implied_volatility.set_smoothing_function(lambda iv, mirror_iv: (iv + mirror_iv) * 0.5)

You can also see our Videos. You can also get in touch with us via Discord.

Did you find this page helpful?

Contribute to the documentation: