Parameter identifiability

A fitted smile can be excellent while its parameters are barely determined: near-indistinguishable SVI smiles arise from very different \((a, b, \rho, m, \sigma)\), especially on narrow strike ranges where the wings are unconstrained. Stable fitted implied vol is not stable fitted parameters — a signal built on day-over-day parameter changes can be pure optimizer noise. pysvi.identifiability quantifies this at any calibrated optimum.

The report

from pysvi import SVI, identifiability_report
from pysvi.calibration import prepare_slice

k, w, F = prepare_slice(df_slice)
model = SVI()
params = model.calibrate(k, w, initialization="multi_start")
print(identifiability_report(model, params, k, w))
IdentifiabilityReport
=====================
Model:            SVI
Quotes:           15 on k in [-0.050, +0.050]
Fit RMSE (w):     2.397e-04
Condition number: 1.1e+05 (column-scaled Jacobian)

  param             value      std err   rel err
  a               0.01144         3.02 26390.6%  <-- poorly identified
  b               0.12117         7.55  6231.4%  <-- poorly identified
  ...

Near-degenerate pairs (|corr| > 0.95):
  a ~ b: corr = -1.000
  ...

Overall: ATTENTION: parameters are not individually trustworthy
(the fitted smile may still be excellent)

A parameter is flagged when its standard error exceeds rel_threshold (default 0.5) of its magnitude; a pair when their correlation exceeds corr_threshold (default 0.95) in absolute value — the optimizer can trade one against the other with almost no change to the fitted smile. report.ok summarizes; every field is individually accessible.

The machinery

  • model.param_jacobian(k, params) — the \(n \times p\) sensitivity matrix \(\partial w(k_i)/\partial \theta_j\) over the model’s free_params (per-slice givens such as \(\theta\), \(T\), \(F\), or a fixed \(\beta\) are not free). Analytic for raw SVI, natural SVI, and SSVI; central finite differences elsewhere.

  • condition_number(J) — of the column-scaled Jacobian: how close the parameter directions are to collinear at the quotes.

  • parameter_uncertainty(model, params, k, w) — the Gauss-Newton covariance \(\hat\sigma^2 (J^\top J)^+\) at the optimum, with \(\hat\sigma^2 = \mathrm{RSS}/(n - p)\) in total-variance space, reported as standard errors and a correlation matrix. With \(n \le p\) the fit is under-determined and every standard error is infinite.

Quote-to-surface sensitivities

The same Gauss-Newton system answers the trader’s question directly: if this quote moves one vol point, what does the surface do?

from pysvi import quote_sensitivity, surface_sensitivity, iv_surface_sensitivity

S_theta = quote_sensitivity(model, params, k, w)            # dtheta/dquote, p x n
S_w     = surface_sensitivity(model, params, k, w, k_eval)  # dw(k_eval)/dquote
S_iv    = iv_surface_sensitivity(model, params, k, w, k_eval, T)  # vol-in, vol-out

These are implicit-function Jacobians at the optimum — (J^T J)^+ J^T propagated through dw/dtheta — so one linear solve replaces a recalibration per bump: hedging, P&L explain and scenario responses in vectorized form. Valid to first order; verified against bump-and-recalibrate in the test suite.

What to do with it

  • Widening the quoted strike range shrinks the uncertainties — often dramatically; the standard errors tell you whether today’s chain supports the parameter you care about.

  • If a signal needs stable parameters, prefer the well-identified combinations (e.g. ATM level and skew, which are read off the smile directly) over raw parameters, or switch to a lower-dimensional model (SSVI’s two shape parameters identify far more sharply than raw SVI’s five).

  • The condition number is a fast health check inside pipelines; the full report is for the post-mortem.