{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "alpha-mind的portfolio文件夹提供了构建组合的工具函数。\n",
    "- 不同的构建方式实质上对应了不同的优化问题。\n",
    "- 该文件夹报告的构建方式包括线性构建、均值方差构建等。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 线性构建： *linear_builder*\n",
    "- 目标: 在线性约束下最大化组合的预期收益率。\n",
    "- 线性约束包括\n",
    "  - 标的权重的上下限 *lbound, ubound*\n",
    "  - 组合风险暴露的上下限 *risk_target* : 函数内部会以因子矩阵乘以权重得到组合的风险暴露。 \n",
    "  - 仓位调整的换手率上限(换手率以目标权重和当前权重向量的一范数表示，即差的绝对值之和) *turn_over_target*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Optimization status - optimal\n",
      "Optimal expect return - -0.3075339260279713\n",
      "Optimial portfolio weights - [3.33333333e-01 3.13308565e-13 2.16666667e-01 3.33333333e-01\n",
      " 1.16666667e-01]\n",
      "Initial portfolio weights - [0.33333333 0.         0.16666667 0.33333333 0.16666667]\n",
      "Turn over amount - 0.10000000000113718\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "from alphamind.portfolio.linearbuilder import linear_builder\n",
    "\n",
    "\"\"\"\n",
    "问题初始化\n",
    "\"\"\"\n",
    "\n",
    "# 假设有5个标的\n",
    "n = 5\n",
    "\n",
    "# 假设当前标的的权重\n",
    "current_pos = np.random.randint(0, n, size=n)\n",
    "current_pos = current_pos / current_pos.sum() \n",
    "\n",
    "# 假设标的预期收益向量\n",
    "expect_return = np.random.randn(n)\n",
    "\n",
    "# 假设标的风险因子矩阵, 仅有一个风险因子\n",
    "risk_factors = np.ones((n, 1))\n",
    "\n",
    "# 约束条件\n",
    "# 约束条件1 - 标的权重的上下限\n",
    "weight_lb = np.zeros(n)\n",
    "weight_ub = 0.5 * np.ones(n)\n",
    "\n",
    "# 限制条件2 - 组合风险暴露的上下限\n",
    "risk_lbound = np.ones(1)\n",
    "risk_ubound = np.ones(1)\n",
    "\n",
    "# 限制条件3 - 仓位调整的换手率上限（下限为0，故无需设置）\n",
    "turn_over_target = 0.1\n",
    "\n",
    "\"\"\"\n",
    "问题求解\n",
    "\"\"\"\n",
    "status, fvalue, x_values = linear_builder(expect_return,\n",
    "                                          weight_lb,\n",
    "                                          weight_ub,\n",
    "                                          risk_factors,\n",
    "                                          (risk_lbound, risk_ubound),\n",
    "                                          turn_over_target,\n",
    "                                          current_pos,\n",
    "                                          method='ecos')\n",
    "\n",
    "\n",
    "print('Optimization status - {}'.format(status))\n",
    "print('Optimal expect return - {}'.format(fvalue))\n",
    "print('Optimial portfolio weights - {}'.format(x_values))\n",
    "print('Initial portfolio weights - {}'.format(current_pos))\n",
    "print('Turn over amount - {}'.format(np.abs(x_values - current_pos).sum()))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 均值方差构建: *mean_variance_builder*\n",
    "- 根据经典的均值方差组合优化问题构建模型。\n",
    "- 目标函数为\n",
    "\n",
    "\n",
    "\\begin{align*}\n",
    "&\\mathop{\\arg\\max}_{\\omega} \\ \\  \\omega_a R - \\frac{1}{2} \\lambda \\sigma   \\\\\n",
    "&s.t.\\quad\n",
    "\\begin{cases}\n",
    "\\omega_L  \\le \\omega_a \\le \\omega_U  \\\\\n",
    "\\sigma_L \\le \\sigma \\le \\sigma_U\n",
    "\\end{cases}\n",
    "\\end{align*}\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "其中\n",
    "- $\\omega_a$是组合的主动权重向量，如果用$\\omega$和$\\omega_b$表示组合标的绝对权重向量和基准指数的标的权重向量，那么$\\omega_a =\\omega -\\omega_b$。\n",
    "- $R$是组合标的预期收益率向量。\n",
    "- $\\lambda$是风险厌恶系数, $\\sigma$是整个组合收益率的方差。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 函数的参数说明和使用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*mean_variance_builder* 接受的参数有\n",
    "- 组合标的的预期收益率向量 *er* \n",
    "- 风险模型 *risk_model* (optional)\n",
    "- 基准指数的标的权重向量*bm*\n",
    "- 组合标的的权重上下限 *lbound, ubound*\n",
    "- 组合风险暴露矩阵 *risk_exposure* (optional)\n",
    "- 组合风险暴露的上下限 *risk_target* (optional)\n",
    "- 风险厌恶系数 *lam* (optional)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "NOTE 1: 组合标的的权重上下限 *lbound, ubound* 是指绝对权重向量的上下限。\n",
    "- 代码中会计算主动权重上下限，即 *$\\omega_L$=lbound - $\\omega_b$*, *$\\omega_U$=ubound - $\\omega_b$*。最后会把$\\omega_b$加回到优化问题的解，得到组合标的最优权重。\n",
    "- 代码中也会计算主动收益的风险上下限， 即*risk_target_lbound - risk_exposure $\\omega_b$*, *risk_target_ubound + risk_exposure $\\omega_b$*。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "NOTE 2: 为了计算组合的方差，*mean_variance_builder*接受两种输入参数：\n",
    "- 标的收益率的协方差矩阵$\\Sigma$，由此可以计算得到\n",
    "\n",
    "\\begin{equation*}\n",
    "    \\sigma = \\omega_a^T\\Sigma \\omega_a\n",
    "\\end{equation*}\n",
    "\n",
    "其中$\\Sigma$对应入参中的risk_model['cov']。\n",
    "\n",
    "- 风险模型：因子收益率的协方差矩阵$F$, 特质收益率的协方差矩阵$\\Delta$(对角矩阵)\n",
    "\\begin{equation*}\n",
    "    \\sigma = \\omega_a^T \\left(X F X^T + \\Delta \\right)  \\omega_a\n",
    "\\end{equation*}\n",
    "\n",
    "由于一般情况下风险因子的数量小于标的股票的数量，所以$\\omega_a^T X F X^T \\omega_a$的时间复杂度要小于$\\omega_a^T\\Sigma \\omega_a$， 所以第二种方法效率更高。\n",
    "\n",
    "其中， $F$对应入参中的risk_model['factor_cov'], $\\Delta$对应risk_model['idsync']，$X$对应risk_model['factor_loading']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "from alphamind.portfolio.meanvariancebuilder import mean_variance_builder\n",
    "\n",
    "\"\"\"\n",
    "问题初始化\n",
    "\"\"\"\n",
    "# 假设有3个标的\n",
    "# 假设标的预期收益向量\n",
    "expect_return = np.array([0.1, 0.2, 0.3])\n",
    "\n",
    "# 假设收益的协方差矩阵\n",
    "cov = np.array([[0.02, 0.01, 0.02],\n",
    "                [0.01, 0.02, 0.03],\n",
    "                [0.02, 0.03, 0.02]])\n",
    "\n",
    "# 假设收益的非系统性方差(Idiosyncratic Risk)\n",
    "ids_var = np.diag([0.01, 0.02, 0.03])\n",
    "\n",
    "# 最终的协方差矩阵为\n",
    "cov += ids_var\n",
    "\n",
    "# 假设标的风险暴露矩阵, 有两个风险因子\n",
    "risk_exposure = np.array([[1., 1., 1.],\n",
    "                         [1., 0., 1.]]).T\n",
    "\n",
    "# 假设基准指数的权重向量\n",
    "bm = np.array([0.3, 0.3, 0.4])\n",
    "\n",
    "# 约束条件\n",
    "# 约束条件1 - 标的绝对权重的上下限\n",
    "lbound = np.array([0., 0., 0.])\n",
    "ubound = np.array([0.4, 0.4, 0.5])\n",
    "\n",
    "# 约束条件2 - 组合风险暴露的上下限\n",
    "risk_target = (np.array([bm.sum(), 0.3]), np.array([bm.sum(), 0.7]))\n",
    "\n",
    "# 建立风险模型，假设直接使用标的的协方差矩阵计算组合的方差\n",
    "risk_model = dict(cov=cov, factor_cov=None, factor_loading=None, idsync=None)\n",
    "\n",
    "\"\"\"\n",
    "问题求解\n",
    "\"\"\"\n",
    "status, _, x = mean_variance_builder(expect_return, risk_model, bm, lbound, ubound, risk_exposure, risk_target)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Optimal weight is [0.09999998 0.4        0.5       ]\n",
      "Risk exposure of optimal potfolio is [0.99999998 0.59999998]\n"
     ]
    }
   ],
   "source": [
    "print('Optimal weight is {0}'.format(x))\n",
    "print('Risk exposure of optimal potfolio is {0}'.format(x @ risk_exposure))"
   ]
  }
 ],
 "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"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
