European gap options¶
The additive gap factory returns the existing VanillaOption facade in C/Go and
Python. Existing vanilla and cash-or-nothing constructors are unchanged. This is
not a standalone payoff-object API or complete payoff/engine compatibility.
| Facade | Construction | Supported pricing path |
|---|---|---|
| C | itofin_option_gap_new |
Existing FD engine attached by itofin_option_set_engine, kind 2 |
| Go | Session.NewGapOption |
SetFdEngine or PriceFd |
| Python | VanillaOption.gap |
set_fd_engine or price_fd |
Signed payoff and validation¶
Let K be the trigger strike and L the payment strike (second_strike in Python,
secondStrike in Go). A call pays S - L if S >= K, otherwise zero. A put pays
L - S if S <= K, otherwise zero. Equality activates the payoff. Negative
payments and negative option values are not clamped to zero.
The factory requires finite nonnegative K, finite signed L, valid expiry and
explicit settings. Zero trigger construction is accepted; that does not guarantee
that the existing log-space FD mesher can price it. The FD process retains its
existing positive-spot/grid/domain requirements. Invalid nonfinite parameters are
rejected before an option is created. C output is written only on success.
Use the existing FdBlackScholesVanillaEngine, supported Douglas or implicit Euler
scheme, and explicitly selected grids/damping when examining convergence. FD
provides NPV, delta, gamma and theta; other unavailable result fields return their
existing errors. Without an engine valuation fails. The analytic European Black
path rejects GapPayoff; the factory does not silently select a vanilla payoff or
expand any analytic/Heston/Monte Carlo engine's supported payoff types.
Inherited FD limits¶
The existing terminal-cell averaging heuristic derives Simpson accuracy from the
sum of endpoint payoffs. Negative endpoint sums and cancelling sums at/below
machine epsilon fall back to the point payoff. This native behavior is preserved,
not repaired by the binding. A discontinuous signed payoff can therefore show
nonmonotone grid errors. Use multiple grids and independently qualified fixtures;
a single finite price is not an accuracy or full QuantLib-parity claim. In the
independent QuantLib 1.43 wheel grid ladder, case gap-put-K100-H-20 uses
spot/trigger 100, payment strike -20, r=0.05, q=0.02, volatility=0.2, 360 days
under Actual360 and Douglas with two damping steps. Its 800-by-800 grid retains
about 0.07371 absolute error against the signed closed-form expectation. Same-grid native matching and finiteness are separate from
this per-route analytic bias; neither establishes high-precision convergence.
The native payoff trigger is inclusive. With the default reference-date-event policy the instrument returns zero at expiry; explicitly including reference-date events changes whether that date is expired. Zero volatility, near-zero time and zero trigger must be interpreted through the existing FD domain/error behavior, not assumed to follow an unavailable analytic engine. No core pricing formula was changed.
Supported ordinary FD cases require finite numerical results. Deliberate zero-volatility, zero-trigger and unsupported-engine probes are qualified separately as inherited diagnostics, not accepted by a passing NaN comparison. Generic option raw-query semantics are unchanged; no global finite-result fix or payoff-name dispatch is introduced by this factory.
Ownership and live inputs¶
The option retains settings, and its attached engine retains the process/curve and volatility graph. Supported quote/curve updates invalidate cached prices. Python dependencies survive wrapper deletion. Go objects are session-owned; close wrappers explicitly, and never mix sessions. C contexts and handles remain thread-confined and typed; release source handles only after dependents retain them. Missing/closed/mixed handles and unavailable Greeks report existing errors.
Examples¶
Python:
"""A signed European gap option through the existing FD engine, not analytic Black."""
# itofin library
from itofin import Settings
from itofin.instruments import OptionType, VanillaOption
from itofin.pricingengines import FdBlackScholesVanillaEngine
from itofin.processes import BlackScholesProcess
from itofin.time import Date, DayCounter
today = Date(15, 6, 2026)
settings = Settings()
settings.set_evaluation_date(today)
process = BlackScholesProcess(100.0, 0.05, 0.02, 0.2, today, DayCounter.actual360())
engine = FdBlackScholesVanillaEngine(process, t_grid=400, x_grid=400, damping_steps=2)
option = VanillaOption.gap(OptionType.Call, 100.0, 160.0, today + 360, settings)
print(f"Signed gap NPV (FD): {option.price_fd(engine):.8f}")
Go:
package main
import (
"fmt"
itofin "github.com/bitbrew-dev/libitofin/sdk/go"
)
func must[T any](value T, err error) T {
if err != nil {
panic(err)
}
return value
}
func check(err error) {
if err != nil {
panic(err)
}
}
func main() {
s := must(itofin.NewSession())
defer func() { check(s.Close()) }()
today := must(itofin.NewDate(15, 6, 2026))
settings := must(s.NewSettings())
check(settings.SetEvaluationDate(today))
dc := must(s.Actual360())
process := must(s.NewBlackScholesProcess(itofin.BlackScholesConfig{Spot: 100, RiskFreeRate: .05, DividendYield: .02, Volatility: .2, ReferenceDate: today, DayCounter: dc}))
grid, damping := uint(400), uint(2)
engine := must(s.NewFdBlackScholesVanillaEngine(process, itofin.FdConfig{TGrid: &grid, XGrid: &grid, DampingSteps: &damping}))
option := must(s.NewGapOption(itofin.Call, 100, 160, must(today.AddDays(360)), settings))
fmt.Printf("Signed gap NPV (FD): %.8f\n", must(option.PriceFd(engine)))
}