aboutsummaryrefslogtreecommitdiffstats
path: root/python/notebooks/Asset allocation.ipynb
diff options
context:
space:
mode:
Diffstat (limited to 'python/notebooks/Asset allocation.ipynb')
-rw-r--r--python/notebooks/Asset allocation.ipynb222
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
+}