Quelle est la vraie longueur d'une obligation ?
L'échéance dit quand arrive le dernier paiement, mais une obligation qui verse de gros coupons restitue une grande part de votre argent plus tôt. La duration de Macaulay est le délai moyen pour recevoir les flux, chaque date de paiement étant pondérée par sa part du prix : sum(t * PV(t)) / prix. Une obligation zéro-coupon a une duration égale à son échéance ; les coupons la réduisent. Notre obligation à 10 ans, 5 %, à 6 % de rendement, a une duration de 7,9 ans. La duration est la vraie longueur de l'obligation dans le temps.
import numpy as np
def bond_cashflows(face, coupon_rate, years, freq):
n = int(round(years * freq))
times = np.arange(1, n + 1) / freq
flows = np.full(n, face * coupon_rate / freq)
flows[-1] += face
return times, flows
times, flows = bond_cashflows(1000, 0.05, 10, 2)
pv = flows / (1 + 0.06 / 2) ** (2 * times) # present value of each payment
price = pv.sum()
macaulay = (times * pv).sum() / price # the average time, weighted by present value
print(round(price, 4), round(macaulay, 4)) # 925.6126 7.895 shorter than the 10-year maturityDe la duration au risque de prix
Diviser par 1 + ytm / freq donne la duration modifiée, qui a un sens direct : c'est la baisse en pourcentage du prix pour une hausse d'une unité (100 points) du rendement ; une duration modifiée de 7,67 signifie environ -7,67 % pour +1 point de pourcentage, en première approximation : dP / P ≈ -D_mod * dy. C'est le chiffre que les salles de risque citent comme la sensibilité de taux de l'obligation. Il explique aussi pourquoi l'obligation longue du sujet précédent a tant plus baissé que la courte : sa duration est plus élevée.
modified = macaulay / (1 + 0.06 / 2)
print(round(modified, 4)) # 7.6654 about -7.67% of price for +1 percentage point of yield
dy = 0.01
print(round(price * (1 - modified * dy), 2)) # 854.66 the first-order estimate after a rise to 7%
# the exact price at 7% is 857.88: duration alone underestimates itConvexité : la courbure
La relation prix-rendement est une courbe, pas une droite, et la duration n'en est que la tangente. La tangente sous-estime le prix pour un mouvement dans les deux sens, car la courbe se redresse : une obligation gagne plus quand les rendements baissent qu'elle ne perd quand ils montent du même montant. La convexité mesure cette courbure, à partir de la dérivée seconde : sum(flux * t * (t + 1/freq) / (1 + y/freq) ** (freq * t + 2)) / prix. Ajouter le terme 0.5 * convexité * dy² à l'estimation par la duration donne une bien meilleure prévision du prix, et cela compte d'autant plus que le mouvement est grand.
convexity = (flows * times * (times + 1 / 2) / (1 + 0.06 / 2) ** (2 * times + 2)).sum() / price
print(round(convexity, 3)) # 71.785
for dy in (0.01, -0.01, 0.03):
exact = bond_price(1000, 0.05, 0.06 + dy, 10)
duration_only = price * (1 - modified * dy)
with_convexity = price * (1 - modified * dy + 0.5 * convexity * dy ** 2)
print(dy, round(exact, 2), round(duration_only, 2), round(with_convexity, 2))
# 0.01 857.88 854.66 857.99
# -0.01 1000.0 996.56 999.88
# 0.03 739.84 712.77 742.67 for a big move convexity matters most# Duration by maturity for a 5% bond at a 6% yield: it grows with maturity, but slower than one for one
for years in (1, 5, 10, 30):
t_, f_ = bond_cashflows(1000, 0.05, years, 2)
p_ = f_ / (1 + 0.06 / 2) ** (2 * t_)
print(years, round((t_ * p_).sum() / p_.sum(), 3))
# 1 0.988
# 5 4.472
# 10 7.895
# 30 14.77bond_price.- Duration de Macaulay =
sum(t * PV) / prix, le délai moyen pour l'argent ; la duration d'un zéro-coupon est son échéance. - Duration modifiée = Macaulay /
(1 + ytm / freq): la baisse relative du prix pour +1 de rendement,dP / P ≈ -D_mod * dy. - Estimation du prix =
prix * (1 - D_mod * dy + 0.5 * convexité * dy²); le terme de convexité corrige la tangente, surtout pour les grands mouvements.
À suivre : les courbes de taux et l'interpolation, car des échéances différentes ont des taux différents.
Lab : ce qui est noté
- Trouver le délai moyen pour récupérer son argent avec
macaulay_duration25 pts - En faire une sensibilité du prix avec
modified_duration25 pts - Capturer la courbure avec
convexity25 pts - Prévoir le nouveau prix sans revaloriser avec
price_estimate25 pts