{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a99da5f2",
   "metadata": {},
   "source": [
    "<div style=\"background-color:#000;\"><img src=\"pqn.png\"></img></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "486ef98c",
   "metadata": {},
   "source": [
    "This code downloads financial data for a set of symbols, computes portfolio and benchmark returns, and calculates the tracking error. It uses yfinance to retrieve adjusted close prices, computes daily percentage returns, and isolates the benchmark returns. The code then calculates the portfolio returns as the sum of individual stock returns and determines the excess returns over the benchmark. Finally, it computes the tracking error and visualizes the portfolio, benchmark, and excess returns over time."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "4d26c658",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "import yfinance as yf\n",
    "import warnings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "a0b4656a",
   "metadata": {},
   "outputs": [],
   "source": [
    "warnings.filterwarnings(\"ignore\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f1b7326a",
   "metadata": {},
   "source": [
    "Define the list of symbols for which financial data will be downloaded"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "a315ffd5",
   "metadata": {},
   "outputs": [],
   "source": [
    "symbols = [\n",
    "    \"XLE\",\n",
    "    \"XLF\",\n",
    "    \"XLU\",\n",
    "    \"XLI\",\n",
    "    \"GDX\",\n",
    "    \"XLK\",\n",
    "    \"XLV\",\n",
    "    \"XLY\",\n",
    "    \"XLP\",\n",
    "    \"XLB\",\n",
    "    \"SPY\"\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e4b9703c",
   "metadata": {},
   "source": [
    "Download adjusted close prices for the defined symbols from yfinance, starting from 2020-01-01"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "42b5e469",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[*********************100%%**********************]  11 of 11 completed\n"
     ]
    }
   ],
   "source": [
    "data = yf.download(symbols, start=\"2020-01-01\")[\"Adj Close\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b746678f",
   "metadata": {},
   "source": [
    "Compute daily percentage returns for the downloaded data and drop missing values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "2b05b225",
   "metadata": {},
   "outputs": [],
   "source": [
    "returns = data.pct_change().dropna()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f3fc9478",
   "metadata": {},
   "source": [
    "Separate the benchmark returns (SPY) from the portfolio returns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "2c659abd",
   "metadata": {},
   "outputs": [],
   "source": [
    "benchmark_returns = returns.pop(\"SPY\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "82487dd0",
   "metadata": {},
   "source": [
    "Calculate portfolio returns as the sum of individual stock returns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "a141cff5",
   "metadata": {},
   "outputs": [],
   "source": [
    "portfolio_returns = returns.sum(axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "151211f2",
   "metadata": {},
   "source": [
    "Calculate excess returns of the portfolio over the benchmark"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "149d8e2a",
   "metadata": {},
   "outputs": [],
   "source": [
    "excess_returns = portfolio_returns - benchmark_returns"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c0bee1b5",
   "metadata": {},
   "source": [
    "Display the first few rows of the portfolio returns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "42484d17",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Date\n",
       "2020-01-03   -0.065825\n",
       "2020-01-06    0.019263\n",
       "2020-01-07   -0.015849\n",
       "2020-01-08   -0.018535\n",
       "2020-01-09    0.044010\n",
       "dtype: float64"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "portfolio_returns.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1bfd2431",
   "metadata": {},
   "source": [
    "Compute the tracking error as the standard deviation of excess returns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "80275ee8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.11853774041100189"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tracking_error = excess_returns.std()\n",
    "tracking_error"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7017a0bd",
   "metadata": {},
   "source": [
    "Visualize the portfolio, benchmark, and excess returns over time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "bc8f2795",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 212268 characters]",
      "text/plain": [
       "<Figure size 1400x700 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(14, 7))\n",
    "plt.plot(portfolio_returns.index, portfolio_returns, label='Portfolio Returns', lw=0.5)\n",
    "plt.plot(benchmark_returns.index, benchmark_returns, label='Benchmark Returns', lw=0.5)\n",
    "plt.plot(excess_returns.index, excess_returns, label='Excess Returns', linestyle='--', lw=0.5)\n",
    "plt.legend()\n",
    "plt.title('Portfolio vs Benchmark Returns')\n",
    "plt.xlabel('Date')\n",
    "plt.ylabel('Returns')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a06c53d0",
   "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
}
