FinanceModels API Reference

Exported API

FinanceModels.FitConvergenceError — Type
FitConvergenceError(retcode, msg)

Thrown by fit when the optimizer does not report a successful solve. retcode is the solver's SciMLBase.ReturnCode (for example ReturnCode.MaxIters or ReturnCode.Failure) and msg describes the failed fit.

A failed solve can leave the parameters at the starting guess, so fit throws rather than return an unfitted model. Address the cause and fit again: a different starting model, a different optimizer, tighter or longer solve_kwargs (for example solve_kwargs = (; maxiters = 10_000)), or quotes that the model can fit. Choose the model deliberately; switching to another interpolation method on failure changes the curve.

source
FinanceModels.Forward — Type

Forward(time,instrument)

The instrument is relative to the Forward time. e.g. if you have a Forward(1.0, Cashflow(1.0, 3.0)) then the instrument is a cashflow that pays 1.0 at time 4.0

source
FinanceModels.NullModel — Type
NullModel()

A singleton type representing a placeholder model for when you don't really need a model. For example: determining nominal cashflows for fixed income contract.

source
FinanceModels.Projection — Type
Projection(contract,model,kind)

The set of contracts and assumptions (model) to project the kind of output desired. Some assets require a projection in order to be valued (e.g. a floating rate bond).

If attempting to collect or otherwise reduce a contract (<:AbstractContract), by default it will get wrapped into a Projection(contract,NullModel(),CashflowProjection())

Use Projection(contract; index) to build a shared-index model store from model_requirements.

source
FinanceModels.Projection — Method
Projection(contract; index)

Project a contract or portfolio using index for every required model key. The model must satisfy all model_requirements. For contracts with different index curves or an FX conversion, pass an explicit model store instead: Projection(contract, Dict("SOFR" => sofr, "EURUSD" => fx)).

The generated store is a Dict{Any, typeof(index)}: keys may have mixed types, while model values keep the concrete type of index. Pass an explicit store if downstream code requires a particular store or key type.

This form is useful inside a valuation closure: rebuilding the projection with a bumped index recomputes floating coupons, including transformed swap legs. Omitting index retains the default model-free projection.

curve = Yield.Constant(0.04)
swap = InterestRateSwap(curve, 5.0; frequency = 1)
value(index, discount_curve) = present_value(discount_curve, Projection(swap; index))
value(curve, curve) # approximately zero
source
FinanceModels.RatePath — Type
RatePath(interp)

A simulated interest-rate path wrapped as an AbstractYieldModel. interp maps time t to the cumulative integral ∫₀ᵗ r(s) ds so that discount(path, t) = exp(-interp(t)).

source
FinanceCore.discount — Method
discount(m::ShortRate.CoxIngersollRoss, t, T, r_t)

Conditional zero-coupon bond price $P(t,T \mid r(t) = r_t)$ under the CIR model. Since the model is time-homogeneous, $P(t,T|r) = P(0, T-t | r)$.

source
FinanceCore.discount — Method
discount(m::ShortRate.HullWhite, t, T, r_t)

Conditional zero-coupon bond price $P(t,T \mid r(t) = r_t)$ under the Hull-White model. Unlike Vasicek/CIR, this depends on t and T separately (not just T-t) because the model is calibrated to an initial term structure.

Formula (Brigo & Mercurio 2006, Proposition 3.2.2):

\[\ln P(t,T) = \ln\frac{P(0,T)}{P(0,t)} + B(t,T) f(0,t) - \frac{\sigma^2}{4a} B(t,T)^2 (1 - e^{-2at}) - B(t,T) r_t\]

source
FinanceCore.discount — Method
discount(m::ShortRate.Vasicek, t, T, r_t)

Conditional zero-coupon bond price $P(t,T \mid r(t) = r_t)$ under the Vasicek model. Since the model is time-homogeneous, $P(t,T|r) = P(0, T-t | r)$.

source
FinanceCore.present_value — Function
present_value(model,contract,current_time=0.0)
present_value(model,projection,current_time=0.0)

Return the value of the contract as corresponding with the valuation assumptions embedded in the model for the given contract or projection with CashflowProjection kind.

Examples

m = Equity.BlackScholesMerton(0.01, 0.02, 0.15)

a = Option.EuroCall(CommonEquity(), 1.0, 1.0)

pv(m, a) # ≈ 0.05410094201902403
source
FinanceModels.InterestRateSwap — Method
InterestRateSwap(curve, tenor; frequency, model_key="OIS")

A convenience method for creating an interest rate swap given a curve and a tenor via a Composite contract consisting of receiving a fixed bond and paying (i.e. the negative of) a floating bond.

The notional is a unit (1.0) amount, and both legs settle frequency times per period. frequency is required because swap conventions differ by market; overnight index swaps on SOFR, €STR, and SONIA settle annually (frequency = 1). The fixed rate is the annualized par coupon for the tenor's schedule on curve, so the swap prices to zero at inception — including non-whole tenors, whose first period is a short stub accruing its actual length.

A Projection, with an indexable model_key is still needed to project a swap. See examples below for what this looks like.

Examples


julia> curve = Yield.Constant(0.05);

julia> swap = InterestRateSwap(curve, 10; frequency = 4);

julia> Projection(swap,Dict("OIS" => curve),CashflowProjection()) |> collect
80-element Vector{Cashflow{Float64, Float64}}:
Cashflow{Float64, Float64}(0.012272234429039283, 0.25)
Cashflow{Float64, Float64}(0.012272234429039283, 0.5)
⋮
Cashflow{Float64, Float64}(-0.012272234429039353, 9.75)
Cashflow{Float64, Float64}(-1.0122722344290394, 10.0)
source
FinanceModels.fit — Method
fit(
    model, 
    quotes, 
    method=Fit.Loss(x -> x^2);
    variables=__default_optic(model), 
    optimizer=__default_optim(model),
    solve_kwargs=(;)
    )

Fit a model to a collection of quotes using a loss function and optimization method.

Arguments

  • model: The initial model to fit, which is generally an instantiated but un-optimized model.
  • quotes: A collection of quotes to fit the model to.
  • method::F=Fit.Loss(x -> x^2): The loss function to use for fitting the model. Defaults to the squared loss function.
    • method can also be Bootstrap() with Spline.Linear(). Other interpolation strategies require a full-curve Fit.Loss.
  • variables=__default_optic(model): The variables to optimize over. This is a tuple of optic => interval pairs specifying which parameters of the model can vary. See extended help for more. Spline and knot-curve fits vary the knot rates and do not take variables.
  • optimizer=__default_optim(model): The optimization algorithm to use. The default optimization for a given model is LBFGS() from Optim.jl (via OptimizationOptimJL), a quasi-Newton method with automatic differentiation via ForwardDiff. See extended help for more on customizing the solver.
  • solve_kwargs=(;): Keyword arguments passed to Optimization.solve with the optimizer, such as (; maxiters = 10_000, abstol = 1e-12) or Optim.jl's g_tol. Use them to tighten a loss fit.
  • extrapolation=:flat_forward: For Spline.SplineCurve fits (including Spline.MonotoneConvex()), the long-end policy beyond the last knot (see Yield.Spline). Also accepts :flat_zero, :linear, Yield.FlatForwardAt(rate), and (except for MonotoneConvex) :extension.

Fitting a Spline.SplineCurve places one knot at each quote maturity and returns the same curve type as ZeroRateCurve: a Yield.MonotoneConvex for Spline.MonotoneConvex() (whose default optimizer is LBFGS()) and a Yield.Spline otherwise (default Newton()). Fit.Bootstrap() accepts Spline.Linear() only. Fitting an existing knot curve (fit(curve, quotes), a Yield.AbstractInterpolatedZeroCurve) fits new knot rates at its tenors with its interpolation method and extrapolation, by the same solve as a spline fit and from the same starting rates (not the curve's own); its default optimizer is the spline's. With fewer quotes than a polynomial or B-spline needs for its order, the order is reduced to one less than the number of knots (see Spline.PolynomialSpline): fit(Spline.Cubic(), quotes) with two quotes returns a linear curve.

The optimization routine will then attempt to modify parameters of model to best fit the quoted prices of the contracts underlying the quotes by calling present_value(model,contract). The optimization will minimize the loss function specified within Fit.Loss(...).

Different types of quotes are appropriate for different kinds of models. For example, if you try to value a set of equity Option.EuroCalls with a Yield.Constant, you will get an error because the present_value(m<:Yield.Constant,o<:Option.EuroCall) is not defined.

Returns

  • The fitted model.

Differentiating through a fit

Spline fits (fit(spline, quotes) and fit(Spline.Linear(), quotes, Fit.Bootstrap()), including FX.Forwards with a spline foreign curve) and fits of an existing knot curve with one knot per quote are differentiable with ForwardDiff: when quote prices, rates, or cashflow amounts are dual numbers, the fitted knot rates carry the exact first-order derivatives of the calibration. They come from the implicit function theorem at the fitted curve, not from the solver's iterations, and their values are the primal fit exactly.

tenors = [1.0, 2.0, 5.0, 10.0]
value(rates) = pv(fit(Spline.Linear(), OISYield.(rates, tenors), Fit.Bootstrap()), cashflows)
ForwardDiff.gradient(value, [0.03, 0.032, 0.035, 0.037])   # ∂value/∂quoted rates
  • The derivatives are first order, with respect to the quotes passed to fit. Nested (higher-order) dual numbers, and dual maturities or cashflow times, throw.
  • A refitted knot curve needs one knot per quote; with another number of knots the implicit solve is not square and throws a DimensionMismatch.
  • The fit must reprice its quotes. Bootstrap does; a loss fit is differentiated only if the largest absolute component of one Newton correction of its knot rates towards the exact fit is at most 1e-6. That correction is a local estimate of the fit's error, not a guaranteed distance to the exact solution.
  • A fitted Spline.MonotoneConvex(), Spline.PCHIP(), or Spline.Akima() curve that lies on a kink of its interpolation (flat quotes, for example) has no derivative there and throws.
  • Other models' fits throw an ArgumentError when given dual quotes.

See Sensitivities Through Calibration for details.

Examples

julia> model = Yield.Constant();

julia> quotes = ZCBPrice([0.9, 0.8, 0.7,0.6]);

julia> fit(model,quotes)
FinanceModels.Yield.Constant{Rate{Float64, Periodic}}(Periodic(0.12822921882254446, 1))

(With UnicodePlots loaded, fitted yield models display as a zero-rate chart instead.)

Extended help

Customizing the Solver

The default solver is LBFGS() from Optim.jl (via OptimizationOptimJL). This is a quasi-Newton method that uses automatic differentiation (ForwardDiff) to compute gradients efficiently.

  • Any solver from OptimizationOptimJL can be used, e.g. fit(...; optimizer=OptimizationOptimJL.Newton()) or fit(...; optimizer=OptimizationOptimJL.NelderMead()).
  • Solver settings go in solve_kwargs, e.g. fit(...; solve_kwargs = (; maxiters = 10_000, g_tol = 1e-12)).
  • More documentation is available from the upstream packages:

Defining the variables

An arbitrarily complex model may be the object we intend to fit - how does fit know what free variables are able to be solved for within the given model? variables is a tuple of optic => interval pairs. What does this mean?

  • An optic (or "lens") is a way to define an accessor to a given object. Example:
julia> using Accessors, AccessibleModels, IntervalSets

julia> obj = (a = "AA", b = "BB");

julia> lens = @optic _.a
(@optic _.a)

julia> lens(obj)
"AA"

An optic argument is a tuple of optic => interval pairs. For example, we might have a model as follows where we want fit to optimize parameters a and b:

struct MyModel <:FinanceModels.AbstractModel
     a 
     b 
end

__default_optic(m::MyModel) = (
    @optic(_.a) => 0.0 .. 100.0,
    @optic(_.b) => -10.0 .. 10.0,
)

In this way, fit know which arbitrary parameters in a given object may be modified. Technically, we are not modifying the immutable MyModel, but instead efficiently creating a new instance. This is enabled by AccessibleModels.jl.

Note that not all optimization algorithms want a bounded interval. In that case, simply leave off the paired range. The prior example would then become:

__default_optic(m::MyModel) = (
    (@optic(_.a),),
    (@optic(_.b),),
)

```

Additional Examples

See the tutorials in the package documentation for FinanceModels.jl or the docstrings of FinanceModels.jl's available model types.

source
FinanceModels.model_requirements — Function
model_requirements(contract) -> iterable

Return the key => model_type requirements for a cashflow projection. Fixed cashflows have no requirements; floating bonds require a yield model, and FX.Converted wrappers require an FX model as well as their inner requirements. Composites, forwards, portfolios, and Transducers eductions retain the requirements of their underlying contracts. Runtime portfolios are traversed lazily; small, fixed contract requirements may be tuples. A key may occur more than once: its model must satisfy every occurrence. Use arrays for large portfolios to avoid compilation costs from deeply nested Composite types.

Custom projectable contracts should implement this function, returning an iterable of pairs (empty, for example (), only when they require no model). The convenience constructor consumes the requirements once. There is no model-independent fallback for unknown contracts: an undeclared AbstractContract raises an ArgumentError that explains how to declare its requirements. Explicit Projection(contract, model_store) remains available without implementing this convenience protocol.

source
FinanceModels.pv_mc — Method
pv_mc(model, contract;
      n_scenarios=1000, timestep=1/12, horizon=nothing,
      rng=Random.default_rng())

Estimate the expected present value of contract under the stochastic model by averaging present_value across simulated scenarios.

Note

pv_mc is designed for fixed-cashflow instruments where each RatePath scenario provides the discount factors. For floating-rate instruments whose cashflows depend on the rate path, project cashflows per scenario using Projection instead.

The horizon should cover the contract's maturity. The default (maturity + 1) ensures this.

source
FinanceModels.short_rate — Method
short_rate(path::RatePath, t)

The instantaneous short rate r(t) for a simulated scenario.

RatePath stores the cumulative integral ∫₀ᵗ r(s) ds as a LinearInterpolation. The short rate is the derivative of this cumulative integral.

Because the cumulative integral is built from trapezoidal steps, the returned rate is piecewise-constant within each timestep — an approximation to the continuous short-rate process, not the exact value.

source
FinanceModels.simulate — Method
simulate(model::AbstractStochasticModel;
         n_scenarios=1000, timestep=1/12, horizon=30.0,
         rng=Random.default_rng())

Generate n_scenarios interest-rate paths. Each path is returned as a RatePath (an AbstractYieldModel) so it plugs directly into present_value, discount, etc.

Discretisation schemes:

  • Vasicek / Hull-White: the exact Gaussian transition density, so the simulated short rate has no discretisation bias at any timestep.
  • Cox-Ingersoll-Ross: full truncation (Lord, Koekkoek & Van Dijk, 2010) — an auxiliary process may go negative while the observed rate is max(x, 0). Weak bias vanishes as timestep → 0, but is material for coarse steps when the Feller condition is strongly violated.

In all cases the cumulative discount integral $∫₀ᵗ r(s)\,ds$ is accumulated with the trapezoidal rule between grid points, so pathwise discount factors (and hence pv_mc) retain an integration error that grows with timestep even when the short-rate transition itself is exact.

source

Unexported API

FinanceModels.ProjectionKind — Type
abstract type ProjectionKind

An abstract type that controls what gets produced from the model.

Subtypes of ProjectionKind define the level of detail in the output of the model. For example, if you just want cashflows or you want a full amortization schedule, you might define an AmortizationSchedule kind which shows principle, interest, etc.

After defining a new ProjectionKind, you need to define the how the projection works for that new output by extending either:

function Transducers.asfoldable(p::Projection{C,M,K}) where {C<:Cashflow,M,K<:CashflowProjection}
    ...
end

or

function Transducers.__foldl__(rf, val, p::Projection{C,M,K}) where {C<:Cashflow,M,K<:CashflowProjection}
    ...
end

There are examples of this in the documentation.

Examples

```julia julia> struct CashflowProjection <: ProjectionKind end CashflowProjection

julia> struct AmortizationSchedule <: ProjectionKind end AmortizationSchedule

source
FinanceModels.ReadOnlyVector — Type
ReadOnlyVector(v::Vector)

Internal read-only view over an owned Vector. It defines no setindex!, so indexed assignment — and therefore .=, fill!, sort!, reverse!, and writes through view — throws Base's CanonicalIndexError; reads behave as a normal AbstractVector (indexing, iteration, searchsortedlast, broadcasting, ==/isequal/hash identical to the equivalent Vector). copy/collect return a mutable Vector. To change a curve's knots, use Accessors.@set curve.rates[i] = x or reconstruct, which rebuild it.

Used by the knot curves (Yield.Spline, Yield.MonotoneConvex), which cache state derived from their knot vectors, so that ordinary array operations on the public fields cannot desynchronise the cache. This is Julia's conventional privacy, not literal immutability: the backing Vector is the internal field _data, and code that mutates it is unsupported.

source
FinanceModels.__default_optic — Method
__default_optic(model)

Returns the variables to optimize over for the given model. This is an optic/lens specifying which parameters of the model can vary. See extended help for more. An optic argument is a tuple of optic => interval pairs specifying which model parameters to optimize and their bounds.

Examples

We might have a model as follows where we want fit to optize parameters a and b:

struct MyModel <:FinanceModels.AbstractModel
        a 
        b 
end

__default_optic(m::MyModel) = (
    @optic(_.a) => 0.0 .. 100.0,
    @optic(_.b) => -10.0 .. 10.0,
)

Extended help

An arbitrarily complex model may be the object we intend to fit - how does fit know what free variables are able to be solved for within the given model? variables is a tuple of optic => interval pairs. What does this mean?

  • An optic (or "lens") is a way to define an accessor to a given object. Example:
julia> using Accessors, AccessibleModels, IntervalSets

julia> obj = (a = "AA", b = "BB");

julia> lens = @optic _.a
(@optic _.a)

julia> lens(obj)
"AA"

An optic argument is a tuple of optic => interval pairs. For example, we might have a model as follows where we want fit to optize parameters a and b:

struct MyModel <:FinanceModels.AbstractModel
        a 
        b 
end

__default_optic(m::MyModel) = (
    @optic(_.a) => 0.0 .. 100.0,
    @optic(_.b) => -10.0 .. 10.0,
)

In this way, fit know which arbitrary parameters in a given object may be modified. Technically, we are not modifying the immutable MyModel, but instead efficiently creating a new instance. This is enabled by AccessibleModels.jl.

Note that not all optimization algorithms want a bounded interval. In that case, simply leave off the paired range. The prior example would then become:

__default_optic(m::MyModel) = (
    (@optic(_.a),),
    (@optic(_.b),),
)

```

source
FinanceModels.__implicit_knot_curve — Method
__implicit_knot_curve(curve, quotes, primal_quotes, extrapolation)

Given the knot curve curve fitted to primal_quotes (one knot per quote) under the primal copy of extrapolation, return it with knot rates carrying the first-order derivatives of the calibration with respect to the dual numbers in quotes and in extrapolation. Returns curve itself when there are none.

source
FinanceModels.__implicit_root — Method
__implicit_root(g, g_primal, x0; bracket = (-1.0, 1.0), who, slope = nothing, scale)

Solve g_primal(x) = 0 from x0, falling back to a bracketed solve on bracket, and return the root with first-order ForwardDiff partials of g propagated by the implicit function theorem: dx = -(∂g/∂θ) / (∂g/∂x).

g_primal must be g evaluated without dual numbers, or g with its dual numbers stripped from the result. slope(x), when given, is ∂g/∂x on primal values in closed form; otherwise it is ForwardDiff.derivative(g_primal, x), which requires g_primal to involve no dual numbers at all. scale(x) is the magnitude, free of units such as a notional, that both checks at the solution use: the residual at an accepted root must be at most sqrt(eps) times it, and the slope must exceed sqrt(eps) times it. The caller chooses it for its residual: implied_quote passes the size of the quote's price and value, the swaption critical rate the size of the terms of its slope. The solvers stop on absolute tolerances, so g should already be expressed relative to such a scale. The value of the result is the primal root exactly; its partials come from one dual correction step. Nested dual numbers, a root that does not solve g_primal, and a vanishing slope throw an ArgumentError naming who.

source
FinanceModels.__rewrap — Method
__rewrap(from::Transducers.Reduction, to)
__rewrap(from, to)

Used to unwrap a Reduction which is a composition of contracts and a transducer and apply the transducers to the associated projection instead of the transducer.

For example, on its own a contract is not project-able, but wrapped in a (default) Projection it can be. But it may also be a lot more convienent to construct contracts which have scaling or negated modifications and let that flow into a projection.

Examples

julia> Bond.Fixed(0.05,Periodic(1),3) |> collect
3-element Vector{Cashflow{Float64, Float64}}:
 Cashflow{Float64, Float64}(0.05, 1.0)
 Cashflow{Float64, Float64}(0.05, 2.0)
 Cashflow{Float64, Float64}(1.05, 3.0)

julia> Bond.Fixed(0.05,Periodic(1),3) |> Map(-) |> collect
3-element Vector{Cashflow{Float64, Float64}}:
 Cashflow{Float64, Float64}(-0.05, 1.0)
 Cashflow{Float64, Float64}(-0.05, 2.0)
 Cashflow{Float64, Float64}(-1.05, 3.0)

julia> Bond.Fixed(0.05,Periodic(1),3) |> Map(-) |> Map(x->x*2) |> collect
3-element Vector{Cashflow{Float64, Float64}}:
 Cashflow{Float64, Float64}(-0.1, 1.0)
 Cashflow{Float64, Float64}(-0.1, 2.0)
 Cashflow{Float64, Float64}(-2.1, 3.0)
source
FinanceModels._zcb_option_price — Method
_zcb_option_price(m::Union{ShortRate.Vasicek, ShortRate.HullWhite}, T, S, K)

Closed-form price of a European call and put on a zero-coupon bond under a Gaussian (Vasicek or Hull-White) one-factor model.

Returns (call_price, put_price).

  • T: option expiry
  • S: bond maturity (S > T)
  • K: strike price

Reference: Brigo & Mercurio (2006), Proposition 3.2.1

source
FinanceModels.cashflows_timepoints — Method
cashflows_timepoints(contracts)
cashflows_timepoints(quotes)

Create a matrix of cashflows and a vector of timepoints for a collection of quotes or contracts. Timepoints need not be spaced evenly.

This is used when constructing SmithWilson yield curves.

Arguments

  • contracts or quotes: A collection of <:AbstractContracts or Quotes.

Returns

  • A tuple (m, times) where m is a matrix of cashflows and times is a vector of timepoints.

Examples

julia> FinanceModels.cashflows_timepoints(ParYield.([0.04,0.02,0.04],[1,4,4]))
([0.02 0.01 0.02; 1.02 0.01 0.02; … ; 0.0 0.01 0.02; 0.0 1.01 1.02], [0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0])
source
FinanceModels.eurocall — Method
eurocall(;S=1.,K=1.,τ=1,r,σ,q=0.)

Calculate the Black-Scholes implied option price for a european call, where:

  • S is the current asset price
  • K is the strike or exercise price
  • τ is the time remaining to maturity (can be typed with \tau[tab])
  • r is the continuously compounded risk free rate
  • σ is the (implied) volatility (can be typed with \sigma[tab])
  • q is the continuously paid dividend rate

Rates should be input as rates (not percentages), e.g.: 0.05 instead of 5 for a rate of five percent.

Experimental

This function is well-tested, but the derivatives functionality (API) may change in a future version of FinanceModels.

Extended Help

This is the same as the formulation presented in the dividend extension of the BS model in Wikipedia.

Other general comments:

  • Swap/OIS curves are generally better sources for r than government debt (e.g. US Treasury) due to the collateralized nature of swap instruments.
  • (Implied) volatility is characterized by a curve that is a function of the strike price (among other things), so take care when using
  • FinanceModels.jl can assist with converting rates to continuously compounded if you need to perform conversions (e.g. convert(Continuous(), r)).
source
FinanceModels.europut — Method
europut(;S=1.,K=1.,τ=1,r,σ,q=0.)

Calculate the Black-Scholes implied option price for a european put, where:

  • S is the current asset price
  • K is the strike or exercise price
  • τ is the time remaining to maturity (can be typed with \tau[tab])
  • r is the continuously compounded risk free rate
  • σ is the (implied) volatility (can be typed with \sigma[tab])
  • q is the continuously paid dividend rate

Rates should be input as rates (not percentages), e.g.: 0.05 instead of 5 for a rate of five percent.

Experimental

This function is well-tested, but the derivatives functionality (API) may change in a future version of FinanceModels.

Extended Help

This is the same as the formulation presented in the dividend extension of the BS model in Wikipedia.

Other general comments:

  • Swap/OIS curves are generally better sources for r than government debt (e.g. US Treasury) due to the collateralized nature of swap instruments.
  • (Implied) volatility is characterized by a curve that is a function of the strike price (among other things), so take care when using
  • FinanceModels.jl can assist with converting rates to continuously compounded if you need to perform conversions (e.g. convert(Continuous(), r)).
source

Please open an issue if you encounter any issues or confusion with the package.