Source code for assets.BasicBonds.Bonds

"""The main Space in the :mod:`~assets.BasicBonds` model.



.. rubric:: Parameters and References

(In all the sample code below,
the global variable ``Bonds`` refers to the
:mod:`~assets.BasicBonds.Bonds` space.)

Attributes:

    ql: The `QuantLib <https://www.quantlib.org/>`_ module.

    date_init: Valuation date as a string in the form of 'YYYY-MM-DD'.


    date_end: Projection end date as a string in the form of 'YYYY-MM-DD'.

    zero_curve: Zero curve at the valuation date as a pandas Series
        indexed with strings indicating various durations.
        This data is used by :func:`riskfree_curve` to create
        QuantLib's ZeroCurve object::

            >>> Bonds.zero_curve

            Duration
            1M     0.0004
            2M     0.0015
            3M     0.0026
            6M     0.0057
            1Y     0.0091
            2Y     0.0136
            3Y     0.0161
            5Y     0.0182
            7Y     0.0192
            10Y    0.0194
            20Y    0.0231
            30Y    0.0225
            Name: Rate, dtype: float64

        The data is saved as an Excel file named "zero_curve.xlsx" in the
        model.

    bond_data: Bond data as a pandas DataFrame.
        By default, a sample table generated by the
        *generate_bond_data.ipynb* notebook included in the library::

            >>> Bonds.bond_data

                     settlement_days  face_value issue_date  ...  tenor coupon_rate z_spread
            bond_id                                          ...
            1                      0      235000 2017-12-12  ...     1Y        0.07   0.0304
            2                      0      324000 2021-11-29  ...     1Y        0.08   0.0304
            3                      0      799000 2017-02-03  ...     6M        0.03   0.0155
            4                      0      679000 2017-11-19  ...     1Y        0.08   0.0229
            5                      0      397000 2018-07-01  ...     6M        0.06   0.0142
                             ...         ...        ...  ...    ...         ...      ...
            996                    0      560000 2019-02-16  ...     1Y        0.06   0.0261
            997                    0      161000 2020-03-12  ...     6M        0.05   0.0199
            998                    0      375000 2019-05-05  ...     1Y        0.03   0.0138
            999                    0      498000 2019-02-21  ...     1Y        0.03   0.0230
            1000                   0      438000 2019-03-14  ...     1Y        0.06   0.0256

            [1000 rows x 8 columns]

        The column names and their data types are as follows::

            >>> Bonds.bond_data.dtypes

            settlement_days             int64
            face_value                  int64
            issue_date         datetime64[ns]
            bond_term                   int64
            maturity_date      datetime64[ns]
            tenor                      object
            coupon_rate               float64
            z_spread                  float64
            dtype: object

        The data is saved as an Excel file named "bond_data.xlsx" in the
        model.


"""

from modelx.serialize.jsonvalues import *

_formula = None

_bases = []

_allow_none = None

_spaces = []

# ---------------------------------------------------------------------------
# Cells

