Para recuperar la curva original, hay que utilizar los mismos tenores clave de la curva original y con la misma interpolación. Por ejemplo, cuando se crea la curva original como:
crv = ql.PiecewiseLinearZero(2, ql.TARGET(), deposits + futures + swaps, ql.Actual365Fixed())
la curva interpola linealmente los tipos cero entre los nodos dados por los vencimientos de los depósitos pasados, los futuros y los swaps. Puede recuperar el conjunto de fechas subyacentes y los tipos correspondientes llamando a crv.nodes()
que devuelve una secuencia de pares (fecha, tasa); por ejemplo, si lo llamo en una curva definida como en este ejemplo me sale:
((Date(8,11,2001), 0.038716178576382605),
(Date(15,11,2001), 0.038716178576382605),
(Date(10,12,2001), 0.037654445569665344),
(Date(8,2,2002), 0.03663450512870074),
(Date(8,5,2002), 0.03704480712236303),
(Date(8,8,2002), 0.037185800177110054),
(Date(8,11,2002), 0.03725571728097072),
(Date(10,11,2003), 0.03633800161641973),
(Date(8,11,2004), 0.039086101826569714),
(Date(8,11,2006), 0.04547303923680055),
(Date(8,11,2011), 0.051542294488560084),
(Date(8,11,2016), 0.055797299887186284))
(La fecha de evaluación utilizada en el ejemplo es el 6 de noviembre de 2001).
Dado que la curva es una PiecewiseLinearZero
los tipos devueltos arriba son tipos cero; y si los utiliza para crear una instancia de ZeroCurve
(que también interpola linealmente)...
dates, rates = zip(*crv.nodes())
crv2 = ql.ZeroCurve(dates, rates, ql.Actual365Fixed())
...obtendrá la misma curva que el original:
spot = crv.referenceDate()
sample_dates = [ spot + ql.Period(i, ql.Weeks) for i in range(15*52) ]
z1 = [ crv.zeroRate(d, ql.Actual365Fixed(), ql.Continuous).rate() for d in sample_dates ]
z2 = [ crv2.zeroRate(d, ql.Actual365Fixed(), ql.Continuous).rate() for d in sample_dates ]
fig = plt.figure(figsize=(12,6))
ax = fig.add_subplot(1,1,1)
ax.plot_date([d.to_date() for d in sample_dates], z1, '.')
ax.plot_date([d.to_date() for d in sample_dates], z2, '-')
El problema es que, si muestreas los índices cero en diferentes nodos, obtendrás puntos en la curva; pero interpolando entre ellos, obtendrás valores diferentes.
sample_nodes = [ spot + ql.Period(3*i, ql.Years) for i in range(6) ]
sample_rates = [ crv.zeroRate(d, ql.Actual365Fixed(), ql.Continuous).rate() for d in sample_nodes ]
crv3 = ql.ZeroCurve(sample_nodes, sample_rates, ql.Actual365Fixed())
z3 = [ crv3.zeroRate(d, ql.Actual365Fixed(), ql.Continuous).rate() for d in sample_dates ]
fig = plt.figure(figsize=(12,6))
ax = fig.add_subplot(1,1,1)
ax.plot_date([d.to_date() for d in sample_dates], z1, '.')
p, = ax.plot_date([d.to_date() for d in sample_dates], z3, '-')
ax.plot_date([d.to_date() for d in sample_nodes], sample_rates, 'o', markersize=8, color=p.get_color())
En resumen: hay que utilizar los mismos nodos e interpolación. Puede recuperar los primeros de la curva original como curve.nodes()
y tendrá que elegir una clase que le proporcione esto último. Para PiecewiseLinearZero
Tendrá que utilizar ZeroCurve
para PiecewiseFlatForward
El nodes
devolverá pares de fechas y tipos de cambio a plazo instantáneos, que puede utilizar para crear una instancia de ForwardCurve
.
Para PiecewiseLogCubicDiscount
El nodes
devolverá pares de fechas y factores de descuento, y tendrá que pasarlos a una curva de descuento interpolada correspondiente; sin embargo, la que actualmente se exporta a Python ( DiscountCurve
) utiliza la interpolación log-lineal, y no la log-cubica. Si quiere utilizar esta última, tendrá que modificar QuantLib-SWIG/SWIG/discountcurve.i
para que también exporte la curva deseada y recompilar las envolturas. Lo mismo ocurre si se quiere utilizar una interpolación diferente para las tasas cero o hacia adelante; los archivos correspondientes a editar son zerocurve.i
y forwardcurve.i
.