Build Status codecov



using Pkg
pkg"add Curves"


A 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. exp(c), log(c)) or with scalars (e.g. c+1 or 2c) are defined point-wise on the y-values of the Curve.

Operations between 2 Curve objects (noted as c1 and c2) are defined as follows:

  1. Interpolate c1 to the x-values of c2.
  2. Do the operation (e.g. adding) on the y-values of c2 and the interpolated y-values of c1.
  3. Repeat steps 1. and 2., but interpolate c2 to the x-values of c1.
  4. 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. The 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")

Use Case

The use case I had in mind was interest rate / FX curves for mathematical finance applications. The 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)