Para recuperar la curva original, necesitas utilizar los mismos tenores clave de la curva original y con la misma interpolación. Por ejemplo, cuando creas la curva original como:
crv = ql.PiecewiseLinearZero(2, ql.TARGET(), deposits + futures + swaps, ql.Actual365Fixed())
la curva interpola linealmente tasas de cero entre los nodos dados por las madureces de los depósitos, futuros e intercambios pasados. Puedes recuperar el conjunto de fechas subyacentes y las tasas correspondientes llamando a crv.nodes()
, que devuelve una secuencia de pares de (fecha, tasa); por ejemplo, si lo llamo en una curva definida como en este ejemplo, obtengo:
((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 6 de noviembre de 2001).
Dado que la curva es una instancia de PiecewiseLinearZero
, las tasas devueltas anteriormente son tasas de cero; y si las utilizas para crear una instancia de ZeroCurve
(la cual también interpola linealmente)...
dates, rates = zip(*crv.nodes())
crv2 = ql.ZeroCurve(dates, rates, ql.Actual365Fixed())
...obtendrás la misma curva que la 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 las tasas de cero en nodos diferentes, obtendrás puntos en la curva; pero al interpolar 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: necesitas usar los mismos nodos e interpolación. Puedes obtener los primeros de la curva original como curve.nodes()
, y tendrás que elegir una clase que proporcione la última. Para PiecewiseLinearZero
, tendrás que usar ZeroCurve
; para PiecewiseFlatForward
, el método nodes
devolverá pares de fechas y tasas instantáneas forward, las cuales puedes utilizar para crear una instancia de ForwardCurve
.
Para PiecewiseLogCubicDiscount
, el método nodes
devolverá pares de fechas y factores de descuento, y tendrías que pasarlos a una curva de descuento interpolada correspondiente; sin embargo, la que actualmente se exporta a Python (DiscountCurve
) utiliza interpolación log-lineal, y no log-cúbica. Si deseas utilizar esta última, tendrás que modificar QuantLib-SWIG/SWIG/discountcurve.i
para que también exporte la curva deseada y compilar nuevamente los envoltorios. Lo mismo sucede si deseas utilizar una interpolación diferente para tasas de cero o forward; los archivos correspondientes a editar son zerocurve.i
y forwardcurve.i
.