/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using QuantConnect.Data;
using QuantConnect.Python;
namespace QuantConnect.Indicators
{
///
/// To provide a base class for option indicator
///
public abstract class OptionIndicatorBase : MultiSymbolIndicator
{
private DateTime _expiry;
///
/// Option's symbol object
///
[PandasIgnore]
public Symbol OptionSymbol { get; init; }
///
/// Mirror option symbol (by option right), for implied volatility
///
protected Symbol _oppositeOptionSymbol { get; private set; }
///
/// Underlying security's symbol object
///
protected Symbol _underlyingSymbol => OptionSymbol.Underlying;
///
/// Option pricing model used to calculate indicator
///
protected OptionPricingModelType _optionModel { get; set; }
///
/// Risk-free rate model
///
protected IRiskFreeInterestRateModel _riskFreeInterestRateModel { get; init; }
///
/// Dividend yield model, for continuous dividend yield
///
protected IDividendYieldModel _dividendYieldModel { get; init; }
///
/// Gets the expiration time of the option
///
[PandasIgnore]
public DateTime Expiry
{
get
{
if (_expiry == default)
{
_expiry = Securities.Option.OptionSymbol.GetSettlementDateTime(OptionSymbol);
}
return _expiry;
}
}
///
/// Gets the option right (call/put) of the option
///
[PandasIgnore]
public OptionRight Right => OptionSymbol.ID.OptionRight;
///
/// Gets the strike price of the option
///
[PandasIgnore]
public decimal Strike => OptionSymbol.ID.StrikePrice;
///
/// Gets the option style (European/American) of the option
///
[PandasIgnore]
public OptionStyle Style => OptionSymbol.ID.OptionStyle;
///
/// Risk Free Rate
///
[PandasIgnore]
public Identity RiskFreeRate { get; set; }
///
/// Dividend Yield
///
[PandasIgnore]
public Identity DividendYield { get; set; }
///
/// Gets the option price level
///
public IndicatorBase Price { get; }
///
/// Gets the mirror option price level, for implied volatility
///
public IndicatorBase OppositePrice { get; private set; }
///
/// Gets the underlying's price level
///
public IndicatorBase UnderlyingPrice { get; }
///
/// Flag if mirror option is implemented for parity type calculation
///
[PandasIgnore]
public bool UseMirrorContract => _oppositeOptionSymbol != null;
///
/// Initializes a new instance of the OptionIndicatorBase class
///
/// The name of this indicator
/// The option to be tracked
/// Risk-free rate model
/// Dividend yield model
/// The mirror option for parity calculation
/// The lookback period of volatility
/// The option pricing model used to estimate the Greek/IV
protected OptionIndicatorBase(string name, Symbol option, IRiskFreeInterestRateModel riskFreeRateModel, IDividendYieldModel dividendYieldModel,
Symbol mirrorOption = null, OptionPricingModelType? optionModel = null, int period = 1)
: base(name, mirrorOption == null ? [option, option.Underlying] : [option, option.Underlying, mirrorOption], period)
{
var sid = option.ID;
if (!sid.SecurityType.IsOption())
{
throw new ArgumentException("OptionIndicatorBase only support SecurityType.Option.");
}
OptionSymbol = option;
_riskFreeInterestRateModel = riskFreeRateModel;
_dividendYieldModel = dividendYieldModel;
_optionModel = optionModel ?? GetOptionModel(optionModel, sid.OptionStyle);
RiskFreeRate = new Identity(name + "_RiskFreeRate");
DividendYield = new Identity(name + "_DividendYield");
Price = new Identity(name + "_Close");
UnderlyingPrice = new Identity(name + "_UnderlyingClose");
DataBySymbol[OptionSymbol].NewInput += (sender, input) => Price.Update(input);
DataBySymbol[_underlyingSymbol].NewInput += (sender, input) => UnderlyingPrice.Update(input);
if (mirrorOption != null)
{
_oppositeOptionSymbol = mirrorOption;
OppositePrice = new Identity(Name + "_OppositeClose");
DataBySymbol[_oppositeOptionSymbol].NewInput += (sender, input) => OppositePrice.Update(input);
}
}
///
/// Computes the next value of this indicator from the given state.
/// This will round the result to 7 decimal places.
///
/// The input given to the indicator
/// A new value for this indicator
protected override decimal ComputeNextValue(IBaseData input)
{
return Math.Round(base.ComputeNextValue(input), 7);
}
///
/// Resets this indicator and all sub-indicators
///
public override void Reset()
{
RiskFreeRate.Reset();
DividendYield.Reset();
Price.Reset();
UnderlyingPrice.Reset();
base.Reset();
if (UseMirrorContract)
{
OppositePrice.Reset();
}
}
///
/// Gets the option pricing model based on the option style, if not specified
///
/// The optional option pricing model, which will be returned if not null
/// The option style
/// The option pricing model based on the option style, if not specified
public static OptionPricingModelType GetOptionModel(OptionPricingModelType? optionModel, OptionStyle optionStyle)
{
if (optionModel.HasValue)
{
return optionModel.Value;
}
// Default values depend on the option style
return optionStyle switch
{
OptionStyle.European => OptionPricingModelType.BlackScholes,
OptionStyle.American => OptionPricingModelType.ForwardTree,
_ => throw new ArgumentOutOfRangeException(nameof(optionStyle), optionStyle, null)
};
}
}
}