Volterra Process

Volterra Process

Recipe for simulating Volterra processes with causal structure that reproduce fractional Brownian motion covariance for any Hurst parameter \( H \in (0,1) \).

Simulating Volterra Processes

Discrete-time Recipe for Volterra Processes

Intuition

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.

Discretization

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).

General Recipe

  1. Choose the Hurst parameter \( H \in (0,1) \).
  2. Define the time grid \( \{t_i\}_{i=0}^n \) with uniform spacing \( \Delta t \).
  3. Compute the Volterra kernel \( K(t_i, t_j) \) for all \( i \geq j \).
  4. Generate independent Gaussian increments \( \Delta W_j \sim N(0, \Delta t) \).
  5. Compute the process recursively: \( X(t_i) = \sum_{j=0}^i K(t_i, t_j) \Delta W_j \).

Python Implementation

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.

Simulation with Causal Structure

Sample Paths

Volterra Kernel

Iterative Construction

Gaussian Noise

Causal Structure

Covariance vs fBM

Volterra Process Equations

$$ X(t) = \int_0^t K(t,s) \, dW(s), \quad K(t,s) = \frac{(t-s)^{H-1/2}}{\Gamma(H+1/2)} $$ $$ \text{Discrete: } X(t_i) = \sum_{j=0}^i K(t_i, t_j) \Delta W_j $$
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

Relevant Articles

Recipes for simulating stochastic processes

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.

View on SSRN
Suggested Citation:
Paolucci, Roman, Recipes for simulating stochastic processes (Fractional Brownian motion) (June 30, 2025). Available at SSRN: https://papers.ssrn.com/sol3/papers.cfm?abstract_id=5332011