Skip to content

How the Heston Model’s Parameters Affect Paths and Option Prices

Original question

Can you walk me through implementing the Heston stochastic volatility model and explain the role of each parameter?
Simulate the risk-neutral log-stock and its square-root variance together using correlated Brownian shocks. Specify the initial variance v0v_0, mean-reversion speed κ\kappa, long-run variance θ\theta, volatility of variance ξ\xi, and stock–variance correlation ρ\rho. A full-truncation Euler scheme is a practical first implementation for learning. Check it against the semi-analytic European option price before relying on it.

Five parameters in the variance equation

With zero dividends, under the pricing measure,
dlog⁡St=(r−vt/2) dt+vt dWtS,dvt=κ(θ−vt) dt+ξvt dWtv,d[WS,Wv]t=ρ dt.d\log S_t=(r-v_t/2)\,dt+\sqrt{v_t}\,dW^S_t,\qquad dv_t=\kappa(\theta-v_t)\,dt+\xi\sqrt{v_t}\,dW^v_t, \qquad d[W^S,W^v]_t=\rho\,dt.

Use independent normal shocks ZvZ_v and Z⊥Z_\perp, then set ZS=ρZv+1−ρ2Z⊥Z_S=\rho Z_v+\sqrt{1-\rho^2}Z_\perp. At each step use v+=max⁡(v,0)v^+=\max(v,0) inside drift and diffusion terms. The stored Euler variance can become negative, so use its positive part at the next update too. The continuous square-root process is nonnegative under admissible parameters, and the Feller condition 2κθ≥ξ22\kappa\theta\ge\xi^2 ensures the zero boundary is unattainable when started positive. This property of the continuous model does not prevent a finite Euler step from overshooting.

It helps to perturb one parameter at a time. Increasing v0v_0 changes today's instantaneous variance, while changing θ\theta moves the level the variance drifts towards. Raising κ\kappa brings that long-run level into the forecast faster, in either direction. A larger ξ\xi increases the spread of possible future variance paths. A negative ρ\rho tends to pair negative stock shocks with positive variance shocks. Because these effects interact in prices, compare a variance-path moment before using a single option price to diagnose the implementation.

In a simulation, record how often the variance update would cross zero and how the chosen scheme handles it. The variance diffusion scales as vt\sqrt{v_t}, so a discrete Euler step can overshoot the boundary even when the continuous process stays nonnegative. Full truncation controls where you take the square root and how the drift is evaluated. It does not remove all finite-step bias. To check whether a discrepancy comes from the numerical scheme, refine the step size with comparable sampling error before changing the calibrated parameters.

Two benchmarks for your simulation

Let v0=0.06v_0=0.06, θ=0.04\theta=0.04, and κ=2\kappa=2 per year. The conditional mean variance at six months is 0.04+(0.06−0.04)e−1≈0.047360.04+(0.06-0.04)e^{-1}\approx0.04736. Simulate and compare the sample mean with this benchmark. Next compare the simulated European call and its Monte Carlo standard error with a characteristic-function price. Keeping nn large while halving the time step helps isolate discretisation bias from sampling noise. Also check whether the sample mean discounted terminal stock is close to S0S_0 under the selected parameters.

Full-truncation Euler in Python

import numpy as np rng = np.random.default_rng(29) n, steps = 60_000, 128 spot, strike, rate, maturity = 100, 100, 0.03, 0.5 v0, kappa, theta, xi, rho = 0.06, 2.0, 0.04, 0.30, -0.65 dt = maturity / steps log_s = np.full(n, np.log(spot)) variance = np.full(n, v0) for _ in range(steps): zv, z_independent = rng.standard_normal((2, n)) zs = rho * zv + np.sqrt(1 - rho**2) * z_independent v_pos = np.maximum(variance, 0) diffusion = np.sqrt(v_pos * dt) log_s += (rate - v_pos / 2) * dt + diffusion * zs variance += kappa * (theta - v_pos) * dt + xi * diffusion * zv pv = np.exp(-rate * maturity) * np.maximum(np.exp(log_s) - strike, 0) print("price, standard error", pv.mean(), pv.std(ddof=1) / np.sqrt(n))
The volatility surface tool lets you explore the shape a model would need to fit.

References and next steps