From 220ba022b95cca015723374f479ff94db8bd504c Mon Sep 17 00:00:00 2001 From: Gavin Chan Date: Fri, 15 Dec 2023 20:06:13 +0000 Subject: [PATCH] docs: add example notebook of alpha sizing rules --- README.md | 2 + ...mpirical_analysis_alpha_sizing_rules.ipynb | 5471 +++++++++++++++++ 2 files changed, 5473 insertions(+) create mode 100644 examples/notebook/crypto_empirical_analysis_alpha_sizing_rules.ipynb diff --git a/README.md b/README.md index 859b507..2bae8d1 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,8 @@ For end-to-end examples, please refer to [examples](https://github.com/factorpri - [NumPy Backend Engine](https://colab.research.google.com/github/factorpricingmodel/factor-pricing-model-risk-model/blob/main/examples/notebook/numpy_backend_engine.ipynb) +- [Empirical Analysis of Alpha Sizing Rules in Cryptocurrency](https://colab.research.google.com/github/factorpricingmodel/factor-pricing-model-risk-model/blob/main/examples/notebook/crypto_empirical_analysis_alpha_sizing_rules.ipynb) + ## Features Basically, there are three major features provided in the library diff --git a/examples/notebook/crypto_empirical_analysis_alpha_sizing_rules.ipynb b/examples/notebook/crypto_empirical_analysis_alpha_sizing_rules.ipynb new file mode 100644 index 0000000..60be6b8 --- /dev/null +++ b/examples/notebook/crypto_empirical_analysis_alpha_sizing_rules.ipynb @@ -0,0 +1,5471 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "MtppsR5tq3MQ" + }, + "source": [ + "\n", + " \n", + " \n", + "
\n", + " Run in Google Colab\n", + " \n", + " View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-zxWvuYoq_Pk" + }, + "source": [ + "## Empirical Analysis of Alpha Sizing Rules in Cryptocurrency\n", + "\n", + "The notebook visits a few alpha sizing rules, e.g. Mean-Variance rule, with several signals. It also demonstrates the usage of factor risk model to improve the performance of the Mean-Variance rule.\n", + "\n", + "### Requirements\n", + "\n", + "- Python 3.6+\n", + "- [Jupyter](http://jupyter.org/)\n", + "- [pandas](http://pandas.pydata.org/)\n", + "- [numpy](http://www.numpy.org/)\n", + "- [factor-pricing-model-risk-model](https://factor-pricing-model-risk-model.readthedocs.io/en/latest/index.html)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "QE5aBycKXxGh", + "outputId": "b33e51f4-33f9-4b4a-f4e3-e0f6a171a201" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: matplotlib in /usr/local/lib/python3.10/dist-packages (3.7.1)\n", + "Requirement already satisfied: factor-pricing-model-risk-model in /usr/local/lib/python3.10/dist-packages (2023.8.0)\n", + "Requirement already satisfied: factor-pricing-model-universe in /usr/local/lib/python3.10/dist-packages (2024.0.0)\n", + "Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (1.2.0)\n", + "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (0.12.1)\n", + "Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (4.46.0)\n", + "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (1.4.5)\n", + "Requirement already satisfied: numpy>=1.20 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (1.23.5)\n", + "Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (23.2)\n", + "Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (9.4.0)\n", + "Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (3.1.1)\n", + "Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (2.8.2)\n", + "Requirement already satisfied: pandas<3.0.0,>=1.3.5 in /usr/local/lib/python3.10/dist-packages (from factor-pricing-model-risk-model) (1.3.5)\n", + "Requirement already satisfied: pydantic<2.0.0,>=1.10.4 in /usr/local/lib/python3.10/dist-packages (from factor-pricing-model-risk-model) (1.10.13)\n", + "Requirement already satisfied: scikit-learn<2.0.0,>=1.1.3 in /usr/local/lib/python3.10/dist-packages (from factor-pricing-model-risk-model) (1.2.2)\n", + "Requirement already satisfied: tqdm<5.0.0,>=4.64.1 in /usr/local/lib/python3.10/dist-packages (from factor-pricing-model-risk-model) (4.66.1)\n", + "Requirement already satisfied: jq<2.0.0,>=1.3.0 in /usr/local/lib/python3.10/dist-packages (from factor-pricing-model-universe) (1.6.0)\n", + "Requirement already satisfied: pyarrow<11.0.0,>=10.0.1 in /usr/local/lib/python3.10/dist-packages (from factor-pricing-model-universe) (10.0.1)\n", + "Requirement already satisfied: pyyaml<7.0,>=6.0 in /usr/local/lib/python3.10/dist-packages (from factor-pricing-model-universe) (6.0.1)\n", + "Requirement already satisfied: pytz>=2017.3 in /usr/local/lib/python3.10/dist-packages (from pandas<3.0.0,>=1.3.5->factor-pricing-model-risk-model) (2023.3.post1)\n", + "Requirement already satisfied: typing-extensions>=4.2.0 in /usr/local/lib/python3.10/dist-packages (from pydantic<2.0.0,>=1.10.4->factor-pricing-model-risk-model) (4.5.0)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.7->matplotlib) (1.16.0)\n", + "Requirement already satisfied: scipy>=1.3.2 in /usr/local/lib/python3.10/dist-packages (from scikit-learn<2.0.0,>=1.1.3->factor-pricing-model-risk-model) (1.11.4)\n", + "Requirement already satisfied: joblib>=1.1.1 in /usr/local/lib/python3.10/dist-packages (from scikit-learn<2.0.0,>=1.1.3->factor-pricing-model-risk-model) (1.3.2)\n", + "Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn<2.0.0,>=1.1.3->factor-pricing-model-risk-model) (3.2.0)\n" + ] + } + ], + "source": [ + "!pip install matplotlib factor-pricing-model-risk-model factor-pricing-model-universe" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ShwiVyuOY9-d" + }, + "outputs": [], + "source": [ + "from functools import partial\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "from IPython.display import display\n", + "\n", + "from fpm_risk_model.dataset.crypto import (\n", + " download_sample_data_model_universe,\n", + ")\n", + "from fpm_universe.pipeline import rolling_validity, ranking\n", + "from fpm_risk_model.rolling_factor_risk_model import RollingFactorRiskModel\n", + "from fpm_risk_model.rolling_risk_model import RollingRiskModel\n", + "from fpm_risk_model.statistical import PCA\n", + "from fpm_risk_model import RollingCovarianceEstimator" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-Cr4cPq9wX_w" + }, + "source": [ + "# Download crypto price and volume data\n", + "\n", + "Cryptocurrencies with a top 1000 market cap are downloaded. Due to the volatile\n", + "price changes in the market, all the daily returns beyond +/-20% are clipped." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "BS3-dY_-Y2t-" + }, + "outputs": [], + "source": [ + "sample_data_model_universe = download_sample_data_model_universe()\n", + "prices = sample_data_model_universe[\"prices\"].shift()\n", + "returns = (\n", + " sample_data_model_universe[\"returns\"]\n", + " .shift()\n", + " .clip(lower=-0.2, upper=0.2)\n", + ")\n", + "volumes = sample_data_model_universe[\"volumes\"].shift()\n", + "volumes_usd = volumes * prices" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yPREUU185iKg" + }, + "source": [ + "# Model universe creation\n", + "\n", + "Instruments are selected for the model universe based on the following criteria:\n", + "\n", + "- 80% valid returns in the past 6 months\n", + "- Top 50% trading volumes in USD\n", + "- Minimum annual volatility of 5% (filtering out stablecoins)\n", + "\n", + "In the meantime, the estimation universe is also defined in the later section on risk model generation. The only difference in the selection of the estimation universe is that only those with top 10%, rather than 50%, trading volumes in USD are included." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 498 + }, + "id": "Z2bd6mNIZN_F", + "outputId": "3dd89df6-7cff-4648-a536-46d171b7a440" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAi4AAAHPCAYAAAB0ulFlAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAACefElEQVR4nOzdd3hUxfrA8e/21E0jhJIEkkBCC4QWqihFumDBcm0X9aKoqOD1imJvP8u1XYooCFZsqNgoYkERKQLSO4SSBJJAet92fn8sOcmyCSQhPe/neXzcM2fO7JxMdnkzZ4pGURQFIYQQQohGQFvfFRBCCCGEqCwJXIQQQgjRaEjgIoQQQohGQwIXIYQQQjQaErgIIYQQotGQwEUIIYQQjYYELkIIIYRoNCRwEUIIIUSjIYGLEEIIIRoNCVxEs7Vp0yZiYmJYtWpVfVelUs6cOcP9999Pv379iImJ4f33368wb0xMDHPmzKm7yokqu+WWW7jlllvquxrn9fXXXxMTE0NSUlJ9V0UIlQQuolaVfPHFxsaSmprqdv6WW25h/Pjx9VCzxufFF1/kjz/+4M477+SVV17hkksuqZd6pKamMmfOHPbt21cv718fDh8+zJw5c+QfcCEaAH19V0A0DxaLhQULFvDEE0/Ud1UarY0bNzJ8+HDuuOOOC+bduXMnOp2uVuqRlpbG3Llzadu2LZ07d66V92hoDh8+zNy5c4mPjyc0NLRGyly0aFGNlFObJk6cyLhx4zAajfVdFSFU0uMi6kTnzp354osvyu11aeoKCgpqpJz09HTMZnOl8ppMJvT6hvF3SWFhYX1XoUEyGo0NPiDQ6XSYTCY0Gk2dvq/8zojzkcBF1Im77roLh8PBwoULz5svKSmJmJgYvv76a7dz547bmDNnDjExMRw9epSHHnqI3r17079/f958800UReHUqVPcfffd9OrVi0GDBrF48eJy39PhcPD6668zaNAg4uLimDp1KqdOnXLLt2PHDu644w569+5Njx49uPnmm9m6datLnpI6HT58mH//+9/07duXG2+88bz3nJiYyP333098fDw9evTguuuu47ffflPPlzxuUxSFJUuWEBMTQ0xMzHnLrOhndfz4cR555BH69OlD7969efTRR93+kfjzzz/5xz/+QZ8+fejZsyejRo3i9ddfB5zjgiZNmgTAo48+qtalpL1KHv3t3r2bm266iR49eqjXVjTuZtiwYTzyyCNu97tlyxaef/55+vfvT58+fXjyySexWCzk5OTw8MMP07dvX/r27csrr7zCuZvcOxwO3n//fcaNG0dsbCwDBw7kySefJDs72+2977rrLrZs2cKkSZOIjY1l+PDhfPPNNy71eeCBBwC49dZb1XvetGkTALt27eKOO+6gX79+dO/enWHDhvHoo4+et31KflZlx7iUjLlasWIF8+fPZ8iQIcTGxvLPf/6T48ePX7C8Rx55hGHDhrmll7R9WTExMTz77LP8/PPPjB8/nm7dujFu3DjWrl3rku/cMS533XUXw4cPL/f9r7/+eq6++mqXtG+//Zarr76a7t27Ex8fz4wZM9w+W+f7nanMz7aybV3ddhINT8P4k0w0eaGhoUycOJEvvviCKVOmEBISUmNlz5gxg6ioKP7973/z+++/M3/+fPz9/fnss8/o378/Dz30EN9//z0vv/wysbGx9O3b1+X6+fPno9FomDJlCunp6XzwwQdMnjyZb7/9Fg8PDwA2bNjAlClT6NatG9OmTUOj0fD111/zz3/+k08++YTu3bu7lPnAAw/Qrl07ZsyY4faPallnzpzhhhtuoLCwkFtuuYWAgACWLVvG3XffzezZs7n88svVf5wffvhhBg0axMSJE6v9s5o+fTqhoaE8+OCD7N27l6VLlxIYGMh//vMfAA4dOsRdd91FTEwM999/P0ajkePHj/P3338DEBUVxf3338/s2bO5/vrr6d27NwC9evVS3yMrK4spU6Ywbtw4JkyYQFBQULXq+vzzz9OiRQvuu+8+duzYweeff46vry/btm2jdevWzJgxg7Vr17Jo0SKio6O58sor1WuffPJJli1bxtVXX80tt9xCUlISS5YsYe/evXz66acYDAY17/Hjx3nggQeYNGkSV111FV999RWPPPIIXbt2pWPHjvTt25dbbrmFjz76iKlTpxIZGan+LNLT07njjjsICAjgzjvvxGw2k5SUxE8//VStewZYuHAhGo2G22+/nby8PN59910eeughli5dWu0yy7N161ZWr17NjTfeiLe3Nx999BH3338/a9asISAgoNxrxowZw8yZM9m5c6fL73xycjLbt2/n4YcfVtPmz5/P//73P8aMGcOkSZPIyMjg448/5qabbuKbb75x6T0s73emsj/byrR1bbST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Uvbv38+YMWNczo8cORKdTsfcuXPdfj6KopCZmXnB96jMz7aybd2Q2klcPOlxEfVi6tSpfPvttxw9epSOHTu6nLv22mtZsGABjz32GN26dWPLli3l9krUFD8/P2688UauvvpqdTp0u3btuO666wBnD8Tzzz/PlClTGD9+PFdffTUhISGkpqayadMmfHx8ePvtt6v13nfeeSfLly9nypQp3HLLLfj5+fHNN9+QlJTEnDlzKj2upKbMmzePLVu2cOmll9K2bVvS09P55JNPaNWqlTr1OTw8HLPZzGeffYa3tzdeXl50795d7ZmqyLXXXstTTz3Ffffdx8CBA9m/fz/r1q2rcOptdcXHx3P99dfzzjvvsG/fPgYNGoTBYODYsWOsWrWKxx57zOWxRmV07twZnU7HwoULyc3NxWg00r9/f77//ns+/fRTRowYQXh4OPn5+XzxxRfqINa6NHbsWF599VWmTZvGLbfcQlFREZ9++ikRERHs2bOnRt/r0ksvxdvbm5dffhmdTseoUaNczoeHhzN9+nRee+01kpOTGTFiBN7e3iQlJfHzzz9z3XXXXXAF6GXLll3wZ1vZtq5MWaLxkMBF1It27doxYcIEli1b5nbu3nvvJSMjgx9//JGVK1cyZMgQ3n333QoH4F2sqVOncuDAARYsWEB+fj4DBgzgqaeeUv/KBujXrx+ff/45b731Fh9//DEFBQUEBwfTvXt3rr/++mq/d4sWLfjss8/473//y8cff0xxcTExMTG8/fbbXHbZZTVwd1UzbNgwkpOT+eqrr8jMzCQgIID4+Hjuu+8+dXCswWDgpZde4vXXX+fpp5/GZrPx4osvXjBwue6660hKSuLLL7/kjz/+oHfv3rz33ntMnjy5xu/j2WefpVu3bnz22We88cYb6HQ62rZty4QJE1zWnKms4OBgnnnmGd555x0ee+wx7HY7H374IfHx8ezatYsVK1Zw5swZfH196d69O6+++uoFfx41LSAggLlz5/LSSy/x3//+V12v5/jx4zUeuJhMJoYNG8b333/PwIEDy12r584776R9+/a8//77zJs3D4BWrVoxaNCgchfKO1dlf7aVaeuG1E7i4mmU6oxWFEIIIYSoBzLGRQghhBCNhgQuQgghhGg0JHARQgghRKMhgYsQQgghGg0JXIQQQgjRaEjgIoQQQohGQwIXIYQQQjQa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AoLU9A4donn77fcIDW1XK3XRaJxBi93u/B1zOBxkZxdQWFh7m1yKiul0Wsxmzwq/D0TdkzapmtQzeQCYDDoyM90XnbtYDak9/Pw80Worub1AVQo2m834+/urQQs4x6Z06dKFw4cPM27cOAC3mUE5OTlnK+anlpOXl+dWfk5Ojtvjo+qw2x3l/sNktzedZyovv/wGLVoEqvfZu3dfsrOz+fzzJUye/K9K/wLUhYiIyAtnqgPe3j506xZ74YzVVPLIruyju6YQJDd2FX0fiPojbVI5B05kAeBt0tfqz6shtEdVhjxU6V+3Dh06VHiuuLiY8PBwDAaD23osJcclY18iIyM5c+aM22OhhIQEt/Exonz+/v5uadHRMeTn51NUVFjl8nJzc3n11ZeYOHEUQ4cO4Pbbb+avvza65Nm5czv33juFUaMu5fLLh3DrrdezcuUPAEybdifbt//N+vXrGDy4D4MH92HRonfUcw8/PF0tZ9Gid7j88ks4eHA/d911G8OGDeL222/i4MH9FBcX8+qrLzJ69FCuumosX3zxiUsddu/eycyZM5g4cTQjRgxm8uQbWbVquXr+77+3cP/9UwH4179uVetScm7w4D7s379XzV9cXMycOa8zceJohg0byOTJN/L772tc3vOFF57mlluu4++/t3DbbTcyYsRgpky5lf3791X55yyEaD4UReFEai4FRTZSMwtIPp1HWmYBtgv0blhtDvYey2DL/jQA2rVyH1rRnFWpx2Xo0KF8/fXX7Nu3j86dOwOQmZnJnj17mDx5MkajkX79+vHjjz/yz3/+U71uxYoVREVFERrqHEE8ePBgtFotq1ev5tprrwWc41/WrVvHPffcU1P3VmmKooDNUufvq9Iba2Rcys6d2wkObomXV9UWILJarcyYcS8ZGelMmXIPwcEtWb16Bf/5zwMsXryEqKgO5Ofn8fDD0+nePY6nn34Bg8HIsWMJau/av//9CM899wQmkwf33jsdgJYtW1b4njabjRdeeJrrrruRwMBA5s+fw2OPPUxsbA8CAgJ47rkX+eOP35k9+3U6d+5KbGwPAFJSThEb24Mrr7wGo9HErl07eOml51AUhTFjxhMT04kHH5zJ66+/zKxZTxEe3v689/7ss4+zadMG7rzzHsLD27Nq1XIef/xhXnzxVQYPLp09l5GRzv/+9yo33TQZHx8f3nln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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "rolling_validity_universe = rolling_validity(\n", + " values=returns.where(returns.abs() > 0.0),\n", + " threshold_pct=0.8,\n", + " rolling_window=180,\n", + " tolerance_timeframes=5,\n", + " start_datetime=\"2018-06-01\",\n", + " last_datetime=returns.index[-1],\n", + " frequency=\"D\",\n", + ")\n", + "non_stable_validity_universe = ((returns.rolling(30).std() * np.sqrt(365.25)) > 0.05).loc[\"2018-06-01\":]\n", + "ranking_validity_estimation_universe = ranking(\n", + " values=volumes,\n", + " threshold_pct=0.1,\n", + " tolerance_timeframes=30,\n", + " start_datetime=\"2018-06-01\",\n", + " last_datetime=returns.index[-1],\n", + " frequency=\"D\",\n", + ")\n", + "ranking_validity_model_universe = ranking(\n", + " values=volumes,\n", + " threshold_pct=0.5,\n", + " tolerance_timeframes=30,\n", + " start_datetime=\"2018-06-01\",\n", + " last_datetime=returns.index[-1],\n", + " frequency=\"D\",\n", + ")\n", + "estimation_validity = rolling_validity_universe & non_stable_validity_universe & ranking_validity_estimation_universe\n", + "validity = model_validity = rolling_validity_universe & non_stable_validity_universe & ranking_validity_model_universe\n", + "pd.concat({\n", + " '1. model': model_validity.sum(axis=1),\n", + " '2. estimation': estimation_validity.sum(axis=1),\n", + "}, axis=1).plot(title=\"Number of instruments in universes\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "RGfandu4O36m" + }, + "source": [ + "# Magical alpha\n", + "\n", + "Let's create a magical alpha with the assumption that we can predict 100% on the total return next month and assign it with the average forecasted next month return on each day." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ZHGiYYwAblSg" + }, + "outputs": [], + "source": [ + "yyyy_mm = pd.Series(returns.index.map(lambda x: x.strftime(\"%Y-%m\")), index=returns.index)\n", + "month_days = pd.Series(1.0, index=returns.index).groupby(returns.index.map(lambda x: x.strftime(\"%Y-%m\"))).sum()\n", + "forecast_daily_returns = returns.groupby(returns.index.map(lambda x: x.strftime(\"%Y-%m\"))).sum().div(month_days, axis=0)\n", + "magic_score = pd.merge(\n", + " left=yyyy_mm.to_frame(name=\"yyyy_mm\"),\n", + " right=forecast_daily_returns,\n", + " left_on=\"yyyy_mm\",\n", + " right_index=True,\n", + " how=\"left\",\n", + ")\n", + "del magic_score[\"yyyy_mm\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "i04uUd18xolB" + }, + "source": [ + "Also, we define a pipeline to normalize the scores in the cross section and clip the z-scores by +/-3.0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "6AA9T0clxq4G" + }, + "outputs": [], + "source": [ + "def normalize_xs(\n", + " score,\n", + " validity,\n", + " winsorization=3.0,\n", + " max_iter=20,\n", + " atol=1e-4\n", + "):\n", + " score = score.reindex_like(validity).where(validity)\n", + " for idx in range(max_iter):\n", + " xs_mean = score.mean(axis=1)\n", + " xs_std = score.std(axis=1)\n", + " xs_mean_err = xs_mean.abs().fillna(0.0)\n", + " xs_std_err = (xs_std - 1.0).abs().fillna(0.0)\n", + " if ((xs_mean_err < atol) & (xs_std_err < atol)).all():\n", + " break\n", + " score = score.sub(xs_mean, axis=0).div(xs_std, axis=0).clip(upper=winsorization, lower=-winsorization)\n", + "\n", + " assert idx != max_iter - 1, (\n", + " f\"Score does not converage after {max_iter} iterations. \"\n", + " f\"Max mean error: {xs_mean_err.max()}. \"\n", + " f\"Max std error: {xs_std_err.max()}\"\n", + " )\n", + "\n", + " return score" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 408 + }, + "id": "g4_598tVy3TV", + "outputId": "7509023a-17fb-442c-b59f-974e083d11e8" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
ALPINE_Alpine-F1-Team-Fan-TokenVRSC_VerusCoinFRAX_FraxZIL_ZilliqaAION_AionCAKE_PancakeSwapMBOX_MOBOXBIDR_BIDRHIGH_HighstreetANKR_Ankr...SWAP_TrustSwapSRM_SerumDFYN_Dfyn-NetworkPRE_PresearchGMX11857_GMXVEGA_Vega-ProtocolTHN_ThronePART_Paricle-TechnologyCCD_ConcordiumHEZ_Hermez-Network
datetime
2023-05-03-0.374576NaNNaN-0.515495NaN-0.5936670.304838NaN-1.450140-0.452138...-0.063156-0.2744592.003535NaN0.562630NaNNaNNaN0.300323NaN
2023-05-04-0.367738NaNNaN-0.506999NaN-0.5842510.303681NaN-1.430645-0.444388...-0.059982-0.2687991.982389NaN0.558440NaNNaNNaN0.299219NaN
2023-05-05-0.371875NaNNaN-0.509912NaN-0.5864840.293642NaN-1.425438-0.447851...-0.066825-0.2738061.957592NaN0.546161NaNNaNNaN0.289219NaN
2023-05-06-0.372054NaNNaN-0.510197NaN-0.5868290.293973NaN-1.426426-0.448089...-0.066770-0.2739101.959200NaN0.546686NaNNaNNaN0.289547NaN
2023-05-07-0.373284NaNNaN-0.511723NaN-0.5885190.294173NaN-1.429919-0.449481...-0.067344-0.2749291.962974NaN0.547428NaNNaNNaN0.289737NaN
\n", + "

5 rows × 1000 columns

\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ], + "text/plain": [ + " ALPINE_Alpine-F1-Team-Fan-Token VRSC_VerusCoin FRAX_Frax \\\n", + "datetime \n", + "2023-05-03 -0.374576 NaN NaN \n", + "2023-05-04 -0.367738 NaN NaN \n", + "2023-05-05 -0.371875 NaN NaN \n", + "2023-05-06 -0.372054 NaN NaN \n", + "2023-05-07 -0.373284 NaN NaN \n", + "\n", + " ZIL_Zilliqa AION_Aion CAKE_PancakeSwap MBOX_MOBOX BIDR_BIDR \\\n", + "datetime \n", + "2023-05-03 -0.515495 NaN -0.593667 0.304838 NaN \n", + "2023-05-04 -0.506999 NaN -0.584251 0.303681 NaN \n", + "2023-05-05 -0.509912 NaN -0.586484 0.293642 NaN \n", + "2023-05-06 -0.510197 NaN -0.586829 0.293973 NaN \n", + "2023-05-07 -0.511723 NaN -0.588519 0.294173 NaN \n", + "\n", + " HIGH_Highstreet ANKR_Ankr ... SWAP_TrustSwap SRM_Serum \\\n", + "datetime ... \n", + "2023-05-03 -1.450140 -0.452138 ... -0.063156 -0.274459 \n", + "2023-05-04 -1.430645 -0.444388 ... -0.059982 -0.268799 \n", + "2023-05-05 -1.425438 -0.447851 ... -0.066825 -0.273806 \n", + "2023-05-06 -1.426426 -0.448089 ... -0.066770 -0.273910 \n", + "2023-05-07 -1.429919 -0.449481 ... -0.067344 -0.274929 \n", + "\n", + " DFYN_Dfyn-Network PRE_Presearch GMX11857_GMX \\\n", + "datetime \n", + "2023-05-03 2.003535 NaN 0.562630 \n", + "2023-05-04 1.982389 NaN 0.558440 \n", + "2023-05-05 1.957592 NaN 0.546161 \n", + "2023-05-06 1.959200 NaN 0.546686 \n", + "2023-05-07 1.962974 NaN 0.547428 \n", + "\n", + " VEGA_Vega-Protocol THN_Throne PART_Paricle-Technology \\\n", + "datetime \n", + "2023-05-03 NaN NaN NaN \n", + "2023-05-04 NaN NaN NaN \n", + "2023-05-05 NaN NaN NaN \n", + "2023-05-06 NaN NaN NaN \n", + "2023-05-07 NaN NaN NaN \n", + "\n", + " CCD_Concordium HEZ_Hermez-Network \n", + "datetime \n", + "2023-05-03 0.300323 NaN \n", + "2023-05-04 0.299219 NaN \n", + "2023-05-05 0.289219 NaN \n", + "2023-05-06 0.289547 NaN \n", + "2023-05-07 0.289737 NaN \n", + "\n", + "[5 rows x 1000 columns]" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "magic_score = normalize_xs(score=magic_score, validity=validity)\n", + "magic_score.tail()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "THnqDZOHPwAz" + }, + "source": [ + "# Alpha sizing\n", + "\n", + "We will walk through a few alpha sizing rules in this section.\n", + "\n", + "First of all, assume that all the sized positions are traded in a dollar neutral manner without leverage (i.e. leverage = 1.0).\n", + "\n", + "The function `dollar_neutral` demeans the sized positions, and the gross sum of positions is equal to 1.0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "bQkyocYA6h4W" + }, + "outputs": [], + "source": [ + "def dollar_neutral(alpha, leverage=1.0):\n", + " alpha = alpha.sub(alpha.mean(axis=1), axis=0)\n", + " alpha = alpha.div(alpha.abs().sum(axis=1), axis=0) * leverage\n", + " return alpha" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pxit_Ht57lAh" + }, + "source": [ + "Then, the following alpha sizing appraoches will be studied\n", + "\n", + "1. **Proportional**: This method involves allocating assets in the same direction and magnitude as the alpha, with adjustments for dollar neutrality and leverage.\n", + "\n", + "2. **Proportional Signed (1/N rule)**: In this approach, only the sign (positive or negative) of the alpha is considered, and the allocation is divided equally between the long and short sides.\n", + "\n", + "3. **Risk Parity**: This method divides the alpha by the asset's volatility. The rationale behind this is to normalize the alpha with respect to volatility, resulting in dollar exposure per 1% of volatility.\n", + "\n", + "4. **Mean Variance**: Alpha is divided by the asset's variance\n", + "\n", + "5. **Mean Variance (Shrink)**: Alpha divided by the shrunken asset variance `(1 - p) x variance + p x benchmark variance`. For instance, 75% shrink means 25% asset variance and 75% benchmark variance)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "G0b6h4nJ60MI" + }, + "outputs": [], + "source": [ + "def alpha_sizing(\n", + " score,\n", + " returns,\n", + " validity,\n", + " benchmark_variance,\n", + " est_window=180,\n", + " leverage=1.0,\n", + " ic=1.0,\n", + "):\n", + " # Asset volatility\n", + " volatility = returns.rolling(est_window, min_periods=int(est_window * 0.8)).std()\n", + " variance = volatility ** 2\n", + " # Alpha = Score * Volatility * IC\n", + " alpha = (score * volatility * ic).reindex_like(validity)\n", + " # proportional = Alpha\n", + " proportional_alpha = dollar_neutral(alpha, leverage)\n", + " # proportional signed = 1 / (# Positive alpha) - 1 / (# Negative alpha)\n", + " proportional_signed_alpha = (\n", + " (alpha > 0.0).div((alpha > 0.0).sum(axis=1), axis=0).fillna(0.0)\n", + " - (alpha < 0.0).div((alpha < 0.0).sum(axis=1), axis=0).fillna(0.0)\n", + " )\n", + " proportional_signed_alpha = dollar_neutral(proportional_signed_alpha, leverage)\n", + " # Risk parity = alpha / volatility\n", + " risk_parity_alpha = alpha / volatility\n", + " risk_parity_alpha = dollar_neutral(risk_parity_alpha, leverage)\n", + " # Mean variance = alpha / variance\n", + " mean_variance_alpha = alpha / variance\n", + " mean_variance_alpha = dollar_neutral(mean_variance_alpha, leverage)\n", + " # Shrinked 75% mean variance\n", + " shrinked_75_mean_variance_alpha = alpha / (0.25 * variance.add(0.75 * benchmark_variance, axis=0))\n", + " shrinked_75_mean_variance_alpha = dollar_neutral(shrinked_75_mean_variance_alpha, leverage)\n", + " # Shrinked 50% mean variance\n", + " shrinked_50_mean_variance_alpha = alpha / (0.50 * variance.add(0.50 * benchmark_variance, axis=0))\n", + " shrinked_50_mean_variance_alpha = dollar_neutral(shrinked_50_mean_variance_alpha, leverage)\n", + " # Shrinked 25% mean variance\n", + " shrinked_25_mean_variance_alpha = alpha / (0.75 * variance.add(0.25 * benchmark_variance, axis=0))\n", + " shrinked_25_mean_variance_alpha = dollar_neutral(shrinked_25_mean_variance_alpha, leverage)\n", + " # Return in a dict\n", + " return {\n", + " \"proportional\": proportional_alpha,\n", + " \"proportional_signed\": proportional_signed_alpha,\n", + " \"risk_parity\": risk_parity_alpha,\n", + " \"mean_variance\": mean_variance_alpha,\n", + " \"mean_variance_shrink_75\": shrinked_75_mean_variance_alpha,\n", + " \"mean_variance_shrink_50\": shrinked_50_mean_variance_alpha,\n", + " \"mean_variance_shrink_25\": shrinked_25_mean_variance_alpha,\n", + " }" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ufA-47N7c5sh" + }, + "source": [ + "The function `get_performance` computes the performance of the sized positions, including the annualized returns, volatilities, max drawdowns, Sharpe ratios and information ratios (IR)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Rui-valegSpb" + }, + "outputs": [], + "source": [ + "def get_performance(\n", + " alpha,\n", + " returns,\n", + " benchmark_returns,\n", + " validity,\n", + " ann_factor = 365.25\n", + "):\n", + " alpha_returns = (alpha.shift() * returns).sum(axis=1).loc[validity.index]\n", + " idio_returns = (alpha_returns - benchmark_returns).loc[validity.index]\n", + " alpha_cumreturns = alpha_returns.cumsum()\n", + " annual_vol = alpha_returns.std() * np.sqrt(ann_factor)\n", + " max_drawdown = (alpha_cumreturns.expanding().max() - alpha_cumreturns).max()\n", + " drifted_alpha = (alpha.shift() * (1 + returns)).div(1 + alpha_returns, axis=0)\n", + " alpha_turnover = (alpha - drifted_alpha).abs().sum(axis=1)\n", + " return {\n", + " \"Annual Return\": alpha_returns.sum() / len(alpha_returns) * ann_factor,\n", + " \"Annual Volatility\": annual_vol,\n", + " \"Turnover\": alpha_turnover.mean() * ann_factor,\n", + " \"Max Drawdown\": max_drawdown,\n", + " \"Sharpe Ratio\": alpha_returns.mean() * ann_factor / annual_vol,\n", + " \"IR\": idio_returns.mean() * np.sqrt(ann_factor) / idio_returns.std(),\n", + " }" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IPpaW68rdJvc" + }, + "source": [ + "And the function `alpha_performance` combines the performance tables across the mentioned alpha sizing methods." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "C5YYN5-WvNux" + }, + "outputs": [], + "source": [ + "def alpha_performance(\n", + " score,\n", + " returns,\n", + " validity,\n", + " est_window=180,\n", + " leverage=1.0,\n", + " sizing_func=alpha_sizing,\n", + "):\n", + " # Benchmark\n", + " # benchmark_weights = pd.DataFrame(1.0, index=returns.index, columns=[\"BTC_Bitcoin\"])\n", + " benchmark_weights = validity.div(validity.sum(axis=1), axis=0)\n", + " benchmark_returns = (benchmark_weights.shift() * returns).sum(axis=1)\n", + " benchmark_variance = benchmark_returns.rolling(est_window).var()\n", + " # Alpha sizing\n", + " positions = sizing_func(\n", + " score=score,\n", + " returns=returns,\n", + " validity=validity,\n", + " benchmark_variance=benchmark_variance,\n", + " est_window=est_window,\n", + " leverage=leverage,\n", + " )\n", + " # Performance\n", + " performances = {\n", + " key: get_performance(position, returns, benchmark_returns, validity)\n", + " for key, position in positions.items()\n", + " }\n", + " return pd.DataFrame(performances).T" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hOMbteLeXJeB" + }, + "source": [ + "Then we can see the performance of the magical alpha is stunning - a Sharpe ratio of 21 with the proportional sizing method. That's the power of a crystal ball.\n", + "\n", + "The ranking of the Sharpe ratio gives a slightly different result than the one in the book - the proportional sizing rule gives the best performance. In the meantime, it aligns with the fact that the mean variance rule performs the worst among all, and shrinkage with the variance of market returns (benchmark return is used rather than the sector return, as I assume crypto is the only sector) performs better." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 269 + }, + "id": "QvMtJU4nmpPx", + "outputId": "cfb975bf-ece9-404a-bc23-42249c0e8757" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Annual ReturnAnnual VolatilityTurnoverMax DrawdownSharpe RatioIR
proportional3.8363480.17669020.8344640.02826121.7123655.282328
proportional_signed2.0425950.11010819.1067180.02121418.5508862.896311
risk_parity3.7132190.17111520.2809120.03304921.7001875.074068
mean_variance3.3820890.23295419.8046400.06453014.5182774.273183
mean_variance_shrink_753.6647930.16703520.3015920.02943121.9402295.021244
mean_variance_shrink_503.6504150.16769120.2432370.02991921.7686614.990282
mean_variance_shrink_253.6279030.17013920.1336000.03131921.3232144.934158
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ], + "text/plain": [ + " Annual Return Annual Volatility Turnover \\\n", + "proportional 3.836348 0.176690 20.834464 \n", + "proportional_signed 2.042595 0.110108 19.106718 \n", + "risk_parity 3.713219 0.171115 20.280912 \n", + "mean_variance 3.382089 0.232954 19.804640 \n", + "mean_variance_shrink_75 3.664793 0.167035 20.301592 \n", + "mean_variance_shrink_50 3.650415 0.167691 20.243237 \n", + "mean_variance_shrink_25 3.627903 0.170139 20.133600 \n", + "\n", + " Max Drawdown Sharpe Ratio IR \n", + "proportional 0.028261 21.712365 5.282328 \n", + "proportional_signed 0.021214 18.550886 2.896311 \n", + "risk_parity 0.033049 21.700187 5.074068 \n", + "mean_variance 0.064530 14.518277 4.273183 \n", + "mean_variance_shrink_75 0.029431 21.940229 5.021244 \n", + "mean_variance_shrink_50 0.029919 21.768661 4.990282 \n", + "mean_variance_shrink_25 0.031319 21.323214 4.934158 " + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "magic_alpha_performance = alpha_performance(score=magic_score, returns=returns, validity=validity)\n", + "magic_alpha_performance" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_IhRcW8BcD75" + }, + "source": [ + "# Academic Factors\n", + "\n", + "\n", + "In quantitative research, academic factors are key elements derived from empirical finance research that help explain and predict variations in returns. These factors form the cornerstone of many asset pricing models and investment strategies. With the access of price data, we can examine the performance of the most influential factors.\n", + "\n", + "First, the momentum factor is computed as the average of 3-month, 6-month, 9-month, and 1-year annualized returns. To account for the extreme volatility of the market, annualized returns are clipped at +/-100%. The average annualized returns are then smoothed using a 14-day rolling window.\n", + "\n", + "Similar to the magical alpha, the factor is always represented as the normalized z-score in cross section" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 269 + }, + "id": "k3pnRnMnvTCX", + "outputId": "573e794a-7e0c-47af-b74a-1d1e42b98f11" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Annual ReturnAnnual VolatilityTurnoverMax DrawdownSharpe RatioIR
proportional0.0959620.17365313.3112150.1929110.5526070.280928
proportional_signed0.0974840.13790015.2210010.1511030.7069200.286863
risk_parity0.0943170.15199112.9375830.1639670.6205450.279954
mean_variance0.1206290.17303112.4644350.1811930.6971520.313529
mean_variance_shrink_750.0931380.14200312.8658440.1475440.6558830.278992
mean_variance_shrink_500.0934220.13954012.8260870.1437320.6695010.279528
mean_variance_shrink_250.0941740.13664912.7637880.1390410.6891650.280779
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ], + "text/plain": [ + " Annual Return Annual Volatility Turnover \\\n", + "proportional 0.095962 0.173653 13.311215 \n", + "proportional_signed 0.097484 0.137900 15.221001 \n", + "risk_parity 0.094317 0.151991 12.937583 \n", + "mean_variance 0.120629 0.173031 12.464435 \n", + "mean_variance_shrink_75 0.093138 0.142003 12.865844 \n", + "mean_variance_shrink_50 0.093422 0.139540 12.826087 \n", + "mean_variance_shrink_25 0.094174 0.136649 12.763788 \n", + "\n", + " Max Drawdown Sharpe Ratio IR \n", + "proportional 0.192911 0.552607 0.280928 \n", + "proportional_signed 0.151103 0.706920 0.286863 \n", + "risk_parity 0.163967 0.620545 0.279954 \n", + "mean_variance 0.181193 0.697152 0.313529 \n", + "mean_variance_shrink_75 0.147544 0.655883 0.278992 \n", + "mean_variance_shrink_50 0.143732 0.669501 0.279528 \n", + "mean_variance_shrink_25 0.139041 0.689165 0.280779 " + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "lookback_windows = [90, 180, 270, 360]\n", + "cumreturns = returns.cumsum() + 1\n", + "total_return = pd.DataFrame(0.0, index=returns.index, columns=returns.columns)\n", + "for lookback_window in [90, 180, 270, 360]:\n", + " total_return += ((cumreturns / cumreturns.shift(lookback_window) - 1) * 360 / lookback_window).clip(upper=1.0, lower=-1.0)\n", + "\n", + "total_return /= len(lookback_windows)\n", + "momentum_score = total_return.rolling(14).mean()\n", + "momentum_score = normalize_xs(momentum_score, validity=validity, max_iter=50)\n", + "momentum_alpha_performance = alpha_performance(score=momentum_score, returns=returns, validity=validity)\n", + "momentum_alpha_performance" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2tMtKV0qhf57" + }, + "source": [ + "The reversal factor reflects mean reversion in short-term movements. It is defined as the reverse normalized z-score of daily returns, with mean and standard deviation values derived from a period ranging from 3 months to 1 year. The resulting scores are further smoothed using a 14-day rolling window to reduce overall turnover." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 269 + }, + "id": "eoYwko1qPeq_", + "outputId": "3cd3cb7b-9cad-4d3d-eed1-1c4cc32668a9" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Annual ReturnAnnual VolatilityTurnoverMax DrawdownSharpe RatioIR
proportional0.6942550.17270981.1388200.1510404.0197991.093097
proportional_signed0.3907430.10844166.5528270.1152683.6032670.692576
risk_parity0.5932450.16705681.2360440.1771533.5511720.963475
mean_variance0.3818710.23039687.8736240.3620861.6574520.683785
mean_variance_shrink_750.5558360.16148080.7039270.1836513.4421410.911048
mean_variance_shrink_500.5402860.16171180.7226930.1922573.3410580.890649
mean_variance_shrink_250.5163930.16335880.8811130.2079743.1611190.859599
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ], + "text/plain": [ + " Annual Return Annual Volatility Turnover \\\n", + "proportional 0.694255 0.172709 81.138820 \n", + "proportional_signed 0.390743 0.108441 66.552827 \n", + "risk_parity 0.593245 0.167056 81.236044 \n", + "mean_variance 0.381871 0.230396 87.873624 \n", + "mean_variance_shrink_75 0.555836 0.161480 80.703927 \n", + "mean_variance_shrink_50 0.540286 0.161711 80.722693 \n", + "mean_variance_shrink_25 0.516393 0.163358 80.881113 \n", + "\n", + " Max Drawdown Sharpe Ratio IR \n", + "proportional 0.151040 4.019799 1.093097 \n", + "proportional_signed 0.115268 3.603267 0.692576 \n", + "risk_parity 0.177153 3.551172 0.963475 \n", + "mean_variance 0.362086 1.657452 0.683785 \n", + "mean_variance_shrink_75 0.183651 3.442141 0.911048 \n", + "mean_variance_shrink_50 0.192257 3.341058 0.890649 \n", + "mean_variance_shrink_25 0.207974 3.161119 0.859599 " + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "lagged_returns = returns.shift(90)\n", + "reversal_score = -(returns - lagged_returns.rolling(270, min_periods=135).mean()) / lagged_returns.rolling(270, min_periods=135).std()\n", + "reversal_score = reversal_score.rolling(14).mean()\n", + "reversal_score = normalize_xs(reversal_score, validity=validity)\n", + "reversal_alpha_performance = alpha_performance(score=reversal_score, returns=returns, validity=validity)\n", + "reversal_alpha_performance" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "prybmOS92yuD" + }, + "source": [ + "The volatility factor suggests that assets with lower volatility tend to generate higher risk-adjusted returns than those with higher volatility. The signal is defined as the 6-month rolling volatility difference from the cross-sectional mean.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 269 + }, + "id": "fqmjwIx0cZLU", + "outputId": "9ecdb3e5-4a2c-4f2f-91f8-16e6c6ac4c93" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Annual ReturnAnnual VolatilityTurnoverMax DrawdownSharpe RatioIR
proportional0.3940870.15886912.4480760.1813952.4805710.628115
proportional_signed0.2405470.10585515.2162240.1121402.2724300.449246
risk_parity0.3254730.17837511.8402610.2601701.8246560.506093
mean_variance0.1714260.33974112.0691981.0091790.5045780.271053
mean_variance_shrink_750.3276780.16521011.9519600.2279971.9834010.518813
mean_variance_shrink_500.3144630.17250611.8578390.2697061.8229080.496452
mean_variance_shrink_250.2885060.19005011.6749920.3497721.5180490.453389
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ], + "text/plain": [ + " Annual Return Annual Volatility Turnover \\\n", + "proportional 0.394087 0.158869 12.448076 \n", + "proportional_signed 0.240547 0.105855 15.216224 \n", + "risk_parity 0.325473 0.178375 11.840261 \n", + "mean_variance 0.171426 0.339741 12.069198 \n", + "mean_variance_shrink_75 0.327678 0.165210 11.951960 \n", + "mean_variance_shrink_50 0.314463 0.172506 11.857839 \n", + "mean_variance_shrink_25 0.288506 0.190050 11.674992 \n", + "\n", + " Max Drawdown Sharpe Ratio IR \n", + "proportional 0.181395 2.480571 0.628115 \n", + "proportional_signed 0.112140 2.272430 0.449246 \n", + "risk_parity 0.260170 1.824656 0.506093 \n", + "mean_variance 1.009179 0.504578 0.271053 \n", + "mean_variance_shrink_75 0.227997 1.983401 0.518813 \n", + "mean_variance_shrink_50 0.269706 1.822908 0.496452 \n", + "mean_variance_shrink_25 0.349772 1.518049 0.453389 " + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "volatility_score = returns.rolling(180, min_periods=int(180 * 0.8)).std()\n", + "volatility_score = -volatility_score.sub(volatility_score.mean(axis=1), axis=0)\n", + "volatility_score = normalize_xs(volatility_score, validity=validity)\n", + "volatility_alpha_performance = alpha_performance(score=volatility_score, returns=returns, validity=validity)\n", + "volatility_alpha_performance" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "b5IgXAi3BSEf" + }, + "source": [ + "The liquidity factor suggests that less liquid assets may offer higher expected returns as compensation for their lower liquidity. The signal is defined as the inverse of volume (in USD) logarithmically transformed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 269 + }, + "id": "e-8RPhBLiOEm", + "outputId": "c6ab5940-f82a-49b0-d978-0ac368dd302b" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Annual ReturnAnnual VolatilityTurnoverMax DrawdownSharpe RatioIR
proportional0.2841960.13912632.5642120.1658122.0427310.515516
proportional_signed0.1935670.09326930.3659050.1318082.0753710.417228
risk_parity0.2463650.12796731.5090550.1660361.9252300.478382
mean_variance0.1593830.17126329.0685250.2111450.9306330.430619
mean_variance_shrink_750.2199600.12873431.2809400.1679321.7086440.441491
mean_variance_shrink_500.2128910.12778431.1091250.1669141.6660220.434256
mean_variance_shrink_250.2036780.12623630.8141770.1644641.6134670.426601
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ], + "text/plain": [ + " Annual Return Annual Volatility Turnover \\\n", + "proportional 0.284196 0.139126 32.564212 \n", + "proportional_signed 0.193567 0.093269 30.365905 \n", + "risk_parity 0.246365 0.127967 31.509055 \n", + "mean_variance 0.159383 0.171263 29.068525 \n", + "mean_variance_shrink_75 0.219960 0.128734 31.280940 \n", + "mean_variance_shrink_50 0.212891 0.127784 31.109125 \n", + "mean_variance_shrink_25 0.203678 0.126236 30.814177 \n", + "\n", + " Max Drawdown Sharpe Ratio IR \n", + "proportional 0.165812 2.042731 0.515516 \n", + "proportional_signed 0.131808 2.075371 0.417228 \n", + "risk_parity 0.166036 1.925230 0.478382 \n", + "mean_variance 0.211145 0.930633 0.430619 \n", + "mean_variance_shrink_75 0.167932 1.708644 0.441491 \n", + "mean_variance_shrink_50 0.166914 1.666022 0.434256 \n", + "mean_variance_shrink_25 0.164464 1.613467 0.426601 " + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "liquidity_score = volumes_usd.applymap(lambda x: 1 / np.log(x) if x > 1 else 0)\n", + "liquidity_score = normalize_xs(liquidity_score, validity=validity)\n", + "liquidity_alpha_performance = alpha_performance(score=liquidity_score, returns=returns, validity=validity)\n", + "liquidity_alpha_performance" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xM_xZE31E08a" + }, + "source": [ + "The following maximum drawdown and Sharpe ratio summary table shows that\n", + "\n", + "1. The proportional rule outperforms other approaches\n", + "\n", + "2. The MV (Mean-Variance) portfolio turns a profitable liquidity alpha signal into a negative Sharpe ratio\n", + "\n", + "3. The MV portfolio experiences the most significant maximum drawdown among all signals. Its maximum drawdown is 3-5 times higher than that of the proportional approaches." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "YXqjqnf9Ymhr", + "outputId": "d4075ef9-b818-40d2-b48c-775769a604e4" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Turnover
 1. Magic2. Momentum3. Reversal4. Volatility5. Liquidity
proportional20.83446413.31121581.13882012.44807632.564212
proportional_signed19.10671815.22100166.55282715.21622430.365905
risk_parity20.28091212.93758381.23604411.84026131.509055
mean_variance19.80464012.46443587.87362412.06919829.068525
mean_variance_shrink_7520.30159212.86584480.70392711.95196031.280940
mean_variance_shrink_5020.24323712.82608780.72269311.85783931.109125
mean_variance_shrink_2520.13360012.76378880.88111311.67499230.814177
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Maximum drawdown
 1. Magic2. Momentum3. Reversal4. Volatility5. Liquidity
proportional0.0282610.1929110.1510400.1813950.165812
proportional_signed0.0212140.1511030.1152680.1121400.131808
risk_parity0.0330490.1639670.1771530.2601700.166036
mean_variance0.0645300.1811930.3620861.0091790.211145
mean_variance_shrink_750.0294310.1475440.1836510.2279970.167932
mean_variance_shrink_500.0299190.1437320.1922570.2697060.166914
mean_variance_shrink_250.0313190.1390410.2079740.3497720.164464
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Sharpe Ratio
 1. Magic2. Momentum3. Reversal4. Volatility5. Liquidity
proportional21.7123650.5526074.0197992.4805712.042731
proportional_signed18.5508860.7069203.6032672.2724302.075371
risk_parity21.7001870.6205453.5511721.8246561.925230
mean_variance14.5182770.6971521.6574520.5045780.930633
mean_variance_shrink_7521.9402290.6558833.4421411.9834011.708644
mean_variance_shrink_5021.7686610.6695013.3410581.8229081.666022
mean_variance_shrink_2521.3232140.6891653.1611191.5180491.613467
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Information Ratio
 1. Magic2. Momentum3. Reversal4. Volatility5. Liquidity
proportional5.2823280.2809281.0930970.6281150.515516
proportional_signed2.8963110.2868630.6925760.4492460.417228
risk_parity5.0740680.2799540.9634750.5060930.478382
mean_variance4.2731830.3135290.6837850.2710530.430619
mean_variance_shrink_755.0212440.2789920.9110480.5188130.441491
mean_variance_shrink_504.9902820.2795280.8906490.4964520.434256
mean_variance_shrink_254.9341580.2807790.8595990.4533890.426601
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "alpha_performance_summary = pd.concat({\n", + " '1. Magic': magic_alpha_performance,\n", + " '2. Momentum': momentum_alpha_performance,\n", + " '3. Reversal': reversal_alpha_performance,\n", + " '4. Volatility': volatility_alpha_performance,\n", + " '5. Liquidity': liquidity_alpha_performance,\n", + "}, axis=1).swaplevel(i=-1, j=0, axis=1)\n", + "\n", + "display(alpha_performance_summary[\"Turnover\"].style.set_caption(\"Turnover\"))\n", + "display(alpha_performance_summary[\"Max Drawdown\"].style.set_caption(\"Maximum drawdown\"))\n", + "display(alpha_performance_summary[\"Sharpe Ratio\"].style.set_caption(\"Sharpe Ratio\"))\n", + "display(alpha_performance_summary[\"IR\"].style.set_caption(\"Information Ratio\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Ic0Dz3w7aX7F" + }, + "source": [ + "# Factor Risk model\n", + "\n", + "In the above mean variance approach, the alphas are divided by the asset variances only, but the asset correlations are neglected. To improve the covariance estimation on the model universe, multi-factor risk model is needed.\n", + "\n", + "In the below code, a risk model is created by PCA technical model from estimation universe, and then transformed to the model universe (a wider universe). A rolling covariance estimator is then generated to derive the covariances from the transformed risk model with volatility adjustment and shrinkage.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ti5RHd4ufqRQ" + }, + "outputs": [], + "source": [ + "risk_model = PCA(n_components=10)\n", + "rolling_risk_model = RollingFactorRiskModel(model=risk_model, window=180)\n", + "rolling_risk_model = rolling_risk_model.fit(\n", + " X=returns.iloc[returns.index.get_loc(estimation_validity.index[0]) - 180:],\n", + " validity=estimation_validity,\n", + ")\n", + "rolling_risk_model = rolling_risk_model.transform(returns, model_validity)\n", + "cov_estimator = RollingCovarianceEstimator(rolling_risk_model)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cDm64EM7h_Wy" + }, + "source": [ + "The function `alpha_sizing_covariance` is similar to the previous `alpha_sizing` function to generates sized positions among various appraoches, while the only difference is the mean variance portfolio is dervied by the inverse of covariance matrix (considering the pairwise correlation), rather than only the asset variance per sa." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "8XAJZv2Ufy8B" + }, + "outputs": [], + "source": [ + "def alpha_sizing_covariance(\n", + " score,\n", + " returns,\n", + " validity,\n", + " benchmark_variance,\n", + " cov_estimator,\n", + " est_window=180,\n", + " leverage=1.0,\n", + " ic=1.0,\n", + "):\n", + " def _mro(w, covs, p=1.0, benchmark_variance=None):\n", + " instmts = validity.loc[w.name][validity.loc[w.name]].index\n", + " cov = covs[w.name]\n", + " if p != 1.0:\n", + " cov = cov * (1 - p)\n", + " cov.values[np.diag_indices_from(cov)] += p * benchmark_variance[w.name]\n", + " return pd.Series(np.linalg.pinv(cov) @ w[instmts], index=instmts)\n", + " # Asset volatility\n", + " volatility = returns.rolling(est_window, min_periods=int(est_window * 0.8)).std()\n", + " variance = volatility ** 2\n", + " covs = cov_estimator.cov(volatility=volatility)\n", + " # Alpha = Score * Volatility * IC\n", + " alpha = (score * volatility * ic).reindex_like(validity)\n", + " # proportional = Alpha\n", + " proportional_alpha = dollar_neutral(alpha, leverage)\n", + " # proportional signed = 1 / (# Positive alpha) - 1 / (# Negative alpha)\n", + " proportional_signed_alpha = (\n", + " (alpha > 0.0).div((alpha > 0.0).sum(axis=1), axis=0).fillna(0.0)\n", + " - (alpha < 0.0).div((alpha < 0.0).sum(axis=1), axis=0).fillna(0.0)\n", + " )\n", + " proportional_signed_alpha = dollar_neutral(proportional_signed_alpha, leverage)\n", + " # Risk parity = alpha / volatility\n", + " risk_parity_alpha = alpha / volatility\n", + " risk_parity_alpha = dollar_neutral(risk_parity_alpha, leverage)\n", + " # Mean variance = alpha / variance\n", + " mean_variance_alpha = alpha.apply(lambda x: _mro(x, covs), axis=1)\n", + " mean_variance_alpha = dollar_neutral(mean_variance_alpha, leverage)\n", + " # Shrinked 75% mean variance\n", + " shrinked_75_mean_variance_alpha = alpha.apply(lambda x: _mro(x, covs, p=0.75, benchmark_variance=benchmark_variance), axis=1)\n", + " shrinked_75_mean_variance_alpha = dollar_neutral(shrinked_75_mean_variance_alpha, leverage)\n", + " # Shrinked 50% mean variance\n", + " shrinked_50_mean_variance_alpha = alpha.apply(lambda x: _mro(x, covs, p=0.50, benchmark_variance=benchmark_variance), axis=1)\n", + " shrinked_50_mean_variance_alpha = dollar_neutral(shrinked_50_mean_variance_alpha, leverage)\n", + " # Shrinked 25% mean variance\n", + " shrinked_25_mean_variance_alpha = alpha.apply(lambda x: _mro(x, covs, p=0.25, benchmark_variance=benchmark_variance), axis=1)\n", + " shrinked_25_mean_variance_alpha = dollar_neutral(shrinked_25_mean_variance_alpha, leverage)\n", + " # Return in a dict\n", + " return {\n", + " \"proportional\": proportional_alpha,\n", + " \"proportional_signed\": proportional_signed_alpha,\n", + " \"risk_parity\": risk_parity_alpha,\n", + " \"mean_variance\": mean_variance_alpha,\n", + " \"mean_variance_shrink_75\": shrinked_75_mean_variance_alpha,\n", + " \"mean_variance_shrink_50\": shrinked_50_mean_variance_alpha,\n", + " \"mean_variance_shrink_25\": shrinked_25_mean_variance_alpha,\n", + " }" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PdfCVUsnij-v" + }, + "source": [ + "As the mean variance sizing approach nows requires running inversing the covariance matrix and then multiplying with the alphas in rolling basis, the total runtime is much longer than the one in the previous function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "background_save": true + }, + "id": "ok0eO-KF9-IU", + "outputId": "c9ad78e9-aa26-43e9-8d71-716bf926ffcb" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Annual ReturnAnnual VolatilityTurnoverMax DrawdownSharpe RatioIR
proportional3.7867780.17557522.6022160.02822121.5678345.205063
proportional_signed2.0425950.11010819.1067180.02121418.5508862.896311
risk_parity3.7132190.17111522.0257880.03304921.7001875.074068
mean_variance3.1750650.18245929.9990110.04836717.4015314.177750
mean_variance_shrink_753.6906000.15140626.2440050.02647924.3755835.118855
mean_variance_shrink_503.5483450.14435728.0599500.02479724.5803674.918852
mean_variance_shrink_253.4197750.14117529.2913090.02314924.2235864.730015
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ], + "text/plain": [ + " Annual Return Annual Volatility Turnover \\\n", + "proportional 3.786778 0.175575 22.602216 \n", + "proportional_signed 2.042595 0.110108 19.106718 \n", + "risk_parity 3.713219 0.171115 22.025788 \n", + "mean_variance 3.175065 0.182459 29.999011 \n", + "mean_variance_shrink_75 3.690600 0.151406 26.244005 \n", + "mean_variance_shrink_50 3.548345 0.144357 28.059950 \n", + "mean_variance_shrink_25 3.419775 0.141175 29.291309 \n", + "\n", + " Max Drawdown Sharpe Ratio IR \n", + "proportional 0.028221 21.567834 5.205063 \n", + "proportional_signed 0.021214 18.550886 2.896311 \n", + "risk_parity 0.033049 21.700187 5.074068 \n", + "mean_variance 0.048367 17.401531 4.177750 \n", + "mean_variance_shrink_75 0.026479 24.375583 5.118855 \n", + "mean_variance_shrink_50 0.024797 24.580367 4.918852 \n", + "mean_variance_shrink_25 0.023149 24.223586 4.730015 " + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "magic_alpha_performance_cov = alpha_performance(\n", + " score=magic_score.fillna(0.0),\n", + " returns=returns,\n", + " validity=validity,\n", + " sizing_func=partial(alpha_sizing_covariance, cov_estimator=cov_estimator),\n", + ")\n", + "magic_alpha_performance_cov" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "background_save": true, + "base_uri": "https://localhost:8080/", + "height": 269 + }, + "id": "e7se_e2ItEOr", + "outputId": "044a4fd0-414e-41ec-f0d9-d5e35fee71de" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Annual ReturnAnnual VolatilityTurnoverMax DrawdownSharpe RatioIR
proportional0.0881000.16813915.4351340.1918340.5239700.271099
proportional_signed0.0974840.13790015.2210010.1511030.7069200.286863
risk_parity0.0943170.15199114.8109420.1639670.6205450.279954
mean_variance0.0548910.10908820.0770920.1512870.5031790.233891
mean_variance_shrink_750.0511870.12006617.2987910.1524890.4263260.226229
mean_variance_shrink_500.0453940.10202118.1781510.1463740.4449520.219601
mean_variance_shrink_250.0449530.09270018.8956490.1358700.4849240.219504
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ], + "text/plain": [ + " Annual Return Annual Volatility Turnover \\\n", + "proportional 0.088100 0.168139 15.435134 \n", + "proportional_signed 0.097484 0.137900 15.221001 \n", + "risk_parity 0.094317 0.151991 14.810942 \n", + "mean_variance 0.054891 0.109088 20.077092 \n", + "mean_variance_shrink_75 0.051187 0.120066 17.298791 \n", + "mean_variance_shrink_50 0.045394 0.102021 18.178151 \n", + "mean_variance_shrink_25 0.044953 0.092700 18.895649 \n", + "\n", + " Max Drawdown Sharpe Ratio IR \n", + "proportional 0.191834 0.523970 0.271099 \n", + "proportional_signed 0.151103 0.706920 0.286863 \n", + "risk_parity 0.163967 0.620545 0.279954 \n", + "mean_variance 0.151287 0.503179 0.233891 \n", + "mean_variance_shrink_75 0.152489 0.426326 0.226229 \n", + "mean_variance_shrink_50 0.146374 0.444952 0.219601 \n", + "mean_variance_shrink_25 0.135870 0.484924 0.219504 " + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "momentum_alpha_performance_cov = alpha_performance(\n", + " score=momentum_score.fillna(0.0),\n", + " returns=returns,\n", + " validity=validity,\n", + " sizing_func=partial(alpha_sizing_covariance, cov_estimator=cov_estimator),\n", + ")\n", + "momentum_alpha_performance_cov" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "background_save": true + }, + "id": "o8Z_Lnxp8TD4", + "outputId": "b3f1c981-5515-41a1-de98-af0b80fc304a" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Annual ReturnAnnual VolatilityTurnoverMax DrawdownSharpe RatioIR
proportional0.6911660.17053483.2523160.1511614.0529571.089576
proportional_signed0.3907430.10844166.5528270.1152683.6032670.692576
risk_parity0.5932450.16705683.3192010.1771533.5511720.963475
mean_variance0.3870160.17102488.3991350.1708712.2629320.702483
mean_variance_shrink_750.6520670.14046283.8353420.0729004.6423031.046994
mean_variance_shrink_500.6000200.13338484.6124200.0731194.4984310.979008
mean_variance_shrink_250.5437350.13012585.1248710.0874684.1785540.904013
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ], + "text/plain": [ + " Annual Return Annual Volatility Turnover \\\n", + "proportional 0.691166 0.170534 83.252316 \n", + "proportional_signed 0.390743 0.108441 66.552827 \n", + "risk_parity 0.593245 0.167056 83.319201 \n", + "mean_variance 0.387016 0.171024 88.399135 \n", + "mean_variance_shrink_75 0.652067 0.140462 83.835342 \n", + "mean_variance_shrink_50 0.600020 0.133384 84.612420 \n", + "mean_variance_shrink_25 0.543735 0.130125 85.124871 \n", + "\n", + " Max Drawdown Sharpe Ratio IR \n", + "proportional 0.151161 4.052957 1.089576 \n", + "proportional_signed 0.115268 3.603267 0.692576 \n", + "risk_parity 0.177153 3.551172 0.963475 \n", + "mean_variance 0.170871 2.262932 0.702483 \n", + "mean_variance_shrink_75 0.072900 4.642303 1.046994 \n", + "mean_variance_shrink_50 0.073119 4.498431 0.979008 \n", + "mean_variance_shrink_25 0.087468 4.178554 0.904013 " + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reversal_alpha_performance_cov = alpha_performance(\n", + " score=reversal_score.fillna(0.0),\n", + " returns=returns,\n", + " validity=validity,\n", + " sizing_func=partial(alpha_sizing_covariance, cov_estimator=cov_estimator),\n", + ")\n", + "reversal_alpha_performance_cov" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "background_save": true + }, + "id": "2AOzHN2YdShN", + "outputId": "22ca154c-90c0-4ca4-fd01-efbe840b218d" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Annual ReturnAnnual VolatilityTurnoverMax DrawdownSharpe RatioIR
proportional0.4122400.15856314.1547750.1746892.5998500.640041
proportional_signed0.2405470.10585515.2162240.1121402.2724300.449246
risk_parity0.3254730.17837513.7122880.2601691.8246560.506093
mean_variance0.1978470.27962517.6494720.7527950.7075430.314233
mean_variance_shrink_750.3862330.13312917.4899860.0896652.9012030.621489
mean_variance_shrink_500.3594090.13340519.0048050.0911532.6941170.577597
mean_variance_shrink_250.3265840.14443819.8015540.1474472.2610580.523377
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ], + "text/plain": [ + " Annual Return Annual Volatility Turnover \\\n", + "proportional 0.412240 0.158563 14.154775 \n", + "proportional_signed 0.240547 0.105855 15.216224 \n", + "risk_parity 0.325473 0.178375 13.712288 \n", + "mean_variance 0.197847 0.279625 17.649472 \n", + "mean_variance_shrink_75 0.386233 0.133129 17.489986 \n", + "mean_variance_shrink_50 0.359409 0.133405 19.004805 \n", + "mean_variance_shrink_25 0.326584 0.144438 19.801554 \n", + "\n", + " Max Drawdown Sharpe Ratio IR \n", + "proportional 0.174689 2.599850 0.640041 \n", + "proportional_signed 0.112140 2.272430 0.449246 \n", + "risk_parity 0.260169 1.824656 0.506093 \n", + "mean_variance 0.752795 0.707543 0.314233 \n", + "mean_variance_shrink_75 0.089665 2.901203 0.621489 \n", + "mean_variance_shrink_50 0.091153 2.694117 0.577597 \n", + "mean_variance_shrink_25 0.147447 2.261058 0.523377 " + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "volatility_alpha_performance_cov = alpha_performance(\n", + " score=volatility_score.fillna(0.0),\n", + " returns=returns,\n", + " validity=validity,\n", + " sizing_func=partial(alpha_sizing_covariance, cov_estimator=cov_estimator),\n", + ")\n", + "volatility_alpha_performance_cov" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "background_save": true + }, + "id": "K4TbQvAR4Rl6", + "outputId": "7053fdf6-3d24-4b67-e012-5cfba18e4e79" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Annual ReturnAnnual VolatilityTurnoverMax DrawdownSharpe RatioIR
proportional0.2685620.13621734.2042810.1663311.9715830.498828
proportional_signed0.1935670.09326930.3659050.1318082.0753710.417228
risk_parity0.2463650.12796733.3571330.1660361.9252300.478382
mean_variance0.2560570.13302536.0980730.2167211.9248770.547086
mean_variance_shrink_750.3345360.11725336.2601780.1285442.8531180.589842
mean_variance_shrink_500.3309420.11246237.1149780.1299362.9426890.590571
mean_variance_shrink_250.3154400.10907937.4426250.1453652.8918480.576548
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ], + "text/plain": [ + " Annual Return Annual Volatility Turnover \\\n", + "proportional 0.268562 0.136217 34.204281 \n", + "proportional_signed 0.193567 0.093269 30.365905 \n", + "risk_parity 0.246365 0.127967 33.357133 \n", + "mean_variance 0.256057 0.133025 36.098073 \n", + "mean_variance_shrink_75 0.334536 0.117253 36.260178 \n", + "mean_variance_shrink_50 0.330942 0.112462 37.114978 \n", + "mean_variance_shrink_25 0.315440 0.109079 37.442625 \n", + "\n", + " Max Drawdown Sharpe Ratio IR \n", + "proportional 0.166331 1.971583 0.498828 \n", + "proportional_signed 0.131808 2.075371 0.417228 \n", + "risk_parity 0.166036 1.925230 0.478382 \n", + "mean_variance 0.216721 1.924877 0.547086 \n", + "mean_variance_shrink_75 0.128544 2.853118 0.589842 \n", + "mean_variance_shrink_50 0.129936 2.942689 0.590571 \n", + "mean_variance_shrink_25 0.145365 2.891848 0.576548 " + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "liquidity_alpha_performance_cov = alpha_performance(\n", + " score=liquidity_score.fillna(0.0),\n", + " returns=returns,\n", + " validity=validity,\n", + " sizing_func=partial(alpha_sizing_covariance, cov_estimator=cov_estimator),\n", + ")\n", + "liquidity_alpha_performance_cov" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "h0-HjZHsi6tQ" + }, + "source": [ + "Finally, all the performances are combined in two summary tables - one on maximum drawdown and another on Sharpe ratio.\n", + "\n", + "It is interesting to see the following results\n", + "\n", + "1. The maximum drawdown of mean variance approaches have been reduced, compared to the original function\n", + "\n", + "2. The performance of mean variance appraoches have been greatly improved, e.g. 75% shrinkage one outperforms in reversal and liquidity factor.\n", + "\n", + "In summary, with a robust covariance estimation and covariance shrinkage, the mean-variance appraoch can turn into an excellent benchmark in alpha sizing." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "background_save": true + }, + "id": "-5GHl6Yeg05q", + "outputId": "c1e61fea-dd2c-4571-a7b8-532a95e2a6cf" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Turnover
 1. Magic2. Momentum3. Reversal4. Volatility5. Liquidity
