Coverage Report

Created: 2026-08-14 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/pricingengines/basket/pearsonspreadengine.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2026 Yassine Idyiahia
5
6
 This file is part of QuantLib, a free-software/open-source library
7
 for financial quantitative analysts and developers - http://quantlib.org/
8
9
 QuantLib is free software: you can redistribute it and/or modify it
10
 under the terms of the QuantLib license.  You should have received a
11
 copy of the license along with this program; if not, please email
12
 <quantlib-dev@lists.sf.net>. The license is also available online at
13
 <https://www.quantlib.org/license.shtml>.
14
15
 This program is distributed in the hope that it will be useful, but WITHOUT
16
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17
 FOR A PARTICULAR PURPOSE.  See the license for more details.
18
*/
19
20
#include <ql/pricingengines/basket/pearsonspreadengine.hpp>
21
#include <ql/pricingengines/blackcalculator.hpp>
22
#include <ql/math/distributions/normaldistribution.hpp>
23
#include <ql/math/integrals/gausslobattointegral.hpp>
24
#include <utility>
25
26
namespace QuantLib {
27
28
    PearsonSpreadEngine::PearsonSpreadEngine(
29
        ext::shared_ptr<GeneralizedBlackScholesProcess> process1,
30
        ext::shared_ptr<GeneralizedBlackScholesProcess> process2,
31
        Real correlation,
32
        Real integrationTolerance,
33
        Size maxIntegrationIterations,
34
        Real nStd)
35
0
    : SpreadBlackScholesVanillaEngine(
36
0
          std::move(process1), std::move(process2), correlation),
37
0
      integrationTolerance_(integrationTolerance),
38
0
      maxIntegrationIterations_(maxIntegrationIterations),
39
0
      nStd_(nStd) {
40
0
    }
41
42
    Real PearsonSpreadEngine::calculate(
43
        Real f1, Real f2, Real strike, Option::Type optionType,
44
0
        Real variance1, Real variance2, DiscountFactor df) const {
45
46
0
        const Real sigma1 = std::sqrt(variance1);
47
0
        const Real sigma2 = std::sqrt(variance2);
48
49
        // Under the forward measure, the joint dynamics are:
50
        //   ln F1 ~ N(mu1, sigma1^2),  ln F2 ~ N(mu2, sigma2^2)
51
        //   with correlation rho_
52
        //
53
        // Condition on z = standard normal driving F2:
54
        //   F2(z) = f2 * exp(-0.5*sigma2^2 + sigma2*z)
55
        //
56
        // Conditional on z, F1 is log-normal with:
57
        //   conditional mean of ln F1:  mu1_cond = ln(f1) - 0.5*sigma1^2 + rho_*sigma1*z
58
        //   conditional variance:       sigma1_cond^2 = sigma1^2*(1 - rho_^2)
59
        //
60
        // The conditional spread option value is a Black call/put:
61
        //   BS(F1_cond, K + F2(z), sigma1_cond)
62
63
0
        const Real sigma1Cond = sigma1 * std::sqrt(std::max(1.0 - rho_ * rho_, 0.0));
64
65
0
        const NormalDistribution phi;
66
67
        // Integrate over the standard normal variable z
68
        // that drives F2. Truncate at +/- 8 standard deviations.
69
0
        auto integrand = [&](Real z) -> Real {
70
0
            const Real f2z = f2 * std::exp(-0.5 * variance2 + sigma2 * z);
71
0
            const Real effectiveStrike = f2z + strike;
72
73
0
            if (effectiveStrike <= 0.0)
74
0
                return phi(z) * std::max(0.0, f1 * std::exp(rho_ * sigma1 * z
75
0
                           - 0.5 * rho_ * rho_ * variance1) - effectiveStrike);
76
77
0
            const Real f1Cond = f1 * std::exp(rho_ * sigma1 * z
78
0
                                              - 0.5 * rho_ * rho_ * variance1);
79
80
0
            BlackCalculator black(
81
0
                ext::make_shared<PlainVanillaPayoff>(optionType, effectiveStrike),
82
0
                f1Cond, sigma1Cond, 1.0);
83
84
0
            return phi(z) * black.value();
85
0
        };
86
87
0
        GaussLobattoIntegral integrator(maxIntegrationIterations_,
88
0
                                       integrationTolerance_);
89
0
        const Real undiscounted = integrator(integrand, -nStd_, nStd_);
90
91
0
        return df * undiscounted;
92
0
    }
93
}