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

588 lines
12 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"id": "72b48e39",
"metadata": {},
"source": [
"<div style=\"background-color:#000;\"><img src=\"pqn.png\"></img></div>"
]
},
{
"cell_type": "markdown",
"id": "82dd16a7",
"metadata": {},
"source": [
"This notebook demonstrates how to construct and optimize a portfolio using historical asset returns and various risk measures. It utilizes `riskfolio` to model the portfolio and `yfinance` to fetch historical stock data. The notebook calculates expected returns and covariance matrices, integrates benchmark indices, and applies tracking error constraints. It then optimizes the portfolio to maximize risk-adjusted returns, plotting the efficient frontier and optimal asset allocations. This is practical for portfolio managers and financial analysts to enhance investment strategies."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a6872e45",
"metadata": {},
"outputs": [],
"source": [
"import riskfolio as rp\n",
"import pandas as pd\n",
"import yfinance as yf\n",
"import warnings\n",
"warnings.filterwarnings(\"ignore\")"
]
},
{
"cell_type": "markdown",
"id": "93795c66",
"metadata": {},
"source": [
"Define the list of assets for portfolio construction"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6014ff8a",
"metadata": {},
"outputs": [],
"source": [
"assets = [\n",
" \"JCI\",\n",
" \"TGT\",\n",
" \"CMCSA\",\n",
" \"CPB\",\n",
" \"MO\",\n",
" \"APA\",\n",
" \"MMC\",\n",
" \"JPM\",\n",
" \"ZION\",\n",
" \"PSA\",\n",
" \"BAX\",\n",
" \"BMY\",\n",
" \"LUV\",\n",
" \"PCAR\",\n",
" \"TXT\",\n",
" \"TMO\",\n",
" \"DE\",\n",
" \"MSFT\",\n",
" \"HPQ\",\n",
" \"SEE\",\n",
" \"VZ\",\n",
" \"CNP\",\n",
" \"NI\",\n",
" \"T\",\n",
" \"BA\",\n",
" \"^GSPC\",\n",
"]"
]
},
{
"cell_type": "markdown",
"id": "9b37333e",
"metadata": {},
"source": [
"Fetch historical stock data for the defined assets from Yahoo Finance"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "72998b3d",
"metadata": {},
"outputs": [],
"source": [
"data = yf.download(assets, start=\"2016-01-01\", end=\"2019-12-30\")"
]
},
{
"cell_type": "markdown",
"id": "bc2f3f83",
"metadata": {},
"source": [
"Select adjusted close prices and rename columns to match asset tickers"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0a2267eb",
"metadata": {},
"outputs": [],
"source": [
"data = data.loc[:, (\"Adj Close\", slice(None))]\n",
"data.columns = assets"
]
},
{
"cell_type": "markdown",
"id": "bf01924e",
"metadata": {},
"source": [
"Calculate daily returns of the assets and isolate benchmark returns"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "229bec20",
"metadata": {},
"outputs": [],
"source": [
"returns = data.pct_change().dropna()\n",
"bench_returns = returns.pop(\"^GSPC\").to_frame()"
]
},
{
"cell_type": "markdown",
"id": "15f6af5c",
"metadata": {},
"source": [
"Build the portfolio object and load the calculated returns into it"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fee416cb",
"metadata": {},
"outputs": [],
"source": [
"port = rp.Portfolio(returns=returns)"
]
},
{
"cell_type": "markdown",
"id": "d28c6840",
"metadata": {},
"source": [
"Estimate expected returns and covariance matrix using historical data"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7db95d05",
"metadata": {},
"outputs": [],
"source": [
"port.assets_stats(method_mu=\"hist\", method_cov=\"hist\", d=0.94)"
]
},
{
"cell_type": "markdown",
"id": "41e65884",
"metadata": {},
"source": [
"Set to False to indicate no benchmark weights, using index instead"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "dae58fef",
"metadata": {},
"outputs": [],
"source": [
"port.kindbench = False"
]
},
{
"cell_type": "markdown",
"id": "9e80d124",
"metadata": {},
"source": [
"Load benchmark returns into the portfolio object"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9f4a1a21",
"metadata": {},
"outputs": [],
"source": [
"port.benchindex = bench_returns"
]
},
{
"cell_type": "markdown",
"id": "29139cd2",
"metadata": {},
"source": [
"Enable the use of tracking error constraints in the optimization"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e856f599",
"metadata": {},
"outputs": [],
"source": [
"port.allowTE = True"
]
},
{
"cell_type": "markdown",
"id": "74cff10c",
"metadata": {},
"source": [
"Define the maximum allowed tracking error relative to benchmark returns"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3387d4f5",
"metadata": {},
"outputs": [],
"source": [
"port.TE = 0.008"
]
},
{
"cell_type": "markdown",
"id": "6b40e308",
"metadata": {},
"source": [
"Explain the goal of calculating optimal portfolios using different risk measures"
]
},
{
"cell_type": "markdown",
"id": "1aa657fe",
"metadata": {},
"source": [
"Define the model type for optimization, here using historical data"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "daa9ab9a",
"metadata": {},
"outputs": [],
"source": [
"model = \"Classic\""
]
},
{
"cell_type": "markdown",
"id": "ba2c93a8",
"metadata": {},
"source": [
"Specify the risk measure to use, in this case, Conditional Value at Risk (CVaR)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "007d98eb",
"metadata": {},
"outputs": [],
"source": [
"rm = \"CVaR\""
]
},
{
"cell_type": "markdown",
"id": "e0aa6b66",
"metadata": {},
"source": [
"Set the objective function to maximize Sharpe ratio"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "eee2108e",
"metadata": {},
"outputs": [],
"source": [
"obj = \"Sharpe\""
]
},
{
"cell_type": "markdown",
"id": "a6c05fcb",
"metadata": {},
"source": [
"Use historical scenarios for risk measures that depend on scenarios"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1b38bdef",
"metadata": {},
"outputs": [],
"source": [
"hist = True"
]
},
{
"cell_type": "markdown",
"id": "f8182805",
"metadata": {},
"source": [
"Define the risk-free rate for portfolio optimization calculations"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4fb85459",
"metadata": {},
"outputs": [],
"source": [
"rf = 0"
]
},
{
"cell_type": "markdown",
"id": "7714deb9",
"metadata": {},
"source": [
"Set the risk aversion factor, relevant only when the objective is 'Utility'"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b9ad9f21",
"metadata": {},
"outputs": [],
"source": [
"l = 0"
]
},
{
"cell_type": "markdown",
"id": "dc2ea3dc",
"metadata": {},
"source": [
"Perform the portfolio optimization to maximize the Sharpe ratio using CVaR"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "62686a64",
"metadata": {},
"outputs": [],
"source": [
"w = port.optimization(\n",
" model=model,\n",
" rm=rm,\n",
" obj=obj,\n",
" rf=rf,\n",
" l=l,\n",
" hist=hist\n",
")"
]
},
{
"cell_type": "markdown",
"id": "e94c0bf1",
"metadata": {},
"source": [
"Plot the optimized portfolio allocation using a pie chart"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c8ee7569",
"metadata": {},
"outputs": [],
"source": [
"ax = rp.plot_pie(\n",
" w=w,\n",
" title=\"Sharpe Mean CVaR\",\n",
" others=0.05,\n",
" nrow=25,\n",
" cmap=\"tab20\",\n",
" height=6,\n",
" width=10,\n",
" ax=None,\n",
")"
]
},
{
"cell_type": "markdown",
"id": "f0184145",
"metadata": {},
"source": [
"Calculate the efficient frontier for the portfolio using the defined risk measure"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "afe7ab13",
"metadata": {},
"outputs": [],
"source": [
"frontier = port.efficient_frontier(\n",
" model=model, \n",
" rm=rm, \n",
" points=50, \n",
" rf=rf, \n",
" hist=hist\n",
")"
]
},
{
"cell_type": "markdown",
"id": "52a38adc",
"metadata": {},
"source": [
"Plot the efficient frontier and the position of the optimized portfolio"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f34ffd29",
"metadata": {},
"outputs": [],
"source": [
"ax = rp.plot_frontier(\n",
" w_frontier=frontier,\n",
" mu=port.mu,\n",
" cov=port.cov,\n",
" returns=port.returns,\n",
" rm=rm,\n",
" rf=rf,\n",
" cmap=\"viridis\",\n",
" w=w,\n",
" label=\"Max Risk Adjusted Return Portfolio\",\n",
" marker=\"*\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "6a5f9663",
"metadata": {},
"source": [
"Define the list of different risk measures to use for portfolio optimization"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d7359bbf",
"metadata": {},
"outputs": [],
"source": [
"rms = [\n",
" \"MV\",\n",
" \"MAD\",\n",
" \"MSV\",\n",
" \"FLPM\",\n",
" \"SLPM\",\n",
" \"CVaR\",\n",
" \"EVaR\",\n",
" \"WR\",\n",
" \"MDD\",\n",
" \"ADD\",\n",
" \"CDaR\",\n",
" \"UCI\",\n",
" \"EDaR\",\n",
"]"
]
},
{
"cell_type": "markdown",
"id": "c20a237f",
"metadata": {},
"source": [
"Initialize an empty DataFrame to store the weights of optimized portfolios"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "aea2e6de",
"metadata": {},
"outputs": [],
"source": [
"w_s = pd.DataFrame([])"
]
},
{
"cell_type": "markdown",
"id": "d6206bbb",
"metadata": {},
"source": [
"Loop through each risk measure, optimize the portfolio, and store the weights"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e7d9329a",
"metadata": {},
"outputs": [],
"source": [
"for i in rms:\n",
" w = port.optimization(model=model, rm=i, obj=obj, rf=rf, l=l, hist=hist)\n",
" w_s = pd.concat([w_s, w], axis=1)"
]
},
{
"cell_type": "markdown",
"id": "696b88ff",
"metadata": {},
"source": [
"Assign the columns of the DataFrame to the respective risk measures and format the output"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bb0285f3",
"metadata": {},
"outputs": [],
"source": [
"w_s.columns = rms\n",
"w_s.style.format(\"{:.2%}\").background_gradient(cmap=\"YlGn\")"
]
},
{
"cell_type": "markdown",
"id": "6422513e",
"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
}