Reference Every exported name

API reference

Four verbs: read market data, calibrate a model, differentiate the calibration, price with a closed form. The models and the numerical methods come from FinancialMonteCarlo.jl and FinancialFFT.jl.

Calibrate

calibrate fit a model to a surface
Parametrization the variables of the search
default_parametrization one per model
CalibratedData the result

Differentiate

calibrated_parameters p*(v), differentiable
vol_errors the residuals
loss half their sum of squares

Market data

MarketData the surface
retriever, adapter read and parse
CboeInputStrategy, CboeWebInputStrategy, cboe_snapshot CBOE quotes

Pricing

AnalyticMethod closed forms: Black–Scholes, shifted lognormal mixture

Calibration

FinancialCalibrator.calibrate — Function
calibrate(model, pricing_method, mktData::MarketData, opt_method = NelderMead(), opt_options = Optim.Options(g_tol = 1e-6); parametrization = default_parametrization(model), autodiff, polish = true, polish_backend, min_vega = 1e-3, verbose = false)

Calibrate the parameters of model to the implied volatility surface in mktData. Each smile is priced with pricing_method, any FinancialMonteCarlo.AbstractMethod: a FinancialFFT method such as CarrMadanMethod, AnalyticMethod() for models with a closed form, a MonteCarloConfiguration or a VibratoMonteCarlo method. Its execution mode moves to CudaMode when the strikes of mktData are on the GPU. Only out of the money options are priced: puts below the forward, calls above it; and only the quotes whose Black–Scholes vega at the quoted volatility is at least min_vega * S0, whose prices are well above the accuracy of a numerical method. ModelVolatility is NaN at the quotes left out.

The loss (FinancialCalibrator.loss), half the sum of the squared volatility errors, is minimized with the Optim optimizer opt_method and settings opt_options (iterations, time limit, tolerances, ...), starting from the current parameters of model, over the variables of parametrization (see Parametrization; by default default_parametrization, on which every point is a valid model). The default options stop the optimizer at a tolerance of 1e-6 and leave the rest to the polish; with polish = false the default is Optim's own Optim.Options(). To start from a previous solution, pass the model with those parameters (FinancialMonteCarlo.set_parameters!(model, p)); a gradient-based optimizer gets its gradients from autodiff (an ADTypes backend). With polish = true the result is then refined by Levenberg–Marquardt on the volatility errors (their Jacobian by polish_backend, an ADTypes backend through DifferentiationInterface), so that the calibration ends where the gradient of the loss vanishes, not where the tolerances of opt_method stopped it: the calibrated parameters are then a smooth function of the quotes, which the implicit function theorem can differentiate. Nothing is printed unless verbose = true.

source
FinancialCalibrator.Parametrization — Type
Parametrization(to_params = identity, from_params = identity)

The variables a calibration searches over. to_params(x) gives the parameters of the model, in the order of FinancialMonteCarlo.get_parameters, from the variables x; from_params(p) is its inverse, which gives the starting point from the parameters of the model to calibrate. Parametrization() searches over the parameters themselves; the default of calibrate and calibrated_parameters is default_parametrization of the model.

A change of variables keeps every step of the search on a valid model (a positive volatility, a correlation in $(-1, 1)$), and fixes the parameters that the quotes cannot determine. For Heston, whose parameters are $(\sigma, \sigma_0, \lambda, \kappa, \rho, \theta)$, with no market price of volatility risk:

heston_variables = Parametrization(x -> [exp(x[1]), exp(x[2]), zero(x[1]), exp(x[3]), tanh(x[4]), exp(x[5])],
                                   p -> [log(p[1]), log(p[2]), log(p[4]), atanh(p[5]), log(p[6])])

to_params must be generic in the number type of x: the calibration differentiates through it.

source
FinancialCalibrator.default_parametrization — Function
default_parametrization(model)

The Parametrization a calibration of model uses by default: unconstrained variables, so that every point the optimizer tries is a valid model, constraints between the parameters included. The parameters keep the order of FinancialMonteCarlo.get_parameters:

ModelParametersVariables
BlackScholesProcess$\sigma$$\sigma = e^{x}$
MertonProcess$\sigma, \lambda, \mu_J, \sigma_J$$e^{x_1}, e^{x_2}, x_3, e^{x_4}$
KouProcess$\sigma, \lambda, p, \lambda_+, \lambda_-$$e^{x_1}, e^{x_2}, 1/(1 + e^{-x_3}), 1 + e^{x_4}, e^{x_5}$
VarianceGammaProcess$\sigma, \theta, \kappa$$e^{x_1}$, $1/\kappa - \sigma^2/2 - e^{x_2}$, $e^{x_3}$
NormalInverseGaussianProcess$\sigma, \theta, \kappa$$e^{x_1}$, $(1/\kappa - \sigma^2)/2 - e^{x_2}$, $e^{x_3}$
HestonProcess$\sigma, \sigma_0, \lambda, \kappa, \rho, \theta$$e^{x_1}, e^{x_2}, 0, e^{x_3}, \tanh x_4, e^{x_5}$
ShiftedLogNormalMixture$\eta_1 \dots \eta_n, \lambda_1 \dots \lambda_{n-1}, \alpha$$\eta_i = e^{x_i}$, $\lambda_i = e^{y_i} / (1 + \sum_j e^{y_j})$, $\alpha$
LogNormalMixture$\eta_1 \dots \eta_n, \lambda_1 \dots \lambda_{n-1}$as the shifted mixture, without $\alpha$

