Indicator Volatility Models Around Splits and Dividends
Summary
This QuantConnect example shows how to build a volatility model from an indicator and manage corporate-action price discontinuities. It calculates the ratio of a rolling standard deviation to a simple moving average, both using the same seven-period window, and updates them from positive daily prices for Apple shares. The security uses raw data normalization, so splits or dividends can create abrupt changes in the price series.
When either event occurs, the algorithm resets the indicator and warms the volatility model with historical data, following the platform’s stated approach for avoiding artificial volatility jumps. At the end of each day, it checks that the model is ready and that volatility remains positive and no greater than 0.05. The example also verifies that a corporate action occurred and that volatility was checked. These are regression-test conditions for a particular implementation, not evidence of a general trading edge or a universal volatility limit. The document describes data handling and model maintenance rather than entry, exit, or portfolio rules.
Key ideas
- The example derives volatility from the ratio of rolling standard deviation to rolling mean.
- It uses raw Apple prices, which can be discontinuous around splits and dividends.
- After a corporate action, it resets the indicator and warms the model with historical data.
- The stated volatility bound is a regression check for this example, not a general market rule.
Tags
Full text
# IndicatorVolatilityModelAlgorithm
# IndicatorVolatilityModelAlgorithm
Algorithm illustrating the usage of the IndicatorVolatilityModel and how to handle splits and dividends to avoid price discontinuities
## Source (Apache-2.0)
```python
# 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.
from AlgorithmImports import *
### <summary>
### Algorithm illustrating the usage of the IndicatorVolatilityModel and
### how to handle splits and dividends to avoid price discontinuities
### </summary>
class IndicatorVolatilityModelAlgorithm(QCAlgorithm):
_indicator_periods = 7
_data_normalization_mode = DataNormalizationMode.RAW
def initialize(self):
self.set_start_date(2014, 1, 1)
self.set_end_date(2014, 12, 31)
self.set_cash(100000)
equity = self.add_equity("AAPL", Resolution.DAILY, data_normalization_mode=self._data_normalization_mode)
self._aapl = equity.symbol
std = StandardDeviation(self._indicator_periods)
mean = SimpleMovingAverage(self._indicator_periods)
self._indicator = IndicatorExtensions.over(std, mean)
def update_indicator(security, data, indicator):
if data.price > 0:
std.update(data.time, data.price)
mean.update(data.time, data.price)
self._volatility_model = IndicatorVolatilityModel(self._indicator, update_indicator)
equity.set_volatility_model(self._volatility_model)
self._splits_and_dividends_count = 0
self._volatility_checked = False
def on_data(self, slice):
if slice.splits.contains_key(self._aapl) or slice.dividends.contains_key(self._aapl):
self._splits_and_dividends_count += 1
# On a split or dividend event, we need to reset and warm the indicator up as Lean does to BaseVolatilityModel's
# to avoid big jumps in volatility due to price discontinuities
self._indicator.reset()
equity = self.securities[self._aapl]
VolatilityModelExtensions.warm_up(
self._volatility_model,
self,
equity,
equity.resolution,
self._indicator_periods,
self._data_normalization_mode
)
def on_end_of_day(self, symbol):
if symbol != self._aapl or not self._indicator.is_ready:
return
self._volatility_checked = True
# This is expected only in this case, 0.05 is not a magical number of any kind.
# Just making sure we don't get big jumps on volatility
volatility = self.securities[self._aapl].volatility_model.volatility
if volatility <= 0 or volatility > 0.05:
raise RegressionTestException(
"Expected volatility to stay less than 0.05 (not big jumps due to price discontinuities on splits and dividends), "
f"but got {volatility}")
def on_end_of_algorithm(self):
if self._splits_and_dividends_count == 0:
raise RegressionTestException("Expected to get at least one split or dividend event")
if not self._volatility_checked:
raise RegressionTestException("Expected to check volatility at least once")
```Shown in full with attribution under the source's licence. Licence: Apache-2.0
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.