Bond pricing and sector-level risk¶
Present value, yield to maturity, and current yield for a simple cash-flow schedule, followed by a sector-level modified-duration/DTS quadratic-risk example for a small bond portfolio.
Price, yield to maturity, and current yield¶
A 5-year bond paying a 5 coupon annually plus 100 principal at maturity, priced off a flat 3.7% continuously-compounded discount rate.
import numpy as np
from quanttoolbox.bond.pricing import bond_price, bond_ytm, coupon_yield
t = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
cash_flows = np.array([5.0, 5.0, 5.0, 5.0, 105.0])
rate = 0.037
price = bond_price(t, cash_flows, rate, method=1)
print("price:", price)
ytm = bond_ytm(t, cash_flows, price)
print("ytm:", ytm) # -> recovers 0.037, within the bisection tolerance
cy = coupon_yield(t, cash_flows, rate)
print("coupon yield:", cy)
Output:
price: 105.51453826978027
ytm: 0.036998748779296875
coupon yield: 0.047386834856974556
bond_ytm recovers the original discount rate from the price alone,
via optim.bisection.bisection (see Building
blocks) rather than a hand-rolled search loop.
Sector-level modified-duration/DTS risk¶
A 3-bond portfolio split across two sectors, penalized for deviating its per-sector modified duration (MD) and duration-times-spread (DTS) from targets, plus a linear carry term.
from quanttoolbox.bond.pricing import bond_portfolio_quadratic_form
sector = np.array([1, 1, 2]) # bonds 0,1 in sector 1; bond 2 in sector 2
md = np.array([2.0, 4.0, 6.0]) # modified duration per bond
md_star = np.array([3.0, 6.0]) # per-sector MD target
dts = np.array([1.0, 1.0, 2.0]) # duration-times-spread per bond
dts_star = np.array([1.0, 2.0]) # per-sector DTS target
carry = np.array([0.01, 0.02, 0.03]) # carry per bond
w = np.array([0.5, 0.25, 0.25]) # portfolio weights
result = bond_portfolio_quadratic_form(
sector,
varphi_md=0.7,
md=md,
md_star=md_star,
varphi_dts=0.3,
dts=dts,
dts_star=dts_star,
gamma_carry=1.0,
carry=carry,
w=w,
)
print("risk (qf):", result.qf)
Output:
risk (qf): 7.766875000000001
result.md/result.dts are the individual
sustainable_finance.risk.QuadraticFormResult terms the combined
(q, r, c) was built from — each sector's contribution is broken out
in q_j/r_j/c_j on those objects.
bond_portfolio_quadratic_form_vs_benchmark extends this with an
active-share term against a benchmark weight vector; see its docstring
for the full parameterization.