In Finance, one works with curves A LOT. Reasonable interpolation schemes are important to get prices and their dynamics right.
scipy.interpolate
provides an amazing package for working with curves. So, I was surprised to find that interpolators, such as CubicSpline, do not provide the sensitivity of a point on the curve to a move in one of the nodes. This is particularly important during calibration.
To calibrate one's model of the market as a set of rate curves, one typically forms a Jacobian matrix of the sensitivity of the prices of the target instruments to each rate node in the model's curves. It is straightforward to compute the sensitivity of these instruments to the rates of the dates inherent to the instrument itself. The dates in all the instruments, however, do not line up, so we must interpolate.
Computation of the Jacobian looks something like this:
It's the last term, $\frac{\partial y_k}{\partial y_j}$ , that represents the move in some arbitrary point in our curve to the node $j$. This is what I lacked in order to continue with the curve calibration module that I am building.
The approach that I have taken is to differentiate the Linear System used to compute the Spline itself, with respect to each of the nodes. SciPy follows the approach of setting up a system in terms of the first derivative, as in wikipedia's page on CubicSplines. By differentiating (15), one extends the dimension by one, so that $x$ and $b$ in $A x = b$ are matrices instead of vectors.
In [1]:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
In [2]:
from pennies.market.interpolate import CubicSplineWithNodeSens
In [3]:
def f(x):
return np.sin(x)
In [4]:
x = 0.5 * np.arange(10)
y = f(x)
for i in range(len(x)):
print('({}, {}'.format(x[i],y[i]))
In [5]:
cs_sens = CubicSplineWithNodeSens(x, y, bc_type='clamped')
In [6]:
xs = np.linspace(0, 5)
plt.figure(figsize=(12,10))
plt.title('Cubic Approximation of Spline')
plt.plot(x, y, 'o', label='data')
plt.plot(xs, f(xs), label='true')
plt.plot(xs, cs_sens(xs), label='cubic')
plt.legend(loc='lower left', fontsize=20)
plt.show()
In [7]:
# Calculate the sensitivity of x1 to a unit move in each node in the curve (x,y)
x1 = 2.25
y_sens = cs_sens.node_derivative(x1)
plt.figure(figsize=(12, 10))
plt.title('node sensitivities at x={}'.format(x1))
#plt.plot(x, y_sens, 'o', ms=10)
plt.bar(x, y_sens, width=0.4, color='0.75', edgecolor='k')
plt.ylim(min(y_sens) - 0.1, max(y_sens)+0.1)
plt.show()
In [ ]:
In [ ]: