.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "api/gallery/time_series/volatility/plot_07_realized_volatility.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code or to run this example in your browser via JupyterLite. .. rst-class:: sphx-glr-example-title .. _sphx_glr_api_gallery_time_series_volatility_plot_07_realized_volatility.py: Andersen-Bollerslev realized volatility ======================================= GARCH models fit beautifully in-sample, yet regressions of squared daily returns on their variance forecasts gave :math:`R^2` of a few percent, and sceptics concluded the models were useless for forecasting. Torben Andersen and Tim Bollerslev (1998) showed that the fault lay with the yardstick. A squared return :math:`\sigma_t^2 z_t^2` is an unbiased but very noisy measure of the day's variance, and even a perfect forecast explains only a small part of it. Summing squared five-minute returns (*realized variance*) measures the variance almost exactly, and against it the same forecasts explain many times more. .. GENERATED FROM PYTHON SOURCE LINES 17-24 .. code-block:: Python import matplotlib.pyplot as plt import numpy as np from financialkit.constants import TRADING_DAYS_PER_YEAR from financialkit.time_series import GARCH, mincer_zarnowitz, realized_variance from financialkit.time_series.utils import simulate_intraday_returns .. GENERATED FROM PYTHON SOURCE LINES 25-29 A GARCH diffusion sampled every five minutes -------------------------------------------- Daily variances follow a GARCH(1,1); each day's return is the sum of 78 five-minute returns whose variances add up to that day's variance. .. GENERATED FROM PYTHON SOURCE LINES 29-37 .. code-block:: Python ann = TRADING_DAYS_PER_YEAR true = GARCH(omega=0.2**2 / ann * 0.03, alpha=0.1, beta=0.87) daily_var = true.simulate(2500, rng=1998).variance intraday = simulate_intraday_returns(daily_var, 78, rng=1) r = intraday.sum(axis=1) rv = realized_variance(intraday) .. GENERATED FROM PYTHON SOURCE LINES 38-40 One-step forecasts from a GARCH fitted to the daily returns ----------------------------------------------------------- .. GENERATED FROM PYTHON SOURCE LINES 40-51 .. code-block:: Python forecast = GARCH.fit(r).variance fig, ax = plt.subplots(figsize=(8, 3.5)) sl = slice(0, 300) ax.plot(100 * np.sqrt(ann * r[sl] ** 2), color="0.75", lw=0.8, label="|daily return|") ax.plot(100 * np.sqrt(ann * rv[sl]), color="C0", lw=1, label="realized volatility") ax.plot(100 * np.sqrt(ann * forecast[sl]), color="C3", lw=1.5, label="GARCH forecast") ax.set_xlabel("day") ax.set_ylabel("volatility (% a year)") ax.legend(loc="upper right") .. image-sg:: /api/gallery/time_series/volatility/images/sphx_glr_plot_07_realized_volatility_001.png :alt: plot 07 realized volatility :srcset: /api/gallery/time_series/volatility/images/sphx_glr_plot_07_realized_volatility_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none .. GENERATED FROM PYTHON SOURCE LINES 52-57 Mincer-Zarnowitz regressions ---------------------------- The same forecasts, judged against two proxies of the same variance. Even the *true* variance, a perfect forecast, scores a low :math:`R^2` against squared returns. .. GENERATED FROM PYTHON SOURCE LINES 57-75 .. code-block:: Python for name, proxy in (("squared return", r**2), ("realized variance", rv)): print(f"perfect forecast vs {name:17s} R^2 = {mincer_zarnowitz(proxy, daily_var).r_squared:.2f}") fig, axes = plt.subplots(1, 2, figsize=(9, 3.8), sharex=True) for ax, (name, proxy) in zip(axes, (("squared return", r**2), ("realized variance", rv)), strict=True): mz = mincer_zarnowitz(proxy, forecast) ax.scatter(1e4 * forecast, 1e4 * proxy, s=3, alpha=0.4) f = np.linspace(forecast.min(), forecast.max(), 2) ax.plot(1e4 * f, 1e4 * (mz.coef[0] + mz.coef[1] * f), "C3") ax.set_title(f"{name}: $R^2$ = {mz.r_squared:.2f}") ax.set_xlabel("GARCH forecast (bp$^2$)") print(f"{name:17s} a = {1e4 * mz.coef[0]:+.3f} bp^2, b = {mz.coef[1]:.2f}, R^2 = {mz.r_squared:.2f}") axes[0].set_ylabel("proxy (bp$^2$)") axes[0].set_yscale("log") axes[1].set_yscale("log") fig.tight_layout() plt.show() .. image-sg:: /api/gallery/time_series/volatility/images/sphx_glr_plot_07_realized_volatility_002.png :alt: squared return: $R^2$ = 0.02, realized variance: $R^2$ = 0.32 :srcset: /api/gallery/time_series/volatility/images/sphx_glr_plot_07_realized_volatility_002.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none perfect forecast vs squared return R^2 = 0.07 perfect forecast vs realized variance R^2 = 0.87 squared return a = +0.094 bp^2, b = 0.93, R^2 = 0.02 realized variance a = -0.367 bp^2, b = 1.32, R^2 = 0.32 .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.141 seconds) .. _sphx_glr_download_api_gallery_time_series_volatility_plot_07_realized_volatility.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: lite-badge .. image:: images/jupyterlite_badge_logo.svg :target: ../../../../lite/lab/index.html?path=api/gallery/time_series/volatility/plot_07_realized_volatility.ipynb :alt: Launch JupyterLite :width: 150 px .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_07_realized_volatility.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_07_realized_volatility.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_07_realized_volatility.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_