Skip to content
All library documents

Calibrating a Heston Model to Option Prices with SLSQP

Article Quant Q&A · Author: DiracsCallOption

Summary

The document presents an attempt to fit Heston stochastic-volatility parameters to a grid of market call prices using SciPy optimization. It includes a characteristic-function-based pricing routine and loops across strikes and maturities to produce model prices. The initial calibration attempt fails, and the author reports later getting a version working with SciPy’s minimize function and the SLSQP method.

The revised objective compares model and market prices using relative pricing errors, squares the errors, and passes the Heston parameters as the optimization vector with bounds. The example illustrates the general structure of a calibration objective and constrained optimization setup, but it does not explain the original tuple/list exception or provide a diagnosis of the fix. The code also leaves important validation questions unanswered, including optimizer convergence quality, parameter identifiability, numerical integration accuracy, and whether the chosen error metric is appropriate. No calibration results or performance assessment are reported, so this is an implementation example rather than evidence of a reliable fit.

Key ideas

  • Heston calibration can be framed as minimizing discrepancies between model prices and observed option prices.
  • The example uses SLSQP with bounded parameters through SciPy’s minimize interface.
  • A relative price-error objective can prevent large nominal prices from dominating the fit.
  • Price calculations are repeated across a grid of strikes and maturities.
  • The author reports success but does not explain the original error or validate convergence and pricing accuracy.

Tags

Full text
# SciPy Calibrating Heston call option


# SciPy Calibrating Heston call option












I have been attempting to calibrate my Heston model, but I am running into issues with scipy.optimize module. I have tried various scipy optimizers, but they all return the error "TypeError: can only concatenate tuple (not "list") to tuple". Does anyone know how to resolve this issue? Thank you for your time.

update: sorry if the code is a bit long was not aware.

```
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from numba import jit, njit,float64
from scipy.optimize import fmin_slsqp

i=complex(0,1)
sigma, kappa, theta, volvol, rho = 0.1, 0.1, 0.1, 0.1, 0.1
params=[sigma, kappa, theta, volvol, rho]
strikes=[4650,4655,4660,4665,4670]
maturities=[1/48,2/48,3/48,1/12,5/48]
marketPrices=[70.00,66.70,63.50,60.35,57.30,82.50,79.20,76.0,72.80,69.70,92.65,89.35,86.10,82.90,79.75,101.60,98.30,95.10,91.90,88.75,109.85,106.60,103.35,100.20,97.00]
marketPrices=np.array(marketPrices)
rates=[0.05,0.05,0.05,0.05,0.05]
St=4662
np.shape(marketPrices)

@jit
def fHeston(s, St, K, r, T, sigma, kappa, theta, volvol, rho):
    # To be used a lot
    prod = rho * sigma *i *s 
    
    # Calculate d
    d1 = (prod - kappa)**2
    d2 = (sigma**2) * (i*s + s**2)
    d = np.sqrt(d1 + d2)
    
    # Calculate g
    g1 = kappa - prod - d
    g2 = kappa - prod + d
    g = g1/g2
    
    # Calculate first exponential
    exp1 = np.exp(np.log(St) * i *s) * np.exp(i * s* r* T)
    exp2 = 1 - g * np.exp(-d *T)
    exp3 = 1- g
    mainExp1 = exp1 * np.power(exp2/ exp3, -2 * theta * kappa/(sigma **2))
    
    # Calculate second exponential
    exp4 = theta * kappa * T/(sigma **2)
    exp5 = volvol/(sigma **2)
    exp6 = (1 - np.exp(-d * T))/(1 - g * np.exp(-d * T))
    mainExp2 = np.exp((exp4 * g1) + (exp5 *g1 * exp6))
    
    return (mainExp1 * mainExp2)

@jit(forceobj=True)
def priceHestonMid(St, K, r, T, sigma, kappa, theta, volvol, rho):
    P, iterations, maxNumber = 0,1000,100
    ds = maxNumber/iterations
    
    element1 = 0.5 * (St - K * np.exp(-r * T))
    
    # Calculate the complex integral
    # Using j instead of i to avoid confusion
    for j in range(1, iterations):
        s1 = ds * (2*j + 1)/2
        s2 = s1 - i
        
        numerator1 = fHeston(s2,  St, K, r, T, sigma, kappa, theta, volvol, rho)
        numerator2 = K * fHeston(s1,  St, K, r, T, sigma, kappa, theta, volvol, rho)
        denominator = np.exp(np.log(K) * i * s1) *i *s1
        
        P = P + ds *(numerator1 - numerator2)/denominator
    
    element2 = P/np.pi
    
    return np.real((element1 + element2))

# vectorify 
def strikematurePriceHestonMid(St, W, r, Q, sigma, kappa, theta, volvol, rho):
    stuff=[]
    volsur=[]
    e=0
    p=0
    for p in range(5):
        for e in range(5):
            stuff.append(priceHestonMid(St, W[e], r, Q[p], sigma, kappa, theta, volvol, rho))
            
            #volsur[e][p]=stuff[4*p::4*p+4]
            #print(priceHestonMid(St, W[e], r, Q[p], sigma, kappa, theta, volvol, rho))
    
    volsur=np.reshape(stuff,(5,5))
    stuff=np.array(stuff)
    return stuff

def calibratorHeston(St, initialValues = [0.5,0.5,0.5,0.5,-0.5], 
                              lowerBounds = [1e-2,1e-2,1e-2,1e-2,-1], 
                              upperBounds = [10,10,10,10,0]):

    objectiveFunctionHeston = ((marketPrices) - (strikematurePriceHestonMid(St, strikes,  
                                                                        rates[0], 
                                                                        maturities, 
                                                                        sigma,                         
                                                                        kappa,
                                                                        theta,
                                                                        volvol,
                                                                        rho))).sum()
        
    result=fmin_slsqp(objectiveFunctionHeston,initialValues,args=params)   
        
    return result
        
calibratorHeston(4662)
```

FINAL UPDATE: I got it working although I am still not sure why it was not working before. Thanks for help.

```
from scipy.optimize import minimize

def objectiveFunctionHeston(x,St, strikes,rates, maturities):

    objective = ((marketPrices)-(strikematurePriceHestonMid(St, strikes,  
                                                                        rates, 
                                                                       maturities, 
                                                                        sigma=x[0],                         
                                                                        kappa=x[1],
                                                                        theta=x[2],
                                                                        volvol=x[3],
                                                                        rho=x[4])))/marketPrices
    objective=np.square(np.dot(objective,objective))
    return objective

bounds=((1e-2,5),(1e-2,8),(1e-2,10),(1e-2,10),(-1,1))
res = minimize(objectiveFunctionHeston, method='SLSQP',  x0=[sigma, kappa, theta, volvol, rho],args=(St,strikes,rates[0],maturities),
                  bounds = bounds, tol=1e-20, 
                  options={"maxiter":1000})
print(res)
```

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.