Volatility
Key Concepts
Introduction
Volatility models measure the historical volatility of an asset. They are mostly used to calculate the volatility of the underlying security of an Option because the implied volatility of an Option contract needs an initial guess. The historical volatility doesn't need to be the standard deviation of the asset prices. The various volatility models in LEAN each have a unique methodology to calculate volatility.
LEAN also provides an indicator implementation of implied volatility. It provides higher flexibility on Option price model selection, volatility modeling, and allows IV smoothing through call-put pair. For details, see Implied Volatility.
Set Models
To set the volatility model of the underlying security of an Option, set the VolatilityModel
property of the Security
object. The volatility model can have a different resolution than the underlying asset subscription.
// In Initialize var underlyingSecurity= AddEquity("SPY"); underlyingSecurity.VolatilityModel = new StandardDeviationOfReturnsVolatilityModel(30);
# In Initialize underlying_security = self.add_equity("SPY") underlying_security.volatility_model = StandardDeviationOfReturnsVolatilityModel(30)
You can also set the volatility model in a security initializer. If your algorithm has a universe of underlying assets, use the security initializer technique. In order to initialize single security subscriptions with the security initializer, call SetSecurityInitializer
set_security_initializer
before you create the subscriptions.
// In Initialize SetSecurityInitializer(new MySecurityInitializer(BrokerageModel, new FuncSecuritySeeder(GetLastKnownPrices))); // Outside of the algorithm class class MySecurityInitializer : BrokerageModelSecurityInitializer { public MySecurityInitializer(IBrokerageModel brokerageModel, ISecuritySeeder securitySeeder) : base(brokerageModel, securitySeeder) {} public override void Initialize(Security security) { // First, call the superclass definition // This method sets the reality models of each security using the default reality models of the brokerage model base.Initialize(security); // Next, overwrite the volatility model if (security.Type == SecurityType.Equity) { security.VolatilityModel = new StandardDeviationOfReturnsVolatilityModel(30); } } }
# In Initialize self.set_security_initializer(MySecurityInitializer(self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices))) # Outside of the algorithm class class MySecurityInitializer(BrokerageModelSecurityInitializer): def __init__(self, brokerage_model: IBrokerageModel, security_seeder: ISecuritySeeder) -> None: super().__init__(brokerage_model, security_seeder) def initialize(self, security: Security) -> None: # First, call the superclass definition # This method sets the reality models of each security using the default reality models of the brokerage model super().initialize(security) # Next, overwrite the volatility model if security.Type == SecurityType.EQUITY: security.VolatilityModel = StandardDeviationOfReturnsVolatilityModel(30)
To view all the pre-built volatility models, see Supported Models.
Default Behavior
The default underlying volatility model for Equity Options and Index Options is the StandardDeviationOfReturnsVolatilityModel based on 30 days of daily resolution data. The default underlying volatility model for Future Options is the NullVolatilityModel.
Model Structure
Volatility models should extend the BaseVolatilityModel
class. Extensions of the BaseVolatilityModel
class must have Update
update
and GetHistoryRequirements
get_history_requirements
methods and a Volatility
volatility
property. The Update
update
method receives Security
and BaseData
objects and then updates the Volatility
volatility
. The GetHistoryRequirements
get_history_requirements
method receives Security
and DateTime
datetime
objects and then returns a list of HistoryRequest
objects that represent the history requests to warm up the model. Volatility models receive data at each time step in the algorithm to update their state.
// In the Initialize method, set the custom volatility model of the underlying security underlyingSecurity.VolatilityModel = new MyVolatilityModel(); // Define the custom volatility model outside of the algorithm public class MyVolatilityModel : BaseVolatilityModel { public override decimal Volatility { get; } public override void SetSubscriptionDataConfigProvider( ISubscriptionDataConfigProvider subscriptionDataConfigProvider) { SubscriptionDataConfigProvider = subscriptionDataConfigProvider; } public override void Update(Security security, BaseData data) { } public override IEnumerable<HistoryRequest> GetHistoryRequirements( Security security, DateTime utcTime) { return base.GetHistoryRequirements(security, utcTime); } public new IEnumerable<HistoryRequest> GetHistoryRequirements( Security security, DateTime utcTime, Resolution? resolution, int barCount) { return base.GetHistoryRequirements(security, utcTime, resolution, barCount); } }
# In the Initialize method, set the custom volatility model of the underlying security underlying_security.volatility_model = MyVolatilityModel() # Define the custom volatility model outside of the algorithm class MyVolatilityModel(BaseVolatilityModel): volatility: float = 0 def set_subscription_data_config_provider(self, subscription_data_config_provider: ISubscriptionDataConfigProvider) -> None: super().set_subscription_data_config_provider(subscription_data_config_provider) def update(self, security: Security, data: BaseData) -> None: pass def get_history_requirements(self, security: Security, utc_time: datetime, resolution: resolution = None, bar_count: int = None) -> List[HistoryRequest]: return super().get_history_requirements(security, utc_time, resolution, bar_count)
For a full example algorithm, see this backtestthis backtest.
Warm Up Models
To use your volatility model as the inital guess for the implied volatility, warm up the volatility model of the underlying security. If you subscribe to all the Options in the Initialize
initialize
method, set a warm-up period to warm up their volatility models. The warm-up period should provide the volatility models with enough data to compute their values.
// In Initialize SetWarmUp(30, Resolution.Daily); // In OnData if (IsWarmingUp) return;
# In Initialize self.set_warm_up(30, Resolution.DAILY) # In OnData if self.is_warming_up: return
If you have a dynamic universe of underlying assets and add Option contracts to your algorithm with the AddOptionContract
add_option_contract
, AddIndexOptionContract
add_index_option_contract
, or AddFutureOptionContract
add_future_option_contract
methods, warm up the volatility model when the underlying asset enters your universe. We recommend you do this inside a security initializer.
// In Initialize SetSecurityInitializer(new MySecurityInitializer(BrokerageModel, new FuncSecuritySeeder(GetLastKnownPrices), this)); // Outside of the algorithm class class MySecurityInitializer : BrokerageModelSecurityInitializer { private QCAlgorithm _algorithm; public MySecurityInitializer(IBrokerageModel brokerageModel, ISecuritySeeder securitySeeder, QCAlgorithm algorithm) : base(brokerageModel, securitySeeder) { _algorithm = algorithm; } public override void Initialize(Security security) { // First, call the superclass definition // This method sets the reality models of each security using the default reality models of the brokerage model base.Initialize(security); // Next, overwrite and warm up the volatility model if (security.Type == SecurityType.Equity) // Underlying asset type { security.VolatilityModel = new StandardDeviationOfReturnsVolatilityModel(30); foreach (var tradeBar in _algorithm.History(security.Symbol, 30, Resolution.Daily)) { security.VolatilityModel.Update(security, tradeBar); } } } }
# In Initialize self.set_security_initializer(MySecurityInitializer(self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices), self)) # Outside of the algorithm class class MySecurityInitializer(BrokerageModelSecurityInitializer): def __init__(self, brokerage_model: IBrokerageModel, security_seeder: ISecuritySeeder, algorithm: QCAlgorithm) -> None: super().__init__(brokerage_model, security_seeder) self._algorithm = algorithm def initialize(self, security: Security) -> None: # First, call the superclass definition # This method sets the reality models of each security using the default reality models of the brokerage model super().initialize(security) # Next, overwrite and warm up the volatility model if security.type == SecurityType.EQUITY: # Underlying asset type security.volatility_model = StandardDeviationOfReturnsVolatilityModel(30) trade_bars = self._algorithm.history[TradeBar](security.symbol, 30, Resolution.DAILY) for trade_bar in trade_bars: security.volatility_model.update(security, trade_bar)