diff --git a/18_convolutional_neural_nets/06_cnn_for_trading_features_to_clustered_image_format.ipynb b/18_convolutional_neural_nets/06_cnn_for_trading_features_to_clustered_image_format.ipynb new file mode 100644 index 000000000..97a8a20b8 --- /dev/null +++ b/18_convolutional_neural_nets/06_cnn_for_trading_features_to_clustered_image_format.ipynb @@ -0,0 +1,711 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# CNN for Trading - Part 2: From Time-Series Features to Clustered Images" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T16:39:56.783513Z", + "start_time": "2021-02-23T16:39:56.774804Z" + } + }, + "source": [ + "To exploit the grid-like structure of time-series data, we can use CNN architectures for univariate and multivariate time series. In the latter case, we consider different time series as channels, similar to the different color signals.\n", + "\n", + "An alternative approach converts a time series of alpha factors into a two-dimensional format to leverage the ability of CNNs to detect local patterns. [Sezer and Ozbayoglu (2018)](https://www.researchgate.net/publication/324802031_Algorithmic_Financial_Trading_with_Deep_Convolutional_Neural_Networks_Time_Series_to_Image_Conversion_Approach) propose CNN-TA, which computes 15 technical indicators for different intervals and uses hierarchical clustering (see Chapter 13, Data-Driven Risk Factors and Asset Allocation with Unsupervised Learning) to locate indicators that behave similarly close to each other in a two-dimensional grid.\n", + "\n", + "The authors train a CNN similar to the CIFAR-10 example we used earlier to predict whether to buy, hold, or sell an asset on a given day. They compare the CNN performance to \"buy-and-hold\" and other models and find that it outperforms all alternatives using daily price series for Dow 30 stocks and the nine most-traded ETFs over the 2007-2017 time period.\n", + "\n", + "The section on *CNN for Trading* consists of three notebooks that experiment with this approach using daily US equity price data. They demonstrate \n", + "1. How to compute relevant financial features\n", + "2. How to convert a similar set of indicators into image format and cluster them by similarity\n", + "3. How to train a CNN to predict daily returns and evaluate a simple long-short strategy based on the resulting signals." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Selecting and Clustering Features" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The next steps that we will tackle in this notebook are \n", + "1. Select the 15 most relevant features from the 20 candidates to fill the 15×15 input grid.\n", + "2. Apply hierarchical clustering to identify features that behave similarly and order the columns and the rows of the grid accordingly." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Imports & Settings" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T18:59:23.064329Z", + "start_time": "2021-02-23T18:59:23.062565Z" + } + }, + "outputs": [], + "source": [ + "import warnings\n", + "warnings.filterwarnings('ignore')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T18:59:23.721271Z", + "start_time": "2021-02-23T18:59:23.065808Z" + } + }, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "from pathlib import Path\n", + "import pandas as pd\n", + "from tqdm import tqdm\n", + "\n", + "from scipy.spatial.distance import pdist\n", + "from scipy.cluster.hierarchy import dendrogram, linkage, cophenet\n", + "\n", + "from sklearn.preprocessing import StandardScaler\n", + "from sklearn.feature_selection import mutual_info_regression\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T18:59:23.723998Z", + "start_time": "2021-02-23T18:59:23.722349Z" + } + }, + "outputs": [], + "source": [ + "MONTH = 21\n", + "YEAR = 12 * MONTH" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T18:59:23.737796Z", + "start_time": "2021-02-23T18:59:23.724937Z" + } + }, + "outputs": [], + "source": [ + "START = '2001-01-01'\n", + "END = '2017-12-31'" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T18:59:23.750549Z", + "start_time": "2021-02-23T18:59:23.738814Z" + } + }, + "outputs": [], + "source": [ + "sns.set_style('white')\n", + "idx = pd.IndexSlice" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T18:59:23.758227Z", + "start_time": "2021-02-23T18:59:23.751920Z" + } + }, + "outputs": [], + "source": [ + "results_path = Path('results', 'cnn_for_trading')\n", + "if not results_path.exists():\n", + " results_path.mkdir(parents=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load Model Data" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T18:59:30.844081Z", + "start_time": "2021-02-23T18:59:23.759439Z" + } + }, + "outputs": [], + "source": [ + "with pd.HDFStore('data.h5') as store:\n", + " features = store.get('features')\n", + " targets = store.get('targets')" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T18:59:30.896504Z", + "start_time": "2021-02-23T18:59:30.845003Z" + }, + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "MultiIndex: 2378728 entries, ('A', Timestamp('2001-01-02 00:00:00')) to ('ZTS', Timestamp('2017-12-29 00:00:00'))\n", + "Columns: 300 entries, 06_RSI to 85_CMA\n", + "dtypes: float64(300)\n", + "memory usage: 5.3+ GB\n" + ] + } + ], + "source": [ + "features.info()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T18:59:30.938773Z", + "start_time": "2021-02-23T18:59:30.897581Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "MultiIndex: 2378728 entries, ('A', Timestamp('2001-01-02 00:00:00')) to ('ZTS', Timestamp('2017-12-29 00:00:00'))\n", + "Data columns (total 4 columns):\n", + " # Column Dtype \n", + "--- ------ ----- \n", + " 0 r01_fwd float64\n", + " 1 r01dec_fwd float64\n", + " 2 r05_fwd float64\n", + " 3 r05dec_fwd float64\n", + "dtypes: float64(4)\n", + "memory usage: 81.8+ MB\n" + ] + } + ], + "source": [ + "targets.info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Select Features using Mutual Information" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To this end, we estimate the mutual information for each indicator and the 15 intervals with respect to our target, the one-day forward returns. As discussed in Chapter 4, Financial Feature Engineering – How to Research Alpha Factors, scikit-learn provides the `mutual_info_regression()` function that makes this straightforward, albeit time-consuming and memory-intensive. \n", + "\n", + "To accelerate the process, we randomly sample 100,000 observations:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T19:20:52.366522Z", + "start_time": "2021-02-23T18:59:30.939773Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 2/2 [21:21<00:00, 640.71s/it]\n" + ] + } + ], + "source": [ + "mi = {}\n", + "for t in tqdm([1, 5]):\n", + " target = f'r{t:02}_fwd'\n", + " # sample a smaller number to speed up the computation\n", + " df = features.join(targets[target]).dropna().sample(n=100000)\n", + " X = df.drop(target, axis=1)\n", + " y = df[target]\n", + " mi[t] = pd.Series(mutual_info_regression(X=X, y=y),\n", + " index=X.columns).sort_values(ascending=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T19:20:52.376177Z", + "start_time": "2021-02-23T19:20:52.367645Z" + } + }, + "outputs": [], + "source": [ + "mutual_info = pd.DataFrame(mi)\n", + "mutual_info.to_hdf('data.h5', 'mutual_info')" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T19:20:52.391153Z", + "start_time": "2021-02-23T19:20:52.377122Z" + } + }, + "outputs": [], + "source": [ + "mutual_info = pd.read_hdf('data.h5', 'mutual_info')" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T19:20:52.405177Z", + "start_time": "2021-02-23T19:20:52.392078Z" + } + }, + "outputs": [], + "source": [ + "mi_by_indicator = (mutual_info.groupby(mutual_info.\n", + " index.to_series()\n", + " .str.split('_').str[-1])\n", + " .mean()\n", + " .rank(ascending=False)\n", + " .sort_values(by=1))" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T19:20:52.550095Z", + "start_time": "2021-02-23T19:20:52.406148Z" + }, + "scrolled": true + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mutual_info.boxplot()\n", + "sns.despine();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The below figure shows the mutual information, averaged across the 15 intervals for each indicator. NATR, PPO, and Bollinger Bands are most important from this metric's perspective:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T19:20:52.937686Z", + "start_time": "2021-02-23T19:20:52.551300Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "(mutual_info.groupby(mutual_info.index.to_series().str.split('_').str[-1])[1]\n", + " .mean()\n", + " .sort_values().plot.barh(title='Mutual Information with 1-Day Forward Returns'))\n", + "sns.despine()\n", + "plt.tight_layout()\n", + "plt.savefig(results_path / 'mutual_info_cnn_features', dpi=300)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T19:20:52.941987Z", + "start_time": "2021-02-23T19:20:52.938868Z" + } + }, + "outputs": [], + "source": [ + "best_features = mi_by_indicator.head(15).index" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T19:20:52.971896Z", + "start_time": "2021-02-23T19:20:52.943614Z" + } + }, + "outputs": [], + "source": [ + "size = len(best_features)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Hierarchical Feature Clustering" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T19:20:56.199036Z", + "start_time": "2021-02-23T19:20:52.973214Z" + } + }, + "outputs": [], + "source": [ + "features = pd.concat([features.filter(like=f'_{f}') for f in best_features], axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T19:21:00.180174Z", + "start_time": "2021-02-23T19:20:56.200032Z" + } + }, + "outputs": [], + "source": [ + "new_cols = {}\n", + "for feature in best_features:\n", + " fnames = sorted(features.filter(like=f'_{feature}').columns.tolist())\n", + " renamed = [f'{i:02}_{feature}' for i in range(1, len(fnames)+ 1)]\n", + " new_cols.update(dict(zip(fnames, renamed)))\n", + "features = features.rename(columns=new_cols).sort_index(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T19:21:00.194831Z", + "start_time": "2021-02-23T19:21:00.181519Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "MultiIndex: 2378728 entries, ('A', Timestamp('2001-01-02 00:00:00')) to ('ZTS', Timestamp('2017-12-29 00:00:00'))\n", + "Columns: 225 entries, 01_BBH to 15_WMA\n", + "dtypes: float64(225)\n", + "memory usage: 4.1+ GB\n" + ] + } + ], + "source": [ + "features.info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Hierarchical Clustering" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As discussed in the first section of this chapter, CNNs rely on the locality of relevant patterns that is typically found in images where nearby pixels are closely related and changes from one pixel to the next are often gradual.\n", + "\n", + "To organize our indicators in a similar fashion, we will follow Sezer and Ozbayoglu's approach of applying hierarchical clustering. The goal is to identify features that behave similarly and order the columns and the rows of the grid accordingly.\n", + "\n", + "We can build on SciPy's `pairwise_distance()`, `linkage()`, and `dendrogram()` functions that we introduced in *Chapter 13, Data-Driven Risk Factors and Asset Allocation with Unsupervised Learning* alongside other forms of clustering. \n", + "\n", + "We create a helper function that standardizes the input column-wise to avoid distorting distances among features due to differences in scale, and use the Ward criterion that merges clusters to minimize variance. The function\n", + "returns the order of the leaf nodes in the dendrogram that in turn displays the successive formation of larger clusters:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T19:21:00.214677Z", + "start_time": "2021-02-23T19:21:00.196205Z" + } + }, + "outputs": [], + "source": [ + "def cluster_features(data, labels, ax, title):\n", + " data = StandardScaler().fit_transform(data)\n", + " pairwise_distance = pdist(data)\n", + " Z = linkage(data, 'ward')\n", + " c, coph_dists = cophenet(Z, pairwise_distance)\n", + " dend = dendrogram(Z,\n", + " labels=labels,\n", + " orientation='top',\n", + " leaf_rotation=0.,\n", + " leaf_font_size=8.,\n", + " ax=ax)\n", + " ax.set_title(title)\n", + " return dend['ivl']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To obtain the optimized order of technical indicators in the columns and the different intervals in the rows, we use NumPy's `.reshape()` method to ensure that the dimension we would like to cluster appears in the columns of the two-dimensional array we pass to `cluster_features()`." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T19:21:57.861182Z", + "start_time": "2021-02-23T19:21:00.215792Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABDAAAAEYCAYAAACqUwbqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAABPRklEQVR4nO3deXyNd/7//2cWsQSx1JEUNTVjK0mFUCmSChEEibWlUvHRqlSraCldVFWnnampYrSVoahaOpQEMbVELVMadEqitVQ1GionJVFiS8T1+yO/XF9kk/UcPO63m5vknet9vV/XOdc51/u8zvt6vx0MwzAEAAAAAABgxxxtHQAAAAAAAEBhSGAAAAAAAAC7RwIDAAAAAADYPRIYAAAAAADA7pHAAAAAAAAAdo8EBgAAAAAAsHskMIByFBcXJz8/P1uHgSIKCAjQrl27JEmffPKJXnvtNRtHBAAAbtS0aVOdOHFCkjRlyhTNnTvXxhEBKAskMIBiWLdunfr16ydvb2917NhRTz/9tPbt22frsIqkadOmatWqlby9veXt7S0fH59S2WdO58GelGbiaNSoUXrnnXdKtI+TJ0+qadOmunbtWqnEBAAoewEBAfLy8pK3t7c6dOigSZMm6eLFi2XWXs61Iuc67e3trT59+pTKPu3x+rN69WoNHjy4VPY1bdo0jR49ukT7KIsvnVavXq3mzZvL29tbrVu3VkhIiL7++utSbaMsTJo0STNnzrR1GIAkydnWAQB3moULFyoyMlJvvfWWOnbsqAoVKmjnzp2KjY0tlSRAeYqOjlbDhg1tHYYpKytLTk5Otg7D7l27dk3Ozrx9A0B5++STT/Too4/q999/14gRIxQZGalx48aVaZt79+61m/d8wzBkGIYcHfkOtDD5XatbtWql5cuX6/r161q6dKnGjh2r7du3q0aNGiXet7260+KFfePdByiCCxcuaPbs2ZoyZYq6deumKlWqqEKFCgoICNArr7wiScrIyNA777yjjh07qmPHjnrnnXeUkZGR5/5uHbFwY4Y7J/P/r3/9S76+vurYsaO2bNmi7du3KygoSO3atdMnn3xi1p0zZ45efPFFTZw4Ud7e3goODlZCQkKRj9FqteqFF15Q+/btFRAQoM8++8z8W3x8vB5//HH5+PioY8eOmjZtmnlsTz75pCQpJCRE3t7e2rBhQ57fptx4zJMmTdKbb76pZ555Rq1atVJcXFyh7ffr10+tW7fWo48+qnfffbfIxydJYWFh+vDDD/XEE0/I29tb//d//6fU1FTz71FRUercubMeeeQRffzxxzfVnTNnjl5++WXz93379umJJ56Qj4+P/P39tXr1aknStm3bFBoaqtatW8vf319z5swx6wwdOlSS1LZtW3l7e+v777/X9evX9dFHH6lz587y9fXVxIkTdeHCBUn/7xuzlStX6rHHHtOwYcN09epVvfzyy3rkkUfk4+Oj/v3768yZM8V6PAAARVOnTh117NhRhw4dMstiY2MVHBwsHx8fhYWF6eeff5Ykffnllxo1apS5XWBgoF588UXzd39//5v2czt+/vlnDR8+XO3atVNQUJA2bNhg/q2o159br2u3jtIICwvTzJkz9cQTT+jhhx9WUlJSge1v375dPXv2lLe3tzp16qQFCxYU6dhyBAQEaMGCBerdu7fatGmjsWPH6urVq+bf58+fb/a1Vq1adVPdW0cMbNmyRSEhIWrdurW6du2qHTt2SMp+bnr06CFvb2916dJFK1askCRdunRJzzzzjFJSUszRL1artcA+Xk6/LTIyUh06dNDkyZMLPD5HR0f1799fV65cUVJSUr6x5LfvP/74Q88++6zat2+vtm3b6tlnn1VycrJZ58bnzdvbW6NGjVJaWppeeukltW7dWv3799fJkyfN7fN7Tr/44gutW7dOCxYsMPcjFdxfnDNnjsaMGaOXX35ZrVu31po1a0qtDwfIAHDbtm/fbjRv3tzIzMzMd5sPP/zQGDhwoHHmzBnj7NmzxuOPP27MnDnTMAzD+Pbbb41OnTqZ2zZp0sRITEw0f3/llVeMDz74wNy2efPmxpw5c4yMjAzjiy++MB555BFj/PjxxoULF4yjR48aLVu2NH799VfDMAxj9uzZRsuWLY1t27YZ165dM2bMmGEMHDgw3zhvbdswDCMrK8vo27evMWfOHOPq1avGr7/+agQEBBg7duwwDMMwEhISjO+//97IzMw0kpKSjO7duxsLFy7Md59ffvml8cQTT+Tb7iuvvGK0bt3a2Ldvn5GVlWVcunSpwPYHDRpkrFmzxjAMw0hPTze+//57c7+9evUy1q5dm+ex3vq4Dx061OjSpYtx/Phx4/Lly8bQoUON999/3zAMw/jpp5+MVq1aGXv27DGuXr1q/PWvfzWaN29ufPPNN+bj/NJLLxmGYRinTp0yWrVqZaxbt87IyMgwUlNTjR9//NFs8/Dhw0ZWVpZx6NAhw9fX19i8ebNhGIaRlJRkNGnS5KbzaOXKlUbXrl2NX3/91UhPTzdGjx5tvPzyyzdtP2HCBOPixYvG5cuXjeXLlxvPPvuscenSJePatWtGQkKCceHChTyPHwBQcp07dzavBadPnzZ69eplvP3224ZhGMbx48eNhx9+2Pjvf/9rZGRkGJGRkUbXrl3Na1mbNm2MrKwsw2q1Go899pjRsWNHwzAM49dffzV8fHyMrKysXO3lda0wDMO4ePGi4efnZ6xatcrIzMw0Dh48aLRr1844evSoYRhFv/7ceF3La5uhQ4ca/v7+xtGjR43MzEzj/PnzBbbfoUMHY+/evYZhGMa5c+eMgwcPmvtu06aN+bdb3dpn6Ny5s9G/f38jOTnZSEtLM7p3724sW7bMMIzs/pivr69x5MgR4+LFi8b48eNz9S9y+lMHDhwwWrdubfz3v/81srKyjOTkZOPYsWOGYRjG119/bZw4ccK4fv26ERcXZ3h5eZnx3tp3MIzC+3jNmzc3/v73vxtXr141Ll++XOAxZmZmGosWLTJatWplnD9/vtBYbt13amqq8dVXXxmXLl0yLly4YLzwwgtGRESE2dbQoUONrl27GidOnDDOnz9v9OjRw+jWrZvxzTffGJmZmcaECROMSZMm3dY5dePjaRiF9xdnz55tPPTQQ8bmzZuNrKws4/LlywX24YCiYAQGUATnzp1TzZo1CxwGt27dOo0ePVq1a9dWrVq1NHr0aK1du7ZY7Tk7OysiIkIVKlRQz549lZaWpqeeekpVq1ZV48aN1bhxYx05csTcvk2bNvL395eTk5NCQkJ0+PDhAvfft29f+fj4yMfHR9OnT1dCQoJSU1P1/PPPy8XFRQ0aNNCgQYPMLHzLli3VqlUrOTs7q379+nr88ce1d+/eYh1bji5duqhNmzZydHTU0aNHC2zf2dlZv/76q1JTU+Xq6qpWrVqZ+1m3bp169+592+3269dPDz74oCpVqqTu3bub33599dVXeuyxx9S2bVu5uLjoxRdfzHeo7Lp16/Too4+qV69eqlChgmrWrKnmzZtLkh555BE1bdpUjo6OatasmYKDg7Vnz55841m3bp3Cw8PVoEEDubq6avz48dqwYcNN9ym/8MILqlKliipVqiRnZ2edO3dOJ06ckJOTk1q2bKmqVave9vEDAIpu9OjR8vb2lr+/v2rVqqUxY8ZIkjZs2CB/f3916NBBFSpU0IgRI3TlyhV9//335vv6oUOHtHfvXnXs2FF169bVzz//rD179pjXwPy0b9/evFYvWLBA27ZtU7169dS/f385OzurRYsWCgoK0saNGyUV/fpzO/r27avGjRvL2dlZO3fuLLB9Z2dnHTt2TOnp6XJzc1OLFi3M/ezbt69It9uGhYWpbt26qlGjhjp37mxeq//zn/+oX79+atKkiapUqaLnn38+332sWrVK/fv3V4cOHeTo6Ki6devqz3/+syTpscce0wMPPCAHBwe1a9dOHTp0KHBOs8L6eI6OjhozZoxcXFxUqVKlPPdx4MAB+fj4qEOHDoqJidHcuXNVrVq1QmO5dd81a9ZUUFCQKleurKpVqyoiIiJXn6xfv3564IEHVK1aNfn5+alBgwZ69NFH5ezsrO7du+vHH3+UpELPqVsV1l+Usm+V6dq1qxwdHc1+S359OKAouBkJKIIaNWooLS2twHv5UlJSdP/995u/33///UpJSSl2ezlzQuRcCGvXrm3+vWLFijdNIHbfffeZP1eqVElXr14tMNY1a9bcNAfGhg0blJKSclPnIisry/z9l19+0XvvvaeDBw/q8uXLysrKuqljUhweHh7mz6dOnSqw/XfeeUezZ89Wjx49VL9+fT3//PPq3LlzsdqtU6eO+XPlypV16dIlSdnPn7u7u/m3KlWq5Htf6unTp/XAAw/k+bcDBw5oxowZ+umnn5SZmamMjAx1794933hSUlJUr1498/d69erp2rVrOnv2rFl2Y1whISFKTk7W+PHjdf78efXp00fjxo1ThQoVCj5wAECxzZ07V48++qj27Nmjl156SWlpaapevXqua7+jo6M8PDxktVolZd+ysWfPHp04cUJt27ZVtWrVtHfvXu3fv1/t2rUrsM1vv/32puv4v/71L8XHx+e6VuZM8FnU68/tuPVaXVD7s2fP1scff6x//OMfatq0qV566SV5e3sXq91br9U5/amUlBS1bNnS/NuN189bnT59Wv7+/nn+bfv27Zo7d64SExN1/fp1XblyRU2aNMl3X4X18WrWrKmKFSsWeEwPP/ywli9fXuRYbt335cuX9e6772rnzp36448/JEkXL168aT6xG/uFFStWzNVPzOn7FPac3qqw/pp0c59FKt0+HO5tJDCAIvD29lbFihW1ZcuWfDsDFotFv/32mxo3biwp+8JpsVjy3LZy5cq6fPmy+fvvv/+uunXrln7gt8nDw0P169fXpk2b8vz71KlT9dBDD+kf//iHqlatqkWLFuWbnZeyj+/KlSvm77///nuJ2v/Tn/6kDz74QNevX9emTZs0ZswYxcXFqUqVKrdxdLfHYrGY9y1L2R2Ec+fO5RtvfHx8nn976aWXNHToUM2fP18VK1bUO++8o7S0NEmSg4NDnu2eOnXK/P23336Ts7Ozateubd7TemO9ChUq6Pnnn9fzzz+vkydPauTIkXrwwQc1cODAIh8zAKBo2rVrp379+ulvf/ubPvroI1ksFh09etT8u2EYOn36tHlNb9eunbZu3apTp05p1KhRql69utatW6fvv//enEPqdnl4eKht27ZauHBhnn8v6vXn1mt1XvMp3VivsPa9vLz08ccfKzMz86ZJKkuTxWLR6dOnzd9/++23fLf18PDQr7/+mqs8IyNDY8aM0d/+9jd16dJFFSpU0HPPPSfDMCTlf60uqI+XV53bUVgsee37008/1S+//KJ///vfqlOnjg4dOqTQ0NCb6tyuwp7TW9surL+WV53y6MPh3sAtJEARVKtWTWPGjNG0adO0ZcsWXb58WZmZmdq+fbv+/ve/S5KCg4P18ccfKzU1VampqZo7d26+tzY0a9ZM69evV1ZWlnbs2FHi2zFKysvLS1WrVlVkZKSuXLmirKwsHT161PyQfvHiRbm6usrV1VU///xzrm8Q7rvvPiUlJZm/N2vWTD/99JMOHTqkq1ev3jSRWHHaj46OVmpqqhwdHVW9enVJKvVVS4KCgrRt2zbt27dPGRkZmj17tq5fv57ntr1799auXbvMWz3S0tLM4a0XL16Um5ubKlasqPj4eK1fv96sV6tWLTk6Ot70WPXq1UuLFy9WUlKSLl68qJkzZ6pHjx75jp759ttvdeTIEWVlZalq1apydnZmBRcAKEfDhg3Trl27dOjQIfXo0UPbt2/X7t27lZmZqU8//VQuLi7myIO2bdsqLi5OV65ckbu7u3x8fLRz506dO3dODz30UJHafeyxx5SYmKioqChlZmYqMzNT8fHxZvK9qNef5s2ba+/evfrtt9904cIF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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, axes = plt.subplots(figsize=(15, 4), ncols=2)\n", + "\n", + "labels = sorted(best_features)\n", + "title = 'Column Features: Indicators'\n", + "col_order = cluster_features(features.dropna().values.reshape(-1, 15).T,\n", + " labels,\n", + " axes[0],\n", + " title)\n", + "\n", + "labels = list(range(1, 16))\n", + "title = 'Row Features: Indicator Parameters'\n", + "row_order = cluster_features(\n", + " features.dropna().values.reshape(-1, 15, 15).transpose((0, 2, 1)).reshape(-1, 15).T,\n", + " labels, axes[1], title)\n", + "axes[0].set_xlabel('Indicators')\n", + "axes[1].set_xlabel('Parameters')\n", + "sns.despine()\n", + "fig.tight_layout()\n", + "fig.savefig(results_path / 'cnn_clustering', dpi=300)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We reorder the features accordingly and store the result as inputs for the CNN that we will create in the next step.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T19:21:57.869035Z", + "start_time": "2021-02-23T19:21:57.863039Z" + } + }, + "outputs": [], + "source": [ + "feature_order = [f'{i:02}_{j}' for i in row_order for j in col_order]" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T19:21:58.596134Z", + "start_time": "2021-02-23T19:21:57.871605Z" + } + }, + "outputs": [], + "source": [ + "features = features.loc[:, feature_order]" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T19:22:56.029721Z", + "start_time": "2021-02-23T19:21:58.597104Z" + } + }, + "outputs": [], + "source": [ + "features = features.apply(pd.to_numeric, downcast='float')" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T19:22:56.069245Z", + "start_time": "2021-02-23T19:22:56.030777Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "MultiIndex: 2378728 entries, ('A', Timestamp('2001-01-02 00:00:00')) to ('ZTS', Timestamp('2017-12-29 00:00:00'))\n", + "Columns: 225 entries, 01_CMO to 11_WMA\n", + "dtypes: float32(225)\n", + "memory usage: 2.0+ GB\n" + ] + } + ], + "source": [ + "features.info()" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-23T19:22:58.021376Z", + "start_time": "2021-02-23T19:22:56.070350Z" + } + }, + "outputs": [], + "source": [ + "features.to_hdf('data.h5', 'img_data')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": true + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/20_autoencoders_for_conditional_risk_factors/05_conditional_autoencoder_for_asset_pricing_data.ipynb b/20_autoencoders_for_conditional_risk_factors/05_conditional_autoencoder_for_asset_pricing_data.ipynb new file mode 100644 index 000000000..179ad9c6d --- /dev/null +++ b/20_autoencoders_for_conditional_risk_factors/05_conditional_autoencoder_for_asset_pricing_data.ipynb @@ -0,0 +1,1893 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Conditional Autoencoder for Asset Pricing - Part 1: The Data" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:07:31.951910Z", + "start_time": "2021-02-24T15:07:31.351143Z" + } + }, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "from statsmodels.regression.rolling import RollingOLS\n", + "import statsmodels.api as sm\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:07:31.954639Z", + "start_time": "2021-02-24T15:07:31.953021Z" + } + }, + "outputs": [], + "source": [ + "idx = pd.IndexSlice\n", + "sns.set_style('whitegrid')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:07:31.963074Z", + "start_time": "2021-02-24T15:07:31.955671Z" + } + }, + "outputs": [], + "source": [ + "results_path = Path('results', 'asset_pricing')\n", + "if not results_path.exists():\n", + " results_path.mkdir(parents=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load Data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Prices" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:07:33.621475Z", + "start_time": "2021-02-24T15:07:31.963898Z" + } + }, + "outputs": [], + "source": [ + "prices = pd.read_hdf(results_path / 'data.h5', 'stocks/prices/adjusted')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:07:33.932624Z", + "start_time": "2021-02-24T15:07:33.622337Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "MultiIndex: 17661451 entries, ('A', Timestamp('1999-11-18 00:00:00')) to ('ZYXI', Timestamp('2019-12-31 00:00:00'))\n", + "Data columns (total 5 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 close 17661451 non-null float64\n", + " 1 high 17661451 non-null float64\n", + " 2 low 17661451 non-null float64\n", + " 3 open 17661451 non-null float64\n", + " 4 volume 17661451 non-null float64\n", + "dtypes: float64(5)\n", + "memory usage: 742.0+ MB\n" + ] + } + ], + "source": [ + "prices.info(show_counts=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Metadata" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:07:33.997645Z", + "start_time": "2021-02-24T15:07:33.933520Z" + } + }, + "outputs": [], + "source": [ + "metadata = pd.read_hdf(results_path / 'data.h5', 'stocks/info').rename(columns=str.lower)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:07:34.006833Z", + "start_time": "2021-02-24T15:07:33.998994Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Index: 6262 entries, A to ZYXI\n", + "Columns: 109 entries, zip to impliedsharesoutstanding\n", + "dtypes: bool(2), float64(75), int64(3), object(29)\n", + "memory usage: 5.2+ MB\n" + ] + } + ], + "source": [ + "metadata.info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Select tickers with metadata" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:07:34.019711Z", + "start_time": "2021-02-24T15:07:34.007871Z" + } + }, + "outputs": [], + "source": [ + "sectors = (metadata.sector.value_counts() > 50).index" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:07:34.028440Z", + "start_time": "2021-02-24T15:07:34.020656Z" + } + }, + "outputs": [], + "source": [ + "tickers_with_errors = ['FTAI', 'AIRT', 'CYBR', 'KTB']" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:07:34.042811Z", + "start_time": "2021-02-24T15:07:34.029471Z" + } + }, + "outputs": [], + "source": [ + "tickers_with_metadata = metadata[metadata.sector.isin(sectors) & \n", + " metadata.marketcap.notnull() &\n", + " metadata.sharesoutstanding.notnull() & \n", + " (metadata.sharesoutstanding > 0)].index.drop(tickers_with_errors)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:07:34.056060Z", + "start_time": "2021-02-24T15:07:34.044017Z" + } + }, + "outputs": [], + "source": [ + "metadata = metadata.loc[tickers_with_metadata, ['sector', 'sharesoutstanding', 'marketcap']]\n", + "metadata.index.name = 'ticker'" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:30:43.035689Z", + "start_time": "2021-02-24T15:07:34.057118Z" + } + }, + "outputs": [], + "source": [ + "prices = prices.loc[idx[tickers_with_metadata, :], :]" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:30:43.353647Z", + "start_time": "2021-02-24T15:30:43.036510Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":1: FutureWarning: null_counts is deprecated. Use show_counts instead\n", + " prices.info(null_counts=True)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "MultiIndex: 17312229 entries, ('A', Timestamp('1999-11-18 00:00:00')) to ('ZYXI', Timestamp('2019-12-31 00:00:00'))\n", + "Data columns (total 5 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 close 17312229 non-null float64\n", + " 1 high 17312229 non-null float64\n", + " 2 low 17312229 non-null float64\n", + " 3 open 17312229 non-null float64\n", + " 4 volume 17312229 non-null float64\n", + "dtypes: float64(5)\n", + "memory usage: 727.4+ MB\n" + ] + } + ], + "source": [ + "prices.info(null_counts=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:30:43.361769Z", + "start_time": "2021-02-24T15:30:43.354651Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Index: 5749 entries, A to ZYXI\n", + "Data columns (total 3 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 sector 5749 non-null object \n", + " 1 sharesoutstanding 5749 non-null float64\n", + " 2 marketcap 5749 non-null float64\n", + "dtypes: float64(2), object(1)\n", + "memory usage: 179.7+ KB\n" + ] + } + ], + "source": [ + "metadata.info()" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:30:49.145765Z", + "start_time": "2021-02-24T15:30:43.362775Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "DatetimeIndex: 7559 entries, 1990-01-02 to 2019-12-31\n", + "Columns: 4420 entries, A to ZYXI\n", + "dtypes: float64(4420)\n", + "memory usage: 255.0 MB\n" + ] + } + ], + "source": [ + "close = prices.close.unstack('ticker').sort_index()\n", + "close.info()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:30:55.056033Z", + "start_time": "2021-02-24T15:30:49.146672Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "DatetimeIndex: 7559 entries, 1990-01-02 to 2019-12-31\n", + "Columns: 4420 entries, A to ZYXI\n", + "dtypes: float64(4420)\n", + "memory usage: 255.0 MB\n" + ] + } + ], + "source": [ + "volume = prices.volume.unstack('ticker').sort_index()\n", + "volume.info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create weekly returns" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:01.377951Z", + "start_time": "2021-02-24T15:30:55.057292Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "DatetimeIndex: 1565 entries, 1990-01-12 to 2020-01-03\n", + "Freq: W-FRI\n", + "Columns: 4420 entries, A to ZYXI\n", + "dtypes: float64(4420)\n", + "memory usage: 52.8 MB\n" + ] + } + ], + "source": [ + "returns = (prices.close\n", + " .unstack('ticker')\n", + " .resample('W-FRI').last()\n", + " .sort_index().pct_change().iloc[1:])\n", + "returns.info()" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:01.380485Z", + "start_time": "2021-02-24T15:31:01.378804Z" + } + }, + "outputs": [], + "source": [ + "dates = returns.index" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:01.581772Z", + "start_time": "2021-02-24T15:31:01.381581Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/stefan/.pyenv/versions/miniconda3-latest/envs/ml4t-dl/lib/python3.8/site-packages/seaborn/distributions.py:2557: FutureWarning: `distplot` is a deprecated function and will be removed in a future version. Please adapt your code to use either `displot` (a figure-level function with similar flexibility) or `histplot` (an axes-level function for histograms).\n", + " warnings.warn(msg, FutureWarning)\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "sns.distplot(returns.count(1), kde=False);" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:01.874242Z", + "start_time": "2021-02-24T15:31:01.582715Z" + } + }, + "outputs": [], + "source": [ + "with pd.HDFStore(results_path / 'autoencoder.h5') as store:\n", + " store.put('close', close)\n", + " store.put('volume', volume)\n", + " store.put('returns', returns)\n", + " store.put('metadata', metadata)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Factor Engineering" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:01.876666Z", + "start_time": "2021-02-24T15:31:01.875143Z" + } + }, + "outputs": [], + "source": [ + "MONTH = 21" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Price Trend" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Short-Term Reversal" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1-month cumulative return" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:01.885711Z", + "start_time": "2021-02-24T15:31:01.878828Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "DatetimeIndex(['1990-01-12', '1990-01-19', '1990-01-26', '1990-02-02',\n", + " '1990-02-09'],\n", + " dtype='datetime64[ns]', name='date', freq='W-FRI')" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dates[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:02.511175Z", + "start_time": "2021-02-24T15:31:01.887161Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "MultiIndex: 3580621 entries, (Timestamp('1990-02-02 00:00:00', freq='W-FRI'), 'AA') to (Timestamp('2020-01-03 00:00:00', freq='W-FRI'), 'ZYXI')\n", + "Data columns (total 1 columns):\n", + " # Column Dtype \n", + "--- ------ ----- \n", + " 0 mom1m float64\n", + "dtypes: float64(1)\n", + "memory usage: 41.2+ MB\n" + ] + } + ], + "source": [ + "mom1m = close.pct_change(periods=MONTH).resample('W-FRI').last().stack().to_frame('mom1m')\n", + "mom1m.info()" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:02.540510Z", + "start_time": "2021-02-24T15:31:02.512142Z" + } + }, + "outputs": [], + "source": [ + "mom1m.squeeze().to_hdf(results_path / 'autoencoder.h5', 'factor/mom1m')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Stock Momentum" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "11-month cumulative returns ending 1-month before month end" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:03.146297Z", + "start_time": "2021-02-24T15:31:02.541311Z" + } + }, + "outputs": [], + "source": [ + "mom12m = (close\n", + " .pct_change(periods=11 * MONTH)\n", + " .shift(MONTH)\n", + " .resample('W-FRI')\n", + " .last()\n", + " .stack()\n", + " .to_frame('mom12m'))" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:03.196560Z", + "start_time": "2021-02-24T15:31:03.147146Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "MultiIndex: 3375489 entries, (Timestamp('1991-01-04 00:00:00', freq='W-FRI'), 'AA') to (Timestamp('2020-01-03 00:00:00', freq='W-FRI'), 'ZYXI')\n", + "Data columns (total 1 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 mom12m 3375489 non-null float64\n", + "dtypes: float64(1)\n", + "memory usage: 38.8+ MB\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":1: FutureWarning: null_counts is deprecated. Use show_counts instead\n", + " mom12m.info(null_counts=True)\n" + ] + } + ], + "source": [ + "mom12m.info(null_counts=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:03.225828Z", + "start_time": "2021-02-24T15:31:03.197502Z" + } + }, + "outputs": [], + "source": [ + "mom12m.to_hdf(results_path / 'autoencoder.h5', 'factor/mom12m')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Momentum Change" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Cumulative return from months t-6 to t-1 minus months t-12 to t-7." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:04.175407Z", + "start_time": "2021-02-24T15:31:03.226841Z" + } + }, + "outputs": [], + "source": [ + "chmom = (close\n", + " .pct_change(periods=6 * MONTH)\n", + " .sub(close.pct_change(periods=6 * MONTH).shift(6 * MONTH))\n", + " .resample('W-FRI')\n", + " .last()\n", + " .stack()\n", + " .to_frame('chmom'))" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:04.224124Z", + "start_time": "2021-02-24T15:31:04.176245Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "MultiIndex: 3375489 entries, (Timestamp('1991-01-04 00:00:00', freq='W-FRI'), 'AA') to (Timestamp('2020-01-03 00:00:00', freq='W-FRI'), 'ZYXI')\n", + "Data columns (total 1 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 chmom 3375489 non-null float64\n", + "dtypes: float64(1)\n", + "memory usage: 38.8+ MB\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":1: FutureWarning: null_counts is deprecated. Use show_counts instead\n", + " chmom.info(null_counts=True)\n" + ] + } + ], + "source": [ + "chmom.info(null_counts=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:04.253178Z", + "start_time": "2021-02-24T15:31:04.225020Z" + } + }, + "outputs": [], + "source": [ + "chmom.to_hdf(results_path / 'autoencoder.h5', 'factor/chmom')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Industry Momentum" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Equal-weighted avg. industry 12-month returns" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:05.205923Z", + "start_time": "2021-02-24T15:31:04.254036Z" + } + }, + "outputs": [], + "source": [ + "indmom = (close.pct_change(12*MONTH)\n", + " .resample('W-FRI')\n", + " .last()\n", + " .stack()\n", + " .to_frame('close')\n", + " .join(metadata[['sector']]).groupby(['date', 'sector'])\n", + " .close.mean()\n", + " .to_frame('indmom')\n", + " .reset_index())" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:05.214051Z", + "start_time": "2021-02-24T15:31:05.206759Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "RangeIndex: 18495 entries, 0 to 18494\n", + "Data columns (total 3 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 date 18495 non-null datetime64[ns]\n", + " 1 sector 18495 non-null object \n", + " 2 indmom 18495 non-null float64 \n", + "dtypes: datetime64[ns](1), float64(1), object(1)\n", + "memory usage: 433.6+ KB\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":1: FutureWarning: null_counts is deprecated. Use show_counts instead\n", + " indmom.info(null_counts=True)\n" + ] + } + ], + "source": [ + "indmom.info(null_counts=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:06.153300Z", + "start_time": "2021-02-24T15:31:05.215068Z" + } + }, + "outputs": [], + "source": [ + "indmom = (returns\n", + " .stack()\n", + " .to_frame('ret')\n", + " .join(metadata[['sector']])\n", + " .reset_index()\n", + " .merge(indmom)\n", + " .set_index(['date', 'ticker'])\n", + " .loc[:, ['indmom']])" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:06.164686Z", + "start_time": "2021-02-24T15:31:06.154163Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "MultiIndex: 3551199 entries, (Timestamp('1991-01-04 00:00:00'), 'AA') to (Timestamp('2020-01-03 00:00:00'), 'ZTR')\n", + "Data columns (total 1 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 indmom 3551199 non-null float64\n", + "dtypes: float64(1)\n", + "memory usage: 40.8+ MB\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":1: FutureWarning: null_counts is deprecated. Use show_counts instead\n", + " indmom.info(null_counts=True)\n" + ] + } + ], + "source": [ + "indmom.info(null_counts=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:06.193427Z", + "start_time": "2021-02-24T15:31:06.165684Z" + } + }, + "outputs": [], + "source": [ + "indmom.to_hdf(results_path / 'autoencoder.h5', 'factor/indmom')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Recent Max Return" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Max daily returns from calendar month t-1" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:07.585474Z", + "start_time": "2021-02-24T15:31:06.194373Z" + } + }, + "outputs": [], + "source": [ + "maxret = (close\n", + " .pct_change(periods=MONTH)\n", + " .rolling(21)\n", + " .max()\n", + " .resample('W-FRI')\n", + " .last()\n", + " .stack()\n", + " .to_frame('maxret'))" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:07.633250Z", + "start_time": "2021-02-24T15:31:07.586352Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "MultiIndex: 3562402 entries, (Timestamp('1990-03-02 00:00:00', freq='W-FRI'), 'AA') to (Timestamp('2020-01-03 00:00:00', freq='W-FRI'), 'ZYXI')\n", + "Data columns (total 1 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 maxret 3562402 non-null float64\n", + "dtypes: float64(1)\n", + "memory usage: 41.0+ MB\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":1: FutureWarning: null_counts is deprecated. Use show_counts instead\n", + " maxret.info(null_counts=True)\n" + ] + } + ], + "source": [ + "maxret.info(null_counts=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:07.662862Z", + "start_time": "2021-02-24T15:31:07.634151Z" + } + }, + "outputs": [], + "source": [ + "maxret.to_hdf(results_path / 'autoencoder.h5', 'factor/maxret')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Long-Term Reversal" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Cumulative returns months t-36 to t-13." + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:08.227020Z", + "start_time": "2021-02-24T15:31:07.663799Z" + } + }, + "outputs": [], + "source": [ + "mom36m = (close\n", + " .pct_change(periods=24*MONTH)\n", + " .shift(12*MONTH)\n", + " .resample('W-FRI')\n", + " .last()\n", + " .stack()\n", + " .to_frame('mom36m'))" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:08.266863Z", + "start_time": "2021-02-24T15:31:08.227869Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "MultiIndex: 2967391 entries, (Timestamp('1993-01-01 00:00:00', freq='W-FRI'), 'AA') to (Timestamp('2020-01-03 00:00:00', freq='W-FRI'), 'ZYXI')\n", + "Data columns (total 1 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 mom36m 2967391 non-null float64\n", + "dtypes: float64(1)\n", + "memory usage: 34.2+ MB\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":1: FutureWarning: null_counts is deprecated. Use show_counts instead\n", + " mom36m.info(null_counts=True)\n" + ] + } + ], + "source": [ + "mom36m.info(null_counts=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:08.293411Z", + "start_time": "2021-02-24T15:31:08.267936Z" + } + }, + "outputs": [], + "source": [ + "mom36m.to_hdf(results_path / 'autoencoder.h5', 'factor/mom36m')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Liquidity Metrics" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Turnover" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Avg. monthly trading volume for most recent three months scaled by number of shares; we are using the most recent no of shares from yahoo finance" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:09.804423Z", + "start_time": "2021-02-24T15:31:08.294902Z" + } + }, + "outputs": [], + "source": [ + "turn = (volume\n", + " .rolling(3*MONTH)\n", + " .mean()\n", + " .resample('W-FRI')\n", + " .last()\n", + " .div(metadata.sharesoutstanding)\n", + " .stack('ticker')\n", + " .to_frame('turn'))" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:09.852968Z", + "start_time": "2021-02-24T15:31:09.805188Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "MultiIndex: 3506569 entries, (Timestamp('1990-03-30 00:00:00', freq='W-FRI'), 'AA') to (Timestamp('2020-01-03 00:00:00', freq='W-FRI'), 'ZYXI')\n", + "Data columns (total 1 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 turn 3506569 non-null float64\n", + "dtypes: float64(1)\n", + "memory usage: 40.3+ MB\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":1: FutureWarning: null_counts is deprecated. Use show_counts instead\n", + " turn.info(null_counts=True)\n" + ] + } + ], + "source": [ + "turn.info(null_counts=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:09.882475Z", + "start_time": "2021-02-24T15:31:09.854048Z" + } + }, + "outputs": [], + "source": [ + "turn.to_hdf(results_path / 'autoencoder.h5', 'factor/turn')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Turnover Volatility" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Monthly std dev of daily share turnover" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:18.197599Z", + "start_time": "2021-02-24T15:31:09.883665Z" + } + }, + "outputs": [], + "source": [ + "turn_std = (prices\n", + " .volume\n", + " .unstack('ticker')\n", + " .div(metadata.sharesoutstanding)\n", + " .rolling(MONTH)\n", + " .std()\n", + " .resample('W-FRI')\n", + " .last()\n", + " .stack('ticker')\n", + " .to_frame('turn_std'))" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:18.226370Z", + "start_time": "2021-02-24T15:31:18.198490Z" + } + }, + "outputs": [], + "source": [ + "turn_std.to_hdf(results_path / 'autoencoder.h5', 'factor/turn_std')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Log Market Equity" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Natural log of market cap at end of month t-1" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:19.713130Z", + "start_time": "2021-02-24T15:31:18.227239Z" + } + }, + "outputs": [], + "source": [ + "last_price = close.ffill()\n", + "factor = close.div(last_price.iloc[-1])\n", + "mvel = np.log1p(factor.mul(metadata.marketcap).resample('W-FRI').last()).stack().to_frame('mvel')" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:19.767170Z", + "start_time": "2021-02-24T15:31:19.713957Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "MultiIndex: 3597636 entries, (Timestamp('1990-01-05 00:00:00', freq='W-FRI'), 'AA') to (Timestamp('2020-01-03 00:00:00', freq='W-FRI'), 'ZYXI')\n", + "Data columns (total 1 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 mvel 3597636 non-null float64\n", + "dtypes: float64(1)\n", + "memory usage: 41.4+ MB\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":1: FutureWarning: null_counts is deprecated. Use show_counts instead\n", + " mvel.info(null_counts=True)\n" + ] + } + ], + "source": [ + "mvel.info(null_counts=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:19.807236Z", + "start_time": "2021-02-24T15:31:19.768465Z" + } + }, + "outputs": [], + "source": [ + "mvel.to_hdf(results_path / 'autoencoder.h5', 'factor/mvel')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Dollar Volume" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Natural log of trading volume time price per share from month t-2" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:19.875033Z", + "start_time": "2021-02-24T15:31:19.808087Z" + } + }, + "outputs": [], + "source": [ + "dv = close.mul(volume)" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:21.166359Z", + "start_time": "2021-02-24T15:31:19.875995Z" + } + }, + "outputs": [], + "source": [ + "dolvol = (np.log1p(dv.rolling(21)\n", + " .mean()\n", + " .shift(21)\n", + " .resample('W-FRI')\n", + " .last())\n", + " .stack()\n", + " .to_frame('dolvol'))" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:21.193943Z", + "start_time": "2021-02-24T15:31:21.167174Z" + } + }, + "outputs": [], + "source": [ + "dolvol.to_hdf(results_path / 'autoencoder.h5', 'factor/dolvol')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Amihud Illiquidity" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Average of daily (absolute return / dollar volume)" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:22.668882Z", + "start_time": "2021-02-24T15:31:21.194934Z" + } + }, + "outputs": [], + "source": [ + "ill = (close.pct_change().abs()\n", + " .div(dv)\n", + " .rolling(21)\n", + " .mean()\n", + " .resample('W-FRI').last()\n", + " .stack()\n", + " .to_frame('ill'))" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:22.719439Z", + "start_time": "2021-02-24T15:31:22.669807Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "MultiIndex: 3210773 entries, (Timestamp('1990-02-02 00:00:00', freq='W-FRI'), 'AA') to (Timestamp('2020-01-03 00:00:00', freq='W-FRI'), 'ZYXI')\n", + "Data columns (total 1 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 ill 3210773 non-null float64\n", + "dtypes: float64(1)\n", + "memory usage: 36.9+ MB\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":1: FutureWarning: null_counts is deprecated. Use show_counts instead\n", + " ill.info(null_counts=True)\n" + ] + } + ], + "source": [ + "ill.info(null_counts=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:22.748882Z", + "start_time": "2021-02-24T15:31:22.720722Z" + } + }, + "outputs": [], + "source": [ + "ill.to_hdf(results_path / 'autoencoder.h5', 'factor/ill')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Risk Measures" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Return Volatility" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Standard dev of daily returns from month t-1." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:24.135283Z", + "start_time": "2021-02-24T15:31:22.749843Z" + } + }, + "outputs": [], + "source": [ + "retvol = (close.pct_change()\n", + " .rolling(21)\n", + " .std()\n", + " .resample('W-FRI')\n", + " .last()\n", + " .stack()\n", + " .to_frame('retvol'))" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:24.185387Z", + "start_time": "2021-02-24T15:31:24.136130Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "MultiIndex: 3580621 entries, (Timestamp('1990-02-02 00:00:00', freq='W-FRI'), 'AA') to (Timestamp('2020-01-03 00:00:00', freq='W-FRI'), 'ZYXI')\n", + "Data columns (total 1 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 retvol 3580621 non-null float64\n", + "dtypes: float64(1)\n", + "memory usage: 41.2+ MB\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":1: FutureWarning: null_counts is deprecated. Use show_counts instead\n", + " retvol.info(null_counts=True)\n" + ] + } + ], + "source": [ + "retvol.info(null_counts=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:24.217951Z", + "start_time": "2021-02-24T15:31:24.186306Z" + } + }, + "outputs": [], + "source": [ + "retvol.to_hdf(results_path / 'autoencoder.h5', 'factor/retvol')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Market Beta" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Estimated market beta from weekly returns and equal weighted market returns for 3 years ending month t-1 with at least 52 weeks of returns." + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:24.501234Z", + "start_time": "2021-02-24T15:31:24.218804Z" + } + }, + "outputs": [], + "source": [ + "index = close.resample('W-FRI').last().pct_change().mean(1).to_frame('x')" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:31:24.504378Z", + "start_time": "2021-02-24T15:31:24.502062Z" + } + }, + "outputs": [], + "source": [ + "def get_market_beta(y, x=index):\n", + " df = x.join(y.to_frame('y')).dropna()\n", + " model = RollingOLS(endog=df.y, \n", + " exog=sm.add_constant(df[['x']]),\n", + " window=3*52)\n", + "\n", + " return model.fit(params_only=True).params['x']" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:32:27.608190Z", + "start_time": "2021-02-24T15:31:24.505305Z" + } + }, + "outputs": [], + "source": [ + "beta = (returns.dropna(thresh=3*52, axis=1)\n", + " .apply(get_market_beta).stack().to_frame('beta'))" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:32:27.652151Z", + "start_time": "2021-02-24T15:32:27.609024Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "MultiIndex: 2969406 entries, (Timestamp('1993-01-01 00:00:00', freq='W-FRI'), 'AA') to (Timestamp('2020-01-03 00:00:00', freq='W-FRI'), 'ZYXI')\n", + "Data columns (total 1 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 beta 2969406 non-null float64\n", + "dtypes: float64(1)\n", + "memory usage: 34.2+ MB\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":1: FutureWarning: null_counts is deprecated. Use show_counts instead\n", + " beta.info(null_counts=True)\n" + ] + } + ], + "source": [ + "beta.info(null_counts=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:32:27.677282Z", + "start_time": "2021-02-24T15:32:27.652927Z" + } + }, + "outputs": [], + "source": [ + "beta.to_hdf(results_path / 'autoencoder.h5', 'factor/beta')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Beta Squared" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Market beta squared" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:32:27.710494Z", + "start_time": "2021-02-24T15:32:27.678160Z" + } + }, + "outputs": [], + "source": [ + "betasq = beta.beta.pow(2).to_frame('betasq')" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:32:27.726198Z", + "start_time": "2021-02-24T15:32:27.712119Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "MultiIndex: 2969406 entries, (Timestamp('1993-01-01 00:00:00', freq='W-FRI'), 'AA') to (Timestamp('2020-01-03 00:00:00', freq='W-FRI'), 'ZYXI')\n", + "Data columns (total 1 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 betasq 2969406 non-null float64\n", + "dtypes: float64(1)\n", + "memory usage: 34.2+ MB\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":1: FutureWarning: null_counts is deprecated. Use show_counts instead\n", + " betasq.info(null_counts=True)\n" + ] + } + ], + "source": [ + "betasq.info(null_counts=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:32:27.757491Z", + "start_time": "2021-02-24T15:32:27.727756Z" + } + }, + "outputs": [], + "source": [ + "betasq.to_hdf(results_path / 'autoencoder.h5', 'factor/betasq')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Idiosyncratic return volatility" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Standard dev of a regression of residuals of weekly returns on the returns of an equal weighted market index returns for the prior three years." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This takes a while!" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T15:32:27.762385Z", + "start_time": "2021-02-24T15:32:27.760235Z" + } + }, + "outputs": [], + "source": [ + "def get_ols_residuals(y, x=index):\n", + " df = x.join(y.to_frame('y')).dropna()\n", + " model = sm.OLS(endog=df.y, exog=sm.add_constant(df[['x']]))\n", + " result = model.fit()\n", + " return result.resid.std()" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T18:27:18.180440Z", + "start_time": "2021-02-24T15:32:27.763774Z" + } + }, + "outputs": [], + "source": [ + "idiovol = (returns.apply(lambda x: x.rolling(3 * 52)\n", + " .apply(get_ols_residuals)))" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T18:27:18.310136Z", + "start_time": "2021-02-24T18:27:18.181902Z" + } + }, + "outputs": [], + "source": [ + "idiovol = idiovol.stack().to_frame('idiovol')" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T18:27:18.360087Z", + "start_time": "2021-02-24T18:27:18.311025Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "MultiIndex: 2969406 entries, (Timestamp('1993-01-01 00:00:00', freq='W-FRI'), 'AA') to (Timestamp('2020-01-03 00:00:00', freq='W-FRI'), 'ZYXI')\n", + "Data columns (total 1 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 idiovol 2969406 non-null float64\n", + "dtypes: float64(1)\n", + "memory usage: 34.2+ MB\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":1: FutureWarning: null_counts is deprecated. Use show_counts instead\n", + " idiovol.info(null_counts=True)\n" + ] + } + ], + "source": [ + "idiovol.info(null_counts=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "ExecuteTime": { + "end_time": "2021-02-24T18:27:18.394953Z", + "start_time": "2021-02-24T18:27:18.360999Z" + } + }, + "outputs": [], + "source": [ + "idiovol.to_hdf(results_path / 'autoencoder.h5', 'factor/idiovol')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.8" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": true + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/installation/linux/ml4t-backtest.yml b/installation/linux/ml4t-backtest.yml new file mode 100644 index 000000000..81092575e --- /dev/null +++ b/installation/linux/ml4t-backtest.yml @@ -0,0 +1,204 @@ +name: backtest +channels: + - defaults + - conda-forge +dependencies: + - _libgcc_mutex=0.1=conda_forge + - _openmp_mutex=4.5=1_gnu + - argon2-cffi=20.1.0=py36h27cfd23_1 + - attrs=20.3.0=pyhd3eb1b0_0 + - binutils_impl_linux-64=2.35.1=h193b22a_2 + - binutils_linux-64=2.35=h67ddf6f_30 + - bleach=3.3.0=pyhd3eb1b0_0 + - bottleneck=1.3.2=py36heb32a55_1 + - ca-certificates=2021.1.19=h06a4308_0 + - certifi=2020.12.5=py36h06a4308_0 + - cffi=1.14.5=py36h261ae71_0 + - cycler=0.10.0=py36_0 + - dbus=1.13.18=hb2f20db_0 + - decorator=4.4.2=pyhd3eb1b0_0 + - defusedxml=0.6.0=pyhd3eb1b0_0 + - entrypoints=0.3=py36_0 + - expat=2.2.10=he6710b0_2 + - fontconfig=2.13.1=hba837de_1004 + - freetype=2.10.4=h5ab3b9f_0 + - gcc_impl_linux-64=9.3.0=h70c0ae5_18 + - gcc_linux-64=9.3.0=hf25ea35_30 + - gettext=0.19.8.1=h9b4dc7a_1 + - glib=2.66.7=h9c3ff4c_0 + - glib-tools=2.66.7=h9c3ff4c_0 + - gst-plugins-base=1.18.3=h04508c2_0 + - gstreamer=1.18.3=h3560a44_0 + - gxx_impl_linux-64=9.3.0=hd87eabc_18 + - gxx_linux-64=9.3.0=h3fbe746_30 + - icu=68.1=h2531618_0 + - importlib-metadata=3.7.0=py36h5fab9bb_0 + - importlib_metadata=3.7.0=hd8ed1ab_0 + - ipykernel=5.5.0=py36he448a4c_1 + - ipython=5.8.0=py36_1 + - ipython_genutils=0.2.0=pyhd3eb1b0_1 + - ipywidgets=7.6.3=pyhd3eb1b0_1 + - jinja2=2.11.3=pyhd3eb1b0_0 + - jpeg=9d=h36c2ea0_0 + - jsonschema=3.2.0=py_2 + - jupyter=1.0.0=py36_7 + - jupyter_client=6.1.11=pyhd8ed1ab_1 + - jupyter_console=5.2.0=py36_1 + - jupyter_contrib_core=0.3.3=py_2 + - jupyter_core=4.7.1=py36h06a4308_0 + - jupyter_highlight_selected_word=0.2.0=py36h5fab9bb_1002 + - jupyter_latex_envs=1.4.6=py36h9f0ad1d_1001 + - jupyter_nbextensions_configurator=0.4.1=py36h5fab9bb_2 + - jupyterlab_widgets=1.0.0=pyhd3eb1b0_1 + - kernel-headers_linux-64=2.6.32=h77966d4_13 + - kiwisolver=1.3.1=py36h2531618_0 + - krb5=1.17.2=h926e7f8_0 + - lcms2=2.12=hddcbb42_0 + - ld_impl_linux-64=2.35.1=hea4e1c9_2 + - libblas=3.9.0=8_openblas + - libcblas=3.9.0=8_openblas + - libclang=11.0.1=default_ha53f305_1 + - libedit=3.1.20191231=h14c3975_1 + - libevent=2.1.10=hcdb4288_3 + - libffi=3.3=he6710b0_2 + - libgcc-devel_linux-64=9.3.0=h7864c58_18 + - libgcc-ng=9.3.0=h2828fa1_18 + - libgfortran-ng=9.3.0=hff62375_18 + - libgfortran5=9.3.0=hff62375_18 + - libglib=2.66.7=h1f3bc88_0 + - libgomp=9.3.0=h2828fa1_18 + - libiconv=1.16=h516909a_0 + - liblapack=3.9.0=8_openblas + - libllvm11=11.0.1=hf817b99_0 + - libopenblas=0.3.12=pthreads_h4812303_1 + - libpng=1.6.37=hbc83047_0 + - libpq=13.1=hfd2b0eb_1 + - libsodium=1.0.18=h7b6447c_0 + - libstdcxx-devel_linux-64=9.3.0=hb016644_18 + - libstdcxx-ng=9.3.0=h6de172a_18 + - libtiff=4.2.0=hdc55705_0 + - libuuid=2.32.1=h7f98852_1000 + - libwebp-base=1.2.0=h27cfd23_0 + - libxcb=1.14=h7b6447c_0 + - libxkbcommon=1.0.3=he3ba5ed_0 + - libxml2=2.9.10=h72842e0_3 + - libxslt=1.1.33=h15afd5d_2 + - lxml=4.6.2=py36h04a5ba7_1 + - lz4-c=1.9.3=h2531618_0 + - markupsafe=1.1.1=py36h7b6447c_0 + - matplotlib=3.3.4=py36h06a4308_0 + - matplotlib-base=3.3.4=py36h62a2d02_0 + - mistune=0.8.4=py36h7b6447c_0 + - mysql-common=8.0.23=ha770c72_1 + - mysql-libs=8.0.23=h935591d_1 + - nbformat=5.1.2=pyhd3eb1b0_1 + - ncurses=6.2=he6710b0_1 + - nbconvert=5.6.1=py36h9f0ad1d_1 # avoid warnings on notebook lauch + - nb_conda_kernels + - notebook + - nspr=4.29=h9c3ff4c_1 + - nss=3.62=hb5efdd6_0 + - numpy=1.18.1=py36h95a1406_0 + - olefile=0.46=py36_0 + - openssl=1.1.1j=h27cfd23_0 + - packaging=20.9=pyhd3eb1b0_0 + - pandoc=2.11.4=h7f98852_0 + - pandocfilters=1.4.3=py36h06a4308_1 + - pcre=8.44=he6710b0_0 + - pexpect=4.8.0=pyhd3eb1b0_3 + - pickleshare=0.7.5=pyhd3eb1b0_1003 + - pillow=8.1.0=py36he98fc37_0 + - pip=19.2.3 + - prometheus_client=0.9.0=pyhd3eb1b0_0 + - prompt_toolkit=1.0.15=py_1 + - ptyprocess=0.7.0=pyhd3eb1b0_2 + - pycparser=2.20=py_2 + - pygments=2.8.0=pyhd3eb1b0_0 + - pyparsing=2.4.7=pyhd3eb1b0_0 + - pyqt=5.12.3=py36h5fab9bb_7 + - pyqt-impl=5.12.3=py36h7ec31b9_7 + - pyqt5-sip=4.19.18=py36hc4f0c31_7 + - pyqtchart=5.12=py36h7ec31b9_7 + - pyqtwebengine=5.12.1=py36h7ec31b9_7 + - pyrsistent=0.17.3=py36h7b6447c_0 + - python=3.6.13=hffdb5ce_0_cpython + - python-dateutil=2.8.1=pyhd3eb1b0_0 + - python_abi=3.6=1_cp36m + - pytz=2021.1=pyhd3eb1b0_0 + - pyyaml=5.4.1=py36h27cfd23_1 + - pyzmq=22.0.3=py36h81c33ee_0 + - qt=5.12.9=hda022c4_4 + - qtconsole=5.0.2=pyhd3eb1b0_0 + - qtpy=1.9.0=py_0 + - readline=8.1=h27cfd23_0 + - send2trash=1.5.0=pyhd3eb1b0_1 + - setuptools=52.0.0=py36h06a4308_0 + - simplegeneric=0.8.1=py36_2 + - six=1.15.0=py36h06a4308_0 + - sqlite=3.34.0=h74cdb3f_0 + - sysroot_linux-64=2.12=h77966d4_13 + - terminado=0.9.2=py36h06a4308_0 + - testpath=0.4.4=pyhd3eb1b0_0 + - tk=8.6.10=hbc83047_0 + - tornado=6.1=py36h27cfd23_0 + - traitlets=4.3.3=py36_0 + - typing_extensions=3.7.4.3=pyha847dfd_0 + - wcwidth=0.2.5=py_0 + - webencodings=0.5.1=py36_1 + - wheel=0.36.2=pyhd3eb1b0_0 + - widgetsnbextension=3.5.1=py36_0 + - xz=5.2.5=h7b6447c_0 + - yaml=0.2.5=h7b6447c_0 + - zeromq=4.3.4=h9c3ff4c_0 + - zipp=3.4.0=pyhd3eb1b0_0 + - zlib=1.2.11=h7b6447c_3 + - zstd=1.4.8=ha95c52a_1 + - pip: + - alembic==1.5.5 + - alphalens==0.4.0 + - autopep8==1.5.5 + - bcolz==1.2.1 + - cached-property==1.5.2 + - chardet==4.0.0 + - click==7.1.2 + - cvxpy==1.1.10 + - ecos==2.0.7.post1 + - empyrical==0.5.5 + - h5py==3.1.0 + - idna==2.10 + - intervaltree==3.1.0 + - iso3166==1.0.1 + - iso4217==1.6.20180829 + - joblib==1.0.1 + - logbook==1.5.3 + - lru-dict==1.1.7 + - mako==1.1.4 + - multipledispatch==0.6.0 + - networkx==1.11 + - numexpr==2.7.2 + - osqp==0.6.2.post0 + - pandas==0.22.0 + - pandas-datareader==0.8.1 + - patsy==0.5.1 + - pip==19.2.3 + - pycodestyle==2.6.0 + - pyfolio==0.9.2 + - pyportfolioopt==1.4.1 + - python-editor==1.0.4 + - python-interface==1.6.0 + - qdldl==0.1.5.post0 + - requests==2.25.1 + - scikit-learn==0.24.1 + - scipy==1.5.4 + - scs==2.1.2 + - seaborn==0.10.1 + - sortedcontainers==2.3.0 + - sqlalchemy==1.3.23 + - statsmodels==0.12.2 + - tables==3.6.1 + - threadpoolctl==2.1.0 + - toml==0.10.2 + - toolz==0.11.1 + - trading-calendars==2.1.1 + - urllib3==1.26.3 + - git+https://github.com/stefan-jansen/zipline.git@b33e5c955a58d888f55101874f45cd141c61d3e1#egg=zipline \ No newline at end of file