.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "api/gallery/portfolio/mean_variance/plot_03_merton_analytic_frontier.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code or to run this example in your browser via JupyterLite. .. rst-class:: sphx-glr-example-title .. _sphx_glr_api_gallery_portfolio_mean_variance_plot_03_merton_analytic_frontier.py: Merton's analytic frontier ========================== Robert Merton (1972) derived the unconstrained frontier in closed form. Four scalars, .. math:: A = \mathbf 1'\Sigma^{-1}\mu,\quad B = \mu'\Sigma^{-1}\mu,\quad C = \mathbf 1'\Sigma^{-1}\mathbf 1,\quad D = BC - A^2, give the least variance at every mean, :math:`\sigma^2(m) = (Cm^2 - 2Am + B)/D`: a hyperbola with vertex at the minimum-variance portfolio :math:`(1/\sqrt C, A/C)` and asymptotes of slope :math:`\pm\sqrt{D/C}`. Every frontier portfolio is a mix of two of them (two-fund separation). .. GENERATED FROM PYTHON SOURCE LINES 20-33 .. code-block:: Python import matplotlib.pyplot as plt import numpy as np from financialkit.portfolio import efficient_portfolio, frontier_constants from financialkit.portfolio.utils import solve_qp mu = np.array([0.04, 0.06, 0.08, 0.11]) vol = np.array([0.08, 0.12, 0.18, 0.26]) corr = np.array([[1.0, 0.3, 0.2, 0.1], [0.3, 1.0, 0.5, 0.4], [0.2, 0.5, 1.0, 0.6], [0.1, 0.4, 0.6, 1.0]]) cov = corr * np.outer(vol, vol) fc = frontier_constants(mu, cov) print(f"A = {fc.A:.3f}, B = {fc.B:.4f}, C = {fc.C:.2f}, D = {fc.D:.4f}") .. rst-class:: sphx-glr-script-out .. code-block:: none A = 8.271, B = 0.4556, C = 180.40, D = 13.7838 .. GENERATED FROM PYTHON SOURCE LINES 34-36 Closed form against a numerical optimizer ----------------------------------------- .. GENERATED FROM PYTHON SOURCE LINES 36-53 .. code-block:: Python m = np.linspace(-0.05, 0.2, 200) fig, ax = plt.subplots(figsize=(7, 4.5)) ax.plot(fc.volatility(m), m, lw=2, label="Merton's hyperbola") targets = np.linspace(-0.02, 0.18, 9) numeric = [np.sqrt(w @ cov @ w) for w in (solve_qp(cov, np.vstack([np.ones(4), mu]), [1.0, t], bounds=(-10.0, 10.0)) for t in targets)] ax.plot(numeric, targets, "o", color="C3", label="SLSQP") s = np.linspace(0, fc.volatility(0.2), 2) for sign in (1, -1): ax.plot(s, fc.gmv_return + sign * fc.asymptote_slope() * s, ":", color="0.4") ax.plot(fc.gmv_volatility, fc.gmv_return, "D", color="C2", label="vertex: minimum variance") ax.plot(vol, mu, "o", color="0.3") ax.set_xlabel("volatility") ax.set_ylabel("expected return") ax.legend() fig.tight_layout() .. image-sg:: /api/gallery/portfolio/mean_variance/images/sphx_glr_plot_03_merton_analytic_frontier_001.png :alt: plot 03 merton analytic frontier :srcset: /api/gallery/portfolio/mean_variance/images/sphx_glr_plot_03_merton_analytic_frontier_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 54-59 Two-fund separation ------------------- The weights are linear in the target mean, so any two frontier portfolios span the whole frontier. .. GENERATED FROM PYTHON SOURCE LINES 59-64 .. code-block:: Python w1, w2 = efficient_portfolio(mu, cov, 0.05).weights, efficient_portfolio(mu, cov, 0.10).weights w3 = efficient_portfolio(mu, cov, 0.15).weights print("w(15%) = 2 w(10%) - w(5%):", np.allclose(w3, 2 * w2 - w1)) plt.show() .. rst-class:: sphx-glr-script-out .. code-block:: none w(15%) = 2 w(10%) - w(5%): True .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.035 seconds) .. _sphx_glr_download_api_gallery_portfolio_mean_variance_plot_03_merton_analytic_frontier.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: lite-badge .. image:: images/jupyterlite_badge_logo.svg :target: ../../../../lite/lab/index.html?path=api/gallery/portfolio/mean_variance/plot_03_merton_analytic_frontier.ipynb :alt: Launch JupyterLite :width: 150 px .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_03_merton_analytic_frontier.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_03_merton_analytic_frontier.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_03_merton_analytic_frontier.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_