The $\theta$ of Variance Gamma and NIG is bounded above by the other two parameters (the martingale correction must exist), and the weights of a mixture sum to less than one. Heston's market price of volatility risk $\lambda$ is fixed at zero: the quotes cannot separate it from $\kappa$ (the Hessian of the loss is singular, and so is the implicit derivative of calibrated_parameters), so it is not calibrated. Any other model searches over its parameters (Parametrization()).

The starting point must satisfy the constraints strictly: a parameter on the boundary has no finite variable.

source
FinancialCalibrator.CalibratedData — Type
CalibratedData

The result of calibrate: OptimalParam, the calibrated parameters of the model (in the order of FinancialMonteCarlo.get_parameters), OptimalValue, the loss there, and ModelVolatility, the implied volatilities of the calibrated model at the quotes, shaped as the surface (NaN at the quotes left out of the loss).

source

Derivatives of the calibration

FinancialCalibrator.calibrated_parameters — Function
calibrated_parameters(model, pricing_method, mktData, vols = mktData.Volatility, opt_method = NelderMead(), opt_options = Optim.Options(g_tol = 1e-6); parametrization = default_parametrization(model), autodiff, polish_backend, conditions_backend, implicit_backends = (x = ..., y = ...), min_vega = 1e-3)

The parameters that calibrate finds for the quoted implied volatilities vols (a matrix shaped as mktData.Volatility, whose maturities, rates and strikes it takes; the quotes that enter the loss are chosen on mktData.Volatility, see calibrate), as a function of vols that can be differentiated:

∂p_∂v = ForwardDiff.jacobian(V -> calibrated_parameters(model, method, mkt, V), mkt.Volatility)

The parameters are those of the model, parametrization.to_params of the variables the calibration searches over. The derivatives come from the implicit function theorem at the solution, not from differentiating the optimizer: that needs the gradient of the loss to vanish there, which the Levenberg–Marquardt polish of calibrate provides, and a smooth loss around it.

Every automatic differentiation package can differentiate calibrated_parameters from outside: ForwardDiff through its dual numbers, Zygote through the ChainRules rule of ImplicitDifferentiation, Enzyme through its Enzyme rules, and Mooncake through a rule of FinancialCalibrator (an extension, loaded with Mooncake). Inside, the backends are ADTypes objects, ForwardDiff when it is loaded and finite differences otherwise by default:

  • autodiff, polish_backend: the gradients of the optimizer and the Jacobian of the polish, as in calibrate;
  • conditions_backend: the gradient $\nabla_x L$ of the loss in the variables, the optimality conditions;
  • implicit_backends = (x = ..., y = ...): how ImplicitDifferentiation differentiates those conditions, with respect to the quotes (x) and to the variables (y). This differentiates the conditions_backend gradient again (nested).

ForwardDiff everywhere suits a model with few parameters. With a reverse mode outer backend and many quotes, x is the one to change: the pullback needs $(\partial_v \nabla_x L)^\top w$, which forward mode gets one direction per quote.

source
FinancialCalibrator.vol_errors — Function
vol_errors(params, model, method, mktData, cache = loss_cache(model, method, mktData); vols = mktData.Volatility)

Model implied volatilities minus the quoted ones, for the parameters params of model: one entry per quote that enters the loss (see loss_cache: out of the money options, vega at least min_vega * S0), maturity by maturity. Generic in the number type of params and of vols, so that it can be differentiated with respect to both. Throws on invalid parameters, and a DomainError on a price that is not positive.

source
FinancialCalibrator.loss — Function
loss(params, model, method, mktData, cache = loss_cache(model, method, mktData))

Half the sum of the squared volatility errors of vol_errors: smooth in the parameters, also where a maturity is fitted exactly. Inf where the parameters cannot be priced (see numerical_failure), which an Optim optimizer ranks as the worst point; any other exception propagates.

source

Market data

retriever reads the raw data of a calibration and adapter turns it into the Underlying of the models and the MarketData. Besides the bundled DAX surface, CboeInputStrategy reads a saved CBOE option chain, implies the forward and the dividends from put–call parity, and resamples the out of the money volatilities on a moneyness grid; CboeWebInputStrategy downloads the current chain and can save it:

id = CalibrationID("SPX", Dates.format(today(), "dduyyyy"), "USD")
und, mkt = adapter(retriever(id, CboeWebInputStrategy(dir = "market_data")))   # downloaded, and saved in market_data/
und, mkt = adapter(retriever(id, CboeInputStrategy(dir = "market_data")))      # read again from the saved chain
FinancialCalibrator.MarketData — Type
MarketData

Market quotes of a calibration: maturities, zero rates, strikes and the implied volatility surface (Volatility[i, j] is the volatility of maturity i and strike j). Spot and dividends are not part of it: they belong to the Underlying that adapter returns next to it, which the model to calibrate is built on. Strikes and Volatility can live on any array type, e.g. on the GPU: Adapt.adapt(CuArray, mkt) moves them to the device (the per-maturity vectors stay on the host).

source
FinancialCalibrator.retriever — Function
retriever(id::CalibrationID, input) -> RawMarketData

The raw market data of id (CalibrationID(underlying, refDate, currency), with refDate as 22Aug2016), read by the input strategy input: IOFileInputStrategy() reads the JSON files of data/json (spot, zero rates, implied dividends and implied volatilities), CboeInputStrategy an option chain of the CBOE saved on disk, and CboeWebInputStrategy the current one, downloaded. adapter turns the result into the Underlying and the MarketData of a calibration.

source
FinancialCalibrator.adapter — Function
adapter(raw::RawMarketData, T = Float64) -> (underlying, mkt)

Parse the raw market data. Returns the Underlying (spot and implied dividend curve over the maturities) and the MarketData (rates, strikes and volatilities), both with floating point type T.

source
FinancialCalibrator.CboeInputStrategy — Type
CboeInputStrategy(; rate = 0.04, moneyness = 0.8:0.05:1.2, root = nothing, dir = DATA_DIR)

Input strategy of retriever for an option chain of the CBOE delayed quotes, saved by cboe_snapshot as CboeOptions_<underlying><ddMonyyyy>.json in dir. The chain becomes the inputs of adapter:

  • the spot, from the chain;
  • a flat continuously compounded zero rate rate, for every expiry (the CBOE gives no curve);
  • the forward and the dividend yield of every expiry, implied by put–call parity: $F = K + e^{rT} (C - P)$ from the mid prices of the strikes nearest to the spot, and $q = r - \log(F / S_0) / T$;
  • the implied volatilities of the CBOE (iv) of the out of the money options (puts below the forward, calls above), with a positive bid and an implied volatility between 0.1% and 500%, interpolated linearly in moneyness $K / S_0$ on the grid moneyness. The expiries whose quotes do not cover the grid are left out, so that every maturity has the same strikes.

root selects the option root of the chain ("SPX", the monthly options of the S&P 500, or "SPXW", the weeklies); by default it is the underlying of the CalibrationID.

source
FinancialCalibrator.CboeWebInputStrategy — Type
CboeWebInputStrategy(; rate = 0.04, moneyness = 0.8:0.05:1.2, root = nothing, index = true, dir = nothing)

Input strategy of retriever that downloads the current option chain of the underlying of the CalibrationID from the CBOE delayed quotes (an index, index = true, such as "SPX", is requested as _SPX), and turns it into the inputs of adapter as CboeInputStrategy does, with the same rate, moneyness and root. The CBOE gives the current chain only: the date of the CalibrationID should be the current one, and a different one gets a warning. With dir, the chain is also saved there, as cboe_snapshot does, so that CboeInputStrategy(dir = dir) reads it again later:

id = CalibrationID("SPX", Dates.format(today(), "dduyyyy"), "USD")
und, mkt = adapter(retriever(id, CboeWebInputStrategy(dir = "market_data")))
source
FinancialCalibrator.cboe_snapshot — Function
cboe_snapshot(underlying; index = true, currency = "USD", dir = pwd()) -> CalibrationID

Download the option chain of underlying from the CBOE delayed quotes (an index, such as "SPX", is requested as _SPX), save it as it is returned in dir, named after its date, and return the CalibrationID that reads it with CboeInputStrategy from the same directory:

id = cboe_snapshot("SPX"; dir = "market_data")
und, mkt = adapter(retriever(id, CboeInputStrategy(dir = "market_data")))
source

Pricing

FinancialCalibrator.AnalyticMethod — Type
AnalyticMethod(; mode = SerialMode())

Pricing method selecting the closed-form pricer of a model, through the pricer interface of FinancialMonteCarlo: pricer(model, zero_rate, AnalyticMethod(), smile). Implemented for BlackScholesProcess and ShiftedLogNormalMixture. Numerical pricing methods (CarrMadanMethod, CosMethod, ...) come from FinancialFFT, so the kind of calibration is entirely determined by the pricing method. Like every method of FinancialMonteCarlo it carries its execution mode: FinancialMonteCarlo.CudaMode() prices the strikes of a smile on the GPU, in one broadcast.

source

Index