1 votos

Quantlib: ¿Cómo puedo calcular el precio de un bono de cupón cero utilizando el modelo de Hull White?

Estoy tratando de usar QuantLib para modelar la tasa de interés corta y parece que QL tiene algo de material aquí http://gouthamanbalaraman.com/blog/hull-white-simulation-quantlib-python.html

He podido simular usando la estructura temporal real, aunque en el enlace adjunto por simplicidad GB ha asumido forwards planos. Lo que he utilizado a continuación.

Estoy calculando el precio del bono cupón cero en un momento t usando el siguiente código

def price(T, t, r0):
    tau = T - t
    B = (1 - np.exp(-a * tau)) / a
    A = np.exp(-r0 * tau - B * r0 - sigma ** 2 / (4 * a ** 3) *
               (np.exp(-a * T) - np.exp(-a * t)) * (np.exp(2 * a * t) - 1))
    return A * np.exp(-r0 * B)

pero siento que mis precios de bonos zc están por todos lados. ¿Alguna pista de qué estoy haciendo mal aquí?

¿Por casualidad QuantLib proporciona un envoltorio para calcular los precios zc utilizando el modelo HW?

El código completo está debajo

import QuantLib as ql
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

sigma = 0.1
a = 0.1
length = 30 # en años
steps_per_year = 12
timestep = length * steps_per_year
forward_rate = 0.05
day_count = ql.Thirty360()
todays_date = ql.Date(15, 1, 2015)

ql.Settings.instance().evaluationDate = todays_date

spot_curve = ql.FlatForward(todays_date, ql.QuoteHandle(ql.SimpleQuote(forward_rate)), day_count)
spot_curve_handle = ql.YieldTermStructureHandle(spot_curve)

hw_process = ql.HullWhiteProcess(spot_curve_handle, a, sigma)
rng = ql.GaussianRandomSequenceGenerator(ql.UniformRandomSequenceGenerator(timestep, ql.UniformRandomGenerator()))
seq = ql.GaussianPathGenerator(hw_process, length, timestep, rng, False)

def generate_paths(n_scenarios):
    arr = np.zeros((n_scenarios, timestep+1))
    for i in range(n_scenarios):
        sample_path = seq.next()
        path = sample_path.value()
        time = [path.time(j) for j in range(len(path))]
        value = [path[j] for j in range(len(path))]
        arr[i, :] = np.array(value)
    return np.array(time), arr

n_scenarios = 1
time, paths = generate_paths(n_scenarios)
rates = pd.DataFrame(paths).T

n_years = length
num_steps = timestep
dt = 1 / steps_per_year
prices = np.empty_like(rates)

def price(T, t, r0):
    tau = T - t
    B = (1 - np.exp(-a * tau)) / a
    A = np.exp(-r0 * tau - B * r0 - sigma ** 2 / (4 * a ** 3) *
               (np.exp(-a * T) - np.exp(-a * t)) * (np.exp(2 * a * t) - 1))
    return A * np.exp(-r0 * B)

prices[0] = price(n_years,dt*0,rates.values[0])

for steps in range(1,num_steps+1):
    prices[steps] = price(n_years, dt * steps, rates.values[steps])

plt.plot(prices)
plt.show()

```

5voto

Marc Puntos 892

Aquí está el precio en HW[4] para un ZCB en el tiempo $t$:

\begin{align} P(t,T) &= A(t,T) e^{-B(t,T) r(t)}\\ A(t,T) &= {\frac {P(0,T)} {P(0,t)}} \exp \Bigl( B(t,T)F(0,t) - {\frac {\sigma^2} {4a}} B(t,T)^2(1-e^{-2at})\Bigr)\\ B(t,T) &= {\frac {1-e^{-a(T-t)}} {a}} \end{align}

Parece que estás simulando para la tasa $r(t)$ en el tiempo $t$ y colocándola en el argumento de tu función r0. No estoy exactamente seguro de lo que se supone que es r0, pero ten en cuenta que las ecuaciones anteriores incluyen $F(0,t)$ - que es solo una constante en tu simulación (como has usado una curva FlatForward que creo que debería reemplazar la mayor parte de tus términos de r0, pero la última línea debería leer return A * np.exp(-rt * B), donde rt es el valor actual de la tasa. También hay un error de signo en el término B * r0, que debería ser + en lugar de -

Cuando hago estos cambios (y reduzco ligeramente la volatilidad de las tasas, dado que el 10% para 30 años es mucho tiempo y mucha volatilidad para un modelo de tasas cortas), y promedio los precios a lo largo de los caminos, veo lo siguiente, que muestra claramente la atracción al valor nominal del bono a medida que se acerca a su vencimiento:

ZCB price paths in HW

Código modificado completo:

import QuantLib as ql
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

sigma = 0.01
a = 0.1
length = 30 # in years
steps_per_year = 12
timestep = length * steps_per_year
forward_rate = 0.01
day_count = ql.Thirty360()
todays_date = ql.Date(15, 1, 2015)

ql.Settings.instance().evaluationDate = todays_date

spot_curve = ql.FlatForward(todays_date, ql.QuoteHandle(ql.SimpleQuote(forward_rate)), day_count)
spot_curve_handle = ql.YieldTermStructureHandle(spot_curve)

hw_process = ql.HullWhiteProcess(spot_curve_handle, a, sigma)
rng = ql.GaussianRandomSequenceGenerator(ql.UniformRandomSequenceGenerator(timestep, ql.UniformRandomGenerator()))
seq = ql.GaussianPathGenerator(hw_process, length, timestep, rng, False)

def generate_paths(n_scenarios):
    arr = np.zeros((n_scenarios, timestep+1))
    for i in range(n_scenarios):
        sample_path = seq.next()
        path = sample_path.value()
        time = [path.time(j) for j in range(len(path))]
        value = [path[j] for j in range(len(path))]
        arr[i, :] = np.array(value)
    return np.array(time), arr

n_scenarios = 1000
time, paths = generate_paths(n_scenarios)
rates = pd.DataFrame(paths).T

#price a zero coupon bond
n_years = length
num_steps = timestep
dt = 1 / steps_per_year
prices = np.empty_like(rates)

def price(T, t, f0, rt):
    tau = T - t
    B = (1 - np.exp(-a * tau)) / a
    A = np.exp(-f0 * tau + B * f0 - sigma ** 2 / (4 * a ** 3) *
               (np.exp(-a * T) - np.exp(-a * t)) * (np.exp(2 * a * t) - 1))
    return A * np.exp(-rt * B)

prices[0] = price(n_years,dt*0, forward_rate, rates.values[0])

for steps in range(1,num_steps+1):
    prices[steps] = price(n_years, dt * steps, forward_rate, rates.values[steps])

fig = plt.figure()
plt.figure(figsize=(16,10))

plt.subplot(2, 1, 1)
plt.title("Todos los caminos de ZCB")
plt.plot(prices)

plt.subplot(2, 1, 2)

ave = [np.mean(x) for x in prices]
plt.plot(ave)
plt.title("Caminos promedio de ZCB")

plt.show()

[4]: Hull, J., White, A., “Pricing Interest-Rate-Derivative Securities”, Review of Financial Studies, Volume 3, Issue 4, pp. 573-592, 1990

Finanhelp.com

FinanHelp es una comunidad para personas con conocimientos de economía y finanzas, o quiere aprender. Puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X