588 lines
12 KiB
Plaintext
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
|
|
}
|