{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "6ae95e22",
   "metadata": {},
   "source": [
    "<div style=\"background-color:#000;\"><img src=\"pqn.png\"></img></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d03b11d6",
   "metadata": {},
   "source": [
    "This code downloads historical price data for specific stocks and calculates a portfolio's performance. It uses Yahoo Finance data to get adjusted close prices for QQQ, AAPL, and AMZN. The code constructs a simple portfolio of equal shares in AAPL and AMZN, computes the portfolio's value, daily returns, and cumulative returns over time. It then compares the portfolio's cumulative returns to a benchmark (QQQ). Finally, it calculates the information ratio to evaluate the portfolio's performance relative to the benchmark."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "df51ef5b",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import yfinance as yf"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d1365087",
   "metadata": {},
   "source": [
    "Download historical price data for QQQ, AAPL, and AMZN from Yahoo Finance"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6c4473af",
   "metadata": {},
   "outputs": [],
   "source": [
    "data = yf.download([\"QQQ\", \"AAPL\", \"AMZN\"], start=\"2020-01-01\", end=\"2022-07-31\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a22d784e",
   "metadata": {},
   "source": [
    "Extract adjusted close prices for the downloaded data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ab04ef0d",
   "metadata": {},
   "outputs": [],
   "source": [
    "closes = data['Adj Close']\n",
    "benchmark_returns = closes.QQQ.pct_change()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7788fe03",
   "metadata": {},
   "source": [
    "Construct a simple portfolio with equal shares of AAPL and AMZN"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3ac0ea69",
   "metadata": {},
   "outputs": [],
   "source": [
    "aapl_position = closes.AAPL * 50\n",
    "amzn_position = closes.AMZN * 50"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "82e0e793",
   "metadata": {},
   "source": [
    "Compute the portfolio value over time by summing the positions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3e5c9614",
   "metadata": {},
   "outputs": [],
   "source": [
    "portfolio_value = aapl_position + amzn_position"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "75f062eb",
   "metadata": {},
   "source": [
    "Calculate the portfolio's daily profit and loss (PnL)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "98e59e8c",
   "metadata": {},
   "outputs": [],
   "source": [
    "portfolio_pnl = (\n",
    "    (aapl_position - aapl_position.shift()) \n",
    "    + (amzn_position - amzn_position.shift())\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "410af899",
   "metadata": {},
   "source": [
    "Compute the portfolio's daily return by dividing PnL by the portfolio value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e17de052",
   "metadata": {},
   "outputs": [],
   "source": [
    "portfolio_returns = (portfolio_pnl / portfolio_value)\n",
    "portfolio_returns.name = \"Port\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6349bff8",
   "metadata": {},
   "source": [
    "Create cumulative returns for both the portfolio and the benchmark"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ea4a9b96",
   "metadata": {},
   "outputs": [],
   "source": [
    "portfolio_cumulative_returns = (portfolio_returns.fillna(0.0) + 1).cumprod()\n",
    "benchmark_cumulative_returns = (benchmark_returns.fillna(0.0) + 1).cumprod()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1382ec20",
   "metadata": {},
   "source": [
    "Plot the cumulative returns of the portfolio against the benchmark"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9ba13165",
   "metadata": {},
   "outputs": [],
   "source": [
    "portfolio_cumulative_returns = (portfolio_returns.fillna(0.0) + 1).cumprod()\n",
    "benchmark_cumulative_returns = (benchmark_returns.fillna(0.0) + 1).cumprod()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9e6d8de3",
   "metadata": {
    "lines_to_next_cell": 1
   },
   "outputs": [],
   "source": [
    "pd.concat([portfolio_cumulative_returns, benchmark_cumulative_returns], axis=1).plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8454d5ed",
   "metadata": {
    "lines_to_next_cell": 1
   },
   "outputs": [],
   "source": [
    "def information_ratio(portfolio_returns, benchmark_returns):\n",
    "    \"\"\"\n",
    "    Determines the information ratio of a strategy.\n",
    "    \n",
    "    Parameters\n",
    "    ----------\n",
    "    portfolio_returns : pd.Series or np.ndarray\n",
    "        Daily returns of the strategy, noncumulative.\n",
    "    benchmark_returns : int, float\n",
    "        Daily returns of the benchmark or factor, noncumulative.\n",
    "\n",
    "    Returns\n",
    "    -------\n",
    "    information_ratio : float\n",
    "\n",
    "    Note\n",
    "    -----\n",
    "    See https://en.wikipedia.org/wiki/Information_ratio for more details.\n",
    "    \"\"\"\n",
    "    \n",
    "    # Calculate active return by subtracting benchmark returns from portfolio returns\n",
    "    active_return = portfolio_returns - benchmark_returns\n",
    "\n",
    "    # Calculate tracking error as the standard deviation of active returns\n",
    "    tracking_error = active_return.std()\n",
    "\n",
    "    # Return the information ratio, which is the mean active return divided by the tracking error\n",
    "    return active_return.mean() / tracking_error"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7bd6fa3a",
   "metadata": {},
   "source": [
    "Calculate the information ratio of the portfolio relative to the benchmark"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6960c140",
   "metadata": {},
   "outputs": [],
   "source": [
    "information_ratio(portfolio_returns, benchmark_returns)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e225688c",
   "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"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
