Coverage Report

Created: 2026-01-25 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/pricingengines/swaption/gaussian1djamshidianswaptionengine.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2001, 2002, 2003 Sadruddin Rejeb
5
 Copyright (C) 2013 Peter Caspers
6
7
 This file is part of QuantLib, a free-software/open-source library
8
 for financial quantitative analysts and developers - http://quantlib.org/
9
10
 QuantLib is free software: you can redistribute it and/or modify it
11
 under the terms of the QuantLib license.  You should have received a
12
 copy of the license along with this program; if not, please email
13
 <quantlib-dev@lists.sf.net>. The license is also available online at
14
 <https://www.quantlib.org/license.shtml>.
15
16
 This program is distributed in the hope that it will be useful, but WITHOUT
17
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18
 FOR A PARTICULAR PURPOSE.  See the license for more details.
19
*/
20
21
#include <ql/math/solvers1d/brent.hpp>
22
#include <ql/pricingengines/swaption/gaussian1djamshidianswaptionengine.hpp>
23
#include <utility>
24
25
namespace QuantLib {
26
27
    class Gaussian1dJamshidianSwaptionEngine::rStarFinder {
28
      public:
29
        rStarFinder(const ext::shared_ptr<Gaussian1dModel>& model,
30
                    Real nominal,
31
                    const Date& maturityDate,
32
                    const Date& valueDate,
33
                    std::vector<Date> fixedPayDates,
34
                    const std::vector<Real>& amounts,
35
                    const Size startIndex)
36
0
        : strike_(nominal), maturityDate_(maturityDate), valueDate_(valueDate),
37
0
          startIndex_(startIndex), times_(std::move(fixedPayDates)), amounts_(amounts),
38
0
          model_(model) {}
39
40
0
        Real operator()(Rate y) const {
41
0
            Real value = strike_;
42
0
            Size size = times_.size();
43
0
            for (Size i = startIndex_; i < size; i++) {
44
0
                Real dbValue = model_->zerobond(times_[i], maturityDate_, y) /
45
0
                               model_->zerobond(valueDate_, maturityDate_, y);
46
0
                value -= amounts_[i] * dbValue;
47
0
            }
48
0
            return value;
49
0
        }
50
51
      private:
52
        Real strike_;
53
        Date maturityDate_, valueDate_;
54
        Size startIndex_;
55
        std::vector<Date> times_;
56
        const std::vector<Real> &amounts_;
57
        const ext::shared_ptr<Gaussian1dModel> &model_;
58
    };
59
60
0
    void Gaussian1dJamshidianSwaptionEngine::calculate() const {
61
62
0
        QL_REQUIRE(arguments_.settlementMethod != Settlement::ParYieldCurve,
63
0
                   "cash settled (ParYieldCurve) swaptions not priced with "
64
0
                   "Gaussian1dJamshidianSwaptionEngine");
65
66
0
        QL_REQUIRE(arguments_.exercise->type() == Exercise::European,
67
0
                   "cannot use the Jamshidian decomposition "
68
0
                   "on exotic swaptions");
69
70
0
        QL_REQUIRE(arguments_.swap->spread() == 0.0,
71
0
                   "non zero spread (" << arguments_.swap->spread()
72
0
                                       << ") not allowed"); // PC
73
74
0
        QL_REQUIRE(arguments_.nominal != Null<Real>(),
75
0
                   "non-constant nominals are not supported yet");
76
77
0
        Date referenceDate;
78
0
        DayCounter dayCounter;
79
80
0
        referenceDate = model_->termStructure()->referenceDate();
81
0
        dayCounter = model_->termStructure()->dayCounter();
82
83
0
        std::vector<Real> amounts(arguments_.fixedCoupons);
84
0
        amounts.back() += arguments_.nominal;
85
86
0
        Size startIndex = std::upper_bound(arguments_.fixedResetDates.begin(),
87
0
                                           arguments_.fixedResetDates.end(),
88
0
                                           arguments_.exercise->date(0) - 1) -
89
0
                          arguments_.fixedResetDates.begin();
90
        // only consider coupons with start date >= exercise dates
91
92
0
        rStarFinder finder(*model_, arguments_.nominal,
93
0
                           arguments_.exercise->date(0),
94
0
                           arguments_.fixedResetDates[startIndex],
95
0
                           arguments_.fixedPayDates, amounts, startIndex);
96
0
        Brent s1d;
97
0
        Rate minStrike = -8.0;
98
0
        Rate maxStrike = 8.0;
99
0
        s1d.setMaxEvaluations(10000);
100
0
        s1d.setLowerBound(minStrike);
101
0
        s1d.setUpperBound(maxStrike);
102
0
        Rate rStar = s1d.solve(finder, 1e-8, 0.00, minStrike,
103
0
                               maxStrike); // this is actually yStar
104
105
0
        Option::Type w =
106
0
            arguments_.type == Swap::Payer ? Option::Put : Option::Call;
107
0
        Size size = arguments_.fixedCoupons.size();
108
109
0
        Real value = 0.0;
110
0
        for (Size i = startIndex; i < size; i++) {
111
            // Real fixedPayTime =
112
            // dayCounter.yearFraction(referenceDate,arguments_.fixedPayDates[i]);
113
0
            Real strike =
114
0
                model_->zerobond(arguments_.fixedPayDates[i],
115
0
                                 arguments_.exercise->date(0), rStar) /
116
0
                model_->zerobond(arguments_.fixedResetDates[startIndex],
117
0
                                 arguments_.exercise->date(0), rStar);
118
0
            Real dboValue =
119
0
                model_->zerobondOption(w, arguments_.exercise->date(0),
120
0
                                       arguments_.fixedResetDates[startIndex],
121
0
                                       arguments_.fixedPayDates[i], strike);
122
0
            value += amounts[i] * dboValue;
123
0
        }
124
0
        results_.value = value;
125
0
    }
126
}