Spread Z o Spread OA (sin opcionales)
En la biblioteca de Python rateslib
puedes hacer el siguiente cálculo:
Define un bono
y una curva
:
#PYTHON
from rateslib import FixedRateBond, Curve
bono = FixedRateBond(
efectiva=dt(2023, 3, 7), vencimiento="10años",
tasa_fija=3.0, especificacion="ukt"
)
curva = Curve(
nodos={dt(2023, 3, 7): 1.0, dt(2033, 9, 7): 0.68},
calendario="ldn", convencion="act365f",
)
Utiliza la curva
para evaluar el flujo de efectivo descontado al precio medio de mercado actual del bono y derivar un Spread OA basado en un precio asumido de par (100.0):
bono.tasa(curvas=curva, métrica="precio_limpio")
# 94.14639360179893
bono.spreadoa(curvas=curva, precio=100.00)
# -69.37792098276168
Puedes verificar que esto es válido al desplazar la curva
y volver a evaluar el bono
:
curva_desplazada = curva.desplazar(-69.37792098276168)
bono.tasa(curvas=curva_desplazada, métrica="precio_limpio")
# 100.00000000000647
Entonces, ¿qué está sucediendo realmente?
En primer lugar, debes definir qué es un "spread de 1pb a una curva". Esta es una consideración matemática muy importante. Rateslib lo define como agregar 1pb a cada tasa overnight implícita por la curva de descuento. Definirlo de esta manera es útil porque establece propiedades consistentes de un espacio métrico. Definirlo de otra manera, como sugerir que los instrumentos que parametrizan la Curva, por ejemplo, la tasa swap a 5 años y la tasa swap a 10 años se incrementan en 1pb, es muy poco útil porque esto conduce a relaciones arbitrarias dependiendo de esos instrumentos de calibración.
Ahora debes derivar la cantidad real de puntos básicos que la curva necesita desplazarse para reevaluar el bono al precio objetivo. Esto requiere un algoritmo de solución de raíz iterativa. A menudo no hay una solución en forma cerrada para este problema.
El precio de tu bono evaluado en la curva actual es:
$$P(C)$$
Estás buscando un precio objetivo, $P_{tgt}$, que sea igual a un precio evaluado con una curva desplazada por $z$ puntos básicos, es decir:
$$h(z) = P(C + zC_{1bp}) - P_{tgt} = 0.0 $$
Un posible algoritmo es el de Newton-Raphson. Comienza con una suposición inicial, por ejemplo z_0 = 0.0 y aplica:
$$ z_{i+1} = z_i - \frac{h(z_i)}{h'(z_i)} $$
Puedo mostrarte la primera iteración que rateslib llevaría a cabo al evaluar el gradiente necesario para el algoritmo usando diferenciación automática:
curva._establecer_orden_ad(1)
curva_sensibilidad = curva.desplazar(Dual(0.0, ["z"], []), compuesto=False)
p = bono.tasa(curvas=curva_sensibilidad, métrica="precio_limpio")
dh_dp = gradiente(p, ["z"])
# -0.08162224
Por lo tanto,
$$ z_1 = 0.0 - (94.14639 - 100.0) / -0.08162224 = -71.715 puntos básicos$$
Tal vez 3-5 iteraciones más llegarán a -69.3779...
Spread de rendimiento a rendimiento
El spread de rendimiento a rendimiento es simplemente la TIR menos la tasa par en la curva. Muy fácil de calcular, no tan matemáticamente consistente como usar el OASpread.
En este caso, el spread YY sería -73.6862.. puntos básicos
swap = IRS(effective=dt(2023, 3, 7), termination="10y", spec="gbp_irs")
swap.rate(curves=curve)
# 3.736855699417951
bono.tir(precio=100.0, liquidacion=dt(2023, 3, 8))
# 2.9999929845112567