{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 请在环境变量中设置`DB_URI`指向数据库"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "from cvxpy import *\n",
    "from cvxopt import *\n",
    "from alphamind.api import *\n",
    "from alphamind.cython.optimizers import QPOptimizer"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Data Preparing\n",
    "--------------------------"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "risk_penlty = 0.5\n",
    "ref_date = '2018-02-08'\n",
    "\n",
    "engine = SqlEngine(os.environ['DB_URI'])\n",
    "universe = Universe('ashare_ex')\n",
    "codes = engine.fetch_codes(ref_date, universe)\n",
    "\n",
    "risk_cov, risk_exposure = engine.fetch_risk_model(ref_date, codes)\n",
    "factor = engine.fetch_factor(ref_date, 'EPS', codes)\n",
    "\n",
    "total_data = pd.merge(factor, risk_exposure, on='code').dropna()\n",
    "all_styles = risk_styles + industry_styles + macro_styles\n",
    "\n",
    "risk_exposure_values = total_data[all_styles].values.astype(float)\n",
    "special_risk_values = total_data['srisk'].values.astype(float)\n",
    "risk_cov_values = risk_cov[all_styles].values\n",
    "\n",
    "sec_cov_values_full = risk_exposure_values @ risk_cov_values @ risk_exposure_values.T / 10000  + np.diag(special_risk_values ** 2) / 10000\n",
    "signal_full = total_data['EPS'].values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n = 200\n",
    "\n",
    "sec_cov_values = sec_cov_values_full[:n, :n]\n",
    "signal = signal_full[:n]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Optimizing Weights\n",
    "-------------------------------------"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "w = Variable(n)\n",
    "\n",
    "lbound = 0.\n",
    "ubound = 1. / n * 20\n",
    "\n",
    "risk = sum_squares(mul_elemwise(special_risk_values[:n] / 100., w)) + quad_form((w.T * risk_exposure_values[:n]).T, risk_cov_values[:n, :n] / 10000.)\n",
    "\n",
    "objective = Minimize(risk_penlty * risk  - signal * w)\n",
    "constraints = [w >= lbound,\n",
    "               w <= ubound,\n",
    "               sum_entries(w) == 1,]\n",
    "\n",
    "prob = Problem(objective, constraints)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "prob.solve(verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prob.status, prob.value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "prob.solve(verbose=True, solver='CVXOPT')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prob.status, prob.value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "P = matrix(sec_cov_values)\n",
    "q = -matrix(signal)\n",
    "\n",
    "G = np.zeros((2*n, n))\n",
    "h = np.zeros(2*n)\n",
    "for i in range(n):\n",
    "    G[i, i] = 1.\n",
    "    h[i] = 1. / n * 20\n",
    "    G[i+n, i] = -1.\n",
    "    h[i+n] = 0.\n",
    "    \n",
    "G = matrix(G)\n",
    "h = matrix(h)\n",
    "    \n",
    "A = np.ones((1, n))\n",
    "b = np.ones(1)\n",
    "\n",
    "A = matrix(A)\n",
    "b = matrix(b)\n",
    "\n",
    "sol = solvers.qp(P, q, G, h, A, b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "lbound = np.zeros(n)\n",
    "ubound = np.ones(n) * 20 / n\n",
    "cons_matrix = np.ones((1, n))\n",
    "clb = np.ones(1)\n",
    "cub = np.ones(1)\n",
    "qpopt = QPOptimizer(signal,\n",
    "                    None,\n",
    "                    lbound,\n",
    "                    ubound,\n",
    "                    cons_matrix,\n",
    "                    clb,\n",
    "                    cub,\n",
    "                    1.,\n",
    "                    risk_cov_values[:n, :n] / 10000.,\n",
    "                    risk_exposure_values[:n],\n",
    "                    special_risk_values[:n] * special_risk_values[:n] / 10000.)\n",
    "qpopt.feval()\n",
    "qpopt.status()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Performace Timing\n",
    "-------------------------"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import datetime as dt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def time_function(py_callable, n):\n",
    "    start = dt.datetime.now()\n",
    "    val = py_callable(n)\n",
    "    return (dt.datetime.now() - start).total_seconds(), val"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def cvxpy(n):\n",
    "    w = Variable(n)\n",
    "\n",
    "    lbound = 0.\n",
    "    ubound = 0.01\n",
    "    \n",
    "    risk = sum_squares(mul_elemwise(special_risk_values[:n] / 100., w)) + quad_form((w.T * risk_exposure_values[:n]).T, risk_cov_values[:n, :n] / 10000.)\n",
    "\n",
    "    objective = Minimize(risk_penlty * risk  - signal * w)\n",
    "    constraints = [w >= lbound,\n",
    "                   w <= ubound,\n",
    "                   sum_entries(w) == 1,]\n",
    "\n",
    "    prob = Problem(objective, constraints)\n",
    "    prob.solve(verbose=False)\n",
    "    return prob.value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def cvxopt(n):\n",
    "    P = matrix(sec_cov_values)\n",
    "    q = -matrix(signal)\n",
    "\n",
    "    G = np.zeros((2*n, n))\n",
    "    h = np.zeros(2*n)\n",
    "    for i in range(n):\n",
    "        G[i, i] = 1.\n",
    "        h[i] = 0.01\n",
    "        G[i+n, i] = -1.\n",
    "        h[i+n] = 0.\n",
    "\n",
    "    G = matrix(G)\n",
    "    h = matrix(h)\n",
    "\n",
    "    A = np.ones((1, n))\n",
    "    b = np.ones(1)\n",
    "\n",
    "    A = matrix(A)\n",
    "    b = matrix(b)\n",
    "    \n",
    "    solvers.options['show_progress'] = False\n",
    "    sol = solvers.qp(P, q, G, h, A, b)\n",
    "    return sol['primal objective']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def ipopt(n):\n",
    "    lbound = np.zeros(n)\n",
    "    ubound = np.ones(n) * 0.01\n",
    "    cons_matrix = np.ones((1, n))\n",
    "    clb = np.ones(1)\n",
    "    cub = np.ones(1)\n",
    "    qpopt = QPOptimizer(signal, None, lbound, ubound, cons_matrix, clb, cub, 1.,\n",
    "                        risk_cov_values[:n, :n] / 10000.,\n",
    "                        risk_exposure_values[:n],\n",
    "                        special_risk_values[:n] * special_risk_values[:n] / 10000.)\n",
    "    return qpopt.feval()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n_steps = list(range(200, 3201, 200))\n",
    "cvxpy_times = [None] * len(n_steps)\n",
    "cvxopt_times = [None] * len(n_steps)\n",
    "ipopt_times = [None] * len(n_steps)\n",
    "print(\"{0:<8}{1:>12}{2:>12}{3:>12}\".format('Scale(n)', 'cvxpy', 'cvxopt', 'ipopt'))\n",
    "\n",
    "for i, n in enumerate(n_steps):\n",
    "    sec_cov_values = sec_cov_values_full[:n, :n]\n",
    "    signal = signal_full[:n]\n",
    "    cvxpy_times[i], val1 = time_function(cvxpy, n)\n",
    "    cvxopt_times[i], val2 = time_function(cvxopt, n)\n",
    "    ipopt_times[i], val3 = time_function(ipopt, n)\n",
    "    \n",
    "    np.testing.assert_almost_equal(val1, val2, 4)\n",
    "    np.testing.assert_almost_equal(val2, val3, 4)\n",
    "    \n",
    "    print(\"{0:<8}{1:>12.4f}{2:>12.4f}{3:>12.4f}\".format(n, cvxpy_times[i], cvxopt_times[i], ipopt_times[i]))"
   ]
  },
  {
   "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.6.5"
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
