Portfolio & Market Risk

Compute VaR and Expected Shortfall Three Ways

Java compares delta-normal and delta-gamma Cornish-Fisher VaR with empirical VaR and Expected Shortfall from a simulated quadratic P&L series using the displayed factor sensitivities, vols, correlations, confidence and seed.

Sensitivities (dP/dx per risk factor)
Daily volatility
CorrelationOptional
VaR settingsOptional

Historical losses are simulated from the same quadratic P&L model with Java's seeded random generator.

Java source
VarEsRiskStudio.java

This exact source runs in TeaVM. Form changes update its Java literals and reset manual edits.

import com.nablatensor.risk.PnlVector;
import com.nablatensor.risk.ValueAtRisk;
import java.util.Random;

public final class VarEsRiskStudio {
  private static final double[][] GAMMA = {
    {
      -9.0e6, 0, 0
    }, {
      0, -4.0e8, 0
    }, {
      0, 0, -1.5e6
    }
  };
  private VarEsRiskStudio() {}

  public static void main(String[] args) {
    double[] delta = {
      2400000, -18000000, 1100000
    };
    double[] vol = {
      1.1 / 100.0, 0.017 / 100.0, 0.5 / 100.0
    };
    double[][] corr = {
      {
        1.0, -0.15, 0.25
      }, {
        -0.15, 1.0, -0.1
      }, {
        0.25, -0.1, 1.0
      }
    };
    double[][] cov = new double[3][3];
    for (int a = 0; a < 3; a++) for (int b = 0; b < 3; b++) cov[a][b] = corr[a][b] * vol[a] * vol[b];
    double alpha = 99 / 100.0;
    double dn1 = ValueAtRisk.deltaNormal(delta, cov, alpha, 1.0), dn10 = ValueAtRisk.deltaNormal(delta,
        cov, alpha, 10.0);
    double dg1 = ValueAtRisk.deltaGammaCornishFisher(delta, GAMMA, cov, alpha, 1.0);
    var cumulants = ValueAtRisk.quadraticCumulants(delta, GAMMA, cov);
    PnlVector sample = simulate(4000, 42L, delta, cov);
    double hvar = ValueAtRisk.historical(sample, alpha), hes = ValueAtRisk.expectedShortfall(sample,
        alpha);
    System.out.println("RESULT|" + dn1 + "|" + dn10 + "|" + dg1 + "|" + cumulants.skewness() + "|"
        + cumulants.excessKurtosis() + "|" + hvar + "|" + hes);
  }

  private static PnlVector simulate(int n, long seed, double[] delta, double[][] cov) {
    double[][] l = new double[3][3];
    for (int a = 0; a < 3; a++) for (int b = 0; b <= a; b++) {
      double q = cov[a][b];
      for (int k = 0; k < b; k++) q -= l[a][k] * l[b][k];
      l[a][b] = a == b ? Math.sqrt(q) : q / l[b][b];
    }
    Random rng = new Random(seed);
    double[] pnl = new double[n];
    for (int t = 0; t < n; t++) {
      double[] z = {
        rng.nextGaussian(), rng.nextGaussian(), rng.nextGaussian()
      }, x = new double[3];
      for (int a = 0; a < 3; a++) for (int b = 0; b <= a; b++) x[a] += l[a][b] * z[b];
      double linear = 0, quadratic = 0;
      for (int a = 0; a < 3; a++) {
        linear += delta[a] * x[a];
        for (int b = 0; b < 3; b++) quadratic += 0.5 * GAMMA[a][b] * x[a] * x[b];
      }
      pnl[t] = linear + quadratic;
    }
    return new PnlVector(pnl);
  }
}
TeaVM compiles and runs the Java source above in this browser.
Implementation guide

Loss quantiles and tail averages

VaR answers a quantile question; expected shortfall answers the severity question beyond that quantile.

Core mechanism

Historical methods resample observed returns, parametric methods impose a distributional approximation, and Monte Carlo simulates a chosen process. Expected shortfall averages losses in the selected tail rather than discarding their magnitude.

Practical workflow

Set horizon, confidence level, P&amp;L definition and data-cleaning policy; compare approaches; then test stability and explain differences in tails, correlations and distribution assumptions.

Key details

*Keywords: value at risk java, expected shortfall java, delta normal var java, delta gamma cornish fisher java, historical simulation var java, var backtest kupiec christoffersen java*

Feature F3. The pieces were already in the box — a scenario DSL that moves a compiled kernel and re-prices, and an adjoint sweep that returns the sensitivity vector — so this is the thin estimator and backtest layer that turns them into VaR / ES numbers.

Every figure is a positive loss at confidence alpha (e.g. 0.99). Multi-day numbers scale the one-day standard deviation by sqrt(horizonDays).

quadraticCumulants(delta, gamma, sigma) diagonalises Sigma^{1/2} Gamma Sigma^{1/2} (reusing the Jacobi eigensolver from F4's Pca), which turns the quadratic form into a sum of independent a_i y_i + 0.5 b_i y_i^2 terms. The first four cumulants then have closed forms, and the loss quantile comes from a fourth-order Cornish-Fisher expansion. With Gamma = 0 the skew and kurtosis vanish and the result is identical to deltaNormal — a test pins that.

The chi-square reference distributions for one and two degrees of freedom have closed-form survival functions (2 (1 - Phi(sqrt s)) and exp(-s/2)), so there is no incomplete-gamma dependency.

Scope and review point

The three methods are illustrative alternatives, not interchangeable production standards. Data history, liquidity, non-linearity and stressed periods matter materially.