{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "63b4b4b5",
   "metadata": {},
   "source": [
    "<div style=\"background-color:#000;\"><img src=\"pqn.png\"></img></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "89c213c7",
   "metadata": {},
   "source": [
    "## Load financial data, perform factor analysis, and optimize portfolio"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2c15281f",
   "metadata": {},
   "source": [
    "This code loads historical financial data for selected assets and factors. It calculates daily returns and performs factor analysis using principal component regression. The portfolio is optimized using the `riskfolio-lib` library, incorporating the factor loadings. The objective is to maximize the Sharpe ratio while minimizing variance. Finally, it visualizes the optimized portfolio weights in a pie chart."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "0015b086",
   "metadata": {},
   "outputs": [],
   "source": [
    "import yfinance as yf\n",
    "import riskfolio as rf\n",
    "import pandas as pd\n",
    "import warnings\n",
    "pd.options.display.float_format = \"{:.4%}\".format\n",
    "warnings.filterwarnings(\"ignore\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f8c086a1",
   "metadata": {},
   "source": [
    "Define the list of assets in the portfolio"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "fe06a269",
   "metadata": {},
   "outputs": [],
   "source": [
    "mag_7 = [\n",
    "    \"AMZN\",\n",
    "    \"AAPL\",\n",
    "    \"NVDA\",\n",
    "    \"META\",\n",
    "    \"TSLA\",\n",
    "    \"MSFT\",\n",
    "    \"GOOG\",\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ae283cbf",
   "metadata": {},
   "source": [
    "Define the list of factor indices"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "55060d5e",
   "metadata": {},
   "outputs": [],
   "source": [
    "factors = [\"MTUM\", \"QUAL\", \"VLUE\", \"SIZE\", \"USMV\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "454eaad5",
   "metadata": {},
   "source": [
    "Define the date range for historical data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "52b422ae",
   "metadata": {},
   "outputs": [],
   "source": [
    "start = \"2020-01-01\"\n",
    "end = \"2024-07-31\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "563e78fd",
   "metadata": {},
   "source": [
    "Download the historical adjusted close price data for the assets"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "9f8f17a5",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[*********************100%%**********************]  7 of 7 completed\n"
     ]
    }
   ],
   "source": [
    "port_returns = (\n",
    "    yf\n",
    "    .download(\n",
    "        mag_7, \n",
    "        start=start, \n",
    "        end=end\n",
    "    )[\"Adj Close\"]\n",
    "    .pct_change()\n",
    "    .dropna()\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1e72633f",
   "metadata": {},
   "source": [
    "Download the historical adjusted close price data for the factors"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "5c3e89a6",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[*********************100%%**********************]  5 of 5 completed\n"
     ]
    }
   ],
   "source": [
    "factor_returns = (\n",
    "    yf\n",
    "    .download(\n",
    "        factors, \n",
    "        start=start, \n",
    "        end=end\n",
    "    )[\"Adj Close\"]\n",
    "    .pct_change()\n",
    "    .dropna()\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c4793efa",
   "metadata": {},
   "source": [
    "Initialize the Portfolio object with the asset returns data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "5e59c2c6",
   "metadata": {},
   "outputs": [],
   "source": [
    "port = rf.Portfolio(returns=port_returns)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "884c3ab1",
   "metadata": {},
   "source": [
    "Compute historical mean returns and covariance matrix using the Ledoit-Wolf shrinkage method"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "b0f678d4",
   "metadata": {},
   "outputs": [],
   "source": [
    "port.assets_stats(method_mu=\"hist\", method_cov=\"ledoit\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "41f5be20",
   "metadata": {},
   "source": [
    "Set the lower return constraint for the portfolio optimization"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "744d8d78",
   "metadata": {},
   "outputs": [],
   "source": [
    "port.lowerret = 0.00056488 * 1.5"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a7015cc",
   "metadata": {},
   "source": [
    "Compute the loadings matrix using principal component regression on factor returns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "9eddcb56",
   "metadata": {},
   "outputs": [],
   "source": [
    "loadings = rf.loadings_matrix(\n",
    "    X=factor_returns,\n",
    "    Y=port_returns, \n",
    "    feature_selection=\"PCR\",\n",
    "    n_components=0.95\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ee20362f",
   "metadata": {},
   "source": [
    "Display the loadings matrix with a background gradient color mapping"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "63cb59c6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style type=\"text/css\">\n",
       "#T_f520b_row0_col0 {\n",
       "  background-color: #e44c34;\n",
       "  color: #f1f1f1;\n",
       "}\n",
       "#T_f520b_row0_col1 {\n",
       "  background-color: #e75337;\n",
       "  color: #f1f1f1;\n",
       "}\n",
       "#T_f520b_row0_col2 {\n",
       "  background-color: #e24731;\n",
       "  color: #f1f1f1;\n",
       "}\n",
       "#T_f520b_row0_col3 {\n",
       "  background-color: #5ab760;\n",
       "  color: #f1f1f1;\n",
       "}\n",
       "#T_f520b_row0_col4 {\n",
       "  background-color: #219c52;\n",
       "  color: #f1f1f1;\n",
       "}\n",
       "#T_f520b_row0_col5 {\n",
       "  background-color: #4eb15d;\n",
       "  color: #f1f1f1;\n",
       "}\n",
       "#T_f520b_row1_col0, #T_f520b_row1_col4, #T_f520b_row2_col1, #T_f520b_row2_col2, #T_f520b_row5_col3, #T_f520b_row5_col5 {\n",
       "  background-color: #a50026;\n",
       "  color: #f1f1f1;\n",
       "}\n",
       "#T_f520b_row1_col1, #T_f520b_row3_col2 {\n",
       "  background-color: #fba35c;\n",
       "  color: #000000;\n",
       "}\n",
       "#T_f520b_row1_col2 {\n",
       "  background-color: #a90426;\n",
       "  color: #f1f1f1;\n",
       "}\n",
       "#T_f520b_row1_col3 {\n",
       "  background-color: #fa9857;\n",
       "  color: #000000;\n",
       "}\n",
       "#T_f520b_row1_col5 {\n",
       "  background-color: #f5fbb2;\n",
       "  color: #000000;\n",
       "}\n",
       "#T_f520b_row2_col0, #T_f520b_row3_col0 {\n",
       "  background-color: #d22b27;\n",
       "  color: #f1f1f1;\n",
       "}\n",
       "#T_f520b_row2_col3, #T_f520b_row2_col4, #T_f520b_row2_col5, #T_f520b_row5_col0, #T_f520b_row5_col1, #T_f520b_row5_col2 {\n",
       "  background-color: #006837;\n",
       "  color: #f1f1f1;\n",
       "}\n",
       "#T_f520b_row3_col1 {\n",
       "  background-color: #fb9d59;\n",
       "  color: #000000;\n",
       "}\n",
       "#T_f520b_row3_col3 {\n",
       "  background-color: #8ecf67;\n",
       "  color: #000000;\n",
       "}\n",
       "#T_f520b_row3_col4 {\n",
       "  background-color: #118848;\n",
       "  color: #f1f1f1;\n",
       "}\n",
       "#T_f520b_row3_col5 {\n",
       "  background-color: #93d168;\n",
       "  color: #000000;\n",
       "}\n",
       "#T_f520b_row4_col0 {\n",
       "  background-color: #d42d27;\n",
       "  color: #f1f1f1;\n",
       "}\n",
       "#T_f520b_row4_col1 {\n",
       "  background-color: #f57547;\n",
       "  color: #f1f1f1;\n",
       "}\n",
       "#T_f520b_row4_col2 {\n",
       "  background-color: #e54e35;\n",
       "  color: #f1f1f1;\n",
       "}\n",
       "#T_f520b_row4_col3 {\n",
       "  background-color: #addc6f;\n",
       "  color: #000000;\n",
       "}\n",
       "#T_f520b_row4_col4 {\n",
       "  background-color: #96d268;\n",
       "  color: #000000;\n",
       "}\n",
       "#T_f520b_row4_col5 {\n",
       "  background-color: #89cc67;\n",
       "  color: #000000;\n",
       "}\n",
       "#T_f520b_row5_col4 {\n",
       "  background-color: #b3df72;\n",
       "  color: #000000;\n",
       "}\n",
       "#T_f520b_row6_col0 {\n",
       "  background-color: #48ae5c;\n",
       "  color: #f1f1f1;\n",
       "}\n",
       "#T_f520b_row6_col1 {\n",
       "  background-color: #4bb05c;\n",
       "  color: #f1f1f1;\n",
       "}\n",
       "#T_f520b_row6_col2 {\n",
       "  background-color: #7ac665;\n",
       "  color: #000000;\n",
       "}\n",
       "#T_f520b_row6_col3 {\n",
       "  background-color: #c21c27;\n",
       "  color: #f1f1f1;\n",
       "}\n",
       "#T_f520b_row6_col4 {\n",
       "  background-color: #fbfdba;\n",
       "  color: #000000;\n",
       "}\n",
       "#T_f520b_row6_col5 {\n",
       "  background-color: #dd3d2d;\n",
       "  color: #f1f1f1;\n",
       "}\n",
       "</style>\n",
       "<table id=\"T_f520b\">\n",
       "  <thead>\n",
       "    <tr>\n",
       "      <th class=\"blank level0\" >&nbsp;</th>\n",
       "      <th id=\"T_f520b_level0_col0\" class=\"col_heading level0 col0\" >const</th>\n",
       "      <th id=\"T_f520b_level0_col1\" class=\"col_heading level0 col1\" >MTUM</th>\n",
       "      <th id=\"T_f520b_level0_col2\" class=\"col_heading level0 col2\" >QUAL</th>\n",
       "      <th id=\"T_f520b_level0_col3\" class=\"col_heading level0 col3\" >SIZE</th>\n",
       "      <th id=\"T_f520b_level0_col4\" class=\"col_heading level0 col4\" >USMV</th>\n",
       "      <th id=\"T_f520b_level0_col5\" class=\"col_heading level0 col5\" >VLUE</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th id=\"T_f520b_level0_row0\" class=\"row_heading level0 row0\" >AAPL</th>\n",
       "      <td id=\"T_f520b_row0_col0\" class=\"data row0 col0\" >0.0006</td>\n",
       "      <td id=\"T_f520b_row0_col1\" class=\"data row0 col1\" >0.6574</td>\n",
       "      <td id=\"T_f520b_row0_col2\" class=\"data row0 col2\" >0.2702</td>\n",
       "      <td id=\"T_f520b_row0_col3\" class=\"data row0 col3\" >0.0832</td>\n",
       "      <td id=\"T_f520b_row0_col4\" class=\"data row0 col4\" >0.1876</td>\n",
       "      <td id=\"T_f520b_row0_col5\" class=\"data row0 col5\" >-0.0609</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_f520b_level0_row1\" class=\"row_heading level0 row1\" >AMZN</th>\n",
       "      <td id=\"T_f520b_row1_col0\" class=\"data row1 col0\" >0.0003</td>\n",
       "      <td id=\"T_f520b_row1_col1\" class=\"data row1 col1\" >0.7814</td>\n",
       "      <td id=\"T_f520b_row1_col2\" class=\"data row1 col2\" >0.2458</td>\n",
       "      <td id=\"T_f520b_row1_col3\" class=\"data row1 col3\" >0.0017</td>\n",
       "      <td id=\"T_f520b_row1_col4\" class=\"data row1 col4\" >0.1140</td>\n",
       "      <td id=\"T_f520b_row1_col5\" class=\"data row1 col5\" >-0.1846</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_f520b_level0_row2\" class=\"row_heading level0 row2\" >GOOG</th>\n",
       "      <td id=\"T_f520b_row2_col0\" class=\"data row2 col0\" >0.0005</td>\n",
       "      <td id=\"T_f520b_row2_col1\" class=\"data row2 col1\" >0.5038</td>\n",
       "      <td id=\"T_f520b_row2_col2\" class=\"data row2 col2\" >0.2437</td>\n",
       "      <td id=\"T_f520b_row2_col3\" class=\"data row2 col3\" >0.1110</td>\n",
       "      <td id=\"T_f520b_row2_col4\" class=\"data row2 col4\" >0.1968</td>\n",
       "      <td id=\"T_f520b_row2_col5\" class=\"data row2 col5\" >0.0079</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_f520b_level0_row3\" class=\"row_heading level0 row3\" >META</th>\n",
       "      <td id=\"T_f520b_row3_col0\" class=\"data row3 col0\" >0.0005</td>\n",
       "      <td id=\"T_f520b_row3_col1\" class=\"data row3 col1\" >0.7736</td>\n",
       "      <td id=\"T_f520b_row3_col2\" class=\"data row3 col2\" >0.2974</td>\n",
       "      <td id=\"T_f520b_row3_col3\" class=\"data row3 col3\" >0.0713</td>\n",
       "      <td id=\"T_f520b_row3_col4\" class=\"data row3 col4\" >0.1910</td>\n",
       "      <td id=\"T_f520b_row3_col5\" class=\"data row3 col5\" >-0.1023</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_f520b_level0_row4\" class=\"row_heading level0 row4\" >MSFT</th>\n",
       "      <td id=\"T_f520b_row4_col0\" class=\"data row4 col0\" >0.0005</td>\n",
       "      <td id=\"T_f520b_row4_col1\" class=\"data row4 col1\" >0.7110</td>\n",
       "      <td id=\"T_f520b_row4_col2\" class=\"data row4 col2\" >0.2721</td>\n",
       "      <td id=\"T_f520b_row4_col3\" class=\"data row4 col3\" >0.0641</td>\n",
       "      <td id=\"T_f520b_row4_col4\" class=\"data row4 col4\" >0.1738</td>\n",
       "      <td id=\"T_f520b_row4_col5\" class=\"data row4 col5\" >-0.0958</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_f520b_level0_row5\" class=\"row_heading level0 row5\" >NVDA</th>\n",
       "      <td id=\"T_f520b_row5_col0\" class=\"data row5 col0\" >0.0022</td>\n",
       "      <td id=\"T_f520b_row5_col1\" class=\"data row5 col1\" >1.4838</td>\n",
       "      <td id=\"T_f520b_row5_col2\" class=\"data row5 col2\" >0.4343</td>\n",
       "      <td id=\"T_f520b_row5_col3\" class=\"data row5 col3\" >-0.0386</td>\n",
       "      <td id=\"T_f520b_row5_col4\" class=\"data row5 col4\" >0.1696</td>\n",
       "      <td id=\"T_f520b_row5_col5\" class=\"data row5 col5\" >-0.3989</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_f520b_level0_row6\" class=\"row_heading level0 row6\" >TSLA</th>\n",
       "      <td id=\"T_f520b_row6_col0\" class=\"data row6 col0\" >0.0019</td>\n",
       "      <td id=\"T_f520b_row6_col1\" class=\"data row6 col1\" >1.3202</td>\n",
       "      <td id=\"T_f520b_row6_col2\" class=\"data row6 col2\" >0.3901</td>\n",
       "      <td id=\"T_f520b_row6_col3\" class=\"data row6 col3\" >-0.0296</td>\n",
       "      <td id=\"T_f520b_row6_col4\" class=\"data row6 col4\" >0.1562</td>\n",
       "      <td id=\"T_f520b_row6_col5\" class=\"data row6 col5\" >-0.3494</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n"
      ],
      "text/plain": [
       "<pandas.io.formats.style.Styler at 0x307ab26b0>"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "loadings.style.format(\"{:.4f}\").background_gradient(cmap='RdYlGn')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "49caab33",
   "metadata": {},
   "source": [
    "Set the factor returns and compute their statistics"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "8e37737c",
   "metadata": {},
   "outputs": [],
   "source": [
    "port.factors = factor_returns\n",
    "port.factors_stats(\n",
    "    method_mu=\"hist\",\n",
    "    method_cov=\"ledoit\",\n",
    "    feature_selection=\"PCR\",  # Method to select best model, could be PCR or Stepwise,\n",
    "    dict_risk=dict(\n",
    "        n_components=0.95  # 95% of explained variance.\n",
    "    )\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a6aa1350",
   "metadata": {},
   "source": [
    "Optimize the portfolio using a factor model, variance as the risk measure, and Sharpe ratio as the objective function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "94613b72",
   "metadata": {},
   "outputs": [],
   "source": [
    "w = port.optimization(\n",
    "    model=\"FM\",  # Factor model\n",
    "    rm=\"MV\",  # Risk measure used, this time will be variance\n",
    "    obj=\"Sharpe\",  # Objective function, could be MinRisk, MaxRet, Utility or Sharpe\n",
    "    hist=False,  # Use risk factor model for expected returns\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cbec3a31",
   "metadata": {},
   "source": [
    "Plot the optimized portfolio weights as a pie chart"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "f04cc6a6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 68248 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "ax = rf.plot_pie(\n",
    "    w=w,\n",
    "    title='Sharpe FM Mean Variance',\n",
    "    others=0.05,\n",
    "    nrow=25,\n",
    "    cmap=\"tab20\"\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ac56eec5",
   "metadata": {},
   "source": [
    "<a href=\"https://pyquantnews.com/\">PyQuant News</a> is where finance practitioners level up with Python for quant finance, algorithmic trading, and market data analysis. Looking to get started? Check out the fastest growing, top-selling course to <a href=\"https://gettingstartedwithpythonforquantfinance.com/\">get started with Python for quant finance</a>. For educational purposes. Not investment advise. Use at your own risk."
   ]
  }
 ],
 "metadata": {
  "jupytext": {
   "cell_metadata_filter": "-all",
   "main_language": "python",
   "notebook_metadata_filter": "-all"
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
