using Pkg pkg"add Curves"
Curve in this package is essentially a collection of points
(x, y), together with an interpolation and extrapolation method.
Curve objects have a number of standard calculation function defined (like addition, multiplication, logarithm), thus they can be used in algebraic expressions analogue to scalars.
How it Works
Operations on Curves alone (e.g.
log(c)) or with scalars (e.g.
2c) are defined point-wise on the y-values of the Curve.
Operations between 2 Curve objects (noted as
c2) are defined as follows:
c1to the x-values of
- Do the operation (e.g. adding) on the y-values of
c2and the interpolated y-values of c1.
- Repeat steps 1. and 2., but interpolate
c2to the x-values of
- Combine the results of both interpolations and create a new Curve object for the result.
Technically, this package is based on Interpolations.jl. Support of log-interpolation on both axis is added by this package.
Curve objects are defined to be immutable, thus every operation creates a new
Curve object as output.
In financial use cases, the x-axis of curves is often given in maturity tenors, e.g. 1W or 3M.
Tenor type is introduced to support such a notation for the x-axis of curves.
t = Tenor.(("1D", "3W", "1M", "10y", "12m")) @assert t == (Tenor(Curves.TDays, 1), Tenor(Curves.TWeeks, 3), Tenor(Curves.TMonths, 1), Tenor(Curves.TYears, 10), Tenor(Curves.TYears, 1))
Note that the tenor
12M is automatically converted to
1Y to avoid ambiguities.
Tenors can be directly used in Curves:
curve_from_tenors = Curve(["1D", "3W", "1M", "10y"], [0.5, 0.7, 0.75, 0.83]) val = interpolate("1W", curve_from_tenors)
As a shortcut for creating tenor objects, a string macro is provided:
@assert t"1W" == Tenor("1W")
The use case I had in mind was interest rate / FX curves for mathematical finance applications.
Curve objects make it easier to shift market data, e.g. for sensitivity or scenario P&L calculation, or to calculate such shift sizes based on market data time series.
using Curves using Plots # construct zero interest rate curve c_zero_base = Curve(["2D", "1w", "1M", "3M", "6M", "12M"], [0.5, 0.7, 0.75, 0.83, 1.1, 1.5]) # plotting - package Plots required plot(c_zero_base.x, c_zero_base.y) # define zero rate shifts (e.g. for stress testing or sensitivities) c_shifts = Curve([2, 185, 360], [0.1, -0.1, 0.2]) # shift curve c_shifted = c_zero_base + c_shifts # calculate discount factors for the unshifted and shifted curves c_base_df=apply((x,y) -> exp(-x*y/100/365), c_zero_base, logy=true) c_shifted_df = apply((x,y) -> exp(-x*y/100/365), c_shifted, logy=true) # calculate log-returns of discount factors log_ret = log(c_shifted_df/c_base_df) # apply log returns to the base curve - this should give the shifted curve back curve_scenario = *(c_base_df, exp(log_ret), logy=true) @assert curve_scenario ≈ c_shifted_df plot(curve_scenario.x, curve_scenario.y)
Ideas for Further Improvements
- Support of more operations
- Interactions with QuantLib.jl curve objects
- Multi-dimensional structures (especially 2d, e.g. for Volatility surfaces)