diff --git a/exams/Midterm.ipynb b/exams/Midterm.ipynb new file mode 100644 index 0000000..3de5ed9 --- /dev/null +++ b/exams/Midterm.ipynb @@ -0,0 +1,638 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Midterm: Coding Portion\n", + "\n", + "There are two parts below. Each question (marked Q1,Q2, etc.) is worth 2 points." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "import scipy.optimize\n", + "from matplotlib import pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part A: Revisiting Mean-Variance Efficient Portfolios\n", + "\n", + "Suppose that $\\mu$ is a column vector of the means of each asset and suppose that $\\Sigma$ is the associated variance-covariance matrix. Now, suppose that we create a column vector of weights $w$ that tells us the fraction of our wealth that we invest in each asset. Then, the formulas in matrix notation for the mean and variance of the resulting portfolio are\n", + "$$\n", + "\\mu_p = \\mu' w\n", + "$$\n", + "and\n", + "$$\n", + "\\sigma_p^2 = w' \\Sigma w\n", + "$$\n", + "respectively.\n", + "\n", + "I have provided code below to download data from the file `dataAssets.csv`. The code below loads a matrix of asset prices. In the matrix of prices, each row corresponds to a day. The columns correspond, (in order,) to the S&P 500 index, USD index, crude oil index, HYG index, and U.S. 10-yr Treasury index. Here I have converted prices to returns for you." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "url = 'https://raw.githubusercontent.com/jmbejara/comp-econ-sp18/master/HW/hw-01/dataAssets.csv'\n", + "data_assets = pd.read_csv(url, parse_dates=['date'])\n", + "prices = data_assets.iloc[:,1:].values\n", + "dates = data_assets.iloc[:,0].values\n", + "rets = (prices[1:,:]/prices[:-1,:] - 1) * 100" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 0.38854818, -0.38136479, -0.37572583, 0.21891419, -0.36173324],\n", + " [-0.35699979, 0.18400287, 0.51428571, 0.0109218 , -0.41711726],\n", + " [-0.11528775, 0.25515864, 0.27288232, -0.08736486, 1.63667391],\n", + " ...,\n", + " [ 2.50830202, -0.25356182, -0.23887079, 0.54676594, 2.74901149],\n", + " [-1.38975632, 0.0281294 , -3.89638659, -0.12727062, 0.81546637],\n", + " [ 0.52779551, -0.57909428, 4.00906002, 0.24328082, 0.97246206]])" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "rets" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In HW1, we solved a constrained minimization problem of the following form:\n", + "\n", + "\\begin{align*}\n", + "\\min_{w} \\quad & w' \\Sigma w \\\\\n", + "\\text{s.t.} \\quad & w' \\mu = \\mu_p \\\\\n", + "& w' \\mathbb 1 = 1 \n", + "\\end{align*}\n", + "where $w$ is a $k \\times 1$ vector of portfolio weights corresponding to the weights put on $k$ assets, $\\mu$ is an $k \\times 1$ vector of expected (mean) portfolio reaturns of each asset, $\\Sigma$ is the $k \\times k$ variance-covariance matrix of the returns, $\\mathbb 1$ is a $k \\times 1$ conforming vector of ones, and $\\mu_p$ is an arbitrary scalar number. In HW 1, we used `scipy.optimize.fmin_slsqp` to numerically solve this problem. I have recreated the solution below. (Note that we have $k=5$ assets.)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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2MwWRuSPXN40sszVklLNbGV4Km1NRTamqjj22GMZMGAAEyZMYNOmTTz11FMcddRRFBYWcsstt/DRRx8hiSuuuIJrr72W8ePH8/vf/56CggL69evHo48+Sv369UlLS2P48OG888475Ofn8/rrr9OzZ08+++wzrr/+eiC4vvH5559z6623MnfuXPr06cPw4cNp2bIl7733Hnl5eWzfvp2xY8cydOhQNm7cSH5+PnfffTdDhwZnQ5s0acK2bduYOHEid955J61bt2bWrFn07duXF154AUlkZWVx4403sm3bNlq3bs2zzz5LamoqWVlZXHbZZTRq1Igjjzwykb9256q84qDy7syVfDBrJUvW7yCpjjiiWwpXHt2Nwb33oXWTshZSrXjxDDBTgXRJXYHlBEvGRn8FHktwEX8ScBbwqZmZpBbAe8AfzOyr4sxh8Ngq6TBgMnAxwdKqkWXdE/58e28b8Od3ZjNnRcWep+zVvhl3nNp7r8ooKChgypQpvP/++/z5z39m3LhxPPHEEyxevJjp06eTnJzMhg0byMvL45JLLmH8+PH06NGDiy++mEcffZQbbrgBgNatWzNt2jQeeeQRRo0axZNPPsmoUaN4+OGHGThwINu2baNBgwbcc889jBo1infffReAZ599lkmTJjFz5kxatWpFQUEBb731Fs2aNWPdunUcdthhnHbaab+4AD99+nRmz55N+/btGThwIF999RUDBgzg2muv5e2336ZNmza8+uqr3H777Tz99NNceumlPPjggxxzzDHcfPPNe/U7c64migwq73+3kqUbfgoqVx/TjcG929Gqcb2E1S9uAcbMCiSNBD4CkoCnzWy2pLuATDMbS7Bu+WhJ2QQ9l2Hh4SOB7sCfJP0pTBtsZmuAq4FnCdZ6/yB8QBBYXpM0AlgKnB2vtsVbaSOjitPPOOMMAPr27UtOTg4A48aN46qrrvrxdFWrVq2YMWMGXbt2pUePHgAMHz6chx9++McAE1nOm2++CcDAgQO58cYbueCCCzjjjDPo2DGyw/iTQYMG0apVsLy7mXHbbbfx+eefU6dOHZYvX87q1atp167dz47p37//j+X16dOHnJwcWrRowaxZsxg0aBAAhYWFpKamsnnzZjZt2sQxxxwDwEUXXcQHH3yAc7WdmTFn5Rbem7mS9777qacysHtrrvlVNwb3akfLBAaVSHG9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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# MY CODE PROVIDED FOR YOU\n", + "mu = np.mean(rets, axis=0)\n", + "Sigma = np.cov(rets, rowvar=False)\n", + "\n", + "N = 100\n", + "mean_grid = np.linspace(0.0, 0.02, N)\n", + "var_grid = np.zeros(N)\n", + "\n", + "for i in range(N):\n", + " mu_target = mean_grid[i]\n", + " var_func = lambda w: w.transpose() @ Sigma @ w\n", + " con_mean = lambda w: w @ mu - mu_target\n", + " con_port = lambda w: w @ np.ones((5,1)) - 1\n", + " eqcons = [con_mean, con_port]\n", + " w0 = np.array([.2, .2, .2, .2, .2])\n", + " wstar = scipy.optimize.fmin_slsqp(var_func, w0, eqcons=eqcons, acc=1e-09, \n", + " iprint=0)\n", + " var_grid[i] = var_func(wstar)\n", + "\n", + "plt.plot(var_grid, mean_grid, label='Unconstrained')\n", + "plt.xlabel('Portfolio Return Variance')\n", + "plt.ylabel('Mean Portfolio Return')\n", + "plt.title('Mean-Variance Efficient Frontier')\n", + "plt.legend();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This problem, as it turns out, can be solved analytically. \n", + "The solution is given by\n", + "$$\n", + "w^* = \\Sigma^{-1} \n", + "\\begin{bmatrix}\n", + "\\mu & \\mathbb 1\n", + "\\end{bmatrix}\n", + "A^{-1}\n", + "\\begin{bmatrix}\n", + "\\mu_p \\\\\n", + "1\n", + "\\end{bmatrix},\n", + "$$\n", + "where $A$ is a matrix defined by\n", + "$$\n", + "A =\n", + "\\begin{bmatrix}\n", + "\\mu & \\mathbb 1\n", + "\\end{bmatrix}^\\intercal\n", + "\\Sigma^{-1}\n", + "\\begin{bmatrix}\n", + "\\mu & \\mathbb 1\n", + "\\end{bmatrix}.\n", + "$$\n", + "Note that\n", + "$\\begin{bmatrix}\n", + "\\mu & \\mathbb 1\n", + "\\end{bmatrix}$\n", + "is an $N \\times 2$ matrix. The two columns are stacked together.\n", + "\n", + "### Q1. Analytical solution of MVE Frontier\n", + "\n", + "Reconstruct the graph above of the Mean-Variance Efficient Frontier. However, this time, instead of using the numerical solved to solve for the efficient frontier, use the analytical solution I provided. For this exercise, only keep track of the resulting minimal variance. In the above numerical solution, the result is stored in `var_grid`. Your solution should be in the form of a numpy array called `var_grid_exact`. \n", + "\n", + "**Hint:** Compute the inverse of a matrix as follow:\n", + "```python\n", + "import numpy.linalg as la\n", + "a = np.array([[1,2],[3,4]])\n", + "la.inv(a)\n", + "```\n", + "gives\n", + "```\n", + "array([[-2. , 1. ],\n", + " [ 1.5, -0.5]])\n", + "```\n", + "\n", + "Also, to make stacking rows and columns easier, consider the following Numpy functions. It doesn't matter what you choose to use---you don't even need to use these suggested function if you don't want to. I provide these for convenience.\n", + "\n", + " - `np.hstack` https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.hstack.html\n", + " - `np.vstack` https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.vstack.html\n", + " - `np.c_` https://docs.scipy.org/doc/numpy/reference/generated/numpy.c_.html\n", + " - `np.r_` https://docs.scipy.org/doc/numpy/reference/generated/numpy.r_.html#numpy.r_\n", + " \n", + "Also, the following starter code may be helpful:\n", + "```python\n", + "mu = np.mean(rets, axis=0)\n", + "Sigma = np.cov(rets, rowvar=False)\n", + "\n", + "N = 100\n", + "mean_grid = np.linspace(0.0, 0.02, N)\n", + "var_grid_exact = np.zeros(N)\n", + "\n", + "YOUR_CODE_HERE\n", + "\n", + "for i in range(N):\n", + " mu_target = mean_grid[i]\n", + " wstar = YOUR_CODE_HERE\n", + " var_grid_exact[i] = var_func(wstar)\n", + " \n", + "\n", + "\n", + "plt.plot(var_grid_exact, mean_grid, label=\"Analytical Solution\")\n", + "plt.xlabel('Portfolio Return Variance')\n", + "plt.ylabel('Mean Portfolio Return')\n", + "plt.title('Mean-Variance Efficient Frontier')\n", + "plt.legend();\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Q2. Compare Numerical and Analytical Solution\n", + "\n", + "Make sure your result matches the result you got from the solver by using `np.allclose`. Use `np.allclose` to compare `var_grid` and `var_grid_exact`. The result should be `True`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Q3. Bounding the Portfolio Weights\n", + "\n", + "Suppose we take the problem from before and add a new set of constraints. Let's add bounds on the portfolio weights such that no weight may be less than zero. We can interpret this as a constraint that disallows \"short-selling.\" In particular, solve the problem\n", + "\n", + "\\begin{align*}\n", + "\\min_{w} \\quad & w' \\Sigma w \\\\\n", + "\\text{s.t.} \\quad & w' \\mu = \\mu_p \\\\\n", + "& w' \\mathbb 1 = 1 \\\\\n", + "& w_i \\geq 0 \\quad \\text{ for all } i=1,..,k.\n", + "\\end{align*}\n", + "\n", + "Use `scipy.optimize.fmin_slsqp` again. Check the documentation for information on how to include \"bound constraints.\" https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fmin_slsqp.html\n", + "\n", + "\n", + "**Hint**: Edit the code below to incorporate the constraints:\n", + "\n", + "```python\n", + "mu = np.mean(rets, axis=0)\n", + "Sigma = np.cov(rets, rowvar=False)\n", + "\n", + "N = 100\n", + "mean_grid = np.linspace(0.0, .02, N)\n", + "var_grid = np.zeros(N)\n", + "var_grid_no_short = np.zeros(N)\n", + "\n", + "for i in range(N):\n", + " mu_target = mean_grid[i]\n", + " var_func = lambda w: w.transpose() @ Sigma @ w\n", + " con_mean = lambda w: w @ mu - mu_target\n", + " con_port = lambda w: w @ np.ones((5,1)) - 1\n", + " eqcons = [con_mean, con_port]\n", + " w0 = np.array([.2, .2, .2, .2, .2])\n", + " wstar = scipy.optimize.fmin_slsqp(var_func, w0, eqcons=eqcons, acc=1e-09, \n", + " iprint=0)\n", + " var_grid[i] = var_func(wstar)\n", + " wstar = scipy.optimize.fmin_slsqp(var_func, w0, \n", + " eqcons=eqcons,\n", + " bounds=YOUR_CODE_HERE,\n", + " acc=1e-09, \n", + " iprint=0)\n", + " var_grid_no_short[i] = var_func(wstar)\n", + "\n", + "plt.plot(var_grid, mean_grid, label='Unconstrained')\n", + "plt.plot(var_grid_no_short, mean_grid, label='No-Short Selling')\n", + "plt.xlabel('Portfolio Return Variance')\n", + "plt.ylabel('Mean Portfolio Return')\n", + "plt.title('Mean-Variance Efficient Frontier')\n", + "plt.legend()\n", + "\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part B. Revisiting Euro 2012\n", + "\n", + "This part uses data from the UEFA European Under-21 Championship in 2012. Use the provided code below to load the data and to add two new columns: `passes_quartile` and `shoot_acc_quartile`." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Team object\n", + "Goals int64\n", + "Shots on target int64\n", + "Shots off target int64\n", + "Shooting Accuracy float64\n", + "% Goals-to-shots object\n", + "Total shots (inc. Blocked) int64\n", + "Hit Woodwork int64\n", + "Penalty goals int64\n", + "Penalties not scored int64\n", + "Headed goals int64\n", + "Passes int64\n", + "Passes completed int64\n", + "Passing Accuracy object\n", + "Touches int64\n", + "Crosses int64\n", + "Dribbles int64\n", + "Corners Taken int64\n", + "Tackles int64\n", + "Clearances int64\n", + "Interceptions int64\n", + "Clearances off line float64\n", + "Clean Sheets int64\n", + "Blocks int64\n", + "Goals conceded int64\n", + "Saves made int64\n", + "Saves-to-shots ratio object\n", + "Fouls Won int64\n", + "Fouls Conceded int64\n", + "Offsides int64\n", + "Yellow Cards int64\n", + "Red Cards int64\n", + "Subs on int64\n", + "Subs off int64\n", + "Players Used int64\n", + "passes_quartile category\n", + "shoot_acc_quartile category\n", + "dtype: object" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "euro12 = pd.read_csv('https://raw.githubusercontent.com/jokecamp/FootballData/master/UEFA_European_Championship/Euro%202012/Euro%202012%20stats%20TEAM.csv', sep=',')\n", + "euro12['Shooting Accuracy'] = euro12['Shooting Accuracy'].str[:-1].astype(float)\n", + "quartiles = [0, .25, .5, .75, 1.]\n", + "# labels = ['Q1', 'Q2', 'Q3', 'Q4']\n", + "labels = None\n", + "euro12['passes_quartile'] = pd.qcut(euro12['Passes'], q=quartiles, labels=labels)\n", + "euro12['shoot_acc_quartile'] = pd.qcut(euro12['Shooting Accuracy'], q=quartiles, labels=labels)\n", + "euro12.dtypes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Q4. Which team had the most number of \"Passes\"?\n", + "\n", + "Just tell me the team. No need to get fancy here." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Q5. Which team(s) had the most number of \"Headed goals\"?\n", + "\n", + "Just tell me the team. No need to get fancy here." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Q6. Group the data ...\n", + "\n", + "Group the data by `passes_quartile`. What is the average number of `Goals` made in each quartile? **Do this using `pivot_table`.**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Q7. Group the data ...\n", + "\n", + "Group the data by `passes_quartile`. What is the average number of `Goals` made in each quartile? **Do this using `groupby`.**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Q8. Group the data by `passes_quartile` and `shoot_acc_quartile` ...\n", + "\n", + "Group the data by `passes_quartile` and `shoot_acc_quartile` and, within each group, calculate the average number of `Goals` scored. Your solution should look like the dataframe `dfsol` that I have provided in the code cell below. **Complete this exercise using `pivot_table`.**" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "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", + "
shoot_acc_quartile(21.198999999999998, 33.4](33.4, 40.65](40.65, 48.35](48.35, 55.9]
passes_quartile
(850.999, 1190.75]5.01.5NaN4.0
(1190.75, 1522.0]2.0NaN5.04.5
(1522.0, 1934.75]3.56.04.0NaN
(1934.75, 4317.0]NaN3.08.012.0
\n", + "
" + ], + "text/plain": [ + "shoot_acc_quartile (21.198999999999998, 33.4] (33.4, 40.65] (40.65, 48.35] \\\n", + "passes_quartile \n", + "(850.999, 1190.75] 5.0 1.5 NaN \n", + "(1190.75, 1522.0] 2.0 NaN 5.0 \n", + "(1522.0, 1934.75] 3.5 6.0 4.0 \n", + "(1934.75, 4317.0] NaN 3.0 8.0 \n", + "\n", + "shoot_acc_quartile (48.35, 55.9] \n", + "passes_quartile \n", + "(850.999, 1190.75] 4.0 \n", + "(1190.75, 1522.0] 4.5 \n", + "(1522.0, 1934.75] NaN \n", + "(1934.75, 4317.0] 12.0 " + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Your result should look like `dfsol` below.\n", + "from io import StringIO\n", + "sol = '''\n", + "passes_quartile,\"(21.198999999999998, 33.4]\",\"(33.4, 40.65]\",\"(40.65, 48.35]\",\"(48.35, 55.9]\"\n", + "\"(850.999, 1190.75]\",5.0,1.5,,4.0\n", + "\"(1190.75, 1522.0]\",2.0,,5.0,4.5\n", + "\"(1522.0, 1934.75]\",3.5,6.0,4.0,\n", + "\"(1934.75, 4317.0]\",,3.0,8.0,12.0\n", + "'''\n", + "dfsol = pd.read_csv(StringIO(sol), index_col=[0])\n", + "dfsol.columns.name = 'shoot_acc_quartile'\n", + "dfsol" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Q9. Group the data by `passes_quartile` and `shoot_acc_quartile` ...\n", + "\n", + "Group the data by `passes_quartile` and `shoot_acc_quartile` and, within each group, calculate the average number of `Goals` scored. Your solution should look the same as the previous question. **Complete this exercise using `group_by`.**\n", + "\n", + "HINT: You may need to use `unstack()`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.6.5" + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}