Regulatory Capital

Run the Full FRTB Standardised Approach

TeaVM Java applies delta, vega and curvature aggregation across seven illustrative risk classes, alongside the demonstration DRC and RRAO charges. Inputs and demo-table scope are explicit in the editable source.

Book
Trade IDNetting setNotionalSide

IDs and netting-set labels are reported book metadata in this scoped demo; all trade sensitivities enter the same SA aggregation.

Market levels
Simulation (trade 0's real repricing)Optional
Java source
FrtbFullRiskStudio.java

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

import com.nablatensor.engine.Nabla;
import com.nablatensor.quant.EquityMarket;
import com.nablatensor.quant.MonteCarlo;
import com.nablatensor.quant.Products;
import com.nablatensor.risk.CorrelationScenarioEnum;
import com.nablatensor.risk.NestedAggregation;
import com.nablatensor.risk.RiskClassEnum;
import com.nablatensor.risk.RiskFactor;
import com.nablatensor.risk.RiskMeasureEnum;
import com.nablatensor.risk.Sensitivities;
import java.util.HashSet;
import java.util.EnumMap;
import java.util.Map;
import java.util.Set;

public final class FrtbFullRiskStudio {
  static final RiskClassEnum[] RC = RiskClassEnum.values();
  static final Map<RiskClassEnum, Integer> LEVEL_SOURCE = new EnumMap<>(RiskClassEnum.class);
  static {
    LEVEL_SOURCE.put(RiskClassEnum.GIRR, 0);
    LEVEL_SOURCE.put(RiskClassEnum.CSR_NON_SEC, 1);
    LEVEL_SOURCE.put(RiskClassEnum.CSR_SEC, 1);
    LEVEL_SOURCE.put(RiskClassEnum.CSR_SEC_CTP, 1);
    LEVEL_SOURCE.put(RiskClassEnum.EQUITY, 2);
    LEVEL_SOURCE.put(RiskClassEnum.COMMODITY, 3);
    LEVEL_SOURCE.put(RiskClassEnum.FX, 4);
  }
  static final String[][] BUCKET = {
    {
      "USD", "EUR"
    }, {
      "1", "3"
    }, {
      "1", "2"
    }, {
      "1", "2"
    }, {
      "1", "5"
    }, {
      "1", "2"
    }, {
      "EURUSD", "USDJPY"
    }
  };
  static final double[][] RW = {
    {
      .017, .017
    }, {
      .005, .05
    }, {
      .009, .02
    }, {
      .04, .07
    }, {
      .55, .30
    }, {
      .30, .40
    }, {
      .15, .15
    }
  }, RHO = {
    {
      .4, .4
    }, {
      .35, .35
    }, {
      .4, .4
    }, {
      .35, .35
    }, {
      .25, .25
    }, {
      .55, .55
    }, {
      1, 1
    }
  };
  static final double[] GAMMA = {
    .5, .4, .4, .4, .15, .2, .6
  };
  public static void main(String[] a) {
    String[] id = {
      "ASIAN-001", "ASIAN-HEDGE", "MACRO-HEDGE"
    }, ns = {
      "NS-OPTIONS", "NS-OPTIONS", "NS-MACRO"
    };
    double[] notional = {
      12000000, 4500000, 8000000
    };
    int[] side = {
      -1, 1, 1
    };
    double spx = 100, ois = 4.2 / 100.0, spread = 1.2 / 100.0, fx = 1.085, wti = 76.5, iv = 20 / 100.0;
    long paths = 200000L, seed = 42L;
    Set<String> nettingSets = new HashSet<>();
    for (String x : ns) nettingSets.add(x);
    System.out.println("BOOK|" + id.length + "|" + nettingSets.size());
    double cvr = equityCurvature(notional[0], side[0], paths, seed);
    Sensitivities.Builder b = Sensitivities.builder();
    int factorIndex;
    for (int ti = 0; ti < id.length; ti++) {
      double scale = side[ti] * notional[ti] / 1.0e6;
      factorIndex = 0;
      for (int ci = 0; ci < RC.length; ci++) for (String bucket : BUCKET[ci]) {
        double alt = ((factorIndex++ + ti) % 2 == 0) ? 1.0 : -0.55;
        double level = level(RC[ci], spx, ois, spread, fx, wti);
        String name = RC[ci] + "-FACTOR-" + bucket;
        b.add(factor(RC[ci], RiskMeasureEnum.DELTA, bucket, name, 0), scale * level * alt);
        b.add(factor(RC[ci], RiskMeasureEnum.VEGA, bucket, name, 1), scale * iv * (1 + ti * .1));
        b.add(factor(RC[ci], RiskMeasureEnum.CURVATURE, bucket, name, 0), Math.abs(scale) * .02 * alt);
      }
    }
    b.add(RiskFactor.equityDelta("5", id[0])
        .asCurvature(), cvr);
    Sensitivities all = b.build();
    double sbm = 0;
    for (int ci = 0; ci < RC.length; ci++) {
      double d = charge(all, ci, RiskMeasureEnum.DELTA), v = charge(all, ci, RiskMeasureEnum.VEGA),
          c = charge(all, ci, RiskMeasureEnum.CURVATURE);
      System.out.println("CLASS|" + RC[ci] + "|" + d + "|" + v + "|" + c + "|" + (d + v + c));
      sbm += d + v + c;
    }
    double drc = drc(), drcHedged = drcHedged(), rraoOther = Math.abs(notional[0]) / 1e6 * .001,
        rraoExotic = 2.0 * .01, rrao = rraoOther + rraoExotic, total = sbm + drc + rrao;
    System.out.println("TOTAL|" + sbm + "|" + drc + "|" + drcHedged + "|" + rraoExotic + "|" + rraoOther
        + "|" + total);
  }
  static RiskFactor factor(RiskClassEnum rc, RiskMeasureEnum m, String bucket, String name, double tenor) {
    return RiskFactor.of()
        .riskClass(rc)
        .measure(m)
        .bucket(bucket)
        .name(name)
        .tenor(tenor)
        .tenor2(0)
        .build();
  }
  static double level(RiskClassEnum rc, double spx, double ois, double spread, double fx, double wti) {
    int source = LEVEL_SOURCE.get(rc);
    double[] levels = {
      ois, spread, spx / 100, wti / 100, fx
    };
    return levels[source];
  }
  static int bucketIndex(int c, String b) {
    for (int i = 0; i < BUCKET[c].length; i++) if (BUCKET[c][i].equals(b)) return i;
    return 0;
  }
  static double charge(Sensitivities all, int ci, RiskMeasureEnum measure) {
    Sensitivities slice = all.ofClass(RC[ci])
        .ofMeasure(measure);
    double best = 0;
    for (CorrelationScenarioEnum sc : CorrelationScenarioEnum.values()) {
      final int c = ci;
      NestedAggregation.WithinBucketCorrelation rho = (x, y) -> sc.apply(RHO[c][bucketIndex(c, x.bucket())]);
      NestedAggregation.AcrossBucketCorrelation gamma = (x, y) -> x.equals(y) ? 1.0 : sc.apply(GAMMA[c]);
      NestedAggregation agg = measure == RiskMeasureEnum.CURVATURE ? NestedAggregation.curvature(rho,
          gamma) : NestedAggregation.delta(f -> measure == RiskMeasureEnum.DELTA ? RW[c][bucketIndex(c,
          f.bucket())] : 1.0, rho, gamma);
      best = Math.max(best, agg.aggregate(slice)
          .total());
    }
    return best;
  }
  static double drc() {
    double longs = 4.4, shorts = .2, wl = 2.4 * .03 + 2 * .005, ws = .2 * .15, hbr = longs / (longs
        + shorts);
    return Math.max(wl - hbr * ws, 0);
  }
  static double drcHedged() {
    double wl = 2.4 * .03 + 2 * .005;
    return Math.max(wl, 0);
  }
  static double equityCurvature(double notional, int side, long n, long seed) {
    EquityMarket m = EquityMarket.of()
        .spot(100)
        .strike(100)
        .vol(.2)
        .rate(.03)
        .maturity(1)
        .build();
    double units = Math.abs(notional) / 100.0 / 1.0e6, position = side;
    try (MonteCarlo<EquityMarket> greek = MonteCarlo.of(Products.asianCall())
        .market(m)
        .steps(60)
        .fp64()
        .greeks()
        .on("cpu")
        .build(); MonteCarlo<EquityMarket> price = MonteCarlo.of(Products.asianCall())
        .market(m)
        .steps(60)
        .fp64()
        .priceOnly()
        .on("cpu")
        .build()) {
      double base = position * units * price.run(n, seed)
          .price(), delta = position * units * greek.run(n, seed)
          .greek(EquityMarket::spot);
      EquityMarket up = EquityMarket.of()
          .spot(130)
          .strike(100)
          .vol(.2)
          .rate(.03)
          .maturity(1)
          .build(), down = EquityMarket.of()
          .spot(70)
          .strike(100)
          .vol(.2)
          .rate(.03)
          .maturity(1)
          .build();
      double pu = position * units * price.run(up, n, seed)
          .price(), pd = position * units * price.run(down, n, seed)
          .price(), shock = 30;
      return -Math.min(pu - base - shock * delta, pd - base + shock * delta);
    }
  }
}
TeaVM compiles and runs the Java source above in this browser.
Implementation guide

From risk sensitivities to FRTB capital

The full standardised approach combines prescribed sensitivities-based capital with default-risk and residual-risk components.

Core mechanism

SBM calculates delta, vega and curvature by risk class and correlation scenario. DRC captures jump-to-default risk and RRAO applies a gross-notional surcharge; the three resulting charges are summed without diversification between them.

Practical workflow

Classify positions and risk factors, obtain governed sensitivities and shocked revaluations, apply the jurisdictional parameter set, then retain the full calculation lineage and reconciliation output.

Key details

The three jurisdictions share the MAR21/22/23 *structure*; they differ in parameter values, the list of "liquid" currencies for the GIRR √2 relief, index look-through rules, and the EU relief package. The code parameterises all of this (see II.2, and Phase 4 FrtbParameterSet).

The three are computed independently and summed — no diversification between them. SBM is where essentially all of NablaTensor's adjoint-sensitivity value sits; DRC and RRAO are notional/position arithmetic with lookup tables.

Within each risk class, the bank computes a delta, a vega and a curvature capital charge, then sums them. The class total is computed under each of three correlation scenarios; the SBM total is Σ_class max(high, medium, low) — i.e. the max is taken *per risk class*, then summed (MAR21.6).

Delta — linear sensitivity to a delta risk factor k: - Net the sensitivity s_k across the whole book (all instruments, all trades). - Weight it: WS_k = RW_k · s_k, with RW_k the prescribed risk weight for the factor's bucket/tenor.

Vega — sensitivity to an *implied volatility* risk factor. The vega risk factor is the implied vol of an option, indexed by option maturity (and, for GIRR/CSR, additionally by the residual maturity of the underlying). Weighted the same way with a vega risk weight RW_k derived from the risk class's liquidity horizon: RW_k = min( RW_σ · √(LH_class / 10) , 100% ).

Curvature — extra capital for the second-order (gamma) risk that delta misses, measured by two *shocked full repricings* per curvature risk factor:

where δ_ik is instrument i's delta to factor k and RW_k^curv is the curvature risk weight (for most classes = the *highest delta risk weight* of the bucket; for GIRR/FX the shock is a *relative parallel shift*). This is the one part of SBM that needs repricings, not just sensitivities — two per risk factor.

Scope and review point

The interactive book is illustrative. A reportable number needs the applicable rulebook, current tables, approved models, data controls and sign-off procedures.