{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Portfolio optimization\n", "\n", "This tutorial provides a guide to portfolio optimization in **Garpar**.\n", "\n", "Previously, we explored the different sections of the `StocksSet` representation. However, there's one key element we haven't discussed yet: the weights. These are located in the top section alongside each stock and are represented by `W`. They play a crucial role in the optimization process, as they define the percentage of the budget allocated to each stock.\n", "\n", "To start with the tutorial, suppose we have the following `StocksSet` instance:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
StocksS0[W 1.0, H 0.5]S1[W 1.0, H 0.5]S2[W 1.0, H 0.5]S3[W 1.0, H 0.5]S4[W 1.0, H 0.5]
Days
0100.000000100.000000100.000000100.000000100.000000
198.824038101.124632100.939574100.86707999.200673
297.617608102.690200100.080834101.87749598.395740
396.437211101.68384298.911027102.60352499.302223
495.380756102.87702897.918415101.580874100.623724
594.623557101.72979198.816100102.412359101.178809
693.341120100.59776699.593006101.86978299.827506
792.231449101.330151100.881106100.82221598.685957
893.555166102.478027101.71955299.74948097.455211
992.531113103.317722102.79793898.80310898.001995
1091.346297104.206055103.85371199.98789196.873211
\n", "
11 days x 5 stocks - W.Size 5\n", "
" ], "text/plain": [ "Stocks S0[W 1.0, H 0.5] S1[W 1.0, H 0.5] S2[W 1.0, H 0.5] \\\n", "Days \n", "0 100.000000 100.000000 100.000000 \n", "1 98.824038 101.124632 100.939574 \n", "2 97.617608 102.690200 100.080834 \n", "3 96.437211 101.683842 98.911027 \n", "4 95.380756 102.877028 97.918415 \n", "5 94.623557 101.729791 98.816100 \n", "6 93.341120 100.597766 99.593006 \n", "7 92.231449 101.330151 100.881106 \n", "8 93.555166 102.478027 101.719552 \n", "9 92.531113 103.317722 102.797938 \n", "10 91.346297 104.206055 103.853711 \n", "\n", "Stocks S3[W 1.0, H 0.5] S4[W 1.0, H 0.5] \n", "Days \n", "0 100.000000 100.000000 \n", "1 100.867079 99.200673 \n", "2 101.877495 98.395740 \n", "3 102.603524 99.302223 \n", "4 101.580874 100.623724 \n", "5 102.412359 101.178809 \n", "6 101.869782 99.827506 \n", "7 100.822215 98.685957 \n", "8 99.749480 97.455211 \n", "9 98.803108 98.001995 \n", "10 99.987891 96.873211 \n", "StocksSet [11 days x 5 stocks - W.Size 5]" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from garpar.datasets.risso import make_risso_normal\n", "\n", "ss = make_risso_normal(days=10, stocks=5, random_state=702)\n", "ss\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will use the same prices throughout the tutorial. Note that the `W` value of each stock is `1.0` by default, this represents that we haven't applied an optimization model yet." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Your first optimization\n", "\n", "The optimization process currently available in **Garpar** is based on mean-variance models. These models solve the problem of assigning weights by considering the mean and variance of the returns of asset prices. Mean-variance optimization is rooted in Harry Markowitz’s 1952 paper, *Portfolio Selection* ([available here](https://onlinelibrary.wiley.com/doi/10.1111/j.1540-6261.1952.tb01525.x)), which revolutionized portfolio management, transforming it from an art into a science.\n", "\n", "For starters, we need an instance of an optimization model, also referred to as an optimizer. Currently, there are two classes that serve this purpose:\n", "\n", "- **MVOptimizer**: Applies one of several mean-variance models.\n", "- **Markowitz**: Specifically applies the Markowitz mean-variance model.\n", "\n", "We will begin with the `Markowitz` model. Later, we will explore the different mean-variance models that can be applied using `MVOptimizer`." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from garpar.optimize.mean_variance import Markowitz\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "Once we imported the `Markowitz` class, we have to instanciate it. This can be seen as defining parameters for the model. For example, in the Markowitz model, if we want to have a return of at least 15% we would instanciate the model as the following example shows:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "mk = Markowitz(target_return=0.15)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we have both the instance of the model and the `StocksSet`. We can solve the optimization problem." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
StocksS0[W 0.180339, H 0.5]S1[W 0.122161, H 0.5]S2[W 0.236844, H 0.5]S3[W 0.203510, H 0.5]S4[W 0.257145, H 0.5]
Days
0100.000000100.000000100.000000100.000000100.000000
198.824038101.124632100.939574100.86707999.200673
297.617608102.690200100.080834101.87749598.395740
396.437211101.68384298.911027102.60352499.302223
495.380756102.87702897.918415101.580874100.623724
594.623557101.72979198.816100102.412359101.178809
693.341120100.59776699.593006101.86978299.827506
792.231449101.330151100.881106100.82221598.685957
893.555166102.478027101.71955299.74948097.455211
992.531113103.317722102.79793898.80310898.001995
1091.346297104.206055103.85371199.98789196.873211
\n", "
11 days x 5 stocks - W.Size 5\n", "
" ], "text/plain": [ "Stocks S0[W 0.180339, H 0.5] S1[W 0.122161, H 0.5] S2[W 0.236844, H 0.5] \\\n", "Days \n", "0 100.000000 100.000000 100.000000 \n", "1 98.824038 101.124632 100.939574 \n", "2 97.617608 102.690200 100.080834 \n", "3 96.437211 101.683842 98.911027 \n", "4 95.380756 102.877028 97.918415 \n", "5 94.623557 101.729791 98.816100 \n", "6 93.341120 100.597766 99.593006 \n", "7 92.231449 101.330151 100.881106 \n", "8 93.555166 102.478027 101.719552 \n", "9 92.531113 103.317722 102.797938 \n", "10 91.346297 104.206055 103.853711 \n", "\n", "Stocks S3[W 0.203510, H 0.5] S4[W 0.257145, H 0.5] \n", "Days \n", "0 100.000000 100.000000 \n", "1 100.867079 99.200673 \n", "2 101.877495 98.395740 \n", "3 102.603524 99.302223 \n", "4 101.580874 100.623724 \n", "5 102.412359 101.178809 \n", "6 101.869782 99.827506 \n", "7 100.822215 98.685957 \n", "8 99.749480 97.455211 \n", "9 98.803108 98.001995 \n", "10 99.987891 96.873211 \n", "StocksSet [11 days x 5 stocks - W.Size 5]" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mk.optimize(ss)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note how the weights changed, lets see what happens when we try to optimize with a greater target return." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
StocksS0[W 0.180339, H 0.5]S1[W 0.122161, H 0.5]S2[W 0.236844, H 0.5]S3[W 0.203510, H 0.5]S4[W 0.257145, H 0.5]
Days
0100.000000100.000000100.000000100.000000100.000000
198.824038101.124632100.939574100.86707999.200673
297.617608102.690200100.080834101.87749598.395740
396.437211101.68384298.911027102.60352499.302223
495.380756102.87702897.918415101.580874100.623724
594.623557101.72979198.816100102.412359101.178809
693.341120100.59776699.593006101.86978299.827506
792.231449101.330151100.881106100.82221598.685957
893.555166102.478027101.71955299.74948097.455211
992.531113103.317722102.79793898.80310898.001995
1091.346297104.206055103.85371199.98789196.873211
\n", "
11 days x 5 stocks - W.Size 5\n", "
" ], "text/plain": [ "Stocks S0[W 0.180339, H 0.5] S1[W 0.122161, H 0.5] S2[W 0.236844, H 0.5] \\\n", "Days \n", "0 100.000000 100.000000 100.000000 \n", "1 98.824038 101.124632 100.939574 \n", "2 97.617608 102.690200 100.080834 \n", "3 96.437211 101.683842 98.911027 \n", "4 95.380756 102.877028 97.918415 \n", "5 94.623557 101.729791 98.816100 \n", "6 93.341120 100.597766 99.593006 \n", "7 92.231449 101.330151 100.881106 \n", "8 93.555166 102.478027 101.719552 \n", "9 92.531113 103.317722 102.797938 \n", "10 91.346297 104.206055 103.853711 \n", "\n", "Stocks S3[W 0.203510, H 0.5] S4[W 0.257145, H 0.5] \n", "Days \n", "0 100.000000 100.000000 \n", "1 100.867079 99.200673 \n", "2 101.877495 98.395740 \n", "3 102.603524 99.302223 \n", "4 101.580874 100.623724 \n", "5 102.412359 101.178809 \n", "6 101.869782 99.827506 \n", "7 100.822215 98.685957 \n", "8 99.749480 97.455211 \n", "9 98.803108 98.001995 \n", "10 99.987891 96.873211 \n", "StocksSet [11 days x 5 stocks - W.Size 5]" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mk = Markowitz(target_return=0.20)\n", "mk.optimize(ss)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The weights changed quite a bit. That's how you can make your first optimization!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The available models\n", "\n", "Now that we saw how to optimize using the Markowitz mean-variance model, let's see how to repeat this process using other models of the same kind.\n", "\n", "The following models are available to use:\n", "\n", "* Min volatility: Minimize risk and don't take into account return.\n", "* Max Sharpe: Maximize Sharpe Ratio, [described here](https://www.degruyter.com/document/doi/10.1515/9781400829408-022/pdf?licenseType=restricted).\n", "* Max quadratic utility: Maximize quadratic utility [described here](https://d1wqtxts1xzle7.cloudfront.net/83844141/Mixture_Symmetry_and_Quadratic_Utility20220411-2705-1fslpew.pdf?1738490763=&response-content-disposition=inline%3B+filename%3DMixture_Symmetry_and_Quadratic_Utility.pdf&Expires=1739654891&Signature=RdB6j4s10PA~KDfm1iXMh274JDXKFilazXWjzKspjfiKIyGEB6EtejYtSaOPPRvSp~G~AdL3j0W3JoGl7kQCdTtT0reh-ESXbKHRwzrBYMMdik7d2mn2EoF1v4eykHpI7NEh5a4JfH-n5YeY1kaxzbuwzfOCAXw5jVLu-XISNzM~mYRI9b0baME0XZVTirfmmnTG~vVd~uwFte-Mssr9qC8q2aqIfwibfcRtTvBYiBfWSxSUGJZw~ClqCcsdDrRlfPaPg6LpU7Yddceqs0n-9jMvyOs-J-7JLUM1eT9P6Ig2RPW6NGKJZgYMvWkzSrJlxtGgQm0upk0jewmneNPyaA__&Key-Pair-Id=APKAJLOHF5GGSLRBV4ZA).\n", "* Efficient risk: Minimize risk given a return value.\n", "* Efficient return: Maximize return given a risk value.\n", "\n", "We will show how two of these models behave and which values are required." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
StocksS0[W 0.000000, H 0.5]S1[W 0.302386, H 0.5]S2[W 0.382865, H 0.5]S3[W 0.122339, H 0.5]S4[W 0.192410, H 0.5]
Days
0100.000000100.000000100.000000100.000000100.000000
198.824038101.124632100.939574100.86707999.200673
297.617608102.690200100.080834101.87749598.395740
396.437211101.68384298.911027102.60352499.302223
495.380756102.87702897.918415101.580874100.623724
594.623557101.72979198.816100102.412359101.178809
693.341120100.59776699.593006101.86978299.827506
792.231449101.330151100.881106100.82221598.685957
893.555166102.478027101.71955299.74948097.455211
992.531113103.317722102.79793898.80310898.001995
1091.346297104.206055103.85371199.98789196.873211
\n", "
11 days x 5 stocks - W.Size 5\n", "
" ], "text/plain": [ "Stocks S0[W 0.000000, H 0.5] S1[W 0.302386, H 0.5] S2[W 0.382865, H 0.5] \\\n", "Days \n", "0 100.000000 100.000000 100.000000 \n", "1 98.824038 101.124632 100.939574 \n", "2 97.617608 102.690200 100.080834 \n", "3 96.437211 101.683842 98.911027 \n", "4 95.380756 102.877028 97.918415 \n", "5 94.623557 101.729791 98.816100 \n", "6 93.341120 100.597766 99.593006 \n", "7 92.231449 101.330151 100.881106 \n", "8 93.555166 102.478027 101.719552 \n", "9 92.531113 103.317722 102.797938 \n", "10 91.346297 104.206055 103.853711 \n", "\n", "Stocks S3[W 0.122339, H 0.5] S4[W 0.192410, H 0.5] \n", "Days \n", "0 100.000000 100.000000 \n", "1 100.867079 99.200673 \n", "2 101.877495 98.395740 \n", "3 102.603524 99.302223 \n", "4 101.580874 100.623724 \n", "5 102.412359 101.178809 \n", "6 101.869782 99.827506 \n", "7 100.822215 98.685957 \n", "8 99.749480 97.455211 \n", "9 98.803108 98.001995 \n", "10 99.987891 96.873211 \n", "StocksSet [11 days x 5 stocks - W.Size 5]" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from garpar.optimize.mean_variance import MVOptimizer\n", "\n", "max_sharpe_model = MVOptimizer(model=\"max_sharpe\", risk_free_rate=0.01)\n", "max_sharpe_model.optimize(ss)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we have a named parameter `risk_free_rate` that represents a rate of return received at zero-risk assets. You can consult [here](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=1317436) for details." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
StocksS0[W-0.000003, H 0.5]S1[W 1.000000, H 0.5]S2[W 0.000003, H 0.5]S3[W-0.000001, H 0.5]S4[W 0.000001, H 0.5]
Days
0100.000000100.000000100.000000100.000000100.000000
198.824038101.124632100.939574100.86707999.200673
297.617608102.690200100.080834101.87749598.395740
396.437211101.68384298.911027102.60352499.302223
495.380756102.87702897.918415101.580874100.623724
594.623557101.72979198.816100102.412359101.178809
693.341120100.59776699.593006101.86978299.827506
792.231449101.330151100.881106100.82221598.685957
893.555166102.478027101.71955299.74948097.455211
992.531113103.317722102.79793898.80310898.001995
1091.346297104.206055103.85371199.98789196.873211
\n", "
11 days x 5 stocks - W.Size 5\n", "
" ], "text/plain": [ "Stocks S0[W-0.000003, H 0.5] S1[W 1.000000, H 0.5] S2[W 0.000003, H 0.5] \\\n", "Days \n", "0 100.000000 100.000000 100.000000 \n", "1 98.824038 101.124632 100.939574 \n", "2 97.617608 102.690200 100.080834 \n", "3 96.437211 101.683842 98.911027 \n", "4 95.380756 102.877028 97.918415 \n", "5 94.623557 101.729791 98.816100 \n", "6 93.341120 100.597766 99.593006 \n", "7 92.231449 101.330151 100.881106 \n", "8 93.555166 102.478027 101.719552 \n", "9 92.531113 103.317722 102.797938 \n", "10 91.346297 104.206055 103.853711 \n", "\n", "Stocks S3[W-0.000001, H 0.5] S4[W 0.000001, H 0.5] \n", "Days \n", "0 100.000000 100.000000 \n", "1 100.867079 99.200673 \n", "2 101.877495 98.395740 \n", "3 102.603524 99.302223 \n", "4 101.580874 100.623724 \n", "5 102.412359 101.178809 \n", "6 101.869782 99.827506 \n", "7 100.822215 98.685957 \n", "8 99.749480 97.455211 \n", "9 98.803108 98.001995 \n", "10 99.987891 96.873211 \n", "StocksSet [11 days x 5 stocks - W.Size 5]" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "max_quadratic_utility_model = MVOptimizer(model=\"max_quadratic_utility\", risk_aversion=0.65)\n", "max_quadratic_utility_model.optimize(ss)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This model uses the parameter `risk_aversion`, which represents how much an investor prefers outcomes with low uncertainty. You can learn more about risk aversion [here](https://scholar.google.com/scholar_url?url=https://onlinelibrary.wiley.com/doi/abs/10.1111/j.1540-6261.1983.tb02291.x%3Fcasa_token%3DfAcRdkc8w3sAAAAA:XLknTwxobkFK76KtIRpytNAYH6GkjxIsapdXkMtWyUyXL8I6v1j0aX7tpCoT_AwjTJNGUpGnHEsCue-WJw&hl=es&sa=T&oi=gsb&ct=res&cd=2&d=12100987240872776321&ei=1PuxZ5PpBOehieoP4b3H4A0&scisig=AFWwaeZGLl6QLFyi0fhPHZZiz3dY).\n", "\n", "In some cases a market neutral strategy can be used. Allowing a profit with the increase or decrease of a stock value. By default the weights are bounded to be between 0 and 1. This can be changed by defining the attribute `weight_bounds`. Allowing the usage of market neutral models." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
StocksS0[W-1.0, H 0.5]S1[W 1.0, H 0.5]S2[W 1.0, H 0.5]S3[W 0.0, H 0.5]S4[W-1.0, H 0.5]
Days
0100.000000100.000000100.000000100.000000100.000000
198.824038101.124632100.939574100.86707999.200673
297.617608102.690200100.080834101.87749598.395740
396.437211101.68384298.911027102.60352499.302223
495.380756102.87702897.918415101.580874100.623724
594.623557101.72979198.816100102.412359101.178809
693.341120100.59776699.593006101.86978299.827506
792.231449101.330151100.881106100.82221598.685957
893.555166102.478027101.71955299.74948097.455211
992.531113103.317722102.79793898.80310898.001995
1091.346297104.206055103.85371199.98789196.873211
\n", "
11 days x 5 stocks - W.Size 5\n", "
" ], "text/plain": [ "Stocks S0[W-1.0, H 0.5] S1[W 1.0, H 0.5] S2[W 1.0, H 0.5] \\\n", "Days \n", "0 100.000000 100.000000 100.000000 \n", "1 98.824038 101.124632 100.939574 \n", "2 97.617608 102.690200 100.080834 \n", "3 96.437211 101.683842 98.911027 \n", "4 95.380756 102.877028 97.918415 \n", "5 94.623557 101.729791 98.816100 \n", "6 93.341120 100.597766 99.593006 \n", "7 92.231449 101.330151 100.881106 \n", "8 93.555166 102.478027 101.719552 \n", "9 92.531113 103.317722 102.797938 \n", "10 91.346297 104.206055 103.853711 \n", "\n", "Stocks S3[W 0.0, H 0.5] S4[W-1.0, H 0.5] \n", "Days \n", "0 100.000000 100.000000 \n", "1 100.867079 99.200673 \n", "2 101.877495 98.395740 \n", "3 102.603524 99.302223 \n", "4 101.580874 100.623724 \n", "5 102.412359 101.178809 \n", "6 101.869782 99.827506 \n", "7 100.822215 98.685957 \n", "8 99.749480 97.455211 \n", "9 98.803108 98.001995 \n", "10 99.987891 96.873211 \n", "StocksSet [11 days x 5 stocks - W.Size 5]" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "max_quadratic_utility_neutral = MVOptimizer(model=\"max_quadratic_utility\", risk_aversion=10, market_neutral=True, weight_bounds=(-1, 1))\n", "max_quadratic_utility_neutral.optimize(ss)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we don't change this, the system will show a warning." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/ebner/.pyenv/versions/garpar/lib/python3.13/site-packages/pypfopt/efficient_frontier/efficient_frontier.py:172: RuntimeWarning: Market neutrality requires shorting - bounds have been amended\n", " warnings.warn(\n" ] }, { "data": { "text/html": [ "
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
StocksS0[W-1.0, H 0.5]S1[W 1.0, H 0.5]S2[W 1.0, H 0.5]S3[W 0.0, H 0.5]S4[W-1.0, H 0.5]
Days
0100.000000100.000000100.000000100.000000100.000000
198.824038101.124632100.939574100.86707999.200673
297.617608102.690200100.080834101.87749598.395740
396.437211101.68384298.911027102.60352499.302223
495.380756102.87702897.918415101.580874100.623724
594.623557101.72979198.816100102.412359101.178809
693.341120100.59776699.593006101.86978299.827506
792.231449101.330151100.881106100.82221598.685957
893.555166102.478027101.71955299.74948097.455211
992.531113103.317722102.79793898.80310898.001995
1091.346297104.206055103.85371199.98789196.873211
\n", "
11 days x 5 stocks - W.Size 5\n", "
" ], "text/plain": [ "Stocks S0[W-1.0, H 0.5] S1[W 1.0, H 0.5] S2[W 1.0, H 0.5] \\\n", "Days \n", "0 100.000000 100.000000 100.000000 \n", "1 98.824038 101.124632 100.939574 \n", "2 97.617608 102.690200 100.080834 \n", "3 96.437211 101.683842 98.911027 \n", "4 95.380756 102.877028 97.918415 \n", "5 94.623557 101.729791 98.816100 \n", "6 93.341120 100.597766 99.593006 \n", "7 92.231449 101.330151 100.881106 \n", "8 93.555166 102.478027 101.719552 \n", "9 92.531113 103.317722 102.797938 \n", "10 91.346297 104.206055 103.853711 \n", "\n", "Stocks S3[W 0.0, H 0.5] S4[W-1.0, H 0.5] \n", "Days \n", "0 100.000000 100.000000 \n", "1 100.867079 99.200673 \n", "2 101.877495 98.395740 \n", "3 102.603524 99.302223 \n", "4 101.580874 100.623724 \n", "5 102.412359 101.178809 \n", "6 101.869782 99.827506 \n", "7 100.822215 98.685957 \n", "8 99.749480 97.455211 \n", "9 98.803108 98.001995 \n", "10 99.987891 96.873211 \n", "StocksSet [11 days x 5 stocks - W.Size 5]" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "max_quadratic_utility_neutral = MVOptimizer(model=\"max_quadratic_utility\", risk_aversion=10, market_neutral=True)\n", "max_quadratic_utility_neutral.optimize(ss)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The warning names the concept \"shorting\", in this context shorting means that the weights can be negative. More about how shorting works [here](https://www.sciencedirect.com/science/article/pii/S0304405X0200226X?casa_token=J97TfsEiGVUAAAAA:lgV9gs3Z1Ta4syU0iL9o6JY6cxf3f1Ld9FEzpgl3aJyDRIniZ2ZKtaksSFFepwxj8IXBZRq1htcQ).\n", "\n", "If we don't define a value that is required by the model, the system will also raise a warning and the optimization process will follow with a coerced value. We can see an example in the following code fragment:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/ebner/FAMAF/Tesis/garpar/garpar/optimize/mean_variance.py:259: UserWarning: No risk_free_rate specified, coercing it\n", " warnings.warn(\"No risk_free_rate specified, coercing it\")\n" ] }, { "data": { "text/html": [ "
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
StocksS0[W 0.000000, H 0.5]S1[W 0.278483, H 0.5]S2[W 0.369474, H 0.5]S3[W 0.137504, H 0.5]S4[W 0.214540, H 0.5]
Days
0100.000000100.000000100.000000100.000000100.000000
198.824038101.124632100.939574100.86707999.200673
297.617608102.690200100.080834101.87749598.395740
396.437211101.68384298.911027102.60352499.302223
495.380756102.87702897.918415101.580874100.623724
594.623557101.72979198.816100102.412359101.178809
693.341120100.59776699.593006101.86978299.827506
792.231449101.330151100.881106100.82221598.685957
893.555166102.478027101.71955299.74948097.455211
992.531113103.317722102.79793898.80310898.001995
1091.346297104.206055103.85371199.98789196.873211
\n", "
11 days x 5 stocks - W.Size 5\n", "
" ], "text/plain": [ "Stocks S0[W 0.000000, H 0.5] S1[W 0.278483, H 0.5] S2[W 0.369474, H 0.5] \\\n", "Days \n", "0 100.000000 100.000000 100.000000 \n", "1 98.824038 101.124632 100.939574 \n", "2 97.617608 102.690200 100.080834 \n", "3 96.437211 101.683842 98.911027 \n", "4 95.380756 102.877028 97.918415 \n", "5 94.623557 101.729791 98.816100 \n", "6 93.341120 100.597766 99.593006 \n", "7 92.231449 101.330151 100.881106 \n", "8 93.555166 102.478027 101.719552 \n", "9 92.531113 103.317722 102.797938 \n", "10 91.346297 104.206055 103.853711 \n", "\n", "Stocks S3[W 0.137504, H 0.5] S4[W 0.214540, H 0.5] \n", "Days \n", "0 100.000000 100.000000 \n", "1 100.867079 99.200673 \n", "2 101.877495 98.395740 \n", "3 102.603524 99.302223 \n", "4 101.580874 100.623724 \n", "5 102.412359 101.178809 \n", "6 101.869782 99.827506 \n", "7 100.822215 98.685957 \n", "8 99.749480 97.455211 \n", "9 98.803108 98.001995 \n", "10 99.987891 96.873211 \n", "StocksSet [11 days x 5 stocks - W.Size 5]" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "max_sharpe_coerced = MVOptimizer(model=\"max_sharpe\")\n", "max_sharpe_coerced.optimize(ss)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This concludes the portfolio optimization tutorial. Please refer to the [**API section**](../api/garpar.optimize.rst) for more information.\n", "\n", "There is one more tutorial that consists of creating a custom optimizer. You can find it [here](optimizer_creation.ipynb)." ] } ], "metadata": { "kernelspec": { "display_name": "garpar", "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.13.2" } }, "nbformat": 4, "nbformat_minor": 2 }