Files
strategy-lab/to_explore/pyquantnews/11_InformationRatio.ipynb
David Brazda e3da60c647 daily update
2024-10-21 20:57:56 +02:00

270 lines
6.9 KiB
Plaintext

{
"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
}