Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Script valuation and claim integration

ScriptEngine prepares a dated payoff program and evaluates it over market paths. Preparation parses the program once and identifies the observations needed from the model. Each path then supplies those observations, updates script variables and accumulates payments.

For a selected result variable, the engine also propagates derivatives to registered market and model inputs. Temporary differentiation state is released after each path to bound memory use.

Preparing the program

Construction receives an EventStream, reference date, local currency and local discount index. Variable indexing assigns storage slots and payment identities. Market calls become per-event SimulationDataRequest values and flattened model requests with observation dates. Conditional transformation, branch analysis and domain processing prepare the evaluator and remove statically impossible branches.

The program’s horizon includes requested observations and payments, not only source-event dates. This is important for a fixing event that declares a much later settlement. The model must be configured for the complete requested timeline.

The following fragment assumes an existing event stream and differentiable market model:

let engine = ScriptEngine::new(
    events, reference_date, Currency::USD, MarketIndex::SOFR,
)?;
let values = engine.evaluate(&mut model, Some("swap"))?;
let value = values.get("swap");

The selected variable identifies the result whose weighted adjoints are propagated; returned averaged variables are not limited to that one name. Without conditionals the engine uses exact expression evaluation. Conditional programs use the smoothed evaluator described in the language chapter.

Pathwise AD and reporting

Curve pillars and model inputs must be registered on the current tape before evaluation. For each path the engine rewinds to its mark, generates requested responses, evaluates the program and accumulates its contribution. It propagates the selected weighted result back to pre-mark dependencies before discarding path-local nodes. After the path loop, adjoints reach the registered leaves.

This bounds temporary tape memory to one path rather than all paths at once. Parallel evaluation constructs a model and tape per worker through ScriptModelSetup, returning partial values, labeled sensitivities and cashflows. The total path count determines normalization. Reproducible path indices do not promise bitwise identical floating-point reductions across arbitrary thread layouts.

evaluate_with_cashflows additionally collects executed payments. Each ExpectedCashflow distinguishes mean undiscounted amount in payment currency from mean discounted, numeraire-deflated present value in local currency. These are raw sampled payments.

Martingale controls and reconciliation

For a selected result, at least 16 paths and eligible discount/forward requests, the engine estimates martingale control coefficients on a separate pilot sample of up to 64 paths. Known initial-curve expectations anchor discounted bond/forward controls. The main estimator is schematically

\[ \widehat V_{\rm controlled} =\frac1n\sum_p\left[Y_p- \widehat\beta^\mathsf T(C_p-E[C])\right]. \]

The fitted coefficients are held fixed during differentiation. The adjusted estimator reduces variance; assess its remaining error through sampling comparisons.

Only the selected result variable receives this adjustment. Expected cashflow present values remain unadjusted sampled payments. Consequently their sum need not equal the reported controlled result at finite path count. Equality also requires that the selected variable actually represents the total payment accumulator rather than another state quantity. Use the estimator’s sampling error when assessing a stochastic reconciliation; a deterministic comparison has a different tolerance.

From payments to exposure claims

ScriptedProduct::contingent_claims creates one claim per payment identity. Each uses unit notional and LongReceive, because scale and direction already belong to the script expression. Claims share one compiled engine.

At an exposure date, a scripted claim replays the required program against its response block and returns that payment’s value. The live-claim schedule controls which payments remain. Required discount, rate, FX and spot requests must all be supported by the market model; the claim bridge does not create missing dynamics.

Scripted and native claims can share a netting set. The script must specify any exercise rule it uses; this claim bridge does not solve general optimal stopping for scripted callable products.

The Rust examples compare native and scripted swaps under controlled matching setups. Python’s evaluate_script is a narrower averaged-variable service, described in Working in Python; it does not expose every cashflow, path or claim-bridge facility available in Rust.