Core mechanism
The model combines continuous diffusion with random jump arrivals and jump sizes. Smoothing or another suitable estimator gives a stable sensitivity estimate where a raw discontinuous payoff would produce unstable pathwise results.
TeaVM Java simulates Merton lognormal jumps against the exact Poisson-series call price, then compares Black-Scholes, Merton and Kou prices and implied vols across a strike smile.
Kou smile inputs remain the original illustrative values: intensity 1, up probability 0.4, eta-up 10 and eta-down 7. The source uses seeded common random numbers across strike rows.
This exact source runs in TeaVM. Form changes update its Java literals and reset manual edits.
import com.nablatensor.quant.OptionTypeEnum;
import com.nablatensor.quant.analytic.CostOfCarry;
import java.util.Random;
public final class JumpDiffusionRiskStudio {
private static final double S = 100.00000000, K = 100.00000000, VOL = 0.15000000000, RATE = 0.030000000000,
T = 0.50000000000;
private static final double LAMBDA = 1.2000000000, JMEAN = -0.090000000000, JVOL = 0.18000000000;
private static final int STEPS = 20;
private static final long PATHS = 20000L, SEED = 42L;
public static void main(String[] args) {
double mc = priceMerton(K, SEED), exact = exactMerton(K);
System.out.println("RESULT|" + mc + "|" + exact + "|" + Math.abs(mc - exact));
int[] ks = {
80, 90, 100, 110, 120
};
for (int k : ks) {
double bs = CostOfCarry.price(OptionTypeEnum.CALL, S, k, T, RATE, RATE, VOL), m = priceMerton(k,
SEED + 7), q = priceKou(k, SEED + 7);
System.out.println("ROW|" + k + "|" + bs + "|" + m + "|" + q + "|" + implied(m, k) + "|" + implied(q,
k));
}
}
private static double priceMerton(double strike, long seed) {
Random random = new Random(seed);
double dt = T / STEPS, kappa = Math.exp(JMEAN + .5 * JVOL * JVOL) - 1, disc = Math.exp(-RATE * T),
sum = 0;
for (long p = 0; p < PATHS; p++) {
double s = S;
for (int t = 0; t < STEPS; t++) {
double z = random.nextGaussian(), u = random.nextDouble(), zj = random.nextGaussian(), prob = LAMBDA * dt,
jump = 1 / (1 + Math.exp((u - prob) / 2e-4));
double drift = (RATE - .5 * VOL * VOL) * dt - Math.log(1 + LAMBDA * kappa * dt);
double diffused = s * Math.exp(drift + VOL * Math.sqrt(dt) * z);
s = diffused * (1 + jump * (Math.exp(JMEAN + JVOL * zj) - 1));
}
sum += Math.max(s - strike, 0);
}
return disc * sum / PATHS;
}
private static double exactMerton(double strike) {
double kappa = Math.exp(JMEAN + .5 * JVOL * JVOL) - 1, lp = LAMBDA * (1 + kappa), lt = lp * T,
w = Math.exp(-lt), cum = 0, sum = 0;
for (int n = 0; n < 256; n++) {
double sn = Math.sqrt(VOL * VOL + n * JVOL * JVOL / T), rn = RATE - LAMBDA * kappa + n * Math.log(1
+ kappa) / T;
sum += w * CostOfCarry.price(OptionTypeEnum.CALL, S, strike, T, rn, rn, sn);
cum += w;
if (cum > 1 - 1e-14 && n > 0) break;
w *= lt / (n + 1);
}
return sum;
}
private static double priceKou(double strike, long seed) {
double pUp = .4, etaUp = 10, etaDown = 7, lambda = 1, kappa = pUp * etaUp / (etaUp - 1) + (1 - pUp) * etaDown / (etaDown
+ 1) - 1, dt = T / STEPS, disc = Math.exp(-RATE * T), sum = 0;
Random random = new Random(seed);
for (long p = 0; p < PATHS; p++) {
double s = S;
for (int t = 0; t < STEPS; t++) {
double z = random.nextGaussian(), uj = random.nextDouble(), us = random.nextDouble(), um = Math.min(1 - 1e-15,
random.nextDouble()), jump = 1 / (1 + Math.exp((uj - lambda * dt) / 2e-4)), isUp = 1 / (1
+ Math.exp((us - pUp) / 1e-3)), mag = -Math.log(1 - um), logJump = isUp * mag / etaUp
+ (1 - isUp) * (-mag / etaDown);
double drift = (RATE - .5 * VOL * VOL) * dt - Math.log(1 + lambda * kappa * dt), diffused = s * Math.exp(drift
+ VOL * Math.sqrt(dt) * z);
s = diffused * (1 + jump * (Math.exp(logJump) - 1));
}
sum += Math.max(s - strike, 0);
}
return disc * sum / PATHS;
}
private static double implied(double price, double strike) {
double lo = 1e-4, hi = 3;
for (int j = 0; j < 80; j++) {
double mid = .5 * (lo + hi), v = CostOfCarry.price(OptionTypeEnum.CALL, S, strike, T, RATE,
RATE, mid);
if (v > price) hi = mid;
else lo = mid;
}
return .5 * (lo + hi);
}
}
A jump model improves tail and discontinuity representation, but makes a naive pathwise derivative unreliable at the discontinuity.
The model combines continuous diffusion with random jump arrivals and jump sizes. Smoothing or another suitable estimator gives a stable sensitivity estimate where a raw discontinuous payoff would produce unstable pathwise results.
Calibrate jump parameters from appropriate market evidence, test pricing and sensitivity stability over smoothing choices, and compare against alternate estimators or controlled bump runs.
*Keywords: merton jump diffusion java, kou double exponential java, jump diffusion monte carlo java, volatility smile jumps java, levy model java*
Feature F7. A diffusion alone cannot fit the short-dated volatility smile — the market prices in the possibility of a gap. These two step blocks add a compound-Poisson jump to geometric Brownian motion.
Both are Seam-5 step blocks with a static european(type, maturity, steps) factory, priced through Nabla.model(market, valuation) like every other model.
Each step draws a diffusion normal and, from a uniform, a smoothed at-most-one-jump indicator 1{U < lambda dt} (nablatensor-ops Smooth.lt). P(>= 2 jumps per step) is O((lambda dt)^2), so this is exact as the monitoring grid gets fine. The jump *count* is not differentiated; the jump-size parameters (jumpMean, jumpVol, or probUp, etaUp, etaDown) and the drift compensator are, so one adjoint sweep returns the jump-parameter risk alongside the spot / vol / rate Greeks.
The compensator is -ln(1 + lambda kappa dt) (not -lambda kappa dt): the at-most-one-jump factor has expectation 1 + lambda kappa dt, so this form makes the discounted spot an exact per-step martingale. A residual O(0.1%) bias from the smoothed indicator is visible only in a high-path put-call-parity check.
Variance-Gamma and Bates (Heston + jumps) need a recordable Gamma subordinator and a Heston-composed step respectively; both are follow-ups.
Smoothing introduces controlled bias; it is a numerical trade-off that must be measured, documented and judged for the intended use.