Quick Start
Basic usage
import numpy as np
from jump_diffusion import JumpDiffusionSimulator, JumpDiffusionEstimator
# Create simulator
simulator = JumpDiffusionSimulator(
mu=0.05, # 5% annual drift
sigma=0.2, # 20% annual volatility
jump_prob=0.1, # 10% jump probability per period
jump_scale=0.15, # jump magnitude scale
jump_skew=2.0 # positive skewness
)
# Simulate a path
times, path, jumps = simulator.simulate_path(T=1.0, n_steps=252)
# Estimate parameters
increments = np.diff(path)
dt = times[1] - times[0]
estimator = JumpDiffusionEstimator(increments, dt)
results = estimator.estimate()
print(f"Estimated drift: {results['parameters']['mu']:.4f}")
print(f"Estimated volatility: {results['parameters']['sigma']:.4f}")
Using a different jump distribution
Jumps follow a skew-normal distribution by default. Other distributions can
be plugged in via jump_distribution, both when simulating and when
estimating:
from jump_diffusion.distributions import SGEDJump
simulator = JumpDiffusionSimulator(
mu=0.05, sigma=0.2, jump_prob=0.1,
jump_distribution=SGEDJump(),
jump_loc=0.0, jump_scale=0.15, jump_nu=1.5, jump_xi=2.0,
)
times, path, jumps = simulator.simulate_path(T=1.0, n_steps=252)
increments = np.diff(path)
dt = times[1] - times[0]
estimator = JumpDiffusionEstimator(increments, dt, jump_distribution=SGEDJump())
results = estimator.estimate()
Distributions without a known closed-form likelihood (like SGED) fall back
to a generic FFT-based convolution to approximate the mixture density, so
adding a new distribution only requires implementing its pdf – see
API Reference and the “Adding New Distributions” section of
CONTRIBUTING.md.
Robust estimation with differential evolution
The default L-BFGS-B optimizer needs a reasonable initial guess: on
harder mixture likelihoods (SGED in particular, and often on real market
data) it can stall at a poor local solution and report
convergence: False. Differential evolution – the applied finding of
the thesis this library is based on (Ospina Arango, 2009) – searches a
data-driven box globally instead, needing no initial guess at all:
estimator = JumpDiffusionEstimator(increments, dt, jump_distribution=SGEDJump())
results = estimator.estimate(method="differential_evolution", seed=42)
The defaults port the thesis configuration (strategy rand/1, the same
population sizing as R’s DEoptim runs, up to 400 generations with
early stopping on convergence). It costs thousands of likelihood
evaluations, so expect seconds instead of milliseconds; pass seed= for
reproducibility, and see
estimate() for the
knobs (maxiter, popsize, tol, bounds …).
Comparing jump distributions
JumpDistributionComparison fits several
candidate jump distributions to the same data and ranks them by AIC/BIC plus
a simulation-based Kolmogorov-Smirnov test:
from jump_diffusion.distributions import (
KouJump,
NormalJump,
SGEDJump,
SkewNormalJump,
StudentTJump,
)
from jump_diffusion.validation import JumpDistributionComparison
comparison = JumpDistributionComparison(increments, dt)
comparison.fit("Normal", NormalJump())
comparison.fit("SkewNormal", SkewNormalJump())
comparison.fit("SGED", SGEDJump())
comparison.fit("Kou", KouJump())
comparison.fit("StudentT", StudentTJump())
print(comparison.compare()) # ranked by AIC, includes KS statistic/p-value
comparison.plot_comparison()
More examples
Ready-to-run scripts and notebooks live in the examples/ and
notebooks/ directories of the repository – see the README for an
annotated list.