[docs]def cashflows(bond_id): """Returns the cashflows of the selected bond. Returns the cashflows of the selected bond as a list. Each element of the list is the total cashflows falling in each projection period defined by :func:`date_`. """ result = [0] * step_size() leg = fixed_rate_bond(bond_id).cashflows() i = 0 # cashflow index for t in range(step_size()): while i < len(leg): if i > 0: # Check if cashflow dates are in order. assert leg[i-1].date() <= leg[i].date() if date_(t) <= leg[i].date() < date_(t+1): result[t] += leg[i].amount() elif date_(t+1) <= leg[i].date(): break i += 1 return result
[docs]def cashflows_total(): """Returns the aggregated cashflows of the entire bond portfolio. Takes the sum of :func:`cashflows` across ``bond_id`` and returns as a list the aggregated cashflows of all the bonds in :attr:`bond_data`. """ result = [0] * step_size() for t in range(step_size()): for i in bond_data.index: result[t] += cashflows(i)[t] return result
[docs]def date_(i): """Date at each projection step Defines projection time steps by returning QuantLib's `Date`_ object that corresponds to the value of the integer index ``i``. By default, ``date_(i)`` starts from the valuation date specified by :attr:`date_init`, and increments annually. .. _Date: https://www.quantlib.org/reference/class_quant_lib_1_1_date.html """ if i == 0: return ql.Date(date_init, "%Y-%m-%d") else: return date_(i-1) + ql.Period('1Y')
[docs]def fixed_rate_bond(bond_id): """Returns QuantLib’s `FixedRateBond`_ object Create QuantLib’s `FixedRateBond`_ object representing a bond specified by the given bond ID. The bond object is created from the attributes in :attr:`bond_data` and :func:`schedule`. A pricing engine for the bond object is created as a `DiscountingBondEngine`_ object from :func:`riskfree_curve` and the ``z_spread`` attribute in :attr:`bond_data`, and associated with the bond object through a `ZeroSpreadedTermStructure`_ object. .. _FixedRateBond: https://www.quantlib.org/reference/class_quant_lib_1_1_fixed_rate_bond.html .. _DiscountingBondEngine: https://quantlib-python-docs.readthedocs.io/en/latest/pricing_engines/bonds.html .. _ZeroSpreadedTermStructure: https://www.quantlib.org/reference/class_quant_lib_1_1_zero_spreaded_term_structure.html """ settlement_days = bond_data.loc[bond_id]['settlement_days'] face_value = bond_data.loc[bond_id]['face_value'] coupons = [bond_data.loc[bond_id]['coupon_rate']] bond = ql.FixedRateBond( int(settlement_days), float(face_value), schedule(bond_id), coupons, ql.Actual360(), # DayCount ql.Unadjusted) spread = bond_data.loc[bond_id]['z_spread'] spread = ql.QuoteHandle(ql.SimpleQuote(spread)) disc_curve = ql.ZeroSpreadedTermStructure( ql.YieldTermStructureHandle(riskfree_curve()), spread, ql.Compounded, ql.Annual) # Set discount curve bondEngine = ql.DiscountingBondEngine( ql.YieldTermStructureHandle(disc_curve)) bond.setPricingEngine(bondEngine) return bond
[docs]def redemptions(bond_id): """Returns cashflows of redemptions For the specified bond, returns a list of redemptions cashflows. Since the redemption cashflow occurs only once, all but one element are zero. """ result = [0] * step_size() leg = fixed_rate_bond(bond_id).redemptions() i = 0 # cashflow index for t in range(step_size()): while i < len(leg): if date_(t) <= leg[i].date() < date_(t+1): result[t] += leg[i].amount() elif date_(t+1) <= leg[i].date(): break i += 1 return result
[docs]def redemptions_total(): """Returns all redemption cashflows Returns a list of redemptions of all the bonds in :attr:`bond_data`. """ result = [0] * step_size() for t in range(step_size()): for i in bond_data.index: result[t] += redemptions(i)[t] return result
[docs]def riskfree_curve(): """Returns `ZeroCurve`_ object Creates QuantLib's `ZeroCurve`_ object from :attr:`zero_curve` and returns it. The `ZeroCurve`_ object is used by :func:`fixed_rate_bond` to construct a discount curve for calculating the market value of the specified bond. .. _ZeroCurve: https://www.quantlib.org/reference/group__yieldtermstructures.html """ ql.Settings.instance().evaluationDate = date_(0) spot_dates = [date_(0)] + list(date_(0) + ql.Period(dur) for dur in zero_curve.index) spot_rates = [0] + list(zero_curve) return ql.ZeroCurve( spot_dates, spot_rates, ql.Actual360(), # dayCount ql.UnitedStates(ql.UnitedStates.Settlement), # calendar ql.Linear(), # Interpolator ql.Compounded, # compounding ql.Annual # frequency )
[docs]def schedule(bond_id): """Returns a `Schedule`_ object Create QuantLib's `Schedule`_ object for the specified bond and returns it. The returned `Schedule`_ object is used to by :func:`fixed_rate_bond` to construct `FixedRateBond`_ object. .. _Schedule: https://www.quantlib.org/reference/class_quant_lib_1_1_schedule.html .. _FixedRateBond: https://www.quantlib.org/reference/class_quant_lib_1_1_fixed_rate_bond.html """ d = bond_data.loc[bond_id]['issue_date'] issue_date = ql.Date(d.day, d.month, d.year) d = bond_data.loc[bond_id]['maturity_date'] maturity_date = ql.Date(d.day, d.month, d.year) tenor = ql.Period( ql.Semiannual if bond_data.loc[bond_id]['tenor'] == '6Y' else ql.Annual) return ql.Schedule( issue_date, maturity_date, tenor, ql.UnitedStates(ql.UnitedStates.Settlement), # calendar ql.Unadjusted, # convention ql.Unadjusted , # terminationDateConvention ql.DateGeneration.Backward, # rule False # endOfMonth )
[docs]def step_size(): """Returns the number of time steps Calculates the number of time steps from :attr:`date_end` and :func:`date_` ren returns it. """ d_end = ql.Date(date_end, "%Y-%m-%d") t = 0 while True: if date_(t) < d_end: t += 1 else: return t
[docs]def z_spread_recalc(bond_id): """Calculate Z-spread For the bond specified by ``bond_id``, Calculate the Z-spread of the bond specified by ``bond_id`` from the bond's market value and :func:`riskfree_curve`. This is for testing that the calculated Z-spread matches the input in :attr:`bond_data`. """ return ql.BondFunctions.zSpread( fixed_rate_bond(bond_id), fixed_rate_bond(bond_id).cleanPrice(), riskfree_curve(), ql.Thirty360(), ql.Compounded, ql.Annual)
[docs]def market_values(): """Returns the market values of the entire bonds Calculates and Returns a list of the market values of :func:`fixed_rate_bond` for all bonds input in :attr:`bond_data`. """ bond = fixed_rate_bond return list( bond(i).notional() * bond(i).cleanPrice() / 100 for i in bond_data.index)
# --------------------------------------------------------------------------- # References date_end = "2053-01-01" date_init = "2022-01-01" bond_data = ("DataSpec", 2323236992288, 2323228088304) ql = ("Module", "QuantLib") zero_curve = ("DataSpec", 2323237349888, 2323227835552)