σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft AcademyRutas
Ctrl K
AO
+1,2 k estudiantes
Iniciar sesiónUnirse ↗
Rutas
AO
+1,2 k estudiantes
Iniciar sesión
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Ruta de Python›Módulo 4 · Valoración de renta fija y opciones›Tema 4.2·~45 minIntermedio

Precios de bonos

Posición en la rutaMódulo 4 de 11 · Tema 2 de 16
4.2 Precios de bonos
—Puntuación del lab— / 100XP total—Racha—

Un bono es un flujo de promesas

Un bono es un préstamo dividido en piezas negociables. El emisor promete un cupón fijo cada periodo y el valor nominal (par) al vencimiento. Un bono a 10 años con valor nominal 1000 y un cupón del 5 % pagado dos veces al año promete 25 cada seis meses y 1000 más en el último pago. Su precio es lo que valen hoy esas promesas: el valor actual de los flujos, descontados al rendimiento que el mercado exige ahora para ese riesgo y ese vencimiento.

import numpy as np

def bond_cashflows(face, coupon_rate, years, freq):
    n = int(round(years * freq))                       # number of payments
    times = np.arange(1, n + 1) / freq                 # in years: 0.5, 1.0, 1.5, ...
    flows = np.full(n, face * coupon_rate / freq)      # a coupon each time
    flows[-1] += face                                  # and the face value with the last one
    return times, flows

times, flows = bond_cashflows(1000, 0.05, 2, 2)        # a 2-year bond, to keep the output short
print(times.tolist())    # [0.5, 1.0, 1.5, 2.0]
print(flows.tolist())    # [25.0, 25.0, 25.0, 1025.0]

Precio y rendimiento se mueven en sentido contrario

Descuenta cada flujo a (1 + ytm / freq) ** (freq * time) y suma. Cuando el rendimiento iguala la tasa del cupón, el bono vale exactamente su valor nominal (cotiza a la par). Si los rendimientos de mercado suben por encima del cupón, los bonos nuevos pagan más que este, así que su precio cae por debajo de la par (un descuento); si bajan, cotiza con una prima. Es el mismo mecanismo del tema anterior, ahora con fechas de pago reales. El rendimiento corriente, cupón anual entre precio, es una medida rápida pero incompleta porque ignora la ganancia o pérdida al vencimiento.

def bond_price(face, coupon_rate, ytm, years, freq=2):
    times, flows = bond_cashflows(face, coupon_rate, years, freq)
    return (flows / (1 + ytm / freq) ** (freq * times)).sum()

for ytm in (0.03, 0.05, 0.06, 0.08):
    print(ytm, round(bond_price(1000, 0.05, ytm, 10), 2))
# 0.03 1171.69   yield below the coupon: premium
# 0.05 1000.0    yield = coupon: par
# 0.06 925.61    yield above the coupon: discount
# 0.08 796.15

print(round(50 / bond_price(1000, 0.05, 0.06, 10), 5))    # 0.05402   current yield: 5.40%, not the 6% yield to maturity

Los bonos largos reaccionan más

El cambio relativo del precio para un movimiento dado del rendimiento depende del vencimiento. Los pagos lejanos se descuentan con una potencia alta, así que son muy sensibles a la tasa. Un bono a 1 año pierde cerca del 1 % cuando los rendimientos suben un punto, uno a 10 años cerca del 7 % y uno a 30 años casi el 14 %. Por eso los inversores en bonos hablan de riesgo de tipos de interés y por eso los siguientes temas construyen medidas (rendimiento al vencimiento, duración) para cuantificarlo.

for years in (1, 5, 10, 30):
    change = bond_price(1000, 0.05, 0.06, years) / 1000 - 1     # the coupon is 5%, the yield rises to 6%
    print(years, round(change, 4))
# 1 -0.0096
# 5 -0.0427
# 10 -0.0744
# 30 -0.1384
# The same 6% yield with annual, semiannual and monthly coupons: the quoting convention matters a little
for freq in (1, 2, 12):
    print(freq, round(bond_price(1000, 0.05, 0.06, 10, freq), 2))
# 1 926.4
# 2 925.61
# 12 924.94
Los rendimientos cotizados siguen una convenciónUn rendimiento del 6 % con cupones semestrales no es una tasa anual efectiva del 6 %: capitaliza dos veces al año, así que su tasa efectiva es del 6,09 %. Indica siempre la capitalización (freq) junto a un rendimiento. Los bonos reales también tienen cupón corrido entre fechas de cupón y convenciones de cómputo de días, que este lab ignora al valorar en fechas de cupón.
  • Precio = suma de flujo / (1 + ytm / freq) ** (freq * tiempo); cupones cada periodo, el nominal con el último.
  • Rendimiento = cupón: precio = par; rendimiento por encima del cupón: descuento; por debajo: prima. Precio y rendimiento se mueven en sentido contrario.
  • A mayor vencimiento, mayor cambio de precio para el mismo movimiento de rendimiento: ese riesgo de tipos es lo que mide la duración después.

A continuación: el rendimiento al vencimiento, en el sentido contrario: del precio de mercado al rendimiento.

Lab: qué se califica

  • Listar los pagos de un bono con bond_cashflows25 pts
  • Descontar los pagos hasta un precio con bond_price25 pts
  • Comparar el cupón con el precio con current_yield25 pts
  • Ver el efecto de un movimiento de tipos con price_change25 pts
Explorador · lab-67-1
▾ lab-67-1
…
bash · en el navegador
learner@quantcraft:~/lab-67-1$
Python aún no cargadoPythonLn 1, Col 1Spaces: 4UTF-8

¿Restablecer este tema?

Todo lo que hay en lab-67-1 vuelve a los archivos iniciales y se limpia el terminal. Tu solución actual se perderá: no se puede deshacer.

Se restablecerá
  • main.py → código inicial
  • Historial del terminal y salida de pruebas
  • Cambios sin enviar
Se conservará
  • Mejor puntuación del lab
  • Tu XP y tu racha
  • Progreso de la lección y notas

Inicia sesión para ejecutar tu código.

Todas las clases se pueden leer gratis. Crea una cuenta o inicia sesión para ejecutar código, lanzar las pruebas y enviar este lab.

  • IDE en el navegador con terminal bash — sin instalar nada
  • Corrección instantánea, XP y tu lugar en la ruta
Crear una cuentaIniciar sesión
O
Continuar con GitHub