kacem.dev
← FIN · Quant finance

market-risk-quant: three VaR methods on one bookDraft

A Value-at-Risk platform that runs Parametric, Monte Carlo and Historical Simulation over the same positions in the same job, reports Expected Shortfall alongside, and treats the disagreement between methods as the output.

2 min projectriskvarjavaspark

Portfolio risk is usually computed by one method, chosen once, and trusted forever. market-risk-quant runs Parametric, Monte Carlo and Historical Simulation VaR over the same positions in the same job, so the disagreement between them is a first-class output rather than a quarterly surprise. Expected Shortfall comes out of the same aggregation step.

Source: github.com/kacemjd/market-risk-quant. Status: active.

The shape of it

flowchart LR
  subgraph in["Triggers"]
    REST["REST · 202 + correlation id"]
    KAFKA["Kafka topic"]
    CRON["Cron · weekdays 18:00"]
  end
  REST --> WF["workflow"]
  KAFKA --> WF
  CRON --> WF
  WF --> SPARK["Spark simulation"]
  SPARK --> PARAM["Parametric"]
  SPARK --> MC["Monte Carlo"]
  SPARK --> HIST["Historical"]
  PARAM --> AGG["VaR / ES aggregation"]
  MC --> AGG
  HIST --> AGG
  AGG --> QDB[("QuestDB")]

Scenario runs arrive three ways: a REST call that returns 202 and a correlation id, a Kafka topic, or a weekday 18:00 cron. Simulation fans out on Spark; results land in QuestDB for time-series queries.

Why three methods

Each method is wrong in a different direction, and the direction is the information.

  • Parametric assumes a covariance matrix and normal returns. Fast, smooth, and blind to fat tails. When it disagrees sharply with the other two, the book has exposure the covariance matrix cannot see.
  • Monte Carlo — correlated GBM paths via Cholesky — lets you stress the distribution you chose. It answers “what if the model is right and the world is unlucky”.
  • Historical Simulation replays what actually happened. No model, no parameters, and no opinion about anything the window did not contain.

Reporting all three on one line, every run, turned “which VaR do we trust” from a philosophical argument into a chart. ES is reported next to each because a quantile hides what lives past it — see ES vs VaR.

Architecture

The domain is pure Java with no framework imports. Hexagonal architecture, enforced by the Maven module graph: business → workflow → processing. The business module has zero infrastructure dependencies, which is the only way that constraint stays true past the second contributor.

Behaviour is specified in Cucumber feature files before it is implemented, so the risk methodology is readable by someone who does not read Java — and reviewable by someone who does not trust it.

MethodsParametric · Monte Carlo (Cholesky-correlated GBM) · Historical Simulation · Expected Shortfall
ArchitectureHexagonal; Maven multi-module; domain has no infrastructure dependencies
SpecBDD — Cucumber feature files drive the methodology
StackJava 21 · Spring Boot 4 · Apache Spark 4 (Scala 2.13) · QuestDB · Kafka (optional)
RunDocker Compose; CI with coverage on every push

Notes on the Spark 4 / Java 21 migration live in their own post.

What it is for

It exists to answer a question I could not answer with a spreadsheet: how much of a risk number is the portfolio, and how much is the method.