Core mechanism
Each named scenario is a set of relative or absolute transformations to named factors. A ladder composes related shocks while preserving the baseline, so a replay result has clear scenario lineage.
The TeaVM Java program records one European call kernel, applies five named relative or additive market shocks, and replays a configurable spot ladder using common random numbers.
This exact source runs in TeaVM. Form changes update its Java literals and reset manual edits.
package com.nablatensor.examples.learn;
import com.nablatensor.engine.Nabla;
import com.nablatensor.quant.EquityMarket;
import com.nablatensor.quant.MonteCarlo;
import com.nablatensor.quant.Products;
public final class ScenarioDslRiskStudio {
private ScenarioDslRiskStudio() {}
public static void main(String[] args) {
EquityMarket base = EquityMarket.of()
.spot(100)
.strike(100)
.vol(20 / 100.0)
.rate(3 / 100.0)
.maturity(1)
.build();
long paths = 200000L, seed = 42L;
try (MonteCarlo<EquityMarket> mc = MonteCarlo.of(Products.europeanCall())
.market(base)
.steps(1)
.fp64()
.greeks()
.on("cpu")
.build()) {
named(mc, "base", base, paths, seed, "no shocks");
named(mc, "rally", base.withSpot(base.spot() * 1.10), paths, seed, "spot relative +10%");
named(mc, "crash", base.withSpot(base.spot() * 0.80)
.withVol(base.vol() + 0.05), paths, seed, "spot relative -20%, vol additive +5pt");
named(mc, "rate hike", base.withRate(base.rate() + 0.01), paths, seed, "rate additive +1pt");
named(mc, "crash + rate cut", base.withSpot(base.spot() * 0.80)
.withVol(base.vol() + 0.05)
.withRate(base.rate() - 0.0075), paths, seed, "spot -20%, vol +5pt, rate -0.75pt");
int points = Math.max(1, Math.min(101, 9));
double range = 25 / 100.0;
for (int n = 0; n < points; n++) {
double rel = points == 1 ? 0.0 : -range + 2.0 * range * n / (points - 1);
EquityMarket m = base.withSpot(base.spot() * (1 + rel));
Nabla.TypedValuation<EquityMarket> v = mc.run(m, paths, seed);
System.out.println("LADDER|" + m.spot() + "|" + v.price() + "|" + v.greek(EquityMarket::spot));
}
}
}
private static void named(MonteCarlo<EquityMarket> mc, String name, EquityMarket m, long n, long seed,
String shocks) {
Nabla.TypedValuation<EquityMarket> v = mc.run(m, n, seed);
System.out.println("NAMED|" + name + "|" + shocks + "|" + v.price() + "|" + v.greek(EquityMarket::spot));
}
}
A small scenario language makes shock definitions composable, readable and reusable across a book.
Each named scenario is a set of relative or absolute transformations to named factors. A ladder composes related shocks while preserving the baseline, so a replay result has clear scenario lineage.
Maintain a factor taxonomy, name scenarios according to intent, test composition rules, and persist the exact scenario definition with every risk result.
Declare shocks as data; the runner expands them onto setInput + replay of an already-compiled kernel — no re-record, no recompile.
The FRTB curvature charge is exactly this pattern: two RELATIVE shocks (±RW·x) per risk factor, re-priced on the compiled kernel — see the curvature showcase.
A named scenario should state both its baseline and its transformation: whether a shock is absolute or relative, which factor identifiers it affects, and how it composes with other shocks. That makes scenario P&L reproducible and prevents a label such as “severe stress” from hiding different implementations over time.
A ladder is particularly useful for sensitivity analysis because each rung changes one economic quantity by a documented amount. The resulting curve of P&L against shock size reveals non-linearity and helps distinguish a local Greek from a full stressed revaluation.
A DSL prevents ambiguous implementation, not ambiguous economics. The choice and severity of shocks still require risk-owner approval.