{ "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 }