proportional22.60221615.43513483.25231614.15477534.204281
proportional_signed19.10671815.22100166.55282715.21622430.365905
risk_parity22.02578814.81094283.31920113.71228833.357133
mean_variance29.99901120.07709288.39913517.64947236.098073
mean_variance_shrink_7526.24400517.29879183.83534217.48998636.260178
mean_variance_shrink_5028.05995018.17815184.61242019.00480537.114978
mean_variance_shrink_2529.29130918.89564985.12487119.80155437.442625
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Maximum drawdown
 1. Magic2. Momentum3. Reversal4. Volatility5. Liquidity
proportional0.0282210.1918340.1511610.1746890.166331
proportional_signed0.0212140.1511030.1152680.1121400.131808
risk_parity0.0330490.1639670.1771530.2601690.166036
mean_variance0.0483670.1512870.1708710.7527950.216721
mean_variance_shrink_750.0264790.1524890.0729000.0896650.128544
mean_variance_shrink_500.0247970.1463740.0731190.0911530.129936
mean_variance_shrink_250.0231490.1358700.0874680.1474470.145365
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Sharpe Ratio
 1. Magic2. Momentum3. Reversal4. Volatility5. Liquidity
proportional21.5678340.5239704.0529572.5998501.971583
proportional_signed18.5508860.7069203.6032672.2724302.075371
risk_parity21.7001870.6205453.5511721.8246561.925230
mean_variance17.4015310.5031792.2629320.7075431.924877
mean_variance_shrink_7524.3755830.4263264.6423032.9012032.853118
mean_variance_shrink_5024.5803670.4449524.4984312.6941172.942689
mean_variance_shrink_2524.2235860.4849244.1785542.2610582.891848
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Information Ratio
 1. Magic2. Momentum3. Reversal4. Volatility5. Liquidity
proportional5.2050630.2710991.0895760.6400410.498828
proportional_signed2.8963110.2868630.6925760.4492460.417228
risk_parity5.0740680.2799540.9634750.5060930.478382
mean_variance4.1777500.2338910.7024830.3142330.547086
mean_variance_shrink_755.1188550.2262291.0469940.6214890.589842
mean_variance_shrink_504.9188520.2196010.9790080.5775970.590571
mean_variance_shrink_254.7300150.2195040.9040130.5233770.576548
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "alpha_performance_summary_cov = pd.concat({\n", + " '1. Magic': magic_alpha_performance_cov,\n", + " '2. Momentum': momentum_alpha_performance_cov,\n", + " '3. Reversal': reversal_alpha_performance_cov,\n", + " '4. Volatility': volatility_alpha_performance_cov,\n", + " '5. Liquidity': liquidity_alpha_performance_cov,\n", + "}, axis=1).swaplevel(i=-1, j=0, axis=1)\n", + "\n", + "display(alpha_performance_summary_cov[\"Turnover\"].style.set_caption(\"Turnover\"))\n", + "display(alpha_performance_summary_cov[\"Max Drawdown\"].style.set_caption(\"Maximum drawdown\"))\n", + "display(alpha_performance_summary_cov[\"Sharpe Ratio\"].style.set_caption(\"Sharpe Ratio\"))\n", + "display(alpha_performance_summary_cov[\"IR\"].style.set_caption(\"Information Ratio\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Reference\n", + "\n", + "- [Gavin Chan, Navigating Alpha Sizing - Insights from a Factor Risk Model and Cryptocurrency Experiment](https://gavincyi.github.io/2023-12-15-navigating-alpha-sizing/)\n", + "\n", + "- [Giuseppe A. Paleologo, Advanced Portfolio Management: A Quant's Guide for Fundamental Investors](https://www.amazon.co.uk/Advanced-Portfolio-Management-Fundamental-Investors/dp/1119789796)\n", + "\n", + "- [Richard C. Grinold, Alpha is Volatility Times IC Times Score](https://www.pm-research.com/content/iijpormgmt/20/4/9)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "background_save": true + }, + "id": "VrsAuSRYbwIT" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +}