diff options
Diffstat (limited to 'python/notebooks/Asset allocation.ipynb')
| -rw-r--r-- | python/notebooks/Asset allocation.ipynb | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/python/notebooks/Asset allocation.ipynb b/python/notebooks/Asset allocation.ipynb new file mode 100644 index 00000000..ac122400 --- /dev/null +++ b/python/notebooks/Asset allocation.ipynb @@ -0,0 +1,222 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from ipywidgets import interact, interactive, fixed, FloatSlider\n", + "from IPython.display import display\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import math\n", + "import cvxpy\n", + "from inspect import signature\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We model the assets in a 1-factor model setting. Namely conditionally on the factor $F$, the assets\n", + "are independent, and $r_i$, the return of asset $i$ is given by:\n", + "$$r_i = \\mu_i + \\delta_i(F-\\bar F) + \\sigma_i \\varepsilon_i$$\n", + "with $\\forall i,\\,\\textrm{Cov}(F, \\varepsilon_i)=0$, and $\\varepsilon\\sim\\mathcal{N}(0, \\mathbf{1})$ \n", + "\n", + "We can show that:\n", + "$$\\textrm{Corr}(r_i, F)=\\rho_i = \\frac{\\delta_i \\sqrt{\\textrm{Var}(F)}}{\\sqrt{\\textrm{Var}(r_i)}}$$\n", + "and with $\\textrm{Var(r_i)}=\\delta_i^2\\textrm{Var}(F) + \\sigma_i^2$, we get:\n", + "$$\\rho_i = \\frac{1}{\\sqrt{1+\\frac{\\sigma_i^2}{\\delta_i^2\\textrm{Var(F)}}}}$$\n", + "$$\\forall i\\neq j,\\;\\textrm{Cov}(r_i, r_j)=\\delta_i\\delta_j\\textrm{Var}(F)$$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def cor2cov(Rho, vol):\n", + " return np.diag(vol) @ Rho @ np.diag(vol)\n", + "\n", + "def rho(sigma, delta, volF):\n", + " \"\"\" computes the correlation between the asset and the factor \"\"\"\n", + " return 1/math.sqrt(1+sigma**2/(delta**2*volF**2))\n", + "\n", + "def resid_vol(rho, delta, volF):\n", + " \"\"\" computes the residual of the asset \"\"\"\n", + " return math.sqrt(delta**2*volF**2*(1/rho**2-1))\n", + "\n", + "def var(rho, delta, volF):\n", + " \"\"\" computes the variance of the asset \"\"\"\n", + " return delta**2*volF**2+resid_vol(rho, delta, volF)**2\n", + "\n", + "volHY = 0.07\n", + "## Edwin's parameters\n", + "rho = {'CLO': 0.6,\n", + " 'CSO': 0.5,\n", + " 'Subprime': 0.3}\n", + "delta = {'CLO': 0.4,\n", + " 'CSO': 0.2,\n", + " 'Subprime': 0.6}\n", + "mu = np.array([0.01, 0.075, 0.065, 0.25])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from matplotlib import pyplot as plt\n", + "plt.style.use('ggplot')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def compute_allocation(rho_clo = 0.9, rho_cso=0.6, rho_subprime=0.2,\n", + " delta_clo=1.2, delta_cso=0.4, delta_subprime=0.8,\n", + " mu_hy=0.02, mu_clo=0.07, mu_cso=0.08, mu_subprime=0.25):\n", + " rho = {'CLO': rho_clo,\n", + " 'CSO': rho_cso,\n", + " 'Subprime': rho_subprime}\n", + " delta = {'CLO': delta_clo,\n", + " 'CSO': delta_cso,\n", + " 'Subprime': delta_subprime}\n", + " assets = ['CLO', 'CSO', 'Subprime']\n", + " mu = np.array([mu_hy, mu_clo, mu_cso, mu_subprime])\n", + " u = volHY * np.array([delta[a] for a in assets])\n", + " Sigma = np.outer(u, u) + np.diag([resid_vol(rho[a], delta[a], volHY)**2\n", + " for a in assets])\n", + " v = volHY**2 * np.array([1] + [delta[a] for a in assets])\n", + " Sigma = np.vstack((v, np.c_[v[1:], Sigma]))\n", + "\n", + " sharpe = mu/np.sqrt(np.diag(Sigma))\n", + "\n", + " gamma = cvxpy.Parameter(sign='positive')\n", + " w = cvxpy.Variable(4)\n", + " ret = mu.T*w\n", + " risk = cvxpy.quad_form(w, Sigma)\n", + " prob = cvxpy.Problem(cvxpy.Maximize(ret-gamma*risk),\n", + " [cvxpy.sum_entries(w[1:]) - 0.1*w[0] == 1,\n", + " w[1:] >= 0,\n", + " w[0] <= 0])\n", + "\n", + " gamma_x = np.linspace(0, 20, 500)\n", + " W = np.empty((4, gamma_x.size))\n", + " for i, val in enumerate(gamma_x):\n", + " gamma.value = val\n", + " prob.solve()\n", + " W[:,i] = np.asarray(w.value).squeeze()\n", + " fund_return = mu@W\n", + " fund_vol = np.array([math.sqrt(W[:,i]@Sigma@W[:,i]) for i in range(gamma_x.size)])\n", + " return (W, fund_return, fund_vol)\n", + "\n", + "def plot_allocation(plot_vol=True, **kwargs):\n", + " W, fund_return, fund_vol = compute_allocation(**kwargs)\n", + " gamma_x = np.linspace(0, 20, 500)\n", + " fig, ax1 = plt.subplots()\n", + " ax1.stackplot(gamma_x, W[1:,], labels=['CLO', 'CSO', 'Subprime'])\n", + " ax1.set_xlabel('risk factor')\n", + " ax1.set_ylabel('portfolio weights')\n", + " ax1.legend()\n", + " # ax1.text(0.3, 0.82, 'RMBS')\n", + " # ax1.text(0.5, 0.45, 'CSO')\n", + " # ax1.text(0.5, 0.15, 'CLO')\n", + " ax1.set_ylim([0, 1])\n", + " ax2 = ax1.twinx()\n", + " if plot_vol:\n", + " ax2.plot(gamma_x, fund_vol, lw=1, color=\"grey\")\n", + " ax2.set_ylabel('fund volatility')\n", + " else:\n", + " ax2.plot(gamma_x, fund_return, lw=1, color=\"grey\")\n", + " ax2.set_ylabel('fund return')\n", + " plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def desc(greek, asset):\n", + " \"\"\"returns the latex label\"\"\"\n", + " return r'${0}_{{\\textrm{{{1}}}}}$'.format('\\\\'+greek, asset)\n", + "\n", + "desc_mapping = {}\n", + "assets = ['CLO', 'CSO', 'Subprime']\n", + "for greek in ['rho', 'delta', 'mu']:\n", + " if greek == 'mu':\n", + " assets.append('HY')\n", + " desc_mapping.update({desc(greek, a): '{0}_{1}'.format(greek, a.lower()) for a in assets})\n", + " \n", + "w = interactive(plot_allocation, rho_clo=FloatSlider(description=desc('rho', 'CLO'), min=0., max=1., step=0.1, value=0.9),\n", + " rho_cso=FloatSlider(description=desc('rho', 'CSO'), min=0., max=1., step=0.1, value=0.6),\n", + " rho_subprime=FloatSlider(description=desc('rho', 'Subprime'), min=0., max=1., step=0.1, value=0.2),\n", + " delta_clo=FloatSlider(description=desc('delta', 'CLO'), min=0., max=2., step=0.1, value=1.2),\n", + " delta_cso=FloatSlider(description=desc('delta', 'CSO'), min=0., max=2., step=0.1, value=0.4),\n", + " delta_subprime=FloatSlider(description=desc('delta', 'Subprime'), min=0., max=2., step=0.1, value=0.8),\n", + " mu_hy = FloatSlider(description=desc('mu', 'HY'), min=0., max=.1, step=0.01, value=0.02),\n", + " mu_clo = FloatSlider(description=desc('mu', 'CLO'), min=0., max=.12, step=0.01, value=0.07),\n", + " mu_cso = FloatSlider(description=desc('mu', 'CSO'), min=0., max=.12, step=0.01, value=0.08),\n", + " mu_subprime = FloatSlider(description=desc('mu', 'Subprime'), min=0.1, max=.35, step=0.01, value=0.25))\n", + "\n", + "## black magic to get back to the function order\n", + "s = signature(compute_allocation)\n", + "param_ordering = {p:i for i, p in enumerate(s.parameters)}\n", + "param_ordering[None] = -1\n", + "w_ordered = sorted([c for c in w.children], key=lambda x: param_ordering[desc_mapping.get(x.description)])\n", + "display(*w_ordered)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "args = w.kwargs.copy()\n", + "args.pop('plot_vol')\n", + "W, fund_return, fund_vol = compute_allocation(**args)\n", + "import pandas as pd\n", + "df = pd.DataFrame(np.c_[W.T, fund_return, fund_vol], columns=['HY', 'CLO', 'CSO', 'Subprime', 'return', 'vol'])\n", + "df.to_csv('weights.csv', index=False)" + ] + }, + { + "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.5.2" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} |
