{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Volatility Forecasting Lab\n",
        "\n",
        "Compare simple volatility forecasts and think carefully about what makes a forecast useful.\n",
        "\n",
        "**How to use this notebook:** We give you the structure, the key ideas, starter code, and places where you need to make the important research decisions yourself."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## What you should have by the end\n",
        "\n",
        "- A working prototype\n",
        "- A few graphs/tables that explain what your model is doing\n",
        "- A short write-up of your assumptions\n",
        "- At least one extension that makes the project feel like yours"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Concept\n",
        "\n",
        "Volatility forecasting is one of those projects that sounds simple but gets deep quickly.\n",
        "\n",
        "You are not just predicting direction. You are estimating uncertainty. This matters for options, risk, sizing, market making, and execution."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Starter Code\n",
        "\n",
        "1. Load or simulate returns.\n",
        "2. Compute realised volatility.\n",
        "3. Compare rolling volatility vs EWMA.\n",
        "4. Add one model of your own.\n",
        "5. Evaluate forecast errors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import pandas as pd\n",
        "\n",
        "rng = np.random.default_rng(99)\n",
        "\n",
        "n = 1500\n",
        "volatility_regime = np.where(np.arange(n) < 750, 0.01, 0.025)\n",
        "returns = volatility_regime * rng.normal(size=n)\n",
        "\n",
        "df = pd.DataFrame({\"returns\": returns})\n",
        "df[\"realised_vol_20\"] = df[\"returns\"].rolling(20).std() * np.sqrt(252)\n",
        "df.head()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def ewma_volatility(returns, lambda_=0.94):\n",
        "    # EWMA volatility estimator.\n",
        "    # TODO:\n",
        "    # - Tune lambda.\n",
        "    # - Compare with rolling windows.\n",
        "    # - Add a GARCH model if you want to go further.\n",
        "    variance = []\n",
        "    var_t = returns.var()\n",
        "\n",
        "    for r in returns:\n",
        "        var_t = lambda_ * var_t + (1 - lambda_) * r**2\n",
        "        variance.append(var_t)\n",
        "\n",
        "    return np.sqrt(np.array(variance) * 252)\n",
        "\n",
        "df[\"ewma_vol\"] = ewma_volatility(df[\"returns\"].fillna(0).values)\n",
        "df.tail()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def evaluate_vol_forecast(realised, forecast):\n",
        "    data = pd.DataFrame({\"realised\": realised, \"forecast\": forecast}).dropna()\n",
        "    data[\"error\"] = data[\"forecast\"] - data[\"realised\"]\n",
        "    return {\n",
        "        \"mae\": data[\"error\"].abs().mean(),\n",
        "        \"rmse\": np.sqrt((data[\"error\"] ** 2).mean()),\n",
        "        \"bias\": data[\"error\"].mean()\n",
        "    }\n",
        "\n",
        "evaluate_vol_forecast(df[\"realised_vol_20\"], df[\"ewma_vol\"])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Open-ended extensions\n",
        "\n",
        "- Add GARCH\n",
        "- Compare volatility forecasts before and after regime shifts\n",
        "- Use realised range estimators\n",
        "- Use implied volatility as a benchmark\n",
        "- Write about whether the best statistical forecast is actually the most useful trading forecast"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Final write-up prompts\n",
        "\n",
        "Answer these before putting the project on your resume:\n",
        "\n",
        "1. What problem were you trying to solve?\n",
        "2. What assumptions did your model make?\n",
        "3. Where did the model work well?\n",
        "4. Where did it fail?\n",
        "5. What would you improve with more time?\n",
        "6. What is one interesting thing you learned that was not obvious before starting?"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "name": "python",
      "version": "3.x"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
