.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "api/gallery/portfolio/bayesian/plot_01_black_litterman.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_bayesian_plot_01_black_litterman.py: Black-Litterman: equilibrium plus views ======================================= Fischer Black and Robert Litterman (1992), at Goldman Sachs, started mean-variance optimization from the expected returns that make the market portfolio optimal, :math:`\Pi = \delta\Sigma w_{mkt}`, rather than from historical averages. Investors' views then tilt those returns in proportion to their confidence. This reproduces He and Litterman's (1999) seven-country example: the view that German equities will beat the rest of Europe by 5% moves weight into Germany, out of France and the UK, and leaves the other countries' weights alone. .. GENERATED FROM PYTHON SOURCE LINES 16-37 .. code-block:: Python import matplotlib.pyplot as plt import numpy as np from financialkit.portfolio import black_litterman, implied_equilibrium_returns names = ["Australia", "Canada", "France", "Germany", "Japan", "UK", "USA"] corr = np.array( [ [1.000, 0.488, 0.478, 0.515, 0.439, 0.512, 0.491], [0.488, 1.000, 0.664, 0.655, 0.310, 0.608, 0.779], [0.478, 0.664, 1.000, 0.861, 0.355, 0.783, 0.668], [0.515, 0.655, 0.861, 1.000, 0.354, 0.777, 0.653], [0.439, 0.310, 0.355, 0.354, 1.000, 0.405, 0.306], [0.512, 0.608, 0.783, 0.777, 0.405, 1.000, 0.652], [0.491, 0.779, 0.668, 0.653, 0.306, 0.652, 1.000], ] ) vol = np.array([0.160, 0.203, 0.248, 0.271, 0.210, 0.200, 0.187]) cov = corr * np.outer(vol, vol) w_mkt = np.array([0.016, 0.022, 0.052, 0.055, 0.116, 0.124, 0.615]) .. GENERATED FROM PYTHON SOURCE LINES 38-40 Reverse optimization -------------------- .. GENERATED FROM PYTHON SOURCE LINES 40-44 .. code-block:: Python pi = implied_equilibrium_returns(cov, w_mkt, delta=2.5) print("equilibrium returns (%):", np.round(100 * pi, 1)) .. rst-class:: sphx-glr-script-out .. code-block:: none equilibrium returns (%): [3.9 6.9 8.4 9. 4.3 6.8 7.6] .. GENERATED FROM PYTHON SOURCE LINES 45-47 One relative view ----------------- .. GENERATED FROM PYTHON SOURCE LINES 47-66 .. code-block:: Python P = np.array([[0.0, 0.0, -0.295, 1.0, 0.0, -0.705, 0.0]]) res = black_litterman(cov, w_mkt, P, [0.05], tau=0.05, delta=2.5) x = np.arange(7) fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(7, 6), sharex=True) ax1.bar(x - 0.2, 100 * res.prior_returns, 0.4, label="equilibrium") ax1.bar(x + 0.2, 100 * res.posterior_returns, 0.4, label="Black-Litterman") ax1.set_ylabel("expected excess return (%)") ax1.legend() ax2.bar(x - 0.2, 100 * w_mkt, 0.4, label="market") ax2.bar(x + 0.2, 100 * res.weights, 0.4, label="optimal with the view") ax2.axhline(0, color="0.3", lw=0.8) ax2.set_xticks(x, names) ax2.set_ylabel("weight (%)") ax2.legend() fig.tight_layout() print("posterior returns (%):", np.round(100 * res.posterior_returns, 1)) print("optimal weights (%): ", np.round(100 * res.weights, 1)) .. image-sg:: /api/gallery/portfolio/bayesian/images/sphx_glr_plot_01_black_litterman_001.png :alt: plot 01 black litterman :srcset: /api/gallery/portfolio/bayesian/images/sphx_glr_plot_01_black_litterman_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none posterior returns (%): [ 4.3 7.6 9.3 11. 4.5 7. 8.1] optimal weights (%): [ 1.5 2.1 -3.9 35.4 11. -9.5 58.6] .. GENERATED FROM PYTHON SOURCE LINES 67-73 Confidence in the view ---------------------- Scaling :math:`\Omega` moves the German weight continuously between the market weight (no confidence) and the weight that makes the view hold exactly. .. GENERATED FROM PYTHON SOURCE LINES 73-85 .. code-block:: Python scales = np.logspace(-3, 3, 40) base = (P @ (0.05 * cov) @ P.T)[0, 0] germany = [black_litterman(cov, w_mkt, P, [0.05], omega=[[s * base]]).weights[3] for s in scales] fig, ax = plt.subplots(figsize=(7, 3.5)) ax.semilogx(scales, 100 * np.array(germany)) ax.axhline(100 * w_mkt[3] / 1.05, color="0.4", ls=":", label="market weight / (1 + tau)") ax.set_xlabel("view uncertainty relative to He-Litterman's default") ax.set_ylabel("Germany weight (%)") ax.legend() fig.tight_layout() plt.show() .. image-sg:: /api/gallery/portfolio/bayesian/images/sphx_glr_plot_01_black_litterman_002.png :alt: plot 01 black litterman :srcset: /api/gallery/portfolio/bayesian/images/sphx_glr_plot_01_black_litterman_002.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.194 seconds) .. _sphx_glr_download_api_gallery_portfolio_bayesian_plot_01_black_litterman.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/bayesian/plot_01_black_litterman.ipynb :alt: Launch JupyterLite :width: 150 px .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_01_black_litterman.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_01_black_litterman.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_01_black_litterman.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_