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

Dated payoff programs

Payoff scripts describe observations, state changes and payments in a dated sequence of events. They are useful for contracts whose coupon or redemption rules can be expressed directly as a small program. The native engine parses the events, determines their market-data requirements and evaluates them against simulated market states.

Variables persist along each path, allowing a later payment to depend on earlier observations. Payment statements apply discounting and currency conversion through the configured market model.

CodedEvent pairs a date with source text. Conversion into an EventStream parses each program into an expression tree. Events must be nonempty, ordered and not precede the reference date; equal-date events execute in vector order. Every variable read must follow an assignment in the current or an earlier event.

State, observations and payments

The following event describes one receive-fixed/pay-floating coupon:

swap = 0;
fixed_rate = 0.035;
accrual = cvg("2027-03-09", "2027-06-09", "Actual360");
floating = RateIndex("SOFR", "2027-03-09", "2027-06-09");
swap pays 10000000 * (fixed_rate - floating) * accrual on "2027-06-09";

Attach this event to the fixing/start date, 9 March. The event observes the rate at that date and declares the payment due on 9 June.

Statements terminate with semicolons; blocks use braces. Assignment, compound assignment, arithmetic, power, comparisons, boolean logic, arrays and bounded for loops provide ordinary state manipulation. Identifiers are case-sensitive. Script strings follow the native payoff-language grammar, including its supported comment and expression syntax.

RateIndex requests a forward observation over the specified interval. Spot("AAPL") requests an equity spot; a currency pair such as Spot("USD","CLP") requests CLP per USD. Df requests a discount factor, and cvg delegates year-fraction measurement to a named day counter. The model must support the requested observations; parsing a name does not create a missing factor.

A payment has the form

accumulator pays amount on "2027-06-09" in "USD";

Date and currency clauses can be omitted, in which case they use the event date and local currency. The amount is contractual before discounting. Evaluation converts/discounts it and deflates by the appropriate numeraire before adding to the accumulator. Every payment node receives a stable identity for cashflow reporting and claim conversion.

Conditional payoffs and smoothing

A hard digital payoff \(\mathbf1_{\{X>K\}}\) has zero pathwise derivative away from its boundary. Merely recording it on a tape does not produce a useful Monte Carlo digital sensitivity. Scripting deliberately smooths market-dependent conditionals with a finite-width transition.

For a comparison reduced to \(x=\text{lhs}-\text{rhs}\), its truth degree is

\[ d_\varepsilon(x)= \begin{cases} 0,&x<-\varepsilon/2,\ (x+\varepsilon/2)/\varepsilon,&|x|\leq\varepsilon/2,\ 1,&x>\varepsilon/2. \end{cases} \]

The evaluated state blends both branches as \(d_\varepsilon V_{\rm then}+(1-d_\varepsilon)V_{\rm else}\). Equality uses a butterfly profile; boolean operations combine truth degrees through products/complements. Nested conditionals need separate branch-state snapshots.

The engine’s automatic width is

\[ \varepsilon=\max\left( 0.02\max(|\text{lhs}|,|\text{rhs}|),10^{-8}\right). \]

A 4% rate barrier therefore receives roughly 8 bp width, whereas an equity barrier at 150 receives width 3. These are economic approximations as well as numerical choices. Price and derivative belong to the smoothed program, not necessarily the exact discontinuous term sheet.

For an explicitly chosen width, fif(x,a,b,eps) blends between two numerical expressions:

coupon = fif(
    RateIndex("SOFR", "2027-03-09", "2027-06-09") - 0.04,
    0.05, 0.0, 0.001
);

Here the digital-style coupon changes from zero to 5% across a 10 bp rate interval. Piecewise min/max payoffs remain differentiable only along their selected active branches.

Products and validation

ScriptedProduct compiles an event stream and discovers payment nodes, including those in branches and loops. It requires at least one payment and rejects payments before the reference date. The same engine is shared by claims, avoiding repeated parsing for each coupon.

Parsing and evaluation use the common QSError categories: ParsingErr for grammar, InvalidValueErr for structural rules, ValueNotSetErr for uninitialized state, NotFoundErr for unavailable responses/results and EvaluationErr for invalid expression operations. Context preserves the event/path that failed, and root_cause() exposes the underlying category. Validate the event stream before starting a long simulation so structural and grammar errors can be resolved first.