Un taux par échéance
Jusqu'ici un seul rendement actualisait tous les paiements d'une obligation. En réalité, l'argent prêté 6 mois, 2 ans et 10 ans rapporte des taux différents, et le profil selon les échéances est la courbe des taux. Un taux zéro-coupon est le taux annuel d'un paiement unique à une date donnée, sans coupons intermédiaires. En général la courbe monte avec l'échéance (les investisseurs veulent plus pour attendre plus longtemps), mais elle peut être plate ou inversée, quand les taux courts dépassent les longs, ce que les marchés lisent comme un signal d'alerte sur la croissance.
import numpy as np
maturities = np.array([0.5, 1, 2, 5, 10, 30]) # years where the market quotes a rate
rates = np.array([0.040, 0.042, 0.045, 0.048, 0.050, 0.052]) # an upward-sloping curve
# The rate at 3 years is not quoted: interpolate linearly between 2 years (4.5%) and 5 years (4.8%)
print(float(np.interp(3, maturities, rates))) # 0.046
print(np.interp([0.25, 7, 40], maturities, rates).tolist()) # [0.04, 0.0488, 0.052] flat outside the quoted rangeFacteurs d'actualisation et taux à terme
Un taux zéro-coupon r pour l'échéance t donne le facteur d'actualisation (1 + r) ** -t : ce que vaut aujourd'hui une unité payée en t. La courbe cache aussi des anticipations. Prêter 2 ans à 4,5 % doit égaler prêter 1 an à 4,2 % puis prêter à nouveau un an à un taux convenu f : (1 + 4,5 %)² = (1 + 4,2 %)(1 + f), donc f = 4,80 %. Ce taux à terme est le taux d'équilibre de la deuxième année, fixé aujourd'hui. En général f = ((1 + r2) ** t2 / (1 + r1) ** t1) ** (1 / (t2 - t1)) - 1.
def discount_factor(rate, t):
return (1 + rate) ** -t
print(round(float(discount_factor(0.045, 2)), 5)) # 0.91573 one unit paid in 2 years
r1, t1, r2, t2 = 0.042, 1, 0.045, 2
forward = ((1 + r2) ** t2 / (1 + r1) ** t1) ** (1 / (t2 - t1)) - 1
print(round(forward, 5)) # 0.04801 the rate for year 2, implied by the curve
print(round((1 + r1) ** t1 * (1 + forward) ** (t2 - t1), 5), round((1 + r2) ** t2, 5)) # 1.09 1.09 both routes agreeValoriser sur toute la courbe
Valoriser avec un rendement unique traite pareillement un paiement à 1 an et un paiement à 10 ans. L'approche par la courbe est plus fidèle : pour chaque paiement, lisez le taux zéro-coupon de sa propre date (en interpolant si besoin), transformez-le en facteur d'actualisation et sommez flux * facteur. Deux obligations aux mêmes flux doivent avoir le même prix quels que soient leurs rendements cotés, et c'est ainsi que les banques valorisent les obligations au prix de marché. Pour une obligation à 3 ans à 5 % sur notre courbe, le prix est de 1011,25, contre 1010,98 avec un 4,6 % plat : proche ici car la courbe est douce, mais les obligations réagissent différemment quand la courbe se tord.
times = np.array([1.0, 2.0, 3.0])
flows = np.array([50.0, 50.0, 1050.0]) # a 3-year 5% bond with annual coupons
zero = np.interp(times, maturities, rates)
print(zero.tolist()) # [0.042, 0.045, 0.046]
print((discount_factor(zero, times)).round(5).tolist()) # [0.95969, 0.91573, 0.87379]
print(round(float((flows * discount_factor(zero, times)).sum()), 4)) # 1011.2462 on the curve
print(round(float((flows / 1.046 ** times).sum()), 4)) # 1010.9752 with one flat 4.6% instead# The forward rates between the quoted maturities, in one array expression
t = np.array([1.0, 2.0, 5.0])
r = np.array([0.042, 0.045, 0.048])
forwards = ((1 + r[1:]) ** t[1:] / (1 + r[:-1]) ** t[:-1]) ** (1 / np.diff(t)) - 1
print(forwards.round(5).tolist()) # [0.04801, 0.05] year 2 and years 3 to 5: an upward-sloping curve implies rising forwardsnp.interp maintient le taux plat, ce qui est une convention, pas un fait. Précisez la méthode chaque fois que vous publiez une courbe.- Une courbe des taux est le taux zéro-coupon par échéance ; lisez les échéances manquantes par interpolation (
np.interp, plat hors de la plage). - Facteur d'actualisation
(1 + r) ** -t; taux à terme((1 + r2) ** t2 / (1 + r1) ** t1) ** (1 / (t2 - t1)) - 1. - Prix = somme de
flux * facteur d'actualisation, chaque flux au taux zéro-coupon de sa date ; précisez la méthode d'interpolation.
À suivre : les gains des options, ce que paie un call ou un put à l'échéance, point de départ de la valorisation des options.
Lab : ce qui est noté
- Lire un taux entre les échéances cotées avec
interpolate_rate25 pts - Transformer un taux en facteur d'actualisation avec
discount_factor25 pts - Lire le taux futur caché dans la courbe avec
forward_rate25 pts - Valoriser un flux de paiements sur toute la courbe avec
price_on_curve25 pts