.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "api/gallery/market_microstructure/execution/plot_02_bertsimas_lo.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_market_microstructure_execution_plot_02_bertsimas_lo.py: Bertsimas and Lo: optimal control of execution costs ==================================================== Dimitris Bertsimas and Andrew Lo (1998) treated buying :math:`\bar S` shares over :math:`T` periods as a dynamic program. If each purchase moves the price permanently by :math:`\theta` per share, :math:`P_k = P_{k-1} + \theta S_k + \varepsilon_k`, the best a risk-neutral trader can do is split the order evenly. If a serially correlated signal :math:`X_k` also drives prices, the optimal order is linear in the remaining shares and the latest signal, and trading ahead of predicted rises pays. .. GENERATED FROM PYTHON SOURCE LINES 16-24 .. code-block:: Python import matplotlib.pyplot as plt import numpy as np from financialkit.market_microstructure import bertsimas_lo S, T, theta = 100_000.0, 20, 5e-5 gamma, rho, sigma_eta = 0.05, 0.8, 1.0 .. GENERATED FROM PYTHON SOURCE LINES 25-27 Without information: the even split ----------------------------------- .. GENERATED FROM PYTHON SOURCE LINES 27-31 .. code-block:: Python plain = bertsimas_lo(S, T, theta) print(f"trades {plain.trades[:3]} ..., excess cost {plain.expected_cost:.0f} = theta S^2 (T + 1) / (2T)") .. rst-class:: sphx-glr-script-out .. code-block:: none trades [5000. 5000. 5000.] ..., excess cost 262500 = theta S^2 (T + 1) / (2T) .. GENERATED FROM PYTHON SOURCE LINES 32-37 A signal tilts the schedule --------------------------- With a positive signal prices are expected to rise, so the trader buys early; with a negative one it waits. .. GENERATED FROM PYTHON SOURCE LINES 37-47 .. code-block:: Python fig, ax = plt.subplots(figsize=(7, 3.5)) k = np.arange(T) for x0, color in [(3.0, "C3"), (0.0, "0.5"), (-3.0, "C0")]: ax.bar(k + 0.27 * np.sign(x0), bertsimas_lo(S, T, theta, gamma, rho, sigma_eta, x0=x0).trades, width=0.27, color=color, label=f"$X_{{-1}}$ = {x0:g}") ax.set_xlabel("period") ax.set_ylabel("shares bought") ax.legend() fig.tight_layout() .. image-sg:: /api/gallery/market_microstructure/execution/images/sphx_glr_plot_02_bertsimas_lo_001.png :alt: plot 02 bertsimas lo :srcset: /api/gallery/market_microstructure/execution/images/sphx_glr_plot_02_bertsimas_lo_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 48-53 The value of reacting to the signal ----------------------------------- Simulate the signal and prices, follow the closed-loop policy along each realised path, and compare costs with the even split. .. GENERATED FROM PYTHON SOURCE LINES 53-76 .. code-block:: Python rng = np.random.default_rng(14) n_paths = 2000 cost_opt, cost_twap = np.empty(n_paths), np.empty(n_paths) for j in range(n_paths): X = np.empty(T + 1) X[0] = rng.standard_normal() * sigma_eta / np.sqrt(1 - rho**2) for t in range(T): X[t + 1] = rho * X[t] + sigma_eta * rng.standard_normal() eps = 0.05 * rng.standard_normal(T) for trades, out in [(bertsimas_lo(S, T, theta, gamma, rho, sigma_eta, x0=X[0], signals=X[:-1]).trades, cost_opt), (np.full(T, S / T), cost_twap)]: P = np.cumsum(theta * trades + gamma * X[1:] + eps) out[j] = P @ trades saving = cost_twap - cost_opt print(f"average saving over TWAP {saving.mean():.0f} ± {saving.std() / np.sqrt(n_paths):.0f} (TWAP excess cost {plain.expected_cost:.0f})") fig, ax = plt.subplots(figsize=(6, 3.5)) ax.hist(saving, bins=50, color="C0") ax.axvline(0, color="k", lw=0.8) ax.set_xlabel("cost of TWAP minus cost of the optimal policy") fig.tight_layout() plt.show() .. image-sg:: /api/gallery/market_microstructure/execution/images/sphx_glr_plot_02_bertsimas_lo_002.png :alt: plot 02 bertsimas lo :srcset: /api/gallery/market_microstructure/execution/images/sphx_glr_plot_02_bertsimas_lo_002.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none average saving over TWAP 4573 ± 289 (TWAP excess cost 262500) .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.152 seconds) .. _sphx_glr_download_api_gallery_market_microstructure_execution_plot_02_bertsimas_lo.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/market_microstructure/execution/plot_02_bertsimas_lo.ipynb :alt: Launch JupyterLite :width: 150 px .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_02_bertsimas_lo.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_02_bertsimas_lo.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_02_bertsimas_lo.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_