{ "cells": [ { "cell_type": "markdown", "id": "72b48e39", "metadata": {}, "source": [ "
" ] }, { "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": [ "PyQuant News 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 get started with Python for quant finance. 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 }