270 lines
6.9 KiB
Plaintext
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
|
|
}
|