.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "api/gallery/fixed_income/curves/plot_02_mcculloch_spline.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_fixed_income_curves_plot_02_mcculloch_spline.py: McCulloch's spline discount function ==================================== Huston McCulloch (1971) fitted the discount function itself with a spline, :math:`d(t) = 1 + \sum_j a_j g_j(t)`. Because every bond price is linear in :math:`d`, it is also linear in the coefficients, so the fit is ordinary least squares on a cross-section of bond prices. His 1971 paper used quadratic splines; the cubic version (1975) gives smooth forward rates. .. GENERATED FROM PYTHON SOURCE LINES 14-22 .. code-block:: Python import matplotlib.pyplot as plt import numpy as np from financialkit.fixed_income import NelsonSiegelCurve, bond_cash_flows, fit_mcculloch_spline rng = np.random.default_rng(1971) true = NelsonSiegelCurve(0.05, -0.03, 0.02, 1.5) .. GENERATED FROM PYTHON SOURCE LINES 23-25 A noisy cross-section of 40 coupon bonds ---------------------------------------- .. GENERATED FROM PYTHON SOURCE LINES 25-31 .. code-block:: Python maturities = np.sort(rng.uniform(0.3, 25, 40)) coupons = np.round(rng.uniform(0.01, 0.07, 40), 3) bonds = [bond_cash_flows(100.0, c, T) for c, T in zip(coupons, maturities, strict=True)] prices = np.array([b.present_value(true) for b in bonds]) + rng.normal(0, 0.05, 40) .. GENERATED FROM PYTHON SOURCE LINES 32-34 Quadratic and cubic fits ------------------------ .. GENERATED FROM PYTHON SOURCE LINES 34-53 .. code-block:: Python knots = [1.0, 3.0, 7.0, 15.0] t = np.linspace(0.1, 25, 300) fig, axes = plt.subplots(1, 2, figsize=(10, 4)) axes[0].plot(t, true.discount(t), "k", lw=3, alpha=0.3, label="true") axes[1].plot(t, 100 * true.zero_rate(t), "k", lw=3, alpha=0.3, label="true") for degree in (2, 3): res = fit_mcculloch_spline(bonds, prices, knots=knots, degree=degree) print(f"degree {degree}: price RMSE {res.rmse:.4f}") axes[0].plot(t, res.curve.discount(t), label=f"degree {degree}") axes[1].plot(t, 100 * res.curve.zero_rate(t), label=f"degree {degree}") for k in knots: axes[0].axvline(k, color="0.85", lw=0.8) axes[0].set_ylabel("discount factor d(t)") axes[1].set_ylabel("zero rate (%)") for ax in axes: ax.set_xlabel("maturity (years)") ax.legend() plt.show() .. image-sg:: /api/gallery/fixed_income/curves/images/sphx_glr_plot_02_mcculloch_spline_001.png :alt: plot 02 mcculloch spline :srcset: /api/gallery/fixed_income/curves/images/sphx_glr_plot_02_mcculloch_spline_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none degree 2: price RMSE 0.0513 degree 3: price RMSE 0.0434 .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.043 seconds) .. _sphx_glr_download_api_gallery_fixed_income_curves_plot_02_mcculloch_spline.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/fixed_income/curves/plot_02_mcculloch_spline.ipynb :alt: Launch JupyterLite :width: 150 px .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_02_mcculloch_spline.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_02_mcculloch_spline.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_02_mcculloch_spline.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_