{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"# Portfolio Rebalancing"
]
},
{
"cell_type": "markdown",
"metadata": {
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"We consider the problem of rebalancing a portfolio of assets over multiple periods.\n",
"We let $h_t \\in \\mathbb{R}^n$ denote the vector of our dollar value \n",
"holdings in $n$ assets,\n",
"at the beginning of period $t$, for $t=1,\\ldots, T$,\n",
"with negative entries meaning short positions.\n",
"We will work with the portfolio weight vector,\n",
"defined as $w_t = h_t/(\\mathbb{1}^Th_t)$, where\n",
"we assume that $\\mathbb{1}^T h_t>0$, *i.e.*, the total\n",
"portfolio value is positive. \n",
"\n",
"The *target portfolio weight vector*\n",
"$w^\\star$ is defined as the solution of the problem"
]
},
{
"cell_type": "markdown",
"source": [
"$$\n",
" \\begin{array}{ll}\n",
" \\mbox{maximize} & \\mu^T w - \\frac{\\gamma}{2} w^T \\Sigma w \\\\\n",
" \\mbox{subject to}\n",
" & \\mathbb{1}^Tw = 1,\n",
" \\end{array}\n",
"$$"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "markdown",
"source": [
"where $w\\in \\mathbb{R}^n$ is the variable, $\\mu$ is the mean return,\n",
"$\\Sigma\\in \\mathbb{S}_{++}^n$ is the return covariance, and $\\gamma>0$ is the\n",
"risk aversion parameter. The data $\\mu$, $\\Sigma$, and $\\gamma$ are given.\n",
"In words, the target weights maximize the risk-adjusted expected return.\n",
"\n",
"At the beginning of each period $t$ we are allowed to rebalance the\n",
"portfolio by buying and selling assets.\n",
"We call the post-trade portfolio weights $\\tilde{w}_t$.\n",
"They are found by solving the (rebalancing) problem"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "markdown",
"source": [
"$$\n",
" \\begin{array}{ll}\n",
" \\mbox{maximize} & \\mu^T w - \\frac{\\gamma}{2} w^T \\Sigma w - \\kappa^T |w-w_t|\\\\\n",
" \\mbox{subject to}\n",
" & \\mathbb{1}^Tw = 1,\n",
" \\end{array}\n",
"$$"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "markdown",
"source": [
"with variable $w \\in \\mathbb{R}^n$,\n",
"where $\\kappa\\in\\mathbb{R}^n_+$ is the vector of (so-called linear) transaction costs\n",
"for the assets. (For example, these could model bid/ask spread.)\n",
"Thus, we choose the post-trade weights to maximize the risk-adjusted expected\n",
"return, minus the transactions costs associated with rebalancing the portfolio.\n",
"Note that the pre-trade weight vector $w_t$\n",
"is known at the time we solve the problem.\n",
"If we have $\\tilde w_t = w_t$, it means that no rebalancing is done at\n",
"the beginning of period $t$; we simply hold our current portfolio.\n",
"(This happens if $w_t = w^\\star$, for example.)\n",
"\n",
"After holding the rebalanced portfolio over the investment period,\n",
"the dollar value of our portfolio becomes $h_{t+1} = \\text{diag}(r_t) \\tilde h_t$,\n",
"where $r_t \\in \\mathbb{R}^n_{++}$ is the (random) vector of asset returns\n",
"over period $t$, and $\\tilde h_t$ is the post-trade portfolio\n",
"given in dollar values (which you do not need to know).\n",
"The next weight vector is then given by\n",
"\n",
"$$\n",
" w_{t+1} =\n",
" \\frac{\\text{diag}{(r_t)}\\tilde{w}_t}{r_t^T\\tilde{w}_t}.\n",
"$$\n",
"\n",
"(If $r_t^T \\tilde w_t \\leq 0$, which means our portfolio has negative\n",
"value after the investment period, we have gone bust, and all trading stops.)\n",
"\n",
"The standard model is that $r_t$ are IID random variables with\n",
"mean and covariance $\\mu$ and $\\Sigma$,\n",
"but this is not relevant in this problem."
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "markdown",
"metadata": {
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"## Problem"
]
},
{
"cell_type": "markdown",
"metadata": {
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"Starting from $w_1= w^\\star$, compute a sequence of \n",
"portfolio weights $\\tilde w_t$ for $t = 1,\\ldots, T$.\n",
"For each $t$, find $\\tilde w_t$ \n",
"by solving the rebalancing problem (with $w_{t}$ a known constant);\n",
"then generate a vector of returns $r_t$ (using our supplied function)\n",
"to compute $w_{t+1}$\n",
"(The sequence of weights is random, so the results won't be the \n",
"same each time you run your script. But they should look similar.)\n",
"\n",
"Report the fraction of periods in which the solution has only zero (or negligible)\n",
"trades, defined as \n",
"\n",
"$$\n",
"\\|\\tilde w_t - w_t \\|_\\infty \\leq 10^{-3}.\n",
"$$\n",
"\n",
"Plot the sequence $\\tilde w_t$ for $t = 1, 2,\\ldots, T$.\n",
"\n",
"We provide below the data,\n",
"a function to generate a (random) vector $r_t$ of market returns, and\n",
"the code to plot the sequence $\\tilde w_t$. (The plotting code also\n",
"draws a dot for every non-negligible trade.) \n",
"\n",
"Carry this out for two values of $\\kappa$,\n",
"$\\kappa = \\kappa_1$ and $\\kappa = \\kappa_2$. \n",
"Briefly comment on what you observe."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"# data and code for multiperiod portfolio rebalancing problem\n",
"import numpy as np\n",
"\n",
"T = 100\n",
"n = 5\n",
"gamma = 8.0\n",
"threshold = 0.001\n",
"Sigma = np.array(\n",
" [\n",
" [1.512e-02, 1.249e-03, 2.762e-04, -5.333e-03, -7.938e-04],\n",
" [1.249e-03, 1.030e-02, 6.740e-05, -1.301e-03, -1.937e-04],\n",
" [2.762e-04, 6.740e-05, 1.001e-02, -2.877e-04, -4.283e-05],\n",
" [-5.333e-03, -1.301e-03, -2.877e-04, 1.556e-02, 8.271e-04],\n",
" [-7.938e-04, -1.937e-04, -4.283e-05, 8.271e-04, 1.012e-02],\n",
" ]\n",
")\n",
"mu = np.array([1.02, 1.028, 1.01, 1.034, 1.017])\n",
"kappa_1 = np.array([0.002, 0.002, 0.002, 0.002, 0.002])\n",
"kappa_2 = np.array([0.004, 0.004, 0.004, 0.004, 0.004])\n",
"\n",
"## Generate returns\n",
"# call this function to generate a vector r of market returns\n",
"generateReturns = lambda: np.random.multivariate_normal(mu, Sigma)\n",
"\n",
"## Plotting code\n",
"# You must provide three objects:\n",
"# - ws: np.array of size T x n,\n",
"# the post-trade weights w_t_tilde;\n",
"# - us: np.array of size T x n,\n",
"# the trades at each period: w_t_tilde - w_t;\n",
"# - w_star: np.array of size n,\n",
"# the \"target\" solution w_star.\n",
"import matplotlib.pyplot as plt\n",
"\n",
"colors = [\"b\", \"r\", \"g\", \"c\", \"m\"]\n",
"plt.figure(figsize=(13, 5))\n",
"for j in range(n):\n",
" plt.plot(range(T), ws[:, j], colors[j])\n",
" plt.plot(range(T), [w_star[j]] * T, colors[j] + \"--\")\n",
" non_zero_trades = abs(us[:, j]) > threshold\n",
" plt.plot(np.arange(T)[non_zero_trades], ws[non_zero_trades, j], colors[j] + \"o\")\n",
"plt.ylabel(\"post-trade weights\")\n",
"plt.xlabel(\"period $t$\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true,
"pycharm": {
"name": "#%%\n"
}
},
"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.7.0"
}
},
"nbformat": 4,
"nbformat_minor": 1
}