Skip to content
All library documents

Fitting an SVI Implied Volatility Curve to Market Data

Article Quant Q&A · Author: User2089

Summary

The document presents an attempt to calibrate the raw SVI parameterization to market implied volatilities. The code converts quoted volatilities to total implied variance, computes model variance at log-moneyness values, and minimizes the root sum of squared residuals. It uses SciPy's differential evolution optimizer with parameter bounds, then plots the fitted curve against the market observations.

The reported fit is unsatisfactory, and the answer suggests trying BFGS through SciPy's minimize function. The text offers no diagnosis of the poor fit, convergence evidence, or comparison of resulting parameter values. Its suggested call also does not clearly show how an initial parameter vector should be supplied to BFGS, so the advice is not a complete reproducible calibration procedure. Calibration quality and parameter constraints remain important considerations.

Key ideas

  • The code calibrates SVI by minimizing residuals between market and model total implied variance.
  • Quoted implied volatilities are converted to total variance using the time to maturity.
  • Differential evolution is used with bounds, but the author reports a curve that does not fit the observations.
  • The answer suggests trying BFGS, without providing a complete setup or evidence that the alternative resolves the problem.

Tags

Full text
# SVI calibration


# SVI calibration












I am trying to calibrate SVI model using the following code

```
import numpy as np
from scipy import optimize
from matplotlib import pyplot as plt
spot = 1.3444
forward = 1.342782
t = 30 / 365.0
vols = np.array([9.420, 9.772, 9.237, 10.144, 9.196, 10.724, 9.265, 11.161, 9.390, 11.908, 9.751]) / 100
strikes = np.array([1.34342148, 1.35800697, 1.32950654, 1.37006384, 1.31948358, 1.38700437, 1.30670715,
                    1.39978993, 1.29765089, 1.42124726, 1.28287975])
total_implied_variance = t * vols ** 2

def sviraw(k, param):
    a = param[0];
    b = param[1];
    m = param[2];
    rho = param[3];
    sigma = param[4];

    totalvariance = a + b * (rho * (k - m) + np.sqrt((k - m)** 2 + sigma**2));
    return totalvariance

def targetfunction(x):
    value=0
    for i in range(11):
        model_total_implied_variance = sviraw(np.log(strikes[i] / forward), x);
        value =value+(total_implied_variance[i]  - model_total_implied_variance) ** 2;
    return value**0.5

bound = [(1e-5, max(total_implied_variance)),(1e-3, 0.99),(min(strikes), max(strikes)),(-0.99, 0.99),(1e-3, 0.99)]
result = optimize.differential_evolution(targetfunction,bound,tol=1e-8)
x=result.x

K = np.linspace(-0.5, 0.5, 60)

newVols = [np.sqrt(sviraw(logmoneyness, x)/t) for logmoneyness in K]
plt.plot(np.log(strikes / forward), vols, marker='o', linestyle='none', label='market')
plt.plot(K, newVols, label='SVI')
plt.title("vol curve")

plt.grid()
plt.legend()
plt.show()
```

But I am getting the following curve that does not fit the input.

## Answer by oronimbus (score 2)

https://quant.stackexchange.com/a/74336

I'd probably use a different solver, e.g. use BFGS in `scipy`:

```
result = optimize.minimize(targetfunction, bound, tol=1e-8, method="BFGS")
```

That seems to do the job. I don't know much about `scipy.optimize.differential_evolution` but it seems to fail minimising your loss function.

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.