{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "7b39d6ef",
   "metadata": {},
   "source": [
    "<div style=\"background-color:#000;\"><img src=\"pqn.png\"></img></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "09b6a2a7",
   "metadata": {},
   "source": [
    "This code fetches historical stock prices for AAPL, WMT, and SPY, calculates daily returns, and constructs a portfolio. It defines functions to compute the annual return and upside capture ratio of the portfolio compared to a benchmark. The upside capture ratio measures the portfolio's performance relative to the benchmark when the benchmark is up. This is useful in practice for portfolio performance analysis and risk management."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7589769e",
   "metadata": {},
   "outputs": [],
   "source": [
    "from openbb_terminal.sdk import openbb"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d7af040b",
   "metadata": {},
   "source": [
    "Fetch historical stock prices for AAPL, WMT, and SPY starting from 2020-01-01"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "01ac1e2a",
   "metadata": {},
   "outputs": [],
   "source": [
    "prices = openbb.economy.index(\n",
    "    [\"AAPL\", \"WMT\", \"SPY\"], \n",
    "    start_date=\"2020-01-01\"\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6cf5e1ea",
   "metadata": {},
   "source": [
    "Calculate daily returns by taking the percentage change and dropping any NaN values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fdd82c4a",
   "metadata": {},
   "outputs": [],
   "source": [
    "returns = prices.pct_change().dropna()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "859f62ce",
   "metadata": {},
   "source": [
    "Construct a portfolio's returns by summing the returns of AAPL and WMT, and selecting SPY and portfolio columns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1138bf9a",
   "metadata": {
    "lines_to_next_cell": 1
   },
   "outputs": [],
   "source": [
    "data = returns.assign(\n",
    "    port=returns[[\"AAPL\", \"WMT\"]].sum(axis=1)\n",
    ")[[\"SPY\", \"port\"]]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a8b57c5b",
   "metadata": {},
   "source": [
    "Compute annualized return for a series of returns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a4341da4",
   "metadata": {
    "lines_to_next_cell": 1
   },
   "outputs": [],
   "source": [
    "def annual_return(returns):\n",
    "    \"\"\"Compute annualized return.\n",
    "    \n",
    "    This function calculates the annualized return of a series of returns.\n",
    "    \n",
    "    Parameters\n",
    "    ----------\n",
    "    returns : pd.Series\n",
    "        Series of daily returns.\n",
    "    \n",
    "    Returns\n",
    "    -------\n",
    "    float\n",
    "        Annualized return.\n",
    "    \"\"\"\n",
    "    \n",
    "    # Calculate the number of years by dividing total days by 252 (trading days in a year)\n",
    "    num_years = len(returns) / 252\n",
    "    \n",
    "    # Calculate the annualized return using the geometric average\n",
    "    return (returns + 1).prod() ** (1 / num_years) - 1"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "567d5ef4",
   "metadata": {},
   "source": [
    "Compute upside capture ratio comparing portfolio returns to benchmark returns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1dea9509",
   "metadata": {
    "lines_to_next_cell": 1
   },
   "outputs": [],
   "source": [
    "def upside_capture(port_returns, bench_returns):\n",
    "    \"\"\"Compute upside capture ratio.\n",
    "    \n",
    "    This function calculates the upside capture ratio of portfolio returns \n",
    "    compared to benchmark returns.\n",
    "    \n",
    "    Parameters\n",
    "    ----------\n",
    "    port_returns : pd.Series\n",
    "        Series of portfolio returns.\n",
    "    bench_returns : pd.Series\n",
    "        Series of benchmark returns.\n",
    "    \n",
    "    Returns\n",
    "    -------\n",
    "    float\n",
    "        Upside capture ratio.\n",
    "    \"\"\"\n",
    "    \n",
    "    # Filter portfolio and benchmark returns where benchmark returns are positive\n",
    "    mask = bench_returns > 0\n",
    "    port_returns = port_returns[mask]\n",
    "    bench_returns = bench_returns[mask]\n",
    "    \n",
    "    # Calculate the upside capture ratio by dividing annualized portfolio return by annualized benchmark return\n",
    "    return (\n",
    "        annual_return(port_returns) \n",
    "        / annual_return(bench_returns)\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "600cd3ac",
   "metadata": {},
   "source": [
    "Calculate the upside capture ratio for the constructed portfolio compared to SPY"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "47239c60",
   "metadata": {},
   "outputs": [],
   "source": [
    "upside_capture(data.port, data.SPY)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4d6184fc",
   "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
}
