Recipe for simulating Volterra processes with causal structure that reproduce fractional Brownian motion covariance for any Hurst parameter \( H \in (0,1) \).
The Volterra process provides a causal representation of fractional Brownian motion (fBM) with the same covariance structure. Instead of directly constructing the covariance matrix, we use a causal kernel \( K(t,s) \) that depends only on past times \( s \leq t \). The continuous-time Volterra process is defined as:
$$
X(t) = \int_0^t K(t,s) \, dW(s)
$$
where \( K(t,s) \) is the Volterra kernel that reproduces fBM covariance:
$$
K(t,s) = \sqrt{c_H} \cdot (t-s)^{H-1/2}, \quad c_H = \frac{\Gamma(2H+1)}{2\Gamma(H+1/2)^2}
$$
This kernel works for all \( H \in (0,1) \) and provides the correct causal structure that makes the process naturally adapted to its filtration.
It may follow more naturally to think of the Volterra representation similarly to the Karhunen-Loève expansion where, in the discrete sense, the covariance matrix is decomposed and used to weight a Gaussian vector; we may think of the Volterra kernel as the decompsed covariance matrix weighting the Gaussian vector.
To discretize the Volterra process, we approximate the integral using a quadrature rule. For a time grid \( \{t_i\}_{i=0}^n \), the discrete Volterra process becomes: $$ X(t_i) = \sum_{j=0}^i K(t_i, t_j) \Delta W_j $$ where \( \Delta W_j = W(t_j) - W(t_{j-1}) \) are independent Gaussian increments and \( K(t_i, t_j) = 0 \) for \( j > i \) (causality).
import numpy as np
from scipy.special import gamma
def volterra_kernel(t, s, H):
"""Compute the Volterra kernel K(t,s) for fBM."""
if s >= t:
return 0.0
dt = t - s
if H == 0.5:
return 1.0 # Standard Brownian motion
else:
c_H = gamma(2*H + 1) / (2 * gamma(H + 0.5)**2)
return np.sqrt(c_H) * (dt**(H-0.5))
def volterra_process_discrete(steps=100, H=0.5, paths=1, T=1):
"""Simulate Volterra process with causal structure."""
t = np.linspace(0, T, steps)
dt = T / (steps - 1)
# Initialize arrays
X = np.zeros((paths, steps))
dW = np.random.normal(0, np.sqrt(dt), (paths, steps))
# Compute Volterra kernel matrix (lower triangular)
K = np.zeros((steps, steps))
for i in range(steps):
for j in range(i + 1):
K[i, j] = volterra_kernel(t[i], t[j], H)
# Generate paths using causal structure
for p in range(paths):
for i in range(steps):
X[p, i] = np.sum(K[i, :i+1] * dW[p, :i+1])
return X, t
Summary: This approach produces the same covariance as fBM but with explicit causal structure, making it naturally adapted to its filtration and suitable for applications requiring causality.
import numpy as np\nfrom scipy.special import gamma\n\ndef volterra_kernel(t, s, H):\n if s > t: return 0.0\n dt = t - s\n return (dt**(H-0.5)) / gamma(H + 0.5)\n\ndef volterra_process_discrete(steps=100, H=0.5, paths=1):\n t = np.linspace(0, 1, steps)\n dt = 1 / (steps - 1)\n X = np.zeros((paths, steps))\n dW = np.random.normal(0, np.sqrt(dt), (paths, steps))\n K = np.zeros((steps, steps))\n for i in range(steps):\n for j in range(i + 1):\n K[i, j] = volterra_kernel(t[i], t[j], H)\n for p in range(paths):\n for i in range(steps):\n X[p, i] = np.sum(K[i, :i+1] * dW[p, :i+1])\n return X, t
This note discusses the necessary steps to simulate a stochastic process with a desired covariance structure. In this context, I outline the Karhunen–Loéve theorem and provide intuition and general recipes to decompose a stochastic process by establishing the integral eigenvalue problem (continuous-time) or, equivalently, the covariance matrix diagonalization problem (discrete-time). I then apply these general recipes to Brownian motion, Brownian bridge, and fractional Brownian motion to numerically simulate paths.