FinanceModels API Reference
FinanceModels.BondFinanceModels.EquityFinanceModels.FXFinanceModels.OptionFinanceModels.ShortRateFinanceModels.SplineFinanceModels.AbstractStochasticModelFinanceModels.CashflowProjectionFinanceModels.CommonEquityFinanceModels.FitConvergenceErrorFinanceModels.ForwardFinanceModels.ModelsFinanceModels.NullModelFinanceModels.ProjectionFinanceModels.ProjectionKindFinanceModels.RatePathFinanceModels.ReadOnlyVectorFinanceModels.InterestRateSwapFinanceModels.__implicit_knot_curveFinanceModels.__implicit_rootFinanceModels.__zcb_option_priceFinanceModels.cashflows_timepointsFinanceModels.closed_formFinanceModels.default_optimizerFinanceModels.default_variablesFinanceModels.eurocallFinanceModels.europutFinanceModels.fitFinanceModels.pv_mcFinanceModels.pv_mcFinanceModels.short_rateFinanceModels.short_rateFinanceModels.simulateFinanceModels.simulateFinanceModels.simulation_stepsFinanceModels.simulation_timesFinanceModels.valuation_model
Exported API
FinanceModels.AbstractStochasticModel — Type
AbstractStochasticModel <: Yield.AbstractYieldModelAbstract supertype for stochastic short-rate models.
FinanceModels.CashflowProjection — Type
CashflowProjection()A concrete subtype of ProjectionKind which is the projection which returns only a reducible collection of Cashflows. Use in conjunction with a Projection.
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.
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
A projected instrument also reads its models on that clock: in Forward(2.0, floater), a one-year floater fixes its coupon on the index rate from time 2 to 3.
FinanceModels.Models — Type
Models(model, store)
Models(model; index = model)A valuation context: the model that values contracts, and the models they read by key. model discounts (discount(ctx, t)) and prices (valuation_model(ctx)); the models a contract reads by key, such as the index curve of a Bond.Floating or the FX model of an FX.Converted leg, come from store (ctx[key]), any collection indexed by key (a Dict or a NamedTuple). Models(model; index) returns index for every key. Models(curve) is its single-curve case, with index = curve.
Any model is a valuation context of its own: present_value(curve, bond) discounts on curve, and present_value(bsm, call) prices on bsm. Models adds the models read by key:
curve = Yield.Constant(0.04)
swap = InterestRateSwap(curve, 5.0; frequency = 1)
present_value(Models(curve), swap) # approximately zero
present_value(Models(ois; index = sofr), swap)
present_value(Models(ois, Dict("SOFR" => sofr, "EURUSD" => fx)), portfolio)
collect(Projection(swap, Models(curve))) # its cashflowsFinanceModels.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.
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())
Projection(contract, models) projects against a valuation context such as Models, whose models the contract reads by key: collect(Projection(swap, Models(curve))).
FinanceModels.RatePath — Type
RatePath(interp::DataInterpolations.LinearInterpolation)A simulated interest-rate path wrapped as an AbstractYieldModel. interp interpolates the cumulative integral ∫₀ᵗ r(s) ds linearly over the path's time grid, so discount(path, t) = exp(-interp(t)) and the path's forward is constant on each step: at a grid time, Yield.instantaneous_forward(path, t) is the slope of the step that starts there, which for a simulated path is the average of that step's two simulated rates. The grid starts at t = 0, where the integral is 0; otherwise construction throws an ArgumentError. The path is defined where interp is; simulate builds paths that throw outside their time grid.
A path from simulate also records the simulated short rate at each grid time, read with short_rate. A path built from an integral alone, as here, records none, and short_rate throws on it.
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)$. The short rate r_t is a number, read as continuously compounded, or a Rate, converted to its continuously compounded value.
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. The short rate r_t is a number, read as continuously compounded, or a Rate, converted to its continuously compounded value.
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\]
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)$. The short rate r_t is a number, read as continuously compounded, or a Rate, converted to its continuously compounded value.
FinanceCore.internal_rate_of_return — Method
FinanceCore.internal_rate_of_return(q::Quote)Return the internal rate of return (yield to maturity) implied by the quote's price and cashflows.
FinanceCore.present_value — Method
present_value(ctx, contract)The value of contract at time 0 in the valuation context ctx: a yield curve or rate that discounts its cashflows, a model with a closed form for it (Equity.BlackScholesMerton for a European option), or a Models context that also holds the models it reads by key.
- A contract with a cashflow projection is worth its projected cashflows, each discounted by
ctx(discount(ctx, t)). A cashflow paid before time 0 accumulates. - A
Compositeis worth the sum of its parts, and a collection of contracts the sum of its contracts' values, so each part keeps its own valuation (a closed form or its projection). Forward,FX.Convertedand transducers (contract |> Map(f)) act on the cashflow stream: a contract under one of them is valued by its projection.
There is no valuation-time argument. For a deterministic curve, the value as of t of the cashflows at or after t discounts each from t:
foldxl(+, Projection(c, curve) |> Filter(cf -> cf.time >= t) |> Map(cf -> cf.amount * discount(curve, t, cf.time)); init = zero(discount(curve, t, t)))Examples
m = Equity.BlackScholesMerton(0.01, 0.02, 0.15)
a = Option.EuroCall(CommonEquity(), 1.0, 1.0)
pv(m, a) # ≈ 0.05410094201902403FinanceModels.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 an integer or a Periodic, and 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 swap reads its index curve under model_key: value it with present_value(Models(curve), swap), and list its cashflows with collect(Projection(swap, Dict("OIS" => curve))).
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)
FinanceModels.closed_form — Function
closed_form(model, contract)The value of contract under model by a formula that depends on the model: Black–Scholes for a European option under Equity.BlackScholesMerton, Jamshidian's decomposition for a swaption under ShortRate.Vasicek or ShortRate.HullWhite.
present_value(ctx, contract) is the extension point for valuing a contract, and closed_form is the kernel it calls when the formula depends on the type of the pricing model. FinanceModels' options, caps, floors and swaptions are valued by present_value(ctx, c) = closed_form(valuation_model(ctx), c), so a model prices them by adding a closed_form method. A custom contract opts in the same way:
# pays S_T - K at T on a unit stock
struct EquityForward <: FinanceCore.AbstractContract
strike::Float64
maturity::Float64
end
FinanceCore.present_value(ctx, c::EquityForward) = FinanceModels.closed_form(valuation_model(ctx), c)
FinanceModels.closed_form(m::Equity.BlackScholesMerton, c::EquityForward) =
exp(-m.q * c.maturity) - c.strike * exp(-m.r * c.maturity)It then values under the model, under Models, in a portfolio and in a Composite:
m = Equity.BlackScholesMerton(0.03, 0.01, 0.2)
fwd = EquityForward(1.0, 2.0)
call = Option.EuroCall(CommonEquity(), 1.0, 2.0)
present_value(m, fwd)
present_value(Models(m; index = Yield.Constant(0.03)), fwd) # the same value
present_value(m, [fwd, call]) # the sum of the two values
present_value(m, Composite(fwd, call)) # the same sumA context whose model has no method for the contract throws a MethodError. A formula that reads only discount factors and models by key (discount(ctx, t), ctx[key]) needs no model kernel: it is written on present_value directly.
FinanceModels.default_optimizer — Method
default_optimizer(model)The optimizer fit uses for model when none is given: OptimizationOptimJL.LBFGS(), and OptimizationOptimJL.Newton() for a spline other than Spline.MonotoneConvex(). To change a custom model's default:
FinanceModels.default_optimizer(m::MyModel) = OptimizationOptimJL.NelderMead()FinanceModels.default_variables — Method
default_variables(model)The parameters fit optimizes for model: a tuple of optic => interval pairs (or 1-tuples of an optic, for an unbounded parameter). For example, to optimize a and b of a custom model:
struct MyModel <: FinanceModels.AbstractModel
a
b
end
FinanceModels.default_variables(m::MyModel) = (
@optic(_.a) => 0.0 .. 100.0,
@optic(_.b) => -10.0 .. 10.0,
)See fit, "Defining the variables".
FinanceModels.fit — Method
fit(
model,
quotes,
method=Fit.Loss(x -> x^2);
variables=default_variables(model),
optimizer=default_optimizer(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.methodcan also beBootstrap()withSpline.Linear(). Other interpolation strategies require a full-curveFit.Loss.
variables=default_variables(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 takevariables.optimizer=default_optimizer(model): The optimization algorithm to use. The default optimization for a given model isLBFGS()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 toOptimization.solvewith the optimizer, such as(; maxiters = 10_000, abstol = 1e-12)or Optim.jl'sg_tol. Use them to tighten a loss fit.extrapolation=:flat_forward: ForSpline.SplineCurvefits (includingSpline.MonotoneConvex()), the long-end policy beyond the last knot (seeYield.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(), orSpline.Akima()curve that lies on a kink of its interpolation (flat quotes, for example) has no derivative there and throws. - Other models'
fits differentiate none of their inputs. Every solve checks its parameters and its loss (or residual) at each evaluation: a caller's dual number that reaches them, from the quotes, a contract type the fit does not strip, or a fixed model field, throws anArgumentErrorinstead of differentiating the solver's iterations.
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())orfit(...; 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
FinanceModels.default_variables(m::MyModel) = (
@optic(_.a) => 0.0 .. 100.0,
@optic(_.b) => -10.0 .. 10.0,
)In this way, fit knows 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:
FinanceModels.default_variables(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.
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.
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 must cover the contract's maturity, since a simulated path throws beyond its horizon. The default (maturity + 1) ensures this.
FinanceModels.short_rate — Method
short_rate(path::RatePath, t)The simulated short rate at grid time t of a path from simulate, as a Continuous rate: the model's state there (for Cox-Ingersoll-Ross, the observed rate max(x, 0)). It is the rate to pass as r_t to a conditional discount(model, t, T, r_t).
t must be one of the path's simulated times, FinanceModels.simulation_times(path). A time within roundoff of one, 8eps at the scale of that time or of the step, whichever is larger, is read as that time. Any other t, between grid times or outside the grid, throws a DomainError: the simulation has no rate there. A path built from an integral alone, RatePath(interp), records no rates, and short_rate throws an ArgumentError.
Yield.instantaneous_forward(path, t) is different: it is the slope of the discount integral over the step that starts at t, the average of that step's two simulated rates, so it depends on the rate at the end of the step.
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. A path is defined on its time grid of simulation_steps(horizon, timestep).nsteps steps, from 0 to horizon (or the first grid point beyond an unaligned horizon); evaluating it outside that range throws rather than extending the path.
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 astimestep → 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.
Each path also records the simulated short rate at its grid times (FinanceModels.simulation_times), read with short_rate.
Paths are repeatable: the same rng state, arguments and package versions give the same paths. A model whose parameters carry ForwardDiff numbers draws the same shocks as its Float64 counterpart, so paired sensitivity runs see identical scenarios. A change to which draws feed which step is recorded in the NEWS.
FinanceModels.simulation_steps — Method
simulation_steps(horizon, timestep) -> (; nsteps, aligned)The number of timestep-long steps a simulation to horizon takes, and whether horizon lies on that grid. With r = horizon / timestep, horizon is aligned when r is within 8eps(r) of an integer n, and the grid has nsteps = n steps: roundoff (0.07 / 0.01 is 7.000000000000001) adds no step. Otherwise nsteps = ceil(r), the first grid point beyond horizon, and aligned = false. horizon and timestep must be finite and positive.
simulate ends an aligned grid at horizon itself, and covers an unaligned horizon with the extra step.
FinanceModels.simulation_times — Method
simulation_times(path::RatePath)A copy of path's time grid, from 0 to its last time. For a path from simulate it is a new Vector{Float64} of multiples of the timestep, ending at an aligned horizon or at the extra step that covers an unaligned one (see FinanceModels.simulation_steps). short_rate answers at these times only.
FinanceModels.valuation_model — Method
valuation_model(ctx)The model that values contracts in the valuation context ctx: the model of a Models context, and any other model itself. A contract with a closed-form value reads the model it needs through it, so that the formula applies alike to present_value(model, c) and to present_value(Models(model, store), c):
FinanceCore.present_value(ctx, c::MyOption) = FinanceModels.closed_form(valuation_model(ctx), c)
FinanceModels.closed_form(m::MyModel, c::MyOption) = ...A context whose model does not price the contract fails in the formula, with a MethodError. See closed_form.
Unexported API
FinanceModels.ProjectionKind — Type
abstract type ProjectionKindAn 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 how the projection works for that new output by extending either:
function Transducers.asfoldable(p::Projection{C,M,K}) where {C<:MyContract,M,K<:MyKind}
...
endor
function Transducers.__foldl__(rf, val, p::Projection{C,M,K}) where {C<:MyContract,M,K<:MyKind}
...
endEither extension point folds inside every wrapper (a portfolio, Composite, Forward, FX.Converted, a transducer over the contract).
There are examples of this in the documentation.
Examples
julia> struct CashflowProjection <: ProjectionKind end
julia> struct AmortizationSchedule <: ProjectionKind endFinanceModels.ReadOnlyVector — Type
ReadOnlyVector(v::Vector)
ReadOnlyVector{Rate{T, Continuous}, T}(v::Vector{T})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.
The second form reads each stored number as a Continuous rate, without a copy: indexing builds the rate, and copy/collect return a Vector of rates. knot_rates views a curve's numeric knot rates this way.
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.
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.
FinanceModels.__implicit_root — Method
__implicit_root(g, g_primal, x0; bracket = (-1.0, 1.0), who, hint = "", 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 the ForwardDiff derivative of g_primal. 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 dual number in g_primal's argument or value, a root that does not solve g_primal, and a vanishing slope throw an ArgumentError naming who (and hint).
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 expiryS: bond maturity (S > T)K: strike price
Reference: Brigo & Mercurio (2006), Proposition 3.2.1
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
contractsorquotes: A collection of<:AbstractContracts orQuotes.
Returns
- A tuple
(m, times)wheretimesis the sorted vector of distinct timepoints andm[i, j]is the total amount that contractjpays attimes[i], in the floating-point type of the amounts (soBigFloatand dual-number amounts are kept).
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])FinanceModels.eurocall — Method
eurocall(;S=1.,K=1.,τ=1,r,σ,q=0.)Calculate the Black-Scholes implied option price for a european call, where:
Sis the current asset priceKis the strike or exercise priceτis the time remaining to maturity (can be typed with \tau[tab])ris the continuously compounded risk free rateσis the (implied) volatility (can be typed with \sigma[tab])qis the continuously paid dividend rate
r and q are numbers, read as continuously compounded, or Rates, converted to their continuously compounded values.
Rates should be input as rates (not percentages), e.g.: 0.05 instead of 5 for a rate of five percent.
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
rthan 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
- A
Ratein another convention is converted for you, e.g.r = Periodic(0.05, 1).
FinanceModels.europut — Method
europut(;S=1.,K=1.,τ=1,r,σ,q=0.)Calculate the Black-Scholes implied option price for a european put, where:
Sis the current asset priceKis the strike or exercise priceτis the time remaining to maturity (can be typed with \tau[tab])ris the continuously compounded risk free rateσis the (implied) volatility (can be typed with \sigma[tab])qis the continuously paid dividend rate
r and q are numbers, read as continuously compounded, or Rates, converted to their continuously compounded values.
Rates should be input as rates (not percentages), e.g.: 0.05 instead of 5 for a rate of five percent.
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
rthan 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
- A
Ratein another convention is converted for you, e.g.r = Periodic(0.05, 1).
Please open an issue if you encounter any issues or confusion with the package.