diff --git a/Challenge - Copy.ipynb b/Challenge - Copy.ipynb new file mode 100644 index 0000000..94a4f6e --- /dev/null +++ b/Challenge - Copy.ipynb @@ -0,0 +1,2074 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "---Python script Start--- 2023-08-05 13:45:05.658109\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import datetime\n", + "import plotly.express as px\n", + "from keras.models import Sequential\n", + "from keras.layers import LSTM, Dense, Dropout\n", + "from sklearn.preprocessing import MinMaxScaler\n", + "from sklearn.model_selection import train_test_split\n", + "import matplotlib.pyplot as plt\n", + "\n", + "print(\"---Python script Start---\", str(datetime.datetime.now()))" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "df = pd.read_csv(\n", + " \"C:\\\\Users\\\\terre\\OneDrive - University of Cape Town\\\\Terrence\\\\Code\\\\prescient-coding-challenge-2023\\\\data\\\\returns_train.csv\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "# data reads\n", + "df_returns_train = pd.read_csv(\"data/returns_train.csv\")\n", + "df_returns_test = pd.read_csv(\"data/returns_test.csv\")\n", + "df_returns_train[\"month_end\"] = pd.to_datetime(arg=df_returns_train[\"month_end\"]).apply(\n", + " lambda d: d.date()\n", + ")\n", + "df_returns_test[\"month_end\"] = pd.to_datetime(arg=df_returns_test[\"month_end\"]).apply(\n", + " lambda d: d.date()\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['Stock1', 'Stock10', 'Stock11', 'Stock14', 'Stock16', 'Stock18', 'Stock19', 'Stock20', 'Stock21', 'Stock22', 'Stock26', 'Stock3', 'Stock30', 'Stock31', 'Stock33', 'Stock34', 'Stock35', 'Stock38', 'Stock39', 'Stock4', 'Stock40', 'Stock41', 'Stock43', 'Stock44', 'Stock46', 'Stock47', 'Stock49', 'Stock5', 'Stock50', 'Stock54', 'Stock56', 'Stock59', 'Stock6', 'Stock61', 'Stock63', 'Stock64', 'Stock66', 'Stock69', 'Stock7', 'Stock70', 'Stock73', 'Stock75', 'Stock76', 'Stock77', 'Stock78', 'Stock79', 'Stock80', 'Stock81', 'Stock84', 'Stock85', 'Stock86', 'Stock87', 'Stock88', 'Stock9']\n" + ] + } + ], + "source": [ + "# Ignoring the month_end from training set\n", + "\n", + "# Get column names as a list but ignore the first column\n", + "header_list = df.columns[1:].tolist()\n", + "print(header_list)\n", + "\n", + "\n", + "def process_dataframe(dataframe):\n", + " # Create a new dataframe without the 'month_end' column\n", + " new_dataframe = dataframe.drop([\"month_end\"], axis=1)\n", + " return new_dataframe\n", + "\n", + "\n", + "clean_df = process_dataframe(df_returns_train)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "def equalise_weights(df: pd.DataFrame):\n", + " \"\"\"\n", + " Function to generate the equal weights, i.e. 1/p for each active stock within a month\n", + "\n", + " Args:\n", + " df: A return data frame. First column is month end and remaining columns are stocks\n", + "\n", + " Returns:\n", + " A dataframe of the same dimension but with values 1/p on active funds within a month\n", + "\n", + " \"\"\"\n", + "\n", + " # create df to house weights\n", + " n_length = len(df)\n", + " df_returns = df\n", + " df_weights = df_returns[:n_length].copy()\n", + " df_weights.set_index(\"month_end\", inplace=True)\n", + "\n", + " # list of stock names\n", + " list_stocks = list(df_returns.columns)\n", + " list_stocks.remove(\"month_end\")\n", + "\n", + " # assign 1/p\n", + " df_weights[list_stocks] = 1 / len(list_stocks)\n", + "\n", + " return df_weights" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 1/50\n", + "2/2 [==============================] - 9s 2s/step - loss: 0.2450 - val_loss: 0.2347\n", + "Epoch 2/50\n", + "2/2 [==============================] - 0s 105ms/step - loss: 0.2135 - val_loss: 0.1985\n", + "Epoch 3/50\n", + "2/2 [==============================] - 0s 96ms/step - loss: 0.1829 - val_loss: 0.1570\n", + "Epoch 4/50\n", + "2/2 [==============================] - 0s 120ms/step - loss: 0.1517 - val_loss: 0.1229\n", + "Epoch 5/50\n", + "2/2 [==============================] - 0s 112ms/step - loss: 0.1333 - val_loss: 0.1003\n", + "Epoch 6/50\n", + "2/2 [==============================] - 0s 117ms/step - loss: 0.1227 - val_loss: 0.0869\n", + "Epoch 7/50\n", + "2/2 [==============================] - 0s 127ms/step - loss: 0.1106 - val_loss: 0.0791\n", + "Epoch 8/50\n", + "2/2 [==============================] - 0s 100ms/step - loss: 0.0945 - val_loss: 0.0737\n", + "Epoch 9/50\n", + "2/2 [==============================] - 0s 101ms/step - loss: 0.0902 - val_loss: 0.0688\n", + "Epoch 10/50\n", + "2/2 [==============================] - 0s 108ms/step - loss: 0.0913 - val_loss: 0.0627\n", + "Epoch 11/50\n", + "2/2 [==============================] - 0s 96ms/step - loss: 0.0879 - val_loss: 0.0580\n", + "Epoch 12/50\n", + "2/2 [==============================] - 0s 109ms/step - loss: 0.0801 - val_loss: 0.0553\n", + "Epoch 13/50\n", + "2/2 [==============================] - 0s 116ms/step - loss: 0.0763 - val_loss: 0.0533\n", + "Epoch 14/50\n", + "2/2 [==============================] - 0s 116ms/step - loss: 0.0735 - val_loss: 0.0517\n", + "Epoch 15/50\n", + "2/2 [==============================] - 0s 104ms/step - loss: 0.0768 - val_loss: 0.0507\n", + "Epoch 16/50\n", + "2/2 [==============================] - 0s 108ms/step - loss: 0.0767 - val_loss: 0.0503\n", + "Epoch 17/50\n", + "2/2 [==============================] - 0s 104ms/step - loss: 0.0686 - val_loss: 0.0504\n", + "Epoch 18/50\n", + "2/2 [==============================] - 0s 97ms/step - loss: 0.0700 - val_loss: 0.0505\n", + "Epoch 19/50\n", + "2/2 [==============================] - 0s 101ms/step - loss: 0.0653 - val_loss: 0.0503\n", + "Epoch 20/50\n", + "2/2 [==============================] - 0s 91ms/step - loss: 0.0664 - val_loss: 0.0502\n", + "Epoch 21/50\n", + "2/2 [==============================] - 0s 104ms/step - loss: 0.0647 - val_loss: 0.0498\n", + "Epoch 22/50\n", + "2/2 [==============================] - 0s 92ms/step - loss: 0.0640 - val_loss: 0.0490\n", + "Epoch 23/50\n", + "2/2 [==============================] - 0s 103ms/step - loss: 0.0667 - val_loss: 0.0478\n", + "Epoch 24/50\n", + "2/2 [==============================] - 0s 101ms/step - loss: 0.0621 - val_loss: 0.0468\n", + "Epoch 25/50\n", + "2/2 [==============================] - 0s 109ms/step - loss: 0.0646 - val_loss: 0.0470\n", + "Epoch 26/50\n", + "2/2 [==============================] - 0s 108ms/step - loss: 0.0616 - val_loss: 0.0481\n", + "Epoch 27/50\n", + "2/2 [==============================] - 0s 104ms/step - loss: 0.0628 - val_loss: 0.0501\n", + "Epoch 28/50\n", + "2/2 [==============================] - 0s 105ms/step - loss: 0.0622 - val_loss: 0.0505\n", + "Epoch 29/50\n", + "2/2 [==============================] - 0s 99ms/step - loss: 0.0704 - val_loss: 0.0480\n", + "Epoch 30/50\n", + "2/2 [==============================] - 0s 95ms/step - loss: 0.0595 - val_loss: 0.0460\n", + "Epoch 31/50\n", + "2/2 [==============================] - 0s 109ms/step - loss: 0.0567 - val_loss: 0.0453\n", + "Epoch 32/50\n", + "2/2 [==============================] - 0s 109ms/step - loss: 0.0621 - val_loss: 0.0453\n", + "Epoch 33/50\n", + "2/2 [==============================] - 0s 102ms/step - loss: 0.0590 - val_loss: 0.0460\n", + "Epoch 34/50\n", + "2/2 [==============================] - 0s 104ms/step - loss: 0.0606 - val_loss: 0.0487\n", + "Epoch 35/50\n", + "2/2 [==============================] - 0s 100ms/step - loss: 0.0588 - val_loss: 0.0516\n", + "Epoch 36/50\n", + "2/2 [==============================] - 0s 104ms/step - loss: 0.0599 - val_loss: 0.0497\n", + "Epoch 37/50\n", + "2/2 [==============================] - 0s 104ms/step - loss: 0.0608 - val_loss: 0.0468\n", + "Epoch 38/50\n", + "2/2 [==============================] - 0s 104ms/step - loss: 0.0551 - val_loss: 0.0456\n", + "Epoch 39/50\n", + "2/2 [==============================] - 0s 97ms/step - loss: 0.0578 - val_loss: 0.0452\n", + "Epoch 40/50\n", + "2/2 [==============================] - 0s 105ms/step - loss: 0.0603 - val_loss: 0.0452\n", + "Epoch 41/50\n", + "2/2 [==============================] - 0s 104ms/step - loss: 0.0609 - val_loss: 0.0454\n", + "Epoch 42/50\n", + "2/2 [==============================] - 0s 103ms/step - loss: 0.0598 - val_loss: 0.0461\n", + "Epoch 43/50\n", + "2/2 [==============================] - 0s 106ms/step - loss: 0.0589 - val_loss: 0.0474\n", + "Epoch 44/50\n", + "2/2 [==============================] - 0s 112ms/step - loss: 0.0555 - val_loss: 0.0498\n", + "Epoch 45/50\n", + "2/2 [==============================] - 0s 92ms/step - loss: 0.0559 - val_loss: 0.0509\n", + "Epoch 46/50\n", + "2/2 [==============================] - 0s 100ms/step - loss: 0.0587 - val_loss: 0.0500\n", + "Epoch 47/50\n", + "2/2 [==============================] - 0s 98ms/step - loss: 0.0595 - val_loss: 0.0484\n", + "Epoch 48/50\n", + "2/2 [==============================] - 0s 108ms/step - loss: 0.0571 - val_loss: 0.0487\n", + "Epoch 49/50\n", + "2/2 [==============================] - 0s 97ms/step - loss: 0.0571 - val_loss: 0.0488\n", + "Epoch 50/50\n", + "2/2 [==============================] - 0s 103ms/step - loss: 0.0598 - val_loss: 0.0485\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Normalization\n", + "scaler = MinMaxScaler(feature_range=(0, 1))\n", + "scaled_data = scaler.fit_transform(clean_df.values)\n", + "\n", + "# Create a data structure with 60 time-steps and 1 output for each stock\n", + "lookback = 60\n", + "\n", + "X, y = [], []\n", + "for i in range(lookback, len(scaled_data)):\n", + " X.append(scaled_data[i - lookback : i])\n", + " y.append(scaled_data[i])\n", + "X, y = np.array(X), np.array(y)\n", + "\n", + "# Split the data into training and validation sets\n", + "X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)\n", + "\n", + "# Initialize the LSTM model\n", + "model = Sequential()\n", + "\n", + "model.add(\n", + " LSTM(\n", + " units=50,\n", + " return_sequences=True,\n", + " input_shape=(X_train.shape[1], X_train.shape[2]),\n", + " )\n", + ")\n", + "model.add(Dropout(0.2))\n", + "\n", + "model.add(LSTM(units=50, return_sequences=True))\n", + "model.add(Dropout(0.2))\n", + "\n", + "model.add(LSTM(units=50, return_sequences=True))\n", + "model.add(Dropout(0.2))\n", + "\n", + "model.add(LSTM(units=50))\n", + "model.add(Dropout(0.2))\n", + "\n", + "model.add(\n", + " Dense(units=clean_df.shape[1])\n", + ") # number of units in the output layer should be equal to the number of stocks\n", + "\n", + "# Compile and train the model\n", + "model.compile(optimizer=\"adam\", loss=\"mean_squared_error\")\n", + "model.fit(X_train, y_train, epochs=50, batch_size=20, validation_data=(X_val, y_val))" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "def generate_portfolio(df_train: pd.DataFrame, df_test: pd.DataFrame):\n", + " \"\"\"\n", + " Function to generate stocks weight allocation for time t+1 using historic data. Initial weights generated as 1/p for active stock within a month\n", + "\n", + " Args:\n", + " df_train: The training set of returns. First column is month end and remaining columns are stocks\n", + " df_test: The testing set of returns. First column is month end and remaining columns are stocks\n", + "\n", + " Returns:\n", + " The returns dataframe and the weights\n", + " \"\"\"\n", + "\n", + " print(\n", + " \"---> training set spans\",\n", + " df_train[\"month_end\"].min(),\n", + " df_train[\"month_end\"].max(),\n", + " )\n", + " print(\n", + " \"---> training set spans\",\n", + " df_test[\"month_end\"].min(),\n", + " df_test[\"month_end\"].max(),\n", + " )\n", + "\n", + " # initialise data\n", + " n_train = len(df_train)\n", + " df_returns = pd.concat(objs=[df_train, df_test], ignore_index=True)\n", + "\n", + " df_weights = equalise_weights(\n", + " df_returns[:n_train]\n", + " ) # df to store weights and create initial\n", + "\n", + " # list of stock names\n", + " list_stocks = list(df_returns.columns)\n", + " list_stocks.remove(\"month_end\")\n", + "\n", + " # Only positive predicted stock are considered\n", + " # Higher volitility have higher weighting\n", + " # Higher prediction through my machine learning algorithm (LSTM) will have higher weighting\n", + " for i in range(len(df_test)):\n", + " df_latest = df_returns[(df_returns[\"month_end\"] < df_test.loc[i, \"month_end\"])]\n", + "\n", + " if len(df_latest) >= lookback:\n", + " latest_returns = df_latest[-lookback:][list_stocks].values\n", + " else:\n", + " continue\n", + "\n", + " # reshape the latest returns to be suitable for the LSTM model\n", + " latest_returns = np.reshape(latest_returns, (1, lookback, len(list_stocks)))\n", + "\n", + " # predict the future returns using the LSTM model\n", + " predicted_returns = model.predict(latest_returns)\n", + "\n", + " # Filter out the stocks with positive predicted returns\n", + " positive_returns_indices = np.where(predicted_returns[0] > 0)[0]\n", + " positive_returns = predicted_returns[0][positive_returns_indices]\n", + " positive_stock_names = np.array(list_stocks)[positive_returns_indices]\n", + "\n", + " # Apply exponential weighting to the positive returns\n", + " exp_returns = np.exp(positive_returns)\n", + "\n", + " # Calculate the recent volatility for each stock\n", + " volatilities = np.std(latest_returns[0], axis=0)\n", + "\n", + " # Scale the weights of each stock correspondto its recent volatility\n", + " volatility_scaled_weights = exp_returns * volatilities[positive_returns_indices]\n", + "\n", + " # Normalize the weights to sum to 1\n", + " weights = volatility_scaled_weights / np.sum(volatility_scaled_weights)\n", + "\n", + " # Create a new weight dataframe\n", + " weights_dict = dict(zip(positive_stock_names, weights))\n", + " weights_dict[\"month_end\"] = df_test.loc[i, \"month_end\"]\n", + " df_this = pd.DataFrame(data=[weights_dict], columns=df_weights.columns)\n", + " df_this = df_this.fillna(0) # fill NaNs for stocks that have been excluded\n", + "\n", + " # Append the weights to the df_weights dataframe\n", + " df_weights = pd.concat(objs=[df_weights, df_this], ignore_index=True)\n", + "\n", + " # 10% limit check\n", + " if len(\n", + " np.array(df_weights[list_stocks])[np.array(df_weights[list_stocks]) > 0.101]\n", + " ):\n", + " raise Exception(r\"---> 10% limit exceeded\")\n", + "\n", + " return df_returns, df_weights" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "def plot_total_return(\n", + " df_returns: pd.DataFrame,\n", + " df_weights_index: pd.DataFrame,\n", + " df_weights_portfolio: pd.DataFrame,\n", + "):\n", + " \"\"\"\n", + " Function to generate the two total return indices.\n", + "\n", + " Args:\n", + " df_returns: Ascending date ordered combined training and test returns data.\n", + " df_weights_index: Index weights. Equally weighted\n", + " df_weights_index: Portfolio weights. Your portfolio should use equally weighted for the training date range. If blank will be ignored\n", + "\n", + " Returns:\n", + " A plot of the two total return indices and the total return indices as a dataframe\n", + " \"\"\"\n", + "\n", + " # list of stock names\n", + " list_stocks = list(df_returns.columns)\n", + " list_stocks.remove(\"month_end\")\n", + "\n", + " # replace nans with 0 in return array\n", + " ar_returns = np.array(df_returns[list_stocks])\n", + " np.nan_to_num(x=ar_returns, copy=False, nan=0)\n", + "\n", + " # calc index\n", + " ar_rtn_index = np.array(df_weights_index[list_stocks]) * ar_returns\n", + " ar_rtn_port = np.array(df_weights_portfolio[list_stocks]) * ar_returns\n", + "\n", + " v_rtn_index = np.sum(ar_rtn_index, axis=1)\n", + " v_rtn_port = np.sum(ar_rtn_port, axis=1)\n", + "\n", + " # add return series to dataframe\n", + " df_rtn = pd.DataFrame(data=df_returns[\"month_end\"], columns=[\"month_end\"])\n", + " df_rtn[\"index\"] = v_rtn_index\n", + " df_rtn[\"portfolio\"] = v_rtn_port\n", + " df_rtn\n", + "\n", + " # create total return\n", + " base_price = 100\n", + " df_rtn.sort_values(by=\"month_end\", inplace=True)\n", + " df_rtn[\"index_tr\"] = ((1 + df_rtn[\"index\"]).cumprod()) * base_price\n", + " df_rtn[\"portfolio_tr\"] = ((1 + df_rtn[\"portfolio\"]).cumprod()) * base_price\n", + " df_rtn\n", + "\n", + " df_rtn_long = df_rtn[[\"month_end\", \"index_tr\", \"portfolio_tr\"]].melt(\n", + " id_vars=\"month_end\", var_name=\"series\", value_name=\"Total Return\"\n", + " )\n", + "\n", + " # plot\n", + " fig1 = px.line(\n", + " data_frame=df_rtn_long, x=\"month_end\", y=\"Total Return\", color=\"series\"\n", + " )\n", + "\n", + " return fig1, df_rtn" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "---> training set spans 2010-01-31 2017-08-31\n", + "---> training set spans 2017-09-30 2022-09-30\n", + "1/1 [==============================] - 2s 2s/step\n", + "1/1 [==============================] - 0s 40ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 34ms/step\n", + "1/1 [==============================] - 0s 37ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 45ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 38ms/step\n", + "1/1 [==============================] - 0s 40ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 39ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 35ms/step\n", + "1/1 [==============================] - 0s 30ms/step\n", + "1/1 [==============================] - 0s 29ms/step\n", + "1/1 [==============================] - 0s 37ms/step\n", + "1/1 [==============================] - 0s 41ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 33ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 33ms/step\n", + "1/1 [==============================] - 0s 31ms/step\n", + "1/1 [==============================] - 0s 35ms/step\n", + "1/1 [==============================] - 0s 35ms/step\n", + "1/1 [==============================] - 0s 34ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 35ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 33ms/step\n", + "1/1 [==============================] - 0s 34ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 37ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 39ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 40ms/step\n", + "1/1 [==============================] - 0s 29ms/step\n", + "1/1 [==============================] - 0s 35ms/step\n", + "1/1 [==============================] - 0s 28ms/step\n", + "1/1 [==============================] - 0s 29ms/step\n", + "1/1 [==============================] - 0s 29ms/step\n", + "1/1 [==============================] - 0s 37ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 29ms/step\n", + "1/1 [==============================] - 0s 92ms/step\n" + ] + }, + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "hovertemplate": "series=index_tr
month_end=%{x}
Total Return=%{y}", + "legendgroup": "index_tr", + "line": { + "color": "#636efa", + "dash": "solid" + }, + "marker": { + "symbol": "circle" + }, + "mode": "lines", + "name": "index_tr", + "orientation": "v", + "showlegend": true, + "type": "scatter", + "x": [ + "2010-01-31", + "2010-02-28", + "2010-03-31", + "2010-04-30", + "2010-05-31", + "2010-06-30", + "2010-07-31", + "2010-08-31", + "2010-09-30", + "2010-10-31", + "2010-11-30", + "2010-12-31", + "2011-01-31", + "2011-02-28", + "2011-03-31", + "2011-04-30", + "2011-05-31", + "2011-06-30", + "2011-07-31", + "2011-08-31", + "2011-09-30", + "2011-10-31", + "2011-11-30", + "2011-12-31", + "2012-01-31", + "2012-02-29", + "2012-03-31", + "2012-04-30", + "2012-05-31", + "2012-06-30", + "2012-07-31", + "2012-08-31", + "2012-09-30", + "2012-10-31", + "2012-11-30", + "2012-12-31", + "2013-01-31", + "2013-02-28", + "2013-03-31", + "2013-04-30", + "2013-05-31", + "2013-06-30", + "2013-07-31", + "2013-08-31", + "2013-09-30", + "2013-10-31", + "2013-11-30", + "2013-12-31", + "2014-01-31", + "2014-02-28", + "2014-03-31", + "2014-04-30", + "2014-05-31", + "2014-06-30", + "2014-07-31", + "2014-08-31", + "2014-09-30", + "2014-10-31", + "2014-11-30", + "2014-12-31", + "2015-01-31", + "2015-02-28", + "2015-03-31", + "2015-04-30", + "2015-05-31", + "2015-06-30", + "2015-07-31", + "2015-08-31", + "2015-09-30", + "2015-10-31", + "2015-11-30", + "2015-12-31", + "2016-01-31", + "2016-02-29", + "2016-03-31", + "2016-04-30", + "2016-05-31", + "2016-06-30", + "2016-07-31", + "2016-08-31", + "2016-09-30", + "2016-10-31", + "2016-11-30", + "2016-12-31", + "2017-01-31", + "2017-02-28", + "2017-03-31", + "2017-04-30", + "2017-05-31", + "2017-06-30", + "2017-07-31", + "2017-08-31", + "2017-09-30", + "2017-10-31", + "2017-11-30", + "2017-12-31", + "2018-01-31", + "2018-02-28", + "2018-03-31", + "2018-04-30", + "2018-05-31", + "2018-06-30", + "2018-07-31", + "2018-08-31", + "2018-09-30", + "2018-10-31", + "2018-11-30", + "2018-12-31", + "2019-01-31", + "2019-02-28", + "2019-03-31", + "2019-04-30", + "2019-05-31", + "2019-06-30", + "2019-07-31", + "2019-08-31", + "2019-09-30", + "2019-10-31", + "2019-11-30", + "2019-12-31", + "2020-01-31", + "2020-02-29", + "2020-03-31", + "2020-04-30", + "2020-05-31", + "2020-06-30", + "2020-07-31", + "2020-08-31", + "2020-09-30", + "2020-10-31", + "2020-11-30", + "2020-12-31", + "2021-01-31", + "2021-02-28", + "2021-03-31", + "2021-04-30", + "2021-05-31", + "2021-06-30", + "2021-07-31", + "2021-08-31", + "2021-09-30", + "2021-10-31", + "2021-11-30", + "2021-12-31", + "2022-01-31", + "2022-02-28", + "2022-03-31", + "2022-04-30", + "2022-05-31", + "2022-06-30", + "2022-07-31", + "2022-08-31", + "2022-09-30" + ], + "xaxis": "x", + "y": [ + 98.40422503465814, + 100.93798662242051, + 107.68288522534036, + 109.7995615285553, + 107.23242817648126, + 106.43966113566464, + 113.91349320384909, + 112.29508785565194, + 122.12549889316125, + 124.87638245152488, + 124.37504919099058, + 131.02440630172168, + 125.53974799975698, + 128.16916055886097, + 130.8873968980463, + 134.06376216689685, + 133.87076190339369, + 132.59760255874266, + 132.19487983933925, + 133.93330703110996, + 130.5237798699564, + 139.62267767903492, + 142.58187781542216, + 140.81510171281383, + 147.43151378288488, + 151.98680235958813, + 152.40074939942423, + 156.02418771795553, + 152.8161411497546, + 155.48775962326914, + 159.96753392766976, + 161.2555092837166, + 163.55646116844596, + 166.76092531656502, + 169.35948468015056, + 179.51302600735073, + 178.97405279416094, + 175.715694663543, + 177.78002433183755, + 173.6270905121469, + 179.05309265268664, + 174.40587342069082, + 177.70371255865953, + 180.45868512898545, + 190.13847786882747, + 199.8342118600708, + 195.61245975954753, + 198.21223347276447, + 192.83928084761467, + 199.0110446369025, + 209.28517002996813, + 216.99871696931413, + 215.54672594503234, + 221.47400674506645, + 229.30151970138036, + 231.71558423761155, + 223.30433148163613, + 230.03817391436593, + 237.31763181271953, + 238.0489478471065, + 253.40103532914986, + 257.65372033222815, + 250.32521657304318, + 261.0487471728084, + 244.49076503231, + 240.52762911874868, + 239.19767530915811, + 235.3547408233148, + 222.77135367139928, + 234.26747289033133, + 222.1090879667571, + 213.67839960700294, + 220.78695617407473, + 238.25962030287462, + 265.5875885745103, + 281.4830979229849, + 265.08963832048636, + 272.6053430199218, + 290.46865219314884, + 277.2773336280136, + 279.3254063118272, + 277.71860735593725, + 274.1748170835136, + 284.22745240063205, + 296.41653958631605, + 293.1716168419007, + 293.7023992516104, + 296.36419976912174, + 289.35664391915265, + 278.34458913811073, + 293.49195242698454, + 302.7447105318491, + 293.3548318464133, + 306.265024468535, + 316.34007571598295, + 320.6050708597117, + 319.8398684632358, + 314.3939885564841, + 299.3651563947235, + 311.75016932500927, + 286.5744449293349, + 284.51792257776367, + 296.12916505146376, + 296.143910254921, + 290.4436965043894, + 286.9509171356384, + 285.7353782660105, + 295.5481203141585, + 303.41923749726544, + 308.1306150282999, + 298.5647435963675, + 309.02289813595524, + 300.56226016303185, + 312.06482538817767, + 298.2095280041462, + 288.517714586796, + 289.2751273268798, + 302.77492930198224, + 296.15403611536595, + 301.300222213508, + 288.1274106750146, + 256.3501391424798, + 198.61104648895602, + 232.17876375277234, + 226.37299938748362, + 251.28070815857097, + 253.4386819534693, + 248.00730686110998, + 251.20981465197428, + 242.3029989334201, + 277.8151656727379, + 304.3408079639736, + 323.87054861742905, + 344.77548375050344, + 366.4195141948255, + 381.31140527510394, + 402.786450399951, + 391.0039272495355, + 405.4842305132651, + 429.09830839640495, + 428.2369585651376, + 437.4693282884787, + 444.0045333993229, + 476.727609087252, + 484.55475348051664, + 493.4639696855345, + 514.924562516531, + 488.9124266125262, + 482.9373630725979, + 440.5963276777227, + 459.5446640353366, + 452.01765890108527, + 450.7681381232569 + ], + "yaxis": "y" + }, + { + "hovertemplate": "series=portfolio_tr
month_end=%{x}
Total Return=%{y}", + "legendgroup": "portfolio_tr", + "line": { + "color": "#EF553B", + "dash": "solid" + }, + "marker": { + "symbol": "circle" + }, + "mode": "lines", + "name": "portfolio_tr", + "orientation": "v", + "showlegend": true, + "type": "scatter", + "x": [ + "2010-01-31", + "2010-02-28", + "2010-03-31", + "2010-04-30", + "2010-05-31", + "2010-06-30", + "2010-07-31", + "2010-08-31", + "2010-09-30", + "2010-10-31", + "2010-11-30", + "2010-12-31", + "2011-01-31", + "2011-02-28", + "2011-03-31", + "2011-04-30", + "2011-05-31", + "2011-06-30", + "2011-07-31", + "2011-08-31", + "2011-09-30", + "2011-10-31", + "2011-11-30", + "2011-12-31", + "2012-01-31", + "2012-02-29", + "2012-03-31", + "2012-04-30", + "2012-05-31", + "2012-06-30", + "2012-07-31", + "2012-08-31", + "2012-09-30", + "2012-10-31", + "2012-11-30", + "2012-12-31", + "2013-01-31", + "2013-02-28", + "2013-03-31", + "2013-04-30", + "2013-05-31", + "2013-06-30", + "2013-07-31", + "2013-08-31", + "2013-09-30", + "2013-10-31", + "2013-11-30", + "2013-12-31", + "2014-01-31", + "2014-02-28", + "2014-03-31", + "2014-04-30", + "2014-05-31", + "2014-06-30", + "2014-07-31", + "2014-08-31", + "2014-09-30", + "2014-10-31", + "2014-11-30", + "2014-12-31", + "2015-01-31", + "2015-02-28", + "2015-03-31", + "2015-04-30", + "2015-05-31", + "2015-06-30", + "2015-07-31", + "2015-08-31", + "2015-09-30", + "2015-10-31", + "2015-11-30", + "2015-12-31", + "2016-01-31", + "2016-02-29", + "2016-03-31", + "2016-04-30", + "2016-05-31", + "2016-06-30", + "2016-07-31", + "2016-08-31", + "2016-09-30", + "2016-10-31", + "2016-11-30", + "2016-12-31", + "2017-01-31", + "2017-02-28", + "2017-03-31", + "2017-04-30", + "2017-05-31", + "2017-06-30", + "2017-07-31", + "2017-08-31", + "2017-09-30", + "2017-10-31", + "2017-11-30", + "2017-12-31", + "2018-01-31", + "2018-02-28", + "2018-03-31", + "2018-04-30", + "2018-05-31", + "2018-06-30", + "2018-07-31", + "2018-08-31", + "2018-09-30", + "2018-10-31", + "2018-11-30", + "2018-12-31", + "2019-01-31", + "2019-02-28", + "2019-03-31", + "2019-04-30", + "2019-05-31", + "2019-06-30", + "2019-07-31", + "2019-08-31", + "2019-09-30", + "2019-10-31", + "2019-11-30", + "2019-12-31", + "2020-01-31", + "2020-02-29", + "2020-03-31", + "2020-04-30", + "2020-05-31", + "2020-06-30", + "2020-07-31", + "2020-08-31", + "2020-09-30", + "2020-10-31", + "2020-11-30", + "2020-12-31", + "2021-01-31", + "2021-02-28", + "2021-03-31", + "2021-04-30", + "2021-05-31", + "2021-06-30", + "2021-07-31", + "2021-08-31", + "2021-09-30", + "2021-10-31", + "2021-11-30", + "2021-12-31", + "2022-01-31", + "2022-02-28", + "2022-03-31", + "2022-04-30", + "2022-05-31", + "2022-06-30", + "2022-07-31", + "2022-08-31", + "2022-09-30" + ], + "xaxis": "x", + "y": [ + 98.40422503465814, + 100.93798662242051, + 107.68288522534036, + 109.7995615285553, + 107.23242817648126, + 106.43966113566464, + 113.91349320384909, + 112.29508785565194, + 122.12549889316125, + 124.87638245152488, + 124.37504919099058, + 131.02440630172168, + 125.53974799975698, + 128.16916055886097, + 130.8873968980463, + 134.06376216689685, + 133.87076190339369, + 132.59760255874266, + 132.19487983933925, + 133.93330703110996, + 130.5237798699564, + 139.62267767903492, + 142.58187781542216, + 140.81510171281383, + 147.43151378288488, + 151.98680235958813, + 152.40074939942423, + 156.02418771795553, + 152.8161411497546, + 155.48775962326914, + 159.96753392766976, + 161.2555092837166, + 163.55646116844596, + 166.76092531656502, + 169.35948468015056, + 179.51302600735073, + 178.97405279416094, + 175.715694663543, + 177.78002433183755, + 173.6270905121469, + 179.05309265268664, + 174.40587342069082, + 177.70371255865953, + 180.45868512898545, + 190.13847786882747, + 199.8342118600708, + 195.61245975954753, + 198.21223347276447, + 192.83928084761467, + 199.0110446369025, + 209.28517002996813, + 216.99871696931413, + 215.54672594503234, + 221.47400674506645, + 229.30151970138036, + 231.71558423761155, + 223.30433148163613, + 230.03817391436593, + 237.31763181271953, + 238.0489478471065, + 253.40103532914986, + 257.65372033222815, + 250.32521657304318, + 261.0487471728084, + 244.49076503231, + 240.52762911874868, + 239.19767530915811, + 235.3547408233148, + 222.77135367139928, + 234.26747289033133, + 222.1090879667571, + 213.67839960700294, + 220.78695617407473, + 238.25962030287462, + 265.5875885745103, + 281.4830979229849, + 265.08963832048636, + 272.6053430199218, + 290.46865219314884, + 277.2773336280136, + 279.3254063118272, + 277.71860735593725, + 274.1748170835136, + 284.22745240063205, + 296.41653958631605, + 293.1716168419007, + 293.7023992516104, + 296.36419976912174, + 289.35664391915265, + 278.34458913811073, + 293.49195242698454, + 302.7447105318491, + 291.81447239388893, + 308.7977586581792, + 317.46640467209306, + 315.980565008889, + 315.17931083241166, + 306.5477692742863, + 288.40980922995055, + 297.170071544132, + 270.633374974646, + 269.6388932710465, + 284.41860050083585, + 282.9270799109806, + 280.95760560937583, + 281.4385654325379, + 276.84088762582905, + 290.3424196043114, + 299.03272616079323, + 310.09030552319416, + 299.9343063556587, + 304.28861170233637, + 298.51836535718417, + 314.30109394554137, + 302.15817828592753, + 297.85961950688727, + 293.81205231426037, + 308.5001351912185, + 300.4854063001455, + 307.7659838198207, + 295.66474834398684, + 267.56228891731695, + 208.70302217050755, + 251.59548311327993, + 246.08365459547525, + 280.4480052539967, + 287.68621870949613, + 280.36611883863156, + 276.8989957223652, + 265.30743125700576, + 306.17412151924503, + 341.61536683363937, + 376.77794483038076, + 410.6183513249361, + 442.736848162654, + 467.70631568237746, + 489.4961534170436, + 471.7811090467055, + 495.75721153606116, + 530.232647687731, + 534.3392603616905, + 546.8094508585345, + 560.036164610925, + 619.8281330411401, + 632.1136715947626, + 635.7435405146244, + 655.6112545518024, + 613.9870246720898, + 605.2064966770056, + 550.9149051677855, + 576.2396043068396, + 557.3222873353085, + 544.2583292772424 + ], + "yaxis": "y" + } + ], + "layout": { + "legend": { + "title": { + "text": "series" + }, + "tracegroupgap": 0 + }, + "margin": { + "t": 60 + }, + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "xaxis": { + "anchor": "y", + "domain": [ + 0, + 1 + ], + "title": { + "text": "month_end" + } + }, + "yaxis": { + "anchor": "x", + "domain": [ + 0, + 1 + ], + "title": { + "text": "Total Return" + } + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# running solution\n", + "df_returns = pd.concat(objs=[df_returns_train, df_returns_test], ignore_index=True)\n", + "df_weights_index = equalise_weights(df_returns)\n", + "df_returns, df_weights_portfolio = generate_portfolio(df_returns_train, df_returns_test)\n", + "fig1, df_rtn = plot_total_return(\n", + " df_returns,\n", + " df_weights_index=df_weights_index,\n", + " df_weights_portfolio=df_weights_portfolio,\n", + ")\n", + "fig1" + ] + } + ], + "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.10.11" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Challenge.ipynb b/Challenge.ipynb new file mode 100644 index 0000000..8304f18 --- /dev/null +++ b/Challenge.ipynb @@ -0,0 +1,3326 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 458, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "---Python script Start--- 2023-08-05 13:39:49.348381\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import datetime\n", + "import plotly.express as px\n", + "from keras.models import Sequential\n", + "from keras.layers import LSTM, Dense, Dropout\n", + "from sklearn.preprocessing import MinMaxScaler\n", + "from sklearn.model_selection import train_test_split\n", + "import matplotlib.pyplot as plt\n", + "\n", + "print(\"---Python script Start---\", str(datetime.datetime.now()))" + ] + }, + { + "cell_type": "code", + "execution_count": 459, + "metadata": {}, + "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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
month_endStock1Stock10Stock11Stock14Stock16Stock18Stock19Stock20Stock21...Stock78Stock79Stock80Stock81Stock84Stock85Stock86Stock87Stock88Stock9
02010-01-310.042603-0.076059-0.149741-0.031251-0.053032-0.113752-0.0716710.0054300.005715...-0.0281680.017445-0.1000000.0180140.0447490.016824-0.111084-0.035229-0.0332040.030927
12010-02-28-0.015034-0.100305-0.0232790.0024840.0770880.0108490.0223830.0954800.067745...0.082448-0.1061950.0666690.000556-0.0108000.006326-0.026405-0.0137590.095241-0.010000
22010-03-310.1115450.0979330.1215010.0643570.1074390.0178830.0008920.0324250.018012...0.0363570.2200490.0989570.0888890.0886300.0472960.1535270.0344160.0417950.024809
32010-04-30-0.040491-0.034262-0.032848-0.0302320.0223880.085504-0.0071590.0527630.017920...0.0405790.004259-0.0521330.005099-0.0161640.012845-0.0047020.0197860.0420460.030303
42010-05-31-0.069359-0.0731880.0043490.010792-0.025548-0.001887-0.0125340.068973-0.026794...-0.025069-0.0622210.004332-0.023857-0.085645-0.041085-0.0833200.095238-0.124253-0.010784
..................................................................
872017-04-300.0576310.089651-0.090908-0.0455440.033207-0.0033310.036885-0.0175100.097012...0.0395480.0093880.0965690.0861400.0970510.036655-0.059447-0.005197-0.1110870.044074
882017-05-31-0.013557-0.027089-0.050713-0.052443-0.132316-0.013774-0.071147-0.045192-0.031881...-0.042451-0.0327990.069729-0.018660-0.0544220.071153-0.0830550.092917-0.039052-0.031945
892017-06-30-0.022060-0.098539-0.0165560.005218-0.008810-0.125433-0.0289370.069444-0.055806...-0.001562-0.055437-0.062808-0.0194750.034172-0.077723-0.0026160.0207830.0383000.011196
902017-07-310.0367950.0025270.0596800.0745630.1350410.0568640.0384760.1098080.023922...0.0968600.0899800.1428300.0013590.0059160.0641590.2349030.0840050.1124430.027391
912017-08-310.0321890.0010310.1010820.0089900.010417-0.0352040.1129290.0469010.082138...0.0733350.0835640.0111630.0168420.0261410.0224220.1164540.0197690.171767-0.005104
\n", + "

92 rows × 55 columns

\n", + "
" + ], + "text/plain": [ + " month_end Stock1 Stock10 Stock11 Stock14 Stock16 Stock18 \n", + "0 2010-01-31 0.042603 -0.076059 -0.149741 -0.031251 -0.053032 -0.113752 \\\n", + "1 2010-02-28 -0.015034 -0.100305 -0.023279 0.002484 0.077088 0.010849 \n", + "2 2010-03-31 0.111545 0.097933 0.121501 0.064357 0.107439 0.017883 \n", + "3 2010-04-30 -0.040491 -0.034262 -0.032848 -0.030232 0.022388 0.085504 \n", + "4 2010-05-31 -0.069359 -0.073188 0.004349 0.010792 -0.025548 -0.001887 \n", + ".. ... ... ... ... ... ... ... \n", + "87 2017-04-30 0.057631 0.089651 -0.090908 -0.045544 0.033207 -0.003331 \n", + "88 2017-05-31 -0.013557 -0.027089 -0.050713 -0.052443 -0.132316 -0.013774 \n", + "89 2017-06-30 -0.022060 -0.098539 -0.016556 0.005218 -0.008810 -0.125433 \n", + "90 2017-07-31 0.036795 0.002527 0.059680 0.074563 0.135041 0.056864 \n", + "91 2017-08-31 0.032189 0.001031 0.101082 0.008990 0.010417 -0.035204 \n", + "\n", + " Stock19 Stock20 Stock21 ... Stock78 Stock79 Stock80 Stock81 \n", + "0 -0.071671 0.005430 0.005715 ... -0.028168 0.017445 -0.100000 0.018014 \\\n", + "1 0.022383 0.095480 0.067745 ... 0.082448 -0.106195 0.066669 0.000556 \n", + "2 0.000892 0.032425 0.018012 ... 0.036357 0.220049 0.098957 0.088889 \n", + "3 -0.007159 0.052763 0.017920 ... 0.040579 0.004259 -0.052133 0.005099 \n", + "4 -0.012534 0.068973 -0.026794 ... -0.025069 -0.062221 0.004332 -0.023857 \n", + ".. ... ... ... ... ... ... ... ... \n", + "87 0.036885 -0.017510 0.097012 ... 0.039548 0.009388 0.096569 0.086140 \n", + "88 -0.071147 -0.045192 -0.031881 ... -0.042451 -0.032799 0.069729 -0.018660 \n", + "89 -0.028937 0.069444 -0.055806 ... -0.001562 -0.055437 -0.062808 -0.019475 \n", + "90 0.038476 0.109808 0.023922 ... 0.096860 0.089980 0.142830 0.001359 \n", + "91 0.112929 0.046901 0.082138 ... 0.073335 0.083564 0.011163 0.016842 \n", + "\n", + " Stock84 Stock85 Stock86 Stock87 Stock88 Stock9 \n", + "0 0.044749 0.016824 -0.111084 -0.035229 -0.033204 0.030927 \n", + "1 -0.010800 0.006326 -0.026405 -0.013759 0.095241 -0.010000 \n", + "2 0.088630 0.047296 0.153527 0.034416 0.041795 0.024809 \n", + "3 -0.016164 0.012845 -0.004702 0.019786 0.042046 0.030303 \n", + "4 -0.085645 -0.041085 -0.083320 0.095238 -0.124253 -0.010784 \n", + ".. ... ... ... ... ... ... \n", + "87 0.097051 0.036655 -0.059447 -0.005197 -0.111087 0.044074 \n", + "88 -0.054422 0.071153 -0.083055 0.092917 -0.039052 -0.031945 \n", + "89 0.034172 -0.077723 -0.002616 0.020783 0.038300 0.011196 \n", + "90 0.005916 0.064159 0.234903 0.084005 0.112443 0.027391 \n", + "91 0.026141 0.022422 0.116454 0.019769 0.171767 -0.005104 \n", + "\n", + "[92 rows x 55 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "df = pd.read_csv(\n", + " \"C:\\\\Users\\\\terre\\OneDrive - University of Cape Town\\\\Terrence\\\\Code\\\\prescient-coding-challenge-2023\\\\data\\\\returns_train.csv\"\n", + ")\n", + "display(df)" + ] + }, + { + "cell_type": "code", + "execution_count": 460, + "metadata": {}, + "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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
month_endStock1Stock10Stock11Stock14Stock16Stock18Stock19Stock20Stock21...Stock78Stock79Stock80Stock81Stock84Stock85Stock86Stock87Stock88Stock9
02017-09-300.0838990.0533190.049182-0.099562-0.114310-0.055291-0.037531-0.006674-0.057661...-0.066003-0.031052-0.004497-0.055256-0.0315160.0230680.031268-0.112615-0.0113070.022963
12017-10-310.0605360.0286760.0296250.029335-0.001841-0.106763-0.012945-0.0266030.044929...0.0489520.0707570.1797950.0421740.008201-0.0067200.093561-0.0467920.215918-0.018551
22017-11-30-0.1021930.027349-0.1281250.0733560.166052-0.0414080.0529440.1869320.106927...0.1239140.1566160.0704520.0147450.1258750.115800-0.046361-0.030274-0.0265210.018901
32017-12-31-0.045036-0.080070-0.1318990.0982280.248923-0.0290560.0567030.1921330.111679...0.1292620.034759-0.0641900.1028040.1534430.1396850.0111550.0042740.1115340.075942
42018-01-310.026665-0.025548-0.009082-0.018691-0.0012650.071279-0.0380670.1679670.013795...-0.0922030.075166-0.0198540.001060-0.0103870.1462720.1331660.122322-0.046485-0.049569
..................................................................
562022-05-31-0.083869-0.024526-0.009009-0.0444680.019794-0.0165040.000652-0.0352890.024911...-0.044724-0.1391940.0663550.0570870.0689010.0004580.068366-0.032767-0.004966-0.004018
572022-06-30-0.004672-0.0530940.0227260.057442-0.138842-0.213426-0.217772-0.120665-0.216392...-0.119287-0.0743270.381054-0.137041-0.158318-0.039239-0.238200-0.078775-0.179747-0.137902
582022-07-310.1450720.021164-0.064891-0.0193890.0225470.1920150.0533930.0155620.031020...0.0014830.020277-0.0047690.0518810.0997400.0199910.0325060.0518580.0938500.125350
592022-08-31-0.023993-0.166561-0.1456500.1219160.0454290.003722-0.1066940.033809-0.021830...-0.042483-0.0475570.029426-0.0679820.0470550.005474-0.046378-0.085405-0.006774-0.136339
602022-09-30-0.083590-0.038025-0.311732-0.102412-0.0749470.055505-0.035172-0.072121-0.032445...-0.1457990.060575-0.0669810.0423200.028347-0.069651-0.010064-0.0322640.123798-0.141075
\n", + "

61 rows × 55 columns

\n", + "
" + ], + "text/plain": [ + " month_end Stock1 Stock10 Stock11 Stock14 Stock16 Stock18 \n", + "0 2017-09-30 0.083899 0.053319 0.049182 -0.099562 -0.114310 -0.055291 \\\n", + "1 2017-10-31 0.060536 0.028676 0.029625 0.029335 -0.001841 -0.106763 \n", + "2 2017-11-30 -0.102193 0.027349 -0.128125 0.073356 0.166052 -0.041408 \n", + "3 2017-12-31 -0.045036 -0.080070 -0.131899 0.098228 0.248923 -0.029056 \n", + "4 2018-01-31 0.026665 -0.025548 -0.009082 -0.018691 -0.001265 0.071279 \n", + ".. ... ... ... ... ... ... ... \n", + "56 2022-05-31 -0.083869 -0.024526 -0.009009 -0.044468 0.019794 -0.016504 \n", + "57 2022-06-30 -0.004672 -0.053094 0.022726 0.057442 -0.138842 -0.213426 \n", + "58 2022-07-31 0.145072 0.021164 -0.064891 -0.019389 0.022547 0.192015 \n", + "59 2022-08-31 -0.023993 -0.166561 -0.145650 0.121916 0.045429 0.003722 \n", + "60 2022-09-30 -0.083590 -0.038025 -0.311732 -0.102412 -0.074947 0.055505 \n", + "\n", + " Stock19 Stock20 Stock21 ... Stock78 Stock79 Stock80 Stock81 \n", + "0 -0.037531 -0.006674 -0.057661 ... -0.066003 -0.031052 -0.004497 -0.055256 \\\n", + "1 -0.012945 -0.026603 0.044929 ... 0.048952 0.070757 0.179795 0.042174 \n", + "2 0.052944 0.186932 0.106927 ... 0.123914 0.156616 0.070452 0.014745 \n", + "3 0.056703 0.192133 0.111679 ... 0.129262 0.034759 -0.064190 0.102804 \n", + "4 -0.038067 0.167967 0.013795 ... -0.092203 0.075166 -0.019854 0.001060 \n", + ".. ... ... ... ... ... ... ... ... \n", + "56 0.000652 -0.035289 0.024911 ... -0.044724 -0.139194 0.066355 0.057087 \n", + "57 -0.217772 -0.120665 -0.216392 ... -0.119287 -0.074327 0.381054 -0.137041 \n", + "58 0.053393 0.015562 0.031020 ... 0.001483 0.020277 -0.004769 0.051881 \n", + "59 -0.106694 0.033809 -0.021830 ... -0.042483 -0.047557 0.029426 -0.067982 \n", + "60 -0.035172 -0.072121 -0.032445 ... -0.145799 0.060575 -0.066981 0.042320 \n", + "\n", + " Stock84 Stock85 Stock86 Stock87 Stock88 Stock9 \n", + "0 -0.031516 0.023068 0.031268 -0.112615 -0.011307 0.022963 \n", + "1 0.008201 -0.006720 0.093561 -0.046792 0.215918 -0.018551 \n", + "2 0.125875 0.115800 -0.046361 -0.030274 -0.026521 0.018901 \n", + "3 0.153443 0.139685 0.011155 0.004274 0.111534 0.075942 \n", + "4 -0.010387 0.146272 0.133166 0.122322 -0.046485 -0.049569 \n", + ".. ... ... ... ... ... ... \n", + "56 0.068901 0.000458 0.068366 -0.032767 -0.004966 -0.004018 \n", + "57 -0.158318 -0.039239 -0.238200 -0.078775 -0.179747 -0.137902 \n", + "58 0.099740 0.019991 0.032506 0.051858 0.093850 0.125350 \n", + "59 0.047055 0.005474 -0.046378 -0.085405 -0.006774 -0.136339 \n", + "60 0.028347 -0.069651 -0.010064 -0.032264 0.123798 -0.141075 \n", + "\n", + "[61 rows x 55 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "df = pd.read_csv(\n", + " \"C:\\\\Users\\\\terre\\OneDrive - University of Cape Town\\\\Terrence\\\\Code\\\\prescient-coding-challenge-2023\\\\data\\\\returns_test.csv\"\n", + ")\n", + "display(df)" + ] + }, + { + "cell_type": "code", + "execution_count": 461, + "metadata": {}, + "outputs": [], + "source": [ + "# data reads\n", + "df_returns_train = pd.read_csv(\"data/returns_train.csv\")\n", + "df_returns_test = pd.read_csv(\"data/returns_test.csv\")\n", + "df_returns_train[\"month_end\"] = pd.to_datetime(arg=df_returns_train[\"month_end\"]).apply(\n", + " lambda d: d.date()\n", + ")\n", + "df_returns_test[\"month_end\"] = pd.to_datetime(arg=df_returns_test[\"month_end\"]).apply(\n", + " lambda d: d.date()\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 462, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['Stock1', 'Stock10', 'Stock11', 'Stock14', 'Stock16', 'Stock18', 'Stock19', 'Stock20', 'Stock21', 'Stock22', 'Stock26', 'Stock3', 'Stock30', 'Stock31', 'Stock33', 'Stock34', 'Stock35', 'Stock38', 'Stock39', 'Stock4', 'Stock40', 'Stock41', 'Stock43', 'Stock44', 'Stock46', 'Stock47', 'Stock49', 'Stock5', 'Stock50', 'Stock54', 'Stock56', 'Stock59', 'Stock6', 'Stock61', 'Stock63', 'Stock64', 'Stock66', 'Stock69', 'Stock7', 'Stock70', 'Stock73', 'Stock75', 'Stock76', 'Stock77', 'Stock78', 'Stock79', 'Stock80', 'Stock81', 'Stock84', 'Stock85', 'Stock86', 'Stock87', 'Stock88', 'Stock9']\n" + ] + }, + { + "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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Stock1Stock10Stock11Stock14Stock16Stock18Stock19Stock20Stock21Stock22...Stock78Stock79Stock80Stock81Stock84Stock85Stock86Stock87Stock88Stock9
00.042603-0.076059-0.149741-0.031251-0.053032-0.113752-0.0716710.0054300.0057150.001399...-0.0281680.017445-0.1000000.0180140.0447490.016824-0.111084-0.035229-0.0332040.030927
1-0.015034-0.100305-0.0232790.0024840.0770880.0108490.0223830.0954800.0677450.082493...0.082448-0.1061950.0666690.000556-0.0108000.006326-0.026405-0.0137590.095241-0.010000
20.1115450.0979330.1215010.0643570.1074390.0178830.0008920.0324250.0180120.031663...0.0363570.2200490.0989570.0888890.0886300.0472960.1535270.0344160.0417950.024809
3-0.040491-0.034262-0.032848-0.0302320.0223880.085504-0.0071590.0527630.0179200.007674...0.0405790.004259-0.0521330.005099-0.0161640.012845-0.0047020.0197860.0420460.030303
4-0.069359-0.0731880.0043490.010792-0.025548-0.001887-0.0125340.068973-0.026794-0.056697...-0.025069-0.0622210.004332-0.023857-0.085645-0.041085-0.0833200.095238-0.124253-0.010784
..................................................................
870.0576310.089651-0.090908-0.0455440.033207-0.0033310.036885-0.0175100.097012-0.000908...0.0395480.0093880.0965690.0861400.0970510.036655-0.059447-0.005197-0.1110870.044074
88-0.013557-0.027089-0.050713-0.052443-0.132316-0.013774-0.071147-0.045192-0.031881-0.000167...-0.042451-0.0327990.069729-0.018660-0.0544220.071153-0.0830550.092917-0.039052-0.031945
89-0.022060-0.098539-0.0165560.005218-0.008810-0.125433-0.0289370.069444-0.055806-0.002845...-0.001562-0.055437-0.062808-0.0194750.034172-0.077723-0.0026160.0207830.0383000.011196
900.0367950.0025270.0596800.0745630.1350410.0568640.0384760.1098080.0239220.023766...0.0968600.0899800.1428300.0013590.0059160.0641590.2349030.0840050.1124430.027391
910.0321890.0010310.1010820.0089900.010417-0.0352040.1129290.0469010.082138-0.010214...0.0733350.0835640.0111630.0168420.0261410.0224220.1164540.0197690.171767-0.005104
\n", + "

92 rows × 54 columns

\n", + "
" + ], + "text/plain": [ + " Stock1 Stock10 Stock11 Stock14 Stock16 Stock18 Stock19 \n", + "0 0.042603 -0.076059 -0.149741 -0.031251 -0.053032 -0.113752 -0.071671 \\\n", + "1 -0.015034 -0.100305 -0.023279 0.002484 0.077088 0.010849 0.022383 \n", + "2 0.111545 0.097933 0.121501 0.064357 0.107439 0.017883 0.000892 \n", + "3 -0.040491 -0.034262 -0.032848 -0.030232 0.022388 0.085504 -0.007159 \n", + "4 -0.069359 -0.073188 0.004349 0.010792 -0.025548 -0.001887 -0.012534 \n", + ".. ... ... ... ... ... ... ... \n", + "87 0.057631 0.089651 -0.090908 -0.045544 0.033207 -0.003331 0.036885 \n", + "88 -0.013557 -0.027089 -0.050713 -0.052443 -0.132316 -0.013774 -0.071147 \n", + "89 -0.022060 -0.098539 -0.016556 0.005218 -0.008810 -0.125433 -0.028937 \n", + "90 0.036795 0.002527 0.059680 0.074563 0.135041 0.056864 0.038476 \n", + "91 0.032189 0.001031 0.101082 0.008990 0.010417 -0.035204 0.112929 \n", + "\n", + " Stock20 Stock21 Stock22 ... Stock78 Stock79 Stock80 Stock81 \n", + "0 0.005430 0.005715 0.001399 ... -0.028168 0.017445 -0.100000 0.018014 \\\n", + "1 0.095480 0.067745 0.082493 ... 0.082448 -0.106195 0.066669 0.000556 \n", + "2 0.032425 0.018012 0.031663 ... 0.036357 0.220049 0.098957 0.088889 \n", + "3 0.052763 0.017920 0.007674 ... 0.040579 0.004259 -0.052133 0.005099 \n", + "4 0.068973 -0.026794 -0.056697 ... -0.025069 -0.062221 0.004332 -0.023857 \n", + ".. ... ... ... ... ... ... ... ... \n", + "87 -0.017510 0.097012 -0.000908 ... 0.039548 0.009388 0.096569 0.086140 \n", + "88 -0.045192 -0.031881 -0.000167 ... -0.042451 -0.032799 0.069729 -0.018660 \n", + "89 0.069444 -0.055806 -0.002845 ... -0.001562 -0.055437 -0.062808 -0.019475 \n", + "90 0.109808 0.023922 0.023766 ... 0.096860 0.089980 0.142830 0.001359 \n", + "91 0.046901 0.082138 -0.010214 ... 0.073335 0.083564 0.011163 0.016842 \n", + "\n", + " Stock84 Stock85 Stock86 Stock87 Stock88 Stock9 \n", + "0 0.044749 0.016824 -0.111084 -0.035229 -0.033204 0.030927 \n", + "1 -0.010800 0.006326 -0.026405 -0.013759 0.095241 -0.010000 \n", + "2 0.088630 0.047296 0.153527 0.034416 0.041795 0.024809 \n", + "3 -0.016164 0.012845 -0.004702 0.019786 0.042046 0.030303 \n", + "4 -0.085645 -0.041085 -0.083320 0.095238 -0.124253 -0.010784 \n", + ".. ... ... ... ... ... ... \n", + "87 0.097051 0.036655 -0.059447 -0.005197 -0.111087 0.044074 \n", + "88 -0.054422 0.071153 -0.083055 0.092917 -0.039052 -0.031945 \n", + "89 0.034172 -0.077723 -0.002616 0.020783 0.038300 0.011196 \n", + "90 0.005916 0.064159 0.234903 0.084005 0.112443 0.027391 \n", + "91 0.026141 0.022422 0.116454 0.019769 0.171767 -0.005104 \n", + "\n", + "[92 rows x 54 columns]" + ] + }, + "execution_count": 462, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Ignoring the month_end from training set\n", + "\n", + "# Get column names as a list but ignore the first column\n", + "header_list = df.columns[1:].tolist()\n", + "print(header_list)\n", + "\n", + "\n", + "def process_dataframe(dataframe):\n", + " # Create a new dataframe without the 'month_end' column\n", + " new_dataframe = dataframe.drop([\"month_end\"], axis=1)\n", + " return new_dataframe\n", + "\n", + "\n", + "clean_df = process_dataframe(df_returns_train)\n", + "clean_df" + ] + }, + { + "cell_type": "code", + "execution_count": 463, + "metadata": {}, + "outputs": [], + "source": [ + "def equalise_weights(df: pd.DataFrame):\n", + " \"\"\"\n", + " Function to generate the equal weights, i.e. 1/p for each active stock within a month\n", + "\n", + " Args:\n", + " df: A return data frame. First column is month end and remaining columns are stocks\n", + "\n", + " Returns:\n", + " A dataframe of the same dimension but with values 1/p on active funds within a month\n", + "\n", + " \"\"\"\n", + "\n", + " # create df to house weights\n", + " n_length = len(df)\n", + " df_returns = df\n", + " df_weights = df_returns[:n_length].copy()\n", + " df_weights.set_index(\"month_end\", inplace=True)\n", + "\n", + " # list of stock names\n", + " list_stocks = list(df_returns.columns)\n", + " list_stocks.remove(\"month_end\")\n", + "\n", + " # assign 1/p\n", + " df_weights[list_stocks] = 1 / len(list_stocks)\n", + "\n", + " return df_weights" + ] + }, + { + "cell_type": "code", + "execution_count": 464, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 1/50\n", + "2/2 [==============================] - 10s 2s/step - loss: 0.2312 - val_loss: 0.2173\n", + "Epoch 2/50\n", + "2/2 [==============================] - 0s 108ms/step - loss: 0.1978 - val_loss: 0.1785\n", + "Epoch 3/50\n", + "2/2 [==============================] - 0s 120ms/step - loss: 0.1697 - val_loss: 0.1472\n", + "Epoch 4/50\n", + "2/2 [==============================] - 0s 128ms/step - loss: 0.1430 - val_loss: 0.1206\n", + "Epoch 5/50\n", + "2/2 [==============================] - 0s 101ms/step - loss: 0.1329 - val_loss: 0.1012\n", + "Epoch 6/50\n", + "2/2 [==============================] - 0s 100ms/step - loss: 0.1153 - val_loss: 0.0879\n", + "Epoch 7/50\n", + "2/2 [==============================] - 0s 117ms/step - loss: 0.1047 - val_loss: 0.0779\n", + "Epoch 8/50\n", + "2/2 [==============================] - 0s 100ms/step - loss: 0.1038 - val_loss: 0.0689\n", + "Epoch 9/50\n", + "2/2 [==============================] - 0s 100ms/step - loss: 0.0922 - val_loss: 0.0630\n", + "Epoch 10/50\n", + "2/2 [==============================] - 0s 103ms/step - loss: 0.0866 - val_loss: 0.0594\n", + "Epoch 11/50\n", + "2/2 [==============================] - 0s 105ms/step - loss: 0.0790 - val_loss: 0.0566\n", + "Epoch 12/50\n", + "2/2 [==============================] - 0s 120ms/step - loss: 0.0709 - val_loss: 0.0538\n", + "Epoch 13/50\n", + "2/2 [==============================] - 0s 104ms/step - loss: 0.0773 - val_loss: 0.0523\n", + "Epoch 14/50\n", + "2/2 [==============================] - 0s 101ms/step - loss: 0.0755 - val_loss: 0.0515\n", + "Epoch 15/50\n", + "2/2 [==============================] - 0s 104ms/step - loss: 0.0786 - val_loss: 0.0504\n", + "Epoch 16/50\n", + "2/2 [==============================] - 0s 132ms/step - loss: 0.0686 - val_loss: 0.0483\n", + "Epoch 17/50\n", + "2/2 [==============================] - 0s 125ms/step - loss: 0.0753 - val_loss: 0.0478\n", + "Epoch 18/50\n", + "2/2 [==============================] - 0s 103ms/step - loss: 0.0664 - val_loss: 0.0490\n", + "Epoch 19/50\n", + "2/2 [==============================] - 0s 102ms/step - loss: 0.0686 - val_loss: 0.0504\n", + "Epoch 20/50\n", + "2/2 [==============================] - 0s 100ms/step - loss: 0.0690 - val_loss: 0.0493\n", + "Epoch 21/50\n", + "2/2 [==============================] - 0s 109ms/step - loss: 0.0679 - val_loss: 0.0480\n", + "Epoch 22/50\n", + "2/2 [==============================] - 0s 108ms/step - loss: 0.0645 - val_loss: 0.0462\n", + "Epoch 23/50\n", + "2/2 [==============================] - 0s 104ms/step - loss: 0.0641 - val_loss: 0.0454\n", + "Epoch 24/50\n", + "2/2 [==============================] - 0s 104ms/step - loss: 0.0677 - val_loss: 0.0459\n", + "Epoch 25/50\n", + "2/2 [==============================] - 0s 102ms/step - loss: 0.0643 - val_loss: 0.0483\n", + "Epoch 26/50\n", + "2/2 [==============================] - 0s 137ms/step - loss: 0.0612 - val_loss: 0.0509\n", + "Epoch 27/50\n", + "2/2 [==============================] - 0s 108ms/step - loss: 0.0626 - val_loss: 0.0511\n", + "Epoch 28/50\n", + "2/2 [==============================] - 0s 113ms/step - loss: 0.0680 - val_loss: 0.0477\n", + "Epoch 29/50\n", + "2/2 [==============================] - 0s 113ms/step - loss: 0.0628 - val_loss: 0.0451\n", + "Epoch 30/50\n", + "2/2 [==============================] - 0s 106ms/step - loss: 0.0618 - val_loss: 0.0445\n", + "Epoch 31/50\n", + "2/2 [==============================] - 0s 104ms/step - loss: 0.0608 - val_loss: 0.0445\n", + "Epoch 32/50\n", + "2/2 [==============================] - 0s 120ms/step - loss: 0.0607 - val_loss: 0.0468\n", + "Epoch 33/50\n", + "2/2 [==============================] - 0s 109ms/step - loss: 0.0648 - val_loss: 0.0511\n", + "Epoch 34/50\n", + "2/2 [==============================] - 0s 122ms/step - loss: 0.0644 - val_loss: 0.0533\n", + "Epoch 35/50\n", + "2/2 [==============================] - 0s 100ms/step - loss: 0.0626 - val_loss: 0.0511\n", + "Epoch 36/50\n", + "2/2 [==============================] - 0s 120ms/step - loss: 0.0589 - val_loss: 0.0471\n", + "Epoch 37/50\n", + "2/2 [==============================] - 0s 117ms/step - loss: 0.0568 - val_loss: 0.0455\n", + "Epoch 38/50\n", + "2/2 [==============================] - 0s 107ms/step - loss: 0.0582 - val_loss: 0.0449\n", + "Epoch 39/50\n", + "2/2 [==============================] - 0s 120ms/step - loss: 0.0613 - val_loss: 0.0446\n", + "Epoch 40/50\n", + "2/2 [==============================] - 0s 112ms/step - loss: 0.0585 - val_loss: 0.0444\n", + "Epoch 41/50\n", + "2/2 [==============================] - 0s 108ms/step - loss: 0.0612 - val_loss: 0.0448\n", + "Epoch 42/50\n", + "2/2 [==============================] - 0s 104ms/step - loss: 0.0546 - val_loss: 0.0460\n", + "Epoch 43/50\n", + "2/2 [==============================] - 0s 124ms/step - loss: 0.0583 - val_loss: 0.0485\n", + "Epoch 44/50\n", + "2/2 [==============================] - 0s 124ms/step - loss: 0.0594 - val_loss: 0.0508\n", + "Epoch 45/50\n", + "2/2 [==============================] - 0s 108ms/step - loss: 0.0604 - val_loss: 0.0515\n", + "Epoch 46/50\n", + "2/2 [==============================] - 0s 124ms/step - loss: 0.0616 - val_loss: 0.0501\n", + "Epoch 47/50\n", + "2/2 [==============================] - 0s 100ms/step - loss: 0.0624 - val_loss: 0.0481\n", + "Epoch 48/50\n", + "2/2 [==============================] - 0s 124ms/step - loss: 0.0595 - val_loss: 0.0465\n", + "Epoch 49/50\n", + "2/2 [==============================] - 0s 133ms/step - loss: 0.0608 - val_loss: 0.0457\n", + "Epoch 50/50\n", + "2/2 [==============================] - 0s 108ms/step - loss: 0.0545 - val_loss: 0.0455\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 464, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Normalization\n", + "scaler = MinMaxScaler(feature_range=(0, 1))\n", + "scaled_data = scaler.fit_transform(clean_df.values)\n", + "\n", + "# Create a data structure with 60 time-steps and 1 output for each stock\n", + "lookback = 60\n", + "\n", + "X, y = [], []\n", + "for i in range(lookback, len(scaled_data)):\n", + " X.append(scaled_data[i - lookback : i])\n", + " y.append(scaled_data[i])\n", + "X, y = np.array(X), np.array(y)\n", + "\n", + "# Split the data into training and validation sets\n", + "X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)\n", + "\n", + "# Initialize the LSTM model\n", + "model = Sequential()\n", + "\n", + "model.add(\n", + " LSTM(\n", + " units=50,\n", + " return_sequences=True,\n", + " input_shape=(X_train.shape[1], X_train.shape[2]),\n", + " )\n", + ")\n", + "model.add(Dropout(0.2))\n", + "\n", + "model.add(LSTM(units=50, return_sequences=True))\n", + "model.add(Dropout(0.2))\n", + "\n", + "model.add(LSTM(units=50, return_sequences=True))\n", + "model.add(Dropout(0.2))\n", + "\n", + "model.add(LSTM(units=50))\n", + "model.add(Dropout(0.2))\n", + "\n", + "model.add(\n", + " Dense(units=clean_df.shape[1])\n", + ") # number of units in the output layer should be equal to the number of stocks\n", + "\n", + "# Compile and train the model\n", + "model.compile(optimizer=\"adam\", loss=\"mean_squared_error\")\n", + "model.fit(X_train, y_train, epochs=50, batch_size=20, validation_data=(X_val, y_val))" + ] + }, + { + "cell_type": "code", + "execution_count": 465, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 1/50\n", + "3/3 [==============================] - 0s 98ms/step - loss: 0.0573 - val_loss: 0.0453\n", + "Epoch 2/50\n", + "3/3 [==============================] - 0s 87ms/step - loss: 0.0584 - val_loss: 0.0451\n", + "Epoch 3/50\n", + "3/3 [==============================] - 0s 85ms/step - loss: 0.0551 - val_loss: 0.0457\n", + "Epoch 4/50\n", + "3/3 [==============================] - 0s 96ms/step - loss: 0.0580 - val_loss: 0.0461\n", + "Epoch 5/50\n", + "3/3 [==============================] - 0s 81ms/step - loss: 0.0595 - val_loss: 0.0464\n", + "Epoch 6/50\n", + "3/3 [==============================] - 0s 78ms/step - loss: 0.0531 - val_loss: 0.0462\n", + "Epoch 7/50\n", + "3/3 [==============================] - 0s 87ms/step - loss: 0.0553 - val_loss: 0.0462\n", + "Epoch 8/50\n", + "3/3 [==============================] - 0s 83ms/step - loss: 0.0563 - val_loss: 0.0451\n", + "Epoch 9/50\n", + "3/3 [==============================] - 0s 91ms/step - loss: 0.0547 - val_loss: 0.0453\n", + "Epoch 10/50\n", + "3/3 [==============================] - 0s 78ms/step - loss: 0.0550 - val_loss: 0.0455\n", + "Epoch 11/50\n", + "3/3 [==============================] - 0s 84ms/step - loss: 0.0552 - val_loss: 0.0468\n", + "Epoch 12/50\n", + "3/3 [==============================] - 0s 80ms/step - loss: 0.0544 - val_loss: 0.0473\n", + "Epoch 13/50\n", + "3/3 [==============================] - 0s 85ms/step - loss: 0.0539 - val_loss: 0.0474\n", + "Epoch 14/50\n", + "3/3 [==============================] - 0s 92ms/step - loss: 0.0566 - val_loss: 0.0466\n", + "Epoch 15/50\n", + "3/3 [==============================] - 0s 86ms/step - loss: 0.0540 - val_loss: 0.0462\n", + "Epoch 16/50\n", + "3/3 [==============================] - 0s 81ms/step - loss: 0.0553 - val_loss: 0.0452\n", + "Epoch 17/50\n", + "3/3 [==============================] - 0s 84ms/step - loss: 0.0538 - val_loss: 0.0448\n", + "Epoch 18/50\n", + "3/3 [==============================] - 0s 94ms/step - loss: 0.0551 - val_loss: 0.0451\n", + "Epoch 19/50\n", + "3/3 [==============================] - 0s 117ms/step - loss: 0.0551 - val_loss: 0.0450\n", + "Epoch 20/50\n", + "3/3 [==============================] - 0s 102ms/step - loss: 0.0527 - val_loss: 0.0449\n", + "Epoch 21/50\n", + "3/3 [==============================] - 0s 100ms/step - loss: 0.0541 - val_loss: 0.0452\n", + "Epoch 22/50\n", + "3/3 [==============================] - 0s 101ms/step - loss: 0.0514 - val_loss: 0.0454\n", + "Epoch 23/50\n", + "3/3 [==============================] - 0s 113ms/step - loss: 0.0548 - val_loss: 0.0447\n", + "Epoch 24/50\n", + "3/3 [==============================] - 0s 89ms/step - loss: 0.0566 - val_loss: 0.0448\n", + "Epoch 25/50\n", + "3/3 [==============================] - 0s 119ms/step - loss: 0.0508 - val_loss: 0.0469\n", + "Epoch 26/50\n", + "3/3 [==============================] - 0s 114ms/step - loss: 0.0543 - val_loss: 0.0483\n", + "Epoch 27/50\n", + "3/3 [==============================] - 0s 82ms/step - loss: 0.0553 - val_loss: 0.0469\n", + "Epoch 28/50\n", + "3/3 [==============================] - 0s 93ms/step - loss: 0.0523 - val_loss: 0.0460\n", + "Epoch 29/50\n", + "3/3 [==============================] - 0s 92ms/step - loss: 0.0499 - val_loss: 0.0442\n", + "Epoch 30/50\n", + "3/3 [==============================] - 0s 83ms/step - loss: 0.0533 - val_loss: 0.0442\n", + "Epoch 31/50\n", + "3/3 [==============================] - 0s 85ms/step - loss: 0.0550 - val_loss: 0.0454\n", + "Epoch 32/50\n", + "3/3 [==============================] - 0s 88ms/step - loss: 0.0515 - val_loss: 0.0463\n", + "Epoch 33/50\n", + "3/3 [==============================] - 0s 94ms/step - loss: 0.0516 - val_loss: 0.0455\n", + "Epoch 34/50\n", + "3/3 [==============================] - 0s 87ms/step - loss: 0.0534 - val_loss: 0.0452\n", + "Epoch 35/50\n", + "3/3 [==============================] - 0s 90ms/step - loss: 0.0531 - val_loss: 0.0450\n", + "Epoch 36/50\n", + "3/3 [==============================] - 0s 92ms/step - loss: 0.0517 - val_loss: 0.0455\n", + "Epoch 37/50\n", + "3/3 [==============================] - 0s 90ms/step - loss: 0.0523 - val_loss: 0.0465\n", + "Epoch 38/50\n", + "3/3 [==============================] - 0s 104ms/step - loss: 0.0545 - val_loss: 0.0479\n", + "Epoch 39/50\n", + "3/3 [==============================] - 0s 88ms/step - loss: 0.0531 - val_loss: 0.0473\n", + "Epoch 40/50\n", + "3/3 [==============================] - 0s 88ms/step - loss: 0.0526 - val_loss: 0.0458\n", + "Epoch 41/50\n", + "3/3 [==============================] - 0s 101ms/step - loss: 0.0551 - val_loss: 0.0450\n", + "Epoch 42/50\n", + "3/3 [==============================] - 0s 99ms/step - loss: 0.0537 - val_loss: 0.0454\n", + "Epoch 43/50\n", + "3/3 [==============================] - 0s 95ms/step - loss: 0.0528 - val_loss: 0.0456\n", + "Epoch 44/50\n", + "3/3 [==============================] - 0s 101ms/step - loss: 0.0524 - val_loss: 0.0460\n", + "Epoch 45/50\n", + "3/3 [==============================] - 0s 90ms/step - loss: 0.0511 - val_loss: 0.0464\n", + "Epoch 46/50\n", + "3/3 [==============================] - 0s 87ms/step - loss: 0.0522 - val_loss: 0.0462\n", + "Epoch 47/50\n", + "3/3 [==============================] - 0s 81ms/step - loss: 0.0513 - val_loss: 0.0463\n", + "Epoch 48/50\n", + "3/3 [==============================] - 0s 94ms/step - loss: 0.0527 - val_loss: 0.0470\n", + "Epoch 49/50\n", + "3/3 [==============================] - 0s 84ms/step - loss: 0.0554 - val_loss: 0.0462\n", + "Epoch 50/50\n", + "3/3 [==============================] - 0s 84ms/step - loss: 0.0497 - val_loss: 0.0456\n" + ] + } + ], + "source": [ + "history = model.fit(\n", + " X_train, y_train, epochs=50, batch_size=10, validation_data=(X_val, y_val)\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 466, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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kZiYSGRkJAkJCVr/JSIiIiLiQ73eXMzKHUd48/ZWdG1Wxelw8pwbON5kWURERERE5HxUKh2YFQ+VfImIiIiISEDxlJs/GGC9vpR8iYiIiIhIQKkYoCNfjhfcEBEREREROR/t61bA5apPi+oXOB3KeVHyJSIiIiIiAaVtrfK0rVXe6TDOm6YdioiIiIiI+ICSLxERERERER9Q8iUiIiIiIuIDSr5ERERERER8QMmXiIiIiIiIDyj5EhERERER8QElXyIiIiIiIj6g5EtERERERMQHlHyJiIiIiIj4gJIvERERERERH1DyJSIiIiIi4gNKvkRERERERHxAyZeIiIiIiIgPKPkSERERERHxASVfIiIiIiIiPqDkS0RERERExAeUfImIiIiIiPiAki8REREREREfUPIlIiIiIiLiA0q+REREREREfEDJl4iIiIiIiA8o+RIREREREfEBJV8iIiIiIiI+oORLRERERETEB5R8iYiIiIiI+ICSLxERERERER9Q8iUiIiIiIuIDSr5ERERERER8QMmXiIiIiIiIDyj5EhERERER8QElXyIiIiIiIj6g5EtERERERMQHlHyJiIgUhvQ02LUcMjKcjkRERPyEki8REZHCsGAU/K8TLHnd6UhERMRPKPkSERHxtvQ0+Pkj2141HtxuZ+MRERG/oORLRETE27YthGPxtn1oK+z52dl4RETELyj5EhER8ba1n2ZuuM74XUREijMlXyIiIt6Ukgwbvrbtyx62n+s/s6mIIiJSrCn5EhER8aZNMyA1GcrVgg6PQYnykLzfpiKKiEix5njyNXbsWGrVqkVERASxsbEsW7bsnPtPmTKFhg0bEhERQbNmzZg+ffpZ9x04cCAul4vRo0fnuH/z5s1cf/31VKxYkbJly3LppZfy3XffeePliIhIcbd2sv1s3htCwqDJDfb7uinOxSQiIn7B0eRr8uTJDB06lBEjRrBq1SpatGhB586d2b9/f677L168mD59+nDXXXfx888/07NnT3r27Mn69ev/tO+0adNYunQpMTExf3qse/fupKWlMX/+fFauXEmLFi3o3r07+/bt8/prFBGRYuTYAdg6z7ab3WI/m2f+3PA1pBx3Ji4REfELjiZfr7zyCgMGDKB///40btyYcePGUbJkSd57771c9x8zZgxdunRh2LBhNGrUiGeeeYZWrVrx+us5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xAeUfImIiPiYy+Xiiy++cDoMERHxMSVfIiJSrNx55524XK4/3bp06eJ0aCIiUsSFOB2AiIiIr3Xp0oX3338/x33h4eEORSMiIsWFRr5ERKTYCQ8PJzo6OsetXLlygE0JfPPNN+natSslSpSgdu3aTJ06Ncffr1u3jquuuooSJUpQoUIF7rnnHo4dO5Zjn/fee48mTZoQHh5OlSpVGDx4cI7HDx48yA033EDJkiWpV68eX331VeG+aBERcZySLxERkTM8+eST9OrVizVr1nD77bdz6623smHDBgCSk5Pp3Lkz5cqVY/ny5UyZMoW5c+fmSK7efPNNBg0axD333MO6dev46quvqFu3bo7nePrpp7nllltYu3Yt1157LbfffjuHDx/26esUERHfcrndbrfTQYiIiPjKnXfeyccff0xERESO+x9//HEef/xxXC4XAwcO5M0338x67OKLL6ZVq1a88cYbvPPOOzz22GPs2rWLUqVKATB9+nR69OjBnj17iIqKomrVqvTv359nn3021xhcLhdPPPEEzzzzDGAJXenSpZkxY4bWnomIFGFa8yUiIsXOlVdemSO5AihfvnzWdlxcXI7H4uLiWL16NQAbNmygRYsWWYkXwCWXXEJGRgabNm3C5XKxZ88eOnbseM4YmjdvnrVdqlQpypYty/79+/P7kkREJAAo+RIRkWKnVKlSf5oG6C0lSpTI036hoaE5fne5XGRkZBRGSCIi4ie05ktEROQMS5cu/dPvjRo1AqBRo0asWbOG5OTkrMd//PFHgoKCaNCgAWXKlKFWrVrMmzfPpzGLiIj/08iXiIgUO6dOnWLfvn057gsJCaFixYoATJkyhTZt2nDppZcyYcIEli1bxv/+9z8Abr/9dkaMGEG/fv0YOXIkBw4c4IEHHqBv375ERUUBMHLkSAYOHEjlypXp2rUrSUlJ/PjjjzzwwAO+faEiIuJXlHyJiEixM3PmTKpUqZLjvgYNGrBx40bAKhFOmjSJ+++/nypVqvDJJ5/QuHFjAEqWLMmsWbMYMmQIbdu2pWTJkvTq1YtXXnkl61j9+vXj5MmT/Pe//+WRRx6hYsWK3HTTTb57gSIi4pdU7VBEROQ0LpeLadOm0bNnT6dDERGRIkZrvkRERERERHxAyZeIiIiIiIgPaM2XiIjIaTQbX0RECotGvkRERERERHxAyZeIiIiIiIgPKPkSERERERHxASVfIiIiIiIiPqDkS0RERERExAeUfImIiIiIiPiAki8REREREREfUPIlIiIiIiLiA/8PweSziqLNEcUAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# summarize history for loss\n", + "plt.figure(figsize=(10, 6))\n", + "plt.plot(history.history[\"loss\"])\n", + "plt.plot(history.history[\"val_loss\"])\n", + "plt.title(\"Model Loss\")\n", + "plt.ylabel(\"Loss\")\n", + "plt.xlabel(\"Epoch\")\n", + "plt.legend([\"Train\", \"Validation\"], loc=\"upper right\")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 467, + "metadata": {}, + "outputs": [], + "source": [ + "model.compile(optimizer=\"adam\", loss=\"mean_squared_error\", metrics=[\"mae\", \"mape\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 468, + "metadata": {}, + "outputs": [], + "source": [ + "def generate_portfolio(df_train: pd.DataFrame, df_test: pd.DataFrame):\n", + " \"\"\"\n", + " Function to generate stocks weight allocation for time t+1 using historic data. Initial weights generated as 1/p for active stock within a month\n", + "\n", + " Args:\n", + " df_train: The training set of returns. First column is month end and remaining columns are stocks\n", + " df_test: The testing set of returns. First column is month end and remaining columns are stocks\n", + "\n", + " Returns:\n", + " The returns dataframe and the weights\n", + " \"\"\"\n", + "\n", + " print(\n", + " \"---> training set spans\",\n", + " df_train[\"month_end\"].min(),\n", + " df_train[\"month_end\"].max(),\n", + " )\n", + " print(\n", + " \"---> training set spans\",\n", + " df_test[\"month_end\"].min(),\n", + " df_test[\"month_end\"].max(),\n", + " )\n", + "\n", + " # initialise data\n", + " n_train = len(df_train)\n", + " df_returns = pd.concat(objs=[df_train, df_test], ignore_index=True)\n", + "\n", + " df_weights = equalise_weights(\n", + " df_returns[:n_train]\n", + " ) # df to store weights and create initial\n", + "\n", + " # list of stock names\n", + " list_stocks = list(df_returns.columns)\n", + " list_stocks.remove(\"month_end\")\n", + "\n", + " # Only positive predicted stock are considered\n", + " # Higher volitility have higher weighting\n", + " # Higher prediction through my machine learning algorithm (LSTM) will have higher weighting\n", + " for i in range(len(df_test)):\n", + " df_latest = df_returns[(df_returns[\"month_end\"] < df_test.loc[i, \"month_end\"])]\n", + "\n", + " if len(df_latest) >= lookback:\n", + " latest_returns = df_latest[-lookback:][list_stocks].values\n", + " else:\n", + " continue\n", + "\n", + " # reshape the latest returns to be suitable for the LSTM model\n", + " latest_returns = np.reshape(latest_returns, (1, lookback, len(list_stocks)))\n", + "\n", + " # predict the future returns using the LSTM model\n", + " predicted_returns = model.predict(latest_returns)\n", + "\n", + " # Filter out the stocks with positive predicted returns\n", + " positive_returns_indices = np.where(predicted_returns[0] > 0)[0]\n", + " positive_returns = predicted_returns[0][positive_returns_indices]\n", + " positive_stock_names = np.array(list_stocks)[positive_returns_indices]\n", + "\n", + " # Apply exponential weighting to the positive returns\n", + " exp_returns = np.exp(positive_returns)\n", + "\n", + " # Calculate the recent volatility for each stock\n", + " volatilities = np.std(latest_returns[0], axis=0)\n", + "\n", + " # Scale the weights of each stock correspondto its recent volatility\n", + " volatility_scaled_weights = exp_returns * volatilities[positive_returns_indices]\n", + "\n", + " # Normalize the weights to sum to 1\n", + " weights = volatility_scaled_weights / np.sum(volatility_scaled_weights)\n", + "\n", + " # Create a new weight dataframe\n", + " weights_dict = dict(zip(positive_stock_names, weights))\n", + " weights_dict[\"month_end\"] = df_test.loc[i, \"month_end\"]\n", + " df_this = pd.DataFrame(data=[weights_dict], columns=df_weights.columns)\n", + " df_this = df_this.fillna(0) # fill NaNs for stocks that have been excluded\n", + "\n", + " # Append the weights to the df_weights dataframe\n", + " df_weights = pd.concat(objs=[df_weights, df_this], ignore_index=True)\n", + "\n", + " # 10% limit check\n", + " if len(\n", + " np.array(df_weights[list_stocks])[np.array(df_weights[list_stocks]) > 0.101]\n", + " ):\n", + " raise Exception(r\"---> 10% limit exceeded\")\n", + "\n", + " return df_returns, df_weights" + ] + }, + { + "cell_type": "code", + "execution_count": 469, + "metadata": {}, + "outputs": [], + "source": [ + "def plot_total_return(\n", + " df_returns: pd.DataFrame,\n", + " df_weights_index: pd.DataFrame,\n", + " df_weights_portfolio: pd.DataFrame,\n", + "):\n", + " \"\"\"\n", + " Function to generate the two total return indices.\n", + "\n", + " Args:\n", + " df_returns: Ascending date ordered combined training and test returns data.\n", + " df_weights_index: Index weights. Equally weighted\n", + " df_weights_index: Portfolio weights. Your portfolio should use equally weighted for the training date range. If blank will be ignored\n", + "\n", + " Returns:\n", + " A plot of the two total return indices and the total return indices as a dataframe\n", + " \"\"\"\n", + "\n", + " # list of stock names\n", + " list_stocks = list(df_returns.columns)\n", + " list_stocks.remove(\"month_end\")\n", + "\n", + " # replace nans with 0 in return array\n", + " ar_returns = np.array(df_returns[list_stocks])\n", + " np.nan_to_num(x=ar_returns, copy=False, nan=0)\n", + "\n", + " # calc index\n", + " ar_rtn_index = np.array(df_weights_index[list_stocks]) * ar_returns\n", + " ar_rtn_port = np.array(df_weights_portfolio[list_stocks]) * ar_returns\n", + "\n", + " v_rtn_index = np.sum(ar_rtn_index, axis=1)\n", + " v_rtn_port = np.sum(ar_rtn_port, axis=1)\n", + "\n", + " # add return series to dataframe\n", + " df_rtn = pd.DataFrame(data=df_returns[\"month_end\"], columns=[\"month_end\"])\n", + " df_rtn[\"index\"] = v_rtn_index\n", + " df_rtn[\"portfolio\"] = v_rtn_port\n", + " df_rtn\n", + "\n", + " # create total return\n", + " base_price = 100\n", + " df_rtn.sort_values(by=\"month_end\", inplace=True)\n", + " df_rtn[\"index_tr\"] = ((1 + df_rtn[\"index\"]).cumprod()) * base_price\n", + " df_rtn[\"portfolio_tr\"] = ((1 + df_rtn[\"portfolio\"]).cumprod()) * base_price\n", + " df_rtn\n", + "\n", + " df_rtn_long = df_rtn[[\"month_end\", \"index_tr\", \"portfolio_tr\"]].melt(\n", + " id_vars=\"month_end\", var_name=\"series\", value_name=\"Total Return\"\n", + " )\n", + "\n", + " # plot\n", + " fig1 = px.line(\n", + " data_frame=df_rtn_long, x=\"month_end\", y=\"Total Return\", color=\"series\"\n", + " )\n", + "\n", + " return fig1, df_rtn" + ] + }, + { + "cell_type": "code", + "execution_count": 470, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "---> training set spans 2010-01-31 2017-08-31\n", + "---> training set spans 2017-09-30 2022-09-30\n", + "1/1 [==============================] - 2s 2s/step\n", + "1/1 [==============================] - 0s 33ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 42ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 38ms/step\n", + "1/1 [==============================] - 0s 33ms/step\n", + "1/1 [==============================] - 0s 30ms/step\n", + "1/1 [==============================] - 0s 38ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 41ms/step\n", + "1/1 [==============================] - 0s 37ms/step\n", + "1/1 [==============================] - 0s 28ms/step\n", + "1/1 [==============================] - 0s 28ms/step\n", + "1/1 [==============================] - 0s 35ms/step\n", + "1/1 [==============================] - 0s 33ms/step\n", + "1/1 [==============================] - 0s 28ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 30ms/step\n", + "1/1 [==============================] - 0s 35ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 28ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 29ms/step\n", + "1/1 [==============================] - 0s 28ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 33ms/step\n", + "1/1 [==============================] - 0s 44ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 37ms/step\n", + "1/1 [==============================] - 0s 29ms/step\n", + "1/1 [==============================] - 0s 37ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 33ms/step\n", + "1/1 [==============================] - 0s 35ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 44ms/step\n", + "1/1 [==============================] - 0s 29ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 40ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 44ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 33ms/step\n", + "1/1 [==============================] - 0s 38ms/step\n", + "1/1 [==============================] - 0s 29ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 36ms/step\n", + "1/1 [==============================] - 0s 28ms/step\n", + "1/1 [==============================] - 0s 32ms/step\n", + "1/1 [==============================] - 0s 39ms/step\n", + "1/1 [==============================] - 0s 60ms/step\n" + ] + }, + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "hovertemplate": "series=index_tr
month_end=%{x}
Total Return=%{y}", + "legendgroup": "index_tr", + "line": { + "color": "#636efa", + "dash": "solid" + }, + "marker": { + "symbol": "circle" + }, + "mode": "lines", + "name": "index_tr", + "orientation": "v", + "showlegend": true, + "type": "scatter", + "x": [ + "2010-01-31", + "2010-02-28", + "2010-03-31", + "2010-04-30", + "2010-05-31", + "2010-06-30", + "2010-07-31", + "2010-08-31", + "2010-09-30", + "2010-10-31", + "2010-11-30", + "2010-12-31", + "2011-01-31", + "2011-02-28", + "2011-03-31", + "2011-04-30", + "2011-05-31", + "2011-06-30", + "2011-07-31", + "2011-08-31", + "2011-09-30", + "2011-10-31", + "2011-11-30", + "2011-12-31", + "2012-01-31", + "2012-02-29", + "2012-03-31", + "2012-04-30", + "2012-05-31", + "2012-06-30", + "2012-07-31", + "2012-08-31", + "2012-09-30", + "2012-10-31", + "2012-11-30", + "2012-12-31", + "2013-01-31", + "2013-02-28", + "2013-03-31", + "2013-04-30", + "2013-05-31", + "2013-06-30", + "2013-07-31", + "2013-08-31", + "2013-09-30", + "2013-10-31", + "2013-11-30", + "2013-12-31", + "2014-01-31", + "2014-02-28", + "2014-03-31", + "2014-04-30", + "2014-05-31", + "2014-06-30", + "2014-07-31", + "2014-08-31", + "2014-09-30", + "2014-10-31", + "2014-11-30", + "2014-12-31", + "2015-01-31", + "2015-02-28", + "2015-03-31", + "2015-04-30", + "2015-05-31", + "2015-06-30", + "2015-07-31", + "2015-08-31", + "2015-09-30", + "2015-10-31", + "2015-11-30", + "2015-12-31", + "2016-01-31", + "2016-02-29", + "2016-03-31", + "2016-04-30", + "2016-05-31", + "2016-06-30", + "2016-07-31", + "2016-08-31", + "2016-09-30", + "2016-10-31", + "2016-11-30", + "2016-12-31", + "2017-01-31", + "2017-02-28", + "2017-03-31", + "2017-04-30", + "2017-05-31", + "2017-06-30", + "2017-07-31", + "2017-08-31", + "2017-09-30", + "2017-10-31", + "2017-11-30", + "2017-12-31", + "2018-01-31", + "2018-02-28", + "2018-03-31", + "2018-04-30", + "2018-05-31", + "2018-06-30", + "2018-07-31", + "2018-08-31", + "2018-09-30", + "2018-10-31", + "2018-11-30", + "2018-12-31", + "2019-01-31", + "2019-02-28", + "2019-03-31", + "2019-04-30", + "2019-05-31", + "2019-06-30", + "2019-07-31", + "2019-08-31", + "2019-09-30", + "2019-10-31", + "2019-11-30", + "2019-12-31", + "2020-01-31", + "2020-02-29", + "2020-03-31", + "2020-04-30", + "2020-05-31", + "2020-06-30", + "2020-07-31", + "2020-08-31", + "2020-09-30", + "2020-10-31", + "2020-11-30", + "2020-12-31", + "2021-01-31", + "2021-02-28", + "2021-03-31", + "2021-04-30", + "2021-05-31", + "2021-06-30", + "2021-07-31", + "2021-08-31", + "2021-09-30", + "2021-10-31", + "2021-11-30", + "2021-12-31", + "2022-01-31", + "2022-02-28", + "2022-03-31", + "2022-04-30", + "2022-05-31", + "2022-06-30", + "2022-07-31", + "2022-08-31", + "2022-09-30" + ], + "xaxis": "x", + "y": [ + 98.40422503465814, + 100.93798662242051, + 107.68288522534036, + 109.7995615285553, + 107.23242817648126, + 106.43966113566464, + 113.91349320384909, + 112.29508785565194, + 122.12549889316125, + 124.87638245152488, + 124.37504919099058, + 131.02440630172168, + 125.53974799975698, + 128.16916055886097, + 130.8873968980463, + 134.06376216689685, + 133.87076190339369, + 132.59760255874266, + 132.19487983933925, + 133.93330703110996, + 130.5237798699564, + 139.62267767903492, + 142.58187781542216, + 140.81510171281383, + 147.43151378288488, + 151.98680235958813, + 152.40074939942423, + 156.02418771795553, + 152.8161411497546, + 155.48775962326914, + 159.96753392766976, + 161.2555092837166, + 163.55646116844596, + 166.76092531656502, + 169.35948468015056, + 179.51302600735073, + 178.97405279416094, + 175.715694663543, + 177.78002433183755, + 173.6270905121469, + 179.05309265268664, + 174.40587342069082, + 177.70371255865953, + 180.45868512898545, + 190.13847786882747, + 199.8342118600708, + 195.61245975954753, + 198.21223347276447, + 192.83928084761467, + 199.0110446369025, + 209.28517002996813, + 216.99871696931413, + 215.54672594503234, + 221.47400674506645, + 229.30151970138036, + 231.71558423761155, + 223.30433148163613, + 230.03817391436593, + 237.31763181271953, + 238.0489478471065, + 253.40103532914986, + 257.65372033222815, + 250.32521657304318, + 261.0487471728084, + 244.49076503231, + 240.52762911874868, + 239.19767530915811, + 235.3547408233148, + 222.77135367139928, + 234.26747289033133, + 222.1090879667571, + 213.67839960700294, + 220.78695617407473, + 238.25962030287462, + 265.5875885745103, + 281.4830979229849, + 265.08963832048636, + 272.6053430199218, + 290.46865219314884, + 277.2773336280136, + 279.3254063118272, + 277.71860735593725, + 274.1748170835136, + 284.22745240063205, + 296.41653958631605, + 293.1716168419007, + 293.7023992516104, + 296.36419976912174, + 289.35664391915265, + 278.34458913811073, + 293.49195242698454, + 302.7447105318491, + 293.3548318464133, + 306.265024468535, + 316.34007571598295, + 320.6050708597117, + 319.8398684632358, + 314.3939885564841, + 299.3651563947235, + 311.75016932500927, + 286.5744449293349, + 284.51792257776367, + 296.12916505146376, + 296.143910254921, + 290.4436965043894, + 286.9509171356384, + 285.7353782660105, + 295.5481203141585, + 303.41923749726544, + 308.1306150282999, + 298.5647435963675, + 309.02289813595524, + 300.56226016303185, + 312.06482538817767, + 298.2095280041462, + 288.517714586796, + 289.2751273268798, + 302.77492930198224, + 296.15403611536595, + 301.300222213508, + 288.1274106750146, + 256.3501391424798, + 198.61104648895602, + 232.17876375277234, + 226.37299938748362, + 251.28070815857097, + 253.4386819534693, + 248.00730686110998, + 251.20981465197428, + 242.3029989334201, + 277.8151656727379, + 304.3408079639736, + 323.87054861742905, + 344.77548375050344, + 366.4195141948255, + 381.31140527510394, + 402.786450399951, + 391.0039272495355, + 405.4842305132651, + 429.09830839640495, + 428.2369585651376, + 437.4693282884787, + 444.0045333993229, + 476.727609087252, + 484.55475348051664, + 493.4639696855345, + 514.924562516531, + 488.9124266125262, + 482.9373630725979, + 440.5963276777227, + 459.5446640353366, + 452.01765890108527, + 450.7681381232569 + ], + "yaxis": "y" + }, + { + "hovertemplate": "series=portfolio_tr
month_end=%{x}
Total Return=%{y}", + "legendgroup": "portfolio_tr", + "line": { + "color": "#EF553B", + "dash": "solid" + }, + "marker": { + "symbol": "circle" + }, + "mode": "lines", + "name": "portfolio_tr", + "orientation": "v", + "showlegend": true, + "type": "scatter", + "x": [ + "2010-01-31", + "2010-02-28", + "2010-03-31", + "2010-04-30", + "2010-05-31", + "2010-06-30", + "2010-07-31", + "2010-08-31", + "2010-09-30", + "2010-10-31", + "2010-11-30", + "2010-12-31", + "2011-01-31", + "2011-02-28", + "2011-03-31", + "2011-04-30", + "2011-05-31", + "2011-06-30", + "2011-07-31", + "2011-08-31", + "2011-09-30", + "2011-10-31", + "2011-11-30", + "2011-12-31", + "2012-01-31", + "2012-02-29", + "2012-03-31", + "2012-04-30", + "2012-05-31", + "2012-06-30", + "2012-07-31", + "2012-08-31", + "2012-09-30", + "2012-10-31", + "2012-11-30", + "2012-12-31", + "2013-01-31", + "2013-02-28", + "2013-03-31", + "2013-04-30", + "2013-05-31", + "2013-06-30", + "2013-07-31", + "2013-08-31", + "2013-09-30", + "2013-10-31", + "2013-11-30", + "2013-12-31", + "2014-01-31", + "2014-02-28", + "2014-03-31", + "2014-04-30", + "2014-05-31", + "2014-06-30", + "2014-07-31", + "2014-08-31", + "2014-09-30", + "2014-10-31", + "2014-11-30", + "2014-12-31", + "2015-01-31", + "2015-02-28", + "2015-03-31", + "2015-04-30", + "2015-05-31", + "2015-06-30", + "2015-07-31", + "2015-08-31", + "2015-09-30", + "2015-10-31", + "2015-11-30", + "2015-12-31", + "2016-01-31", + "2016-02-29", + "2016-03-31", + "2016-04-30", + "2016-05-31", + "2016-06-30", + "2016-07-31", + "2016-08-31", + "2016-09-30", + "2016-10-31", + "2016-11-30", + "2016-12-31", + "2017-01-31", + "2017-02-28", + "2017-03-31", + "2017-04-30", + "2017-05-31", + "2017-06-30", + "2017-07-31", + "2017-08-31", + "2017-09-30", + "2017-10-31", + "2017-11-30", + "2017-12-31", + "2018-01-31", + "2018-02-28", + "2018-03-31", + "2018-04-30", + "2018-05-31", + "2018-06-30", + "2018-07-31", + "2018-08-31", + "2018-09-30", + "2018-10-31", + "2018-11-30", + "2018-12-31", + "2019-01-31", + "2019-02-28", + "2019-03-31", + "2019-04-30", + "2019-05-31", + "2019-06-30", + "2019-07-31", + "2019-08-31", + "2019-09-30", + "2019-10-31", + "2019-11-30", + "2019-12-31", + "2020-01-31", + "2020-02-29", + "2020-03-31", + "2020-04-30", + "2020-05-31", + "2020-06-30", + "2020-07-31", + "2020-08-31", + "2020-09-30", + "2020-10-31", + "2020-11-30", + "2020-12-31", + "2021-01-31", + "2021-02-28", + "2021-03-31", + "2021-04-30", + "2021-05-31", + "2021-06-30", + "2021-07-31", + "2021-08-31", + "2021-09-30", + "2021-10-31", + "2021-11-30", + "2021-12-31", + "2022-01-31", + "2022-02-28", + "2022-03-31", + "2022-04-30", + "2022-05-31", + "2022-06-30", + "2022-07-31", + "2022-08-31", + "2022-09-30" + ], + "xaxis": "x", + "y": [ + 98.40422503465814, + 100.93798662242051, + 107.68288522534036, + 109.7995615285553, + 107.23242817648126, + 106.43966113566464, + 113.91349320384909, + 112.29508785565194, + 122.12549889316125, + 124.87638245152488, + 124.37504919099058, + 131.02440630172168, + 125.53974799975698, + 128.16916055886097, + 130.8873968980463, + 134.06376216689685, + 133.87076190339369, + 132.59760255874266, + 132.19487983933925, + 133.93330703110996, + 130.5237798699564, + 139.62267767903492, + 142.58187781542216, + 140.81510171281383, + 147.43151378288488, + 151.98680235958813, + 152.40074939942423, + 156.02418771795553, + 152.8161411497546, + 155.48775962326914, + 159.96753392766976, + 161.2555092837166, + 163.55646116844596, + 166.76092531656502, + 169.35948468015056, + 179.51302600735073, + 178.97405279416094, + 175.715694663543, + 177.78002433183755, + 173.6270905121469, + 179.05309265268664, + 174.40587342069082, + 177.70371255865953, + 180.45868512898545, + 190.13847786882747, + 199.8342118600708, + 195.61245975954753, + 198.21223347276447, + 192.83928084761467, + 199.0110446369025, + 209.28517002996813, + 216.99871696931413, + 215.54672594503234, + 221.47400674506645, + 229.30151970138036, + 231.71558423761155, + 223.30433148163613, + 230.03817391436593, + 237.31763181271953, + 238.0489478471065, + 253.40103532914986, + 257.65372033222815, + 250.32521657304318, + 261.0487471728084, + 244.49076503231, + 240.52762911874868, + 239.19767530915811, + 235.3547408233148, + 222.77135367139928, + 234.26747289033133, + 222.1090879667571, + 213.67839960700294, + 220.78695617407473, + 238.25962030287462, + 265.5875885745103, + 281.4830979229849, + 265.08963832048636, + 272.6053430199218, + 290.46865219314884, + 277.2773336280136, + 279.3254063118272, + 277.71860735593725, + 274.1748170835136, + 284.22745240063205, + 296.41653958631605, + 293.1716168419007, + 293.7023992516104, + 296.36419976912174, + 289.35664391915265, + 278.34458913811073, + 293.49195242698454, + 302.7447105318491, + 291.80561414251906, + 308.7046914571386, + 317.37340117854444, + 316.00027449376114, + 315.04819761142625, + 306.54761587955977, + 288.5749801417521, + 297.3710943578813, + 270.8644143968313, + 269.88887599309936, + 284.5468402742835, + 283.1334606819686, + 281.18459819952244, + 281.7586332848043, + 277.13224150524445, + 290.7454324362917, + 299.42661412519664, + 310.52344774816237, + 300.32090977991925, + 304.6164403283686, + 298.92563811425674, + 314.8609833784845, + 302.6856522412175, + 298.72485372367873, + 294.70785693989063, + 309.5799623471108, + 301.35372611330456, + 308.74983744621244, + 296.54105058632473, + 268.3931479189067, + 209.3687829199057, + 252.41495812129688, + 246.66633447566832, + 280.8818649402667, + 288.34459739399836, + 281.0120003342246, + 277.5546298421611, + 266.2065334238612, + 306.9437347537798, + 342.3440844746676, + 377.15920697630804, + 410.73728795625203, + 443.0053154812966, + 467.6019738311346, + 489.5185046824169, + 471.5808724758861, + 495.76860453946597, + 530.2732496470309, + 534.0880240810674, + 546.5960135983152, + 559.7921928630421, + 619.5674937724186, + 631.5052822392762, + 635.2106136046119, + 655.0297003162399, + 613.178964349321, + 604.2691492939866, + 549.8169148037354, + 575.2233267859957, + 556.6656617493052, + 544.134608994288 + ], + "yaxis": "y" + } + ], + "layout": { + "legend": { + "title": { + "text": "series" + }, + "tracegroupgap": 0 + }, + "margin": { + "t": 60 + }, + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "xaxis": { + "anchor": "y", + "domain": [ + 0, + 1 + ], + "title": { + "text": "month_end" + } + }, + "yaxis": { + "anchor": "x", + "domain": [ + 0, + 1 + ], + "title": { + "text": "Total Return" + } + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# running solution\n", + "df_returns = pd.concat(objs=[df_returns_train, df_returns_test], ignore_index=True)\n", + "df_weights_index = equalise_weights(df_returns)\n", + "df_returns, df_weights_portfolio = generate_portfolio(df_returns_train, df_returns_test)\n", + "fig1, df_rtn = plot_total_return(\n", + " df_returns,\n", + " df_weights_index=df_weights_index,\n", + " df_weights_portfolio=df_weights_portfolio,\n", + ")\n", + "fig1" + ] + } + ], + "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.10.11" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/solution_skeleton.py b/solution_skeleton.py index 7fc2fa5..ee39af2 100644 --- a/solution_skeleton.py +++ b/solution_skeleton.py @@ -1,173 +1,273 @@ # %% + import numpy as np import pandas as pd import datetime import plotly.express as px +from keras.models import Sequential +from keras.layers import LSTM, Dense, Dropout +from sklearn.preprocessing import MinMaxScaler +from sklearn.model_selection import train_test_split +import matplotlib.pyplot as plt +print("---Python script Start---", str(datetime.datetime.now())) -print('---Python script Start---', str(datetime.datetime.now())) - -# %% +df = pd.read_csv( + "C:\\Users\\terre\OneDrive - University of Cape Town\\Terrence\\Code\\prescient-coding-challenge-2023\\data\\returns_train.csv" +) # data reads -df_returns_train = pd.read_csv('data/returns_train.csv') -df_returns_test = pd.read_csv('data/returns_test.csv') -df_returns_train['month_end'] = pd.to_datetime(arg=df_returns_train['month_end']).apply(lambda d: d.date()) -df_returns_test['month_end'] = pd.to_datetime(arg=df_returns_test['month_end']).apply(lambda d: d.date()) +df_returns_train = pd.read_csv("data/returns_train.csv") +df_returns_test = pd.read_csv("data/returns_test.csv") +df_returns_train["month_end"] = pd.to_datetime(arg=df_returns_train["month_end"]).apply( + lambda d: d.date() +) +df_returns_test["month_end"] = pd.to_datetime(arg=df_returns_test["month_end"]).apply( + lambda d: d.date() +) -# %% +# Ignoring the month_end from training set -def equalise_weights(df: pd.DataFrame): +# Get column names as a list but ignore the first column +header_list = df.columns[1:].tolist() +print(header_list) + + +def process_dataframe(dataframe): + # Create a new dataframe without the 'month_end' column + new_dataframe = dataframe.drop(["month_end"], axis=1) + return new_dataframe + + +clean_df = process_dataframe(df_returns_train) - ''' - Function to generate the equal weights, i.e. 1/p for each active stock within a month - Args: - df: A return data frame. First column is month end and remaining columns are stocks +def equalise_weights(df: pd.DataFrame): + """ + Function to generate the equal weights, i.e. 1/p for each active stock within a month + + Args: + df: A return data frame. First column is month end and remaining columns are stocks - Returns: - A dataframe of the same dimension but with values 1/p on active funds within a month + Returns: + A dataframe of the same dimension but with values 1/p on active funds within a month - ''' + """ # create df to house weights n_length = len(df) df_returns = df df_weights = df_returns[:n_length].copy() - df_weights.set_index('month_end', inplace=True) + df_weights.set_index("month_end", inplace=True) # list of stock names list_stocks = list(df_returns.columns) - list_stocks.remove('month_end') + list_stocks.remove("month_end") # assign 1/p - df_weights[list_stocks] = 1/len(list_stocks) + df_weights[list_stocks] = 1 / len(list_stocks) return df_weights -# %% +# Normalization +scaler = MinMaxScaler(feature_range=(0, 1)) +scaled_data = scaler.fit_transform(clean_df.values) -def generate_portfolio(df_train: pd.DataFrame, df_test: pd.DataFrame): +# Create a data structure with 60 time-steps and 1 output for each stock +lookback = 60 + +X, y = [], [] +for i in range(lookback, len(scaled_data)): + X.append(scaled_data[i - lookback : i]) + y.append(scaled_data[i]) +X, y = np.array(X), np.array(y) + +# Split the data into training and validation sets +X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42) + +# Initialize the LSTM model +model = Sequential() + +model.add( + LSTM( + units=50, + return_sequences=True, + input_shape=(X_train.shape[1], X_train.shape[2]), + ) +) +model.add(Dropout(0.2)) + +model.add(LSTM(units=50, return_sequences=True)) +model.add(Dropout(0.2)) - ''' - Function to generate stocks weight allocation for time t+1 using historic data. Initial weights generated as 1/p for active stock within a month +model.add(LSTM(units=50, return_sequences=True)) +model.add(Dropout(0.2)) - Args: - df_train: The training set of returns. First column is month end and remaining columns are stocks - df_test: The testing set of returns. First column is month end and remaining columns are stocks +model.add(LSTM(units=50)) +model.add(Dropout(0.2)) - Returns: - The returns dataframe and the weights - ''' +model.add( + Dense(units=clean_df.shape[1]) +) # number of units in the output layer should be equal to the number of stocks - print('---> training set spans', df_train['month_end'].min(), df_train['month_end'].max()) - print('---> training set spans', df_test['month_end'].min(), df_test['month_end'].max()) +# Compile and train the model +model.compile(optimizer="adam", loss="mean_squared_error") +model.fit(X_train, y_train, epochs=50, batch_size=20, validation_data=(X_val, y_val)) + + +def generate_portfolio(df_train: pd.DataFrame, df_test: pd.DataFrame): + """ + Function to generate stocks weight allocation for time t+1 using historic data. Initial weights generated as 1/p for active stock within a month + + Args: + df_train: The training set of returns. First column is month end and remaining columns are stocks + df_test: The testing set of returns. First column is month end and remaining columns are stocks + + Returns: + The returns dataframe and the weights + """ + + print( + "---> training set spans", + df_train["month_end"].min(), + df_train["month_end"].max(), + ) + print( + "---> training set spans", + df_test["month_end"].min(), + df_test["month_end"].max(), + ) # initialise data n_train = len(df_train) df_returns = pd.concat(objs=[df_train, df_test], ignore_index=True) - df_weights = equalise_weights(df_returns[:n_train]) # df to store weights and create initial + df_weights = equalise_weights( + df_returns[:n_train] + ) # df to store weights and create initial # list of stock names list_stocks = list(df_returns.columns) - list_stocks.remove('month_end') + list_stocks.remove("month_end") - # <<--------------------- YOUR CODE GOES BELOW THIS LINE --------------------->> + # Only positive predicted stock are considered + # Higher volitility have higher weighting + # Higher prediction through my machine learning algorithm (LSTM) will have higher weighting + for i in range(len(df_test)): + df_latest = df_returns[(df_returns["month_end"] < df_test.loc[i, "month_end"])] - # This is your playground. Delete/modify any of the code here and replace with - # your methodology. Below we provide a simple, naive estimation to illustrate - # how we think you should go about structuring your submission and your comments: + if len(df_latest) >= lookback: + latest_returns = df_latest[-lookback:][list_stocks].values + else: + continue - # We use a static Inverse Volatility Weighting (https://en.wikipedia.org/wiki/Inverse-variance_weighting) - # strategy to generate portfolio weights. - # Use the latest available data at that point in time - - for i in range(len(df_test)): + # reshape the latest returns to be suitable for the LSTM model + latest_returns = np.reshape(latest_returns, (1, lookback, len(list_stocks))) - # latest data at this point - df_latest = df_returns[(df_returns['month_end'] < df_test.loc[i, 'month_end'])] - - # vol calc - df_w = pd.DataFrame() - df_w['vol'] = df_latest.std(numeric_only=True) # calculate stock volatility - df_w['inv_vol'] = 1/df_w['vol'] # calculate the inverse volatility - df_w['tot_inv_vol'] = df_w['inv_vol'].sum() # calculate the total inverse volatility - df_w['weight'] = df_w['inv_vol']/df_w['tot_inv_vol'] # calculate weight based on inverse volatility - df_w.reset_index(inplace=True, names='name') - - # add to all weights - df_this = pd.DataFrame(data=[[df_test.loc[i, 'month_end']] + df_w['weight'].to_list()], columns=df_latest.columns) - df_weights = pd.concat(objs=[df_weights, df_this], ignore_index=True) - - # <<--------------------- YOUR CODE GOES ABOVE THIS LINE --------------------->> - - # 10% limit check - if len(np.array(df_weights[list_stocks])[np.array(df_weights[list_stocks]) > 0.101]): + # predict the future returns using the LSTM model + predicted_returns = model.predict(latest_returns) - raise Exception(r'---> 10% limit exceeded') + # Filter out the stocks with positive predicted returns + positive_returns_indices = np.where(predicted_returns[0] > 0)[0] + positive_returns = predicted_returns[0][positive_returns_indices] + positive_stock_names = np.array(list_stocks)[positive_returns_indices] - return df_returns, df_weights + # Apply exponential weighting to the positive returns + exp_returns = np.exp(positive_returns) + # Calculate the recent volatility for each stock + volatilities = np.std(latest_returns[0], axis=0) -# %% + # Scale the weights of each stock correspondto its recent volatility + volatility_scaled_weights = exp_returns * volatilities[positive_returns_indices] + # Normalize the weights to sum to 1 + weights = volatility_scaled_weights / np.sum(volatility_scaled_weights) -def plot_total_return(df_returns: pd.DataFrame, df_weights_index: pd.DataFrame, df_weights_portfolio: pd.DataFrame): + # Create a new weight dataframe + weights_dict = dict(zip(positive_stock_names, weights)) + weights_dict["month_end"] = df_test.loc[i, "month_end"] + df_this = pd.DataFrame(data=[weights_dict], columns=df_weights.columns) + df_this = df_this.fillna(0) # fill NaNs for stocks that have been excluded - ''' - Function to generate the two total return indices. + # Append the weights to the df_weights dataframe + df_weights = pd.concat(objs=[df_weights, df_this], ignore_index=True) + + # 10% limit check + if len( + np.array(df_weights[list_stocks])[np.array(df_weights[list_stocks]) > 0.101] + ): + raise Exception(r"---> 10% limit exceeded") + + return df_returns, df_weights - Args: - df_returns: Ascending date ordered combined training and test returns data. - df_weights_index: Index weights. Equally weighted - df_weights_index: Portfolio weights. Your portfolio should use equally weighted for the training date range. If blank will be ignored - Returns: - A plot of the two total return indices and the total return indices as a dataframe - ''' +def plot_total_return( + df_returns: pd.DataFrame, + df_weights_index: pd.DataFrame, + df_weights_portfolio: pd.DataFrame, +): + """ + Function to generate the two total return indices. + + Args: + df_returns: Ascending date ordered combined training and test returns data. + df_weights_index: Index weights. Equally weighted + df_weights_index: Portfolio weights. Your portfolio should use equally weighted for the training date range. If blank will be ignored + + Returns: + A plot of the two total return indices and the total return indices as a dataframe + """ # list of stock names list_stocks = list(df_returns.columns) - list_stocks.remove('month_end') + list_stocks.remove("month_end") # replace nans with 0 in return array ar_returns = np.array(df_returns[list_stocks]) np.nan_to_num(x=ar_returns, copy=False, nan=0) # calc index - ar_rtn_index = np.array(df_weights_index[list_stocks])*ar_returns - ar_rtn_port = np.array(df_weights_portfolio[list_stocks])*ar_returns + ar_rtn_index = np.array(df_weights_index[list_stocks]) * ar_returns + ar_rtn_port = np.array(df_weights_portfolio[list_stocks]) * ar_returns v_rtn_index = np.sum(ar_rtn_index, axis=1) v_rtn_port = np.sum(ar_rtn_port, axis=1) # add return series to dataframe - df_rtn = pd.DataFrame(data=df_returns['month_end'], columns=['month_end']) - df_rtn['index'] = v_rtn_index - df_rtn['portfolio'] = v_rtn_port + df_rtn = pd.DataFrame(data=df_returns["month_end"], columns=["month_end"]) + df_rtn["index"] = v_rtn_index + df_rtn["portfolio"] = v_rtn_port df_rtn # create total return base_price = 100 - df_rtn.sort_values(by = 'month_end', inplace = True) - df_rtn['index_tr'] = ((1 + df_rtn['index']).cumprod()) * base_price - df_rtn['portfolio_tr'] = ((1 + df_rtn['portfolio']).cumprod()) * base_price + df_rtn.sort_values(by="month_end", inplace=True) + df_rtn["index_tr"] = ((1 + df_rtn["index"]).cumprod()) * base_price + df_rtn["portfolio_tr"] = ((1 + df_rtn["portfolio"]).cumprod()) * base_price df_rtn - df_rtn_long = df_rtn[['month_end', 'index_tr', 'portfolio_tr']].melt(id_vars='month_end', var_name='series', value_name='Total Return') + df_rtn_long = df_rtn[["month_end", "index_tr", "portfolio_tr"]].melt( + id_vars="month_end", var_name="series", value_name="Total Return" + ) # plot - fig1 = px.line(data_frame=df_rtn_long, x='month_end', y='Total Return', color='series') + fig1 = px.line( + data_frame=df_rtn_long, x="month_end", y="Total Return", color="series" + ) return fig1, df_rtn -# %% # running solution df_returns = pd.concat(objs=[df_returns_train, df_returns_test], ignore_index=True) df_weights_index = equalise_weights(df_returns) df_returns, df_weights_portfolio = generate_portfolio(df_returns_train, df_returns_test) -fig1, df_rtn = plot_total_return(df_returns, df_weights_index=df_weights_index, df_weights_portfolio=df_weights_portfolio) +fig1, df_rtn = plot_total_return( + df_returns, + df_weights_index=df_weights_index, + df_weights_portfolio=df_weights_portfolio, +) + fig1