Puedes consultar aquí una publicación en el blog sobre cómo simular la estructura temporal de rendimientos para el modelo HullWhite.
La idea básica es que una vez que se tienen las trayectorias para la tasa de interés a corto plazo, simplemente se integra (aproximadamente) la tasa de interés a corto plazo a lo largo de cada trayectoria para obtener los factores de descuento.
El promedio de las simulaciones debería coincidir con la estructura temporal inicial.
Aquí hay un ejemplo basado en esa excelente publicación del blog, con una mejora en el tiempo de ejecución mediante la vectorización de la integración.
import QuantLib as ql
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import copy
nPaths = 500
years = 30
startDate = ql.Date(3, 12, 2018)
endDate = startDate + ql.Period(years, ql.Years)
tenor = ql.Period(1, ql.Days)
schedule = ql.MakeSchedule(startDate, endDate, tenor)
dates = [dt for dt in schedule]
times = [ql.Actual360().yearFraction(startDate, dt) for dt in dates]
curve = ql.YieldTermStructureHandle(ql.FlatForward(startDate, 0.04875825, ql.Actual365Fixed()))
reversionSpeed = 0.01
rateVolatility = 0.001
process = ql.HullWhiteProcess(curve, reversionSpeed, rateVolatility)
periods = (endDate - startDate) + 1
sequenceGenerator = ql.UniformRandomSequenceGenerator(periods, ql.UniformRandomGenerator())
gaussianSequenceGenerator = ql.GaussianRandomSequenceGenerator(sequenceGenerator)
pathGenerator = ql.GaussianPathGenerator(process, years, periods, gaussianSequenceGenerator, False)
paths = np.zeros(shape = (nPaths, periods))
for i in range(nPaths):
path = pathGenerator.next().value()
paths[i, :] = np.array([path[j] for j in range(periods)])
dfs = np.zeros(shape=paths.shape)
dt = years / len(schedule)
integral = 0
for j in range(periods):
if j == 0:
dfs[:, 0] = 1
integral = copy.deepcopy(paths[:, 0])
else:
integral += paths[:, j]
dfs[:, j] += np.exp(-integral * dt)
simulatedCurve = ql.DiscountCurve(dates, dfs.mean(0), ql.Actual365Fixed(), ql.NullCalendar())
simulatedDfs = np.array([simulatedCurve.discount(dt) for dt in dates])
dfs_curve = np.array([curve.discount(dt) for dt in dates])
fig, ax = plt.subplots(1,2, figsize=(10,3))
plt.tight_layout()
ax[0].set_title('Factores de Descuento')
ax[0].plot(times, simulatedDfs, linestyle = 'dashed', label = 'curva simulada')
ax[0].plot(times, dfs_curve, linestyle = 'solid', label = 'curva original')
ax[0].legend()
ax[1].set_title('Trayectorias')
ax[1].plot(paths.T, linewidth=0.5)
La salida sería: