Bare-factor G2 process¶
libitofin::processes::G2Process simulates two correlated Gaussian factors:
Included¶
- Five scalar parameters, with zero initial factors.
- Exact conditional means, covariance and Gaussian step generation.
- Finite signed mean reversion, including zero and arbitrarily small values.
- Nonnegative volatility, including deterministic factors, and
rhoin[-1,1]. - Zero steps and singular instantaneous correlations.
- The existing object-safe
StochasticProcessinterface and path generators.
Rust example¶
use libitofin::{
math::array::Array,
processes::G2Process,
stochasticprocess::StochasticProcess,
};
let process = G2Process::new(0.1, 0.01, 0.2, 0.008, -0.75)?;
let state = process.initial_values()?;
let next = process.evolve(0.0, &state, 0.25, &Array::from([0.5, -0.3]))?;
The last argument contains two independent standard-normal draws, not Brownian increments already scaled by the square root of the time step.
Exact transitions¶
For I(k,dt) = (1-exp(-k*dt))/k, with I(0,dt) = dt:
| Quantity | Formula |
|---|---|
| Mean first factor | x * exp(-a*dt) |
| Mean second factor | y * exp(-b*dt) |
| First variance | sigma² * I(2a,dt) |
| Second variance | eta² * I(2b,dt) |
| Cross covariance | rho * sigma * eta * I(a+b,dt) |
Integrated covariance is not generally the instantaneous covariance multiplied
by dt. For unequal reversion rates, the integrated correlation can be less
than one even when rho equals one. The lower-triangular step root reproduces
this exact covariance. Logarithmic scaling and expm1 avoid cancellation and
unnecessary volatility-squared overflow. A factored small-step series and a hyperbolic covariance identity preserve the
second shock for nearly singular transitions. The independent-noise residual is
computed with expm1 before the correlation rounds to one. A twelfth-order
midpoint difference of log(sinh(z)/z) is used when both |a*dt| and |b*dt|
are at most 0.125; the remaining correlated regime uses a stable hyperbolic
identity or the logarithmic kernel ratio. Only bounded positive
log-correlation roundoff is clamped to zero.
If an integrated correlation is subnormal or underflows, the root's first second-factor loading is scaled in signed log space so a representable loading is not lost. Ordinary covariance keeps the Gram product of the root, preserving its singular rounding behavior. When the loading or cross product is zero or subnormal, cross covariance is calculated independently from its exact integral. In extreme cases a root entry itself can be unrepresentable while the cross covariance remains representable; multiplying that rounded root by its transpose cannot recover the retained cross moment.
Validation and native reference¶
Constructor parameters, states and noise must be finite. Fallible dynamics
require two state/noise entries, nonnegative time and step, a finite endpoint,
and representable finite results. Invalid inputs and overflow return errors.
The inherited infallible apply remains ordinary component-wise addition.
Date-to-time conversion is unsupported. Parameters are immutable and there are
no market-data subscriptions.
Tracked regular-step fixtures come from QuantLib 1.43 with an empty yield-curve handle. Algebraic and strict 90-digit Decimal root-regression tests cover zero volatility, zero/small/signed reversion, singular correlation and finite extreme scaling.
QuantLib's inspected instantaneous G2 diffusion and the comparison fork use
rho * sigma as the second factor's first loading. When volatilities differ,
that does not produce the required second variance or cross covariance. This
implementation deliberately uses rho * eta instead. Exact native transitions
are compared separately from that corrected instantaneous loading. Native
zero-step/zero-volatility and zero-reversion divisions are not imported.
Not included¶
- A deterministic term-structure shift
phi(t)or fitted G2 short-rate model. - Interpreting
x+yas a curve-fitted short rate. - G2ForwardProcess, two-factor trees/lattices, calibration or pricing engines.
Existing APIs and models are unchanged. See simulation for other processes and path-generation conventions.
Python and Go bindings¶
The additive immutable bindings expose the same bare factors, not a fitted G2 model or curve. Python accepts exactly two finite state or noise values and returns owned lists, with nested row lists for matrices:
from itofin.processes import G2Process
process = G2Process(0.1, 0.01, 0.2, 0.008, -0.75)
state = process.initial_values()
next_state = process.evolve(0.0, state, 0.25, [0.5, -0.3])
covariance = process.covariance(0.0, state, 0.25)
Go uses fixed [2]float64 states and [2][2]float64 matrices through
Session.NewG2Process(a, sigma, b, eta, rho). Objects require Close, belong to
one session, and calls serialize on its native owner thread. Python objects are
thread-confined. Neither interface has an evaluation date or market-data owner.
Both interfaces expose size/factors, the five coefficients, initial values,
drift, diffusion, expectation, std_deviation, covariance, apply and
evolve (exported PascalCase in Go). std_deviation is a transition loading
matrix, not entrywise standard deviations. apply checks finite two-element
inputs and results around native component-wise addition. The C ABI passes
ItofinG2Vector by value and writes one complete two-double vector or four-double
row-major matrix only on success. Unused inputs for C query kinds are ignored.