Coverage Report

Created: 2026-02-03 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/instruments/swaption.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) 2006 Cristina Duminuco
6
 Copyright (C) 2006 Marco Bianchetti
7
 Copyright (C) 2007 StatPro Italia srl
8
 Copyright (C) 2014 Ferdinando Ametrano
9
 Copyright (C) 2016, 2018 Peter Caspers
10
11
 This file is part of QuantLib, a free-software/open-source library
12
 for financial quantitative analysts and developers - http://quantlib.org/
13
14
 QuantLib is free software: you can redistribute it and/or modify it
15
 under the terms of the QuantLib license.  You should have received a
16
 copy of the license along with this program; if not, please email
17
 <quantlib-dev@lists.sf.net>. The license is also available online at
18
 <https://www.quantlib.org/license.shtml>.
19
20
 This program is distributed in the hope that it will be useful, but WITHOUT
21
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
22
 FOR A PARTICULAR PURPOSE.  See the license for more details.
23
*/
24
25
#include <ql/any.hpp>
26
#include <ql/exercise.hpp>
27
#include <ql/instruments/swaption.hpp>
28
#include <ql/math/solvers1d/newtonsafe.hpp>
29
#include <ql/pricingengines/swaption/blackswaptionengine.hpp>
30
#include <ql/quotes/simplequote.hpp>
31
#include <ql/shared_ptr.hpp>
32
#include <utility>
33
34
namespace QuantLib {
35
36
    namespace {
37
38
        class ImpliedSwaptionVolHelper {
39
          public:
40
            ImpliedSwaptionVolHelper(const Swaption&,
41
                                     Handle<YieldTermStructure> discountCurve,
42
                                     Real targetValue,
43
                                     Real displacement,
44
                                     VolatilityType type);
45
            Real operator()(Volatility x) const;
46
            Real derivative(Volatility x) const;
47
          private:
48
            ext::shared_ptr<PricingEngine> engine_;
49
            Handle<YieldTermStructure> discountCurve_;
50
            Real targetValue_;
51
            ext::shared_ptr<SimpleQuote> vol_;
52
            const Instrument::results* results_;
53
        };
54
55
        ImpliedSwaptionVolHelper::ImpliedSwaptionVolHelper(const Swaption& swaption,
56
                                                           Handle<YieldTermStructure> discountCurve,
57
                                                           Real targetValue,
58
                                                           Real displacement,
59
                                                           VolatilityType type)
60
0
        : discountCurve_(std::move(discountCurve)), targetValue_(targetValue),
61
0
          vol_(ext::make_shared<SimpleQuote>(-1.0)) {
62
63
            // vol_ is set an implausible value, so that calculation is forced
64
            // at first ImpliedSwaptionVolHelper::operator()(Volatility x) call
65
66
0
            Handle<Quote> h(vol_);
67
68
0
            switch (type) {
69
0
            case ShiftedLognormal:
70
0
                engine_ = ext::make_shared<BlackSwaptionEngine>(
71
0
                    discountCurve_, h, Actual365Fixed(), displacement);
72
0
                break;
73
0
            case Normal:
74
0
                engine_ = ext::make_shared<BachelierSwaptionEngine>(
75
0
                    discountCurve_, h, Actual365Fixed());
76
0
                break;
77
0
            default:
78
0
                QL_FAIL("unknown VolatilityType (" << type << ")");
79
0
                break;
80
0
            }
81
0
            swaption.setupArguments(engine_->getArguments());
82
0
            results_ = dynamic_cast<const Instrument::results *>(
83
0
                engine_->getResults());
84
0
        }
85
86
0
        Real ImpliedSwaptionVolHelper::operator()(Volatility x) const {
87
0
            if (x!=vol_->value()) {
88
0
                vol_->setValue(x);
89
0
                engine_->calculate();
90
0
            }
91
0
            return results_->value-targetValue_;
92
0
        }
93
94
0
        Real ImpliedSwaptionVolHelper::derivative(Volatility x) const {
95
0
            if (x!=vol_->value()) {
96
0
                vol_->setValue(x);
97
0
                engine_->calculate();
98
0
            }
99
0
            auto vega_ = results_->additionalResults.find("vega");
100
0
            QL_REQUIRE(vega_ != results_->additionalResults.end(),
101
0
                       "vega not provided");
102
0
            return ext::any_cast<Real>(vega_->second);
103
0
        }
104
    }
105
106
    std::ostream& operator<<(std::ostream& out,
107
0
                             Settlement::Type t) {
108
0
        switch (t) {
109
0
          case Settlement::Physical:
110
0
            return out << "Delivery";
111
0
          case Settlement::Cash:
112
0
            return out << "Cash";
113
0
          default:
114
0
            QL_FAIL("unknown Settlement::Type(" << Integer(t) << ")");
115
0
        }
116
0
    }
117
118
0
    std::ostream& operator<<(std::ostream& out, Settlement::Method m) {
119
0
        switch (m) {
120
0
        case Settlement::PhysicalOTC:
121
0
            return out << "PhysicalOTC";
122
0
        case Settlement::PhysicalCleared:
123
0
            return out << "PhysicalCleared";
124
0
        case Settlement::CollateralizedCashPrice:
125
0
            return out << "CollateralizedCashPrice";
126
0
        case Settlement::ParYieldCurve:
127
0
            return out << "ParYieldCurve";
128
0
        default:
129
0
            QL_FAIL("unknown Settlement::Method(" << Integer(m) << ")");
130
0
        }
131
0
    }
132
133
    Swaption::Swaption(ext::shared_ptr<FixedVsFloatingSwap> swap,
134
                       const ext::shared_ptr<Exercise>& exercise,
135
                       Settlement::Type delivery,
136
                       Settlement::Method settlementMethod)
137
0
    : Option(ext::shared_ptr<Payoff>(), exercise), swap_(std::move(swap)),
138
0
      settlementType_(delivery), settlementMethod_(settlementMethod) {
139
0
        registerWith(swap_);
140
        // When we ask for the NPV of an expired swaption, the
141
        // swap is not recalculated and thus wouldn't forward
142
        // later notifications according to the default behavior of
143
        // LazyObject instances.  This means that even if the
144
        // evaluation date changes so that the swaption is no longer
145
        // expired, the instrument wouldn't be notified and thus it
146
        // wouldn't recalculate.  To avoid this, we override the
147
        // default behavior of the underlying swap.
148
0
        swap_->alwaysForwardNotifications();
149
150
0
        vanilla_ = ext::dynamic_pointer_cast<VanillaSwap>(swap_);
151
0
    }
Unexecuted instantiation: QuantLib::Swaption::Swaption(boost::shared_ptr<QuantLib::FixedVsFloatingSwap>, boost::shared_ptr<QuantLib::Exercise> const&, QuantLib::Settlement::Type, QuantLib::Settlement::Method)
Unexecuted instantiation: QuantLib::Swaption::Swaption(boost::shared_ptr<QuantLib::FixedVsFloatingSwap>, boost::shared_ptr<QuantLib::Exercise> const&, QuantLib::Settlement::Type, QuantLib::Settlement::Method)
152
153
0
    void Swaption::deepUpdate() {
154
0
        swap_->deepUpdate();
155
0
        update();
156
0
    }
157
158
0
    bool Swaption::isExpired() const {
159
0
        return detail::simple_event(exercise_->dates().back()).hasOccurred();
160
0
    }
161
162
0
    void Swaption::setupArguments(PricingEngine::arguments* args) const {
163
164
0
        swap_->setupArguments(args);
165
0
        auto* arguments = dynamic_cast<Swaption::arguments*>(args);
166
167
0
        QL_REQUIRE(arguments != nullptr, "wrong argument type");
168
169
0
        arguments->swap = swap_;
170
0
        arguments->settlementType = settlementType_;
171
0
        arguments->settlementMethod = settlementMethod_;
172
0
        arguments->exercise = exercise_;
173
0
    }
174
175
0
    void Swaption::arguments::validate() const {
176
0
        FixedVsFloatingSwap::arguments::validate();
177
0
        QL_REQUIRE(swap, "swap not set");
178
0
        QL_REQUIRE(exercise, "exercise not set");
179
0
        Settlement::checkTypeAndMethodConsistency(settlementType, settlementMethod);
180
0
    }
181
182
    Volatility Swaption::impliedVolatility(Real targetValue,
183
                                           const Handle<YieldTermStructure>& discountCurve,
184
                                           Volatility guess,
185
                                           Real accuracy,
186
                                           Natural maxEvaluations,
187
                                           Volatility minVol,
188
                                           Volatility maxVol,
189
                                           VolatilityType type,
190
                                           Real displacement,
191
0
                                           PriceType priceType) const {
192
193
0
        QL_REQUIRE(!isExpired(), "instrument expired");
194
0
        QL_REQUIRE(exercise_->type() == Exercise::European, "not a European option");
195
196
0
        if (priceType == Forward) {
197
            // convert to spot
198
0
            targetValue *= discountCurve->discount(exercise_->date(0));
199
0
        }
200
201
0
        ImpliedSwaptionVolHelper f(*this, discountCurve, targetValue, displacement, type);
202
0
        NewtonSafe solver;
203
0
        solver.setMaxEvaluations(maxEvaluations);
204
0
        return solver.solve(f, accuracy, guess, minVol, maxVol);
205
0
    }
206
207
    void Settlement::checkTypeAndMethodConsistency(
208
                                        Settlement::Type settlementType,
209
0
                                        Settlement::Method settlementMethod) {
210
0
        if (settlementType == Physical) {
211
0
            QL_REQUIRE(settlementMethod == PhysicalOTC ||
212
0
                       settlementMethod == PhysicalCleared,
213
0
                       "invalid settlement method for physical settlement");
214
0
        }
215
0
        if (settlementType == Cash) {
216
            QL_REQUIRE(settlementMethod == CollateralizedCashPrice ||
217
0
                       settlementMethod == ParYieldCurve,
218
0
                       "invalid settlement method for cash settlement");
219
0
        }
220
0
    }
221
222
}