Docs / nablatensor-core / com.nablatensor.engine

final class

Nabla

The client-facing entry point: record a valuation once, then value it under as many market states and scenario counts as you like.

try (Nabla.Pricer pricer = Nabla.model(rec -> {
        SDouble spot = rec.input("S0", 100.0);
        SDouble vol  = rec.input("sigma", 0.2);
        ...
        rec.output(payoff);
      })
      .fp32()
      .greeks()
      .fastest()
      .build()) {

  Nabla.Valuation v = pricer.value()
      .with("S0", 110.0)
      .scenarios(10_000_000)
      .run();

  System.out.println(v.price() + " delta " + v.greek("S0"));
}

The split between Model and Pricer is the split between what is expensive and what is not. Building a pricer records the tape and generates a kernel for it; valuing costs a launch. Everything you pass to with is a kernel argument, so moving the market never rebuilds anything — which is why a pricer is worth holding onto rather than creating per valuation.

Methods

static Model model(Consumer<AadRecorder> valuation)

Records a valuation written against ADouble into a reusable model.

static TypedModel<M> model(M defaults, BiConsumer<AadRecorder, Inputs<M>> valuation)

Records a valuation whose inputs are the components of a market record.

The record supplies both the input names and their initial values, so no risk factor is ever named with a string: the valuation reads inputs through accessor references, and greeks() hands back the gradient as a record of the same type.

record EquityMarket(double spot, double vol, double rate) {}

var market = new EquityMarket(100.0, 0.28, 0.03);
try (var pricer = Nabla.model(market, (rec, in) -> {
        SDouble spot = in.of(EquityMarket::spot);
        ...
        rec.output(payoff);
      }).fp32().greeks().fastest().build()) {

  EquityMarket greeks = pricer.value().with(market).run().greeks();
  hedge(greeks.spot());          // delta, checked by the compiler
}