libitofin 0.44.0

A ground-up Rust port of QuantLib: quantitative-finance primitives for pricing, risk, and numerical methods.
Documentation
//! Executable QuantLib 1.43 fixtures from SDK testdata/gjrgarch-paths.json.
//! Independent MT19937/Acklam draws feed QuantLib evolve, not this Rust kernel.

use super::tests::request;
use super::*;

#[test]
fn seeded_paths_match_independent_quantlib_fixtures() {
    let base = GjrRequest {
        beta: 0.88,
        gamma: 0.08,
        lambda: -0.4,
        discretization: GjrGarchDiscretization::FullTruncation,
        ..request()
    };
    let cases = [
        (
            "base",
            base,
            [
                100.0,
                0.04,
                99.59783472336754,
                0.04170867271130308,
                101.73667358097003,
                0.0359298516626409,
                102.49207073993917,
                0.037145057918075396,
                102.80405728819555,
                0.036520207268082985,
                100.0,
                0.04,
                98.73439164497894,
                0.038026194383399735,
                97.51608929071122,
                0.03186956300412412,
                95.80987131149772,
                0.030593020591268432,
                96.9890438463339,
                0.027998711867417456,
                100.0,
                0.04,
                100.32334642890494,
                0.03409744020186439,
                100.96579665690983,
                0.033996024428453404,
                98.60068766740565,
                0.027694759214671866,
                100.56514487655276,
                0.028077771054163612,
            ],
        ),
        (
            "negative_partial",
            GjrRequest {
                alpha: 0.3,
                beta: 0.4,
                discretization: GjrGarchDiscretization::PartialTruncation,
                gamma: 2.0,
                ..base
            },
            [
                100.0,
                0.04,
                99.59783472336754,
                0.11025200542457243,
                103.08476707636152,
                -0.13821807435500852,
                103.09294873174642,
                -0.17265635645640567,
                103.10113103649482,
                -0.2158008238659829,
                100.0,
                0.04,
                98.73439164497894,
                0.06982401808603367,
                97.08087548647406,
                -0.0012413272356038526,
                97.08858062361723,
                -0.0010511415225487753,
                97.0962863723035,
                -0.0008128758252496904,
                100.0,
                0.04,
                100.32334642890494,
                -0.02190735388084541,
                100.33130891522396,
                -0.026941652276129772,
                100.33927203351139,
                -0.033248648728679914,
                100.34723578381741,
                -0.04115008824004071,
            ],
        ),
        (
            "negative_full",
            GjrRequest {
                alpha: 0.3,
                beta: 0.4,
                gamma: 2.0,
                ..base
            },
            [
                100.0,
                0.04,
                99.59783472336754,
                0.11025200542457243,
                103.08476707636152,
                -0.13821807435500852,
                103.09294873174642,
                -0.13771407435500851,
                103.10113103649482,
                -0.1372100743550085,
                100.0,
                0.04,
                98.73439164497894,
                0.06982401808603367,
                97.08087548647406,
                -0.0012413272356038526,
                97.08858062361723,
                -0.0007373272356038527,
                97.0962863723035,
                -0.0002333272356038528,
                100.0,
                0.04,
                100.32334642890494,
                -0.02190735388084541,
                100.33130891522396,
                -0.02140335388084541,
                100.33927203351139,
                -0.02089935388084541,
                100.34723578381741,
                -0.02039535388084541,
            ],
        ),
        (
            "negative_reflection",
            GjrRequest {
                alpha: 0.3,
                beta: 0.4,
                discretization: GjrGarchDiscretization::Reflection,
                gamma: 2.0,
                ..base
            },
            [
                100.0,
                0.04,
                99.59783472336754,
                0.11025200542457245,
                103.08476707636152,
                -0.13821807435500857,
                104.56931692629678,
                0.278934536586951,
                105.39228560981053,
                0.40740762498380473,
                100.0,
                0.04,
                98.73439164497894,
                0.06982401808603367,
                97.08087548647406,
                -0.0012413272356038248,
                96.75039531154385,
                0.0031937601245863433,
                97.14004493593399,
                -1.4658639047070362e-05,
                100.0,
                0.04,
                100.32334642890494,
                -0.021907353880845404,
                100.84065346117623,
                0.03280471488109817,
                98.52000277501935,
                0.007434500617784011,
                99.53732464024702,
                0.007010065229981778,
            ],
        ),
        (
            "negative_gamma",
            GjrRequest {
                gamma: -0.02,
                lambda: 0.3,
                ..base
            },
            [
                100.0,
                0.04,
                99.59783472336754,
                0.038149228114977646,
                101.64312548966691,
                0.03368953158243181,
                102.37431606621067,
                0.03209052596813491,
                102.66500628111332,
                0.02993825369150966,
                100.0,
                0.04,
                98.73439164497894,
                0.036655528856638274,
                97.53838525883374,
                0.03196894320733572,
                95.82912047824252,
                0.02946387030770295,
                96.98667890786332,
                0.026707392519077514,
                100.0,
                0.04,
                100.32334642890494,
                0.03511189807556215,
                100.97509366722429,
                0.0328939245192425,
                98.64820892250286,
                0.028388900144012803,
                100.63816697797823,
                0.026868044920721194,
            ],
        ),
        (
            "zero_horizon",
            GjrRequest {
                horizon: 0.0,
                ..base
            },
            [
                100.0, 0.04, 100.0, 0.04, 100.0, 0.04, 100.0, 0.04, 100.0, 0.04, 100.0, 0.04,
                100.0, 0.04, 100.0, 0.04, 100.0, 0.04, 100.0, 0.04, 100.0, 0.04, 100.0, 0.04,
                100.0, 0.04, 100.0, 0.04, 100.0, 0.04,
            ],
        ),
    ];
    for (name, request, expected) in cases {
        let actual = gjr_paths(&request).unwrap();
        assert_eq!(actual.len(), expected.len());
        for (&actual, expected) in actual.iter().zip(expected) {
            assert!(
                (actual - expected).abs() <= 1e-12 + 2e-13 * expected.abs(),
                "{name} actual={actual:.17}, expected={expected:.17}"
            );
        }
        let terminal = gjr_paths(&GjrRequest {
            terminal_only: true,
            ..request
        })
        .unwrap();
        let stride = (request.steps + 1) * 2;
        for (path, pair) in terminal.chunks_exact(2).enumerate() {
            assert_eq!(
                pair,
                &actual[path * stride + stride - 2..(path + 1) * stride]
            );
        }
    }
}