Coverage Report

Created: 2025-08-05 06:45

/src/quantlib/ql/pricingengines/swaption/basketgeneratingengine.hpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2013 Peter Caspers
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
 <http://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
/*! \file basketgeneratingengine.hpp
21
    \brief base class for pricing engines capable of
22
           generating a calibration basket
23
*/
24
25
#ifndef quantlib_basketgeneratingengine_hpp
26
#define quantlib_basketgeneratingengine_hpp
27
28
#include <ql/cashflows/fixedratecoupon.hpp>
29
#include <ql/cashflows/iborcoupon.hpp>
30
#include <ql/indexes/swapindex.hpp>
31
#include <ql/instruments/vanillaswap.hpp>
32
#include <ql/math/optimization/costfunction.hpp>
33
#include <ql/models/shortrate/onefactormodels/gaussian1dmodel.hpp>
34
#include <ql/qldefines.hpp>
35
#include <ql/termstructures/volatility/swaption/swaptionvolstructure.hpp>
36
#include <ql/termstructures/yieldtermstructure.hpp>
37
#include <utility>
38
39
namespace QuantLib {
40
41
    /* \warning the generated calibrating swaptions have a strike floored at
42
       0.1bp (minus lognormal shift, if applicable), this is not true for atm
43
       swaptions where the strike is generated in the swaption helper.
44
45
       \warning the standardSwapBase index should have associated forward and
46
       discount curves. These curves are used for setup of the swaption helper.
47
       This means that the market price of the calibration instrument is calculated
48
       using these curves. Therefore the model price must be calculated using the
49
       same curves, otherwise the calibration gets incosistent, i.e. the pricing
50
       engine used for model calibration has to be capable of using the same curves
51
       as associated to the index. Also the volatility structure passed to construct
52
       the calibration helper should use curves that are consistent with the model
53
       calibration curve setup. Finally the discountCurve given in the constructor
54
       should be the same curve as the discounting curve of the swapIndex used to
55
       determine the calibration basket. */
56
57
    class BasketGeneratingEngine {
58
59
      public:
60
61
        typedef enum CalibrationBasketType {
62
            Naive,
63
            MaturityStrikeByDeltaGamma
64
        } CalibrationBasketType;
65
66
0
        virtual ~BasketGeneratingEngine() = default;
67
68
        std::vector<ext::shared_ptr<BlackCalibrationHelper>>
69
        calibrationBasket(const ext::shared_ptr<Exercise>& exercise,
70
                          const ext::shared_ptr<SwapIndex>& standardSwapBase,
71
                          const ext::shared_ptr<SwaptionVolatilityStructure>& swaptionVolatility,
72
                          CalibrationBasketType basketType = MaturityStrikeByDeltaGamma) const;
73
74
      protected:
75
        BasketGeneratingEngine(const ext::shared_ptr<Gaussian1dModel>& model,
76
                               Handle<Quote> oas,
77
                               Handle<YieldTermStructure> discountCurve)
78
0
        : onefactormodel_(model), oas_(std::move(oas)), discountCurve_(std::move(discountCurve)) {}
79
80
        BasketGeneratingEngine(Handle<Gaussian1dModel> model,
81
                               Handle<Quote> oas,
82
                               Handle<YieldTermStructure> discountCurve)
83
        : onefactormodel_(std::move(model)), oas_(std::move(oas)),
84
0
          discountCurve_(std::move(discountCurve)) {}
85
86
        virtual Real underlyingNpv(const Date& expiry, Real y) const = 0;
87
88
        virtual Swap::Type underlyingType() const = 0;
89
90
        virtual const Date underlyingLastDate() const = 0;
91
92
        virtual const Array initialGuess(const Date &expiry) const = 0; // return (nominal,
93
                                                                        // maturity, rate)
94
95
      private:
96
97
        const Handle<Gaussian1dModel> onefactormodel_;
98
        const Handle<Quote> oas_;
99
        const Handle<YieldTermStructure> discountCurve_;
100
101
        class MatchHelper : public CostFunction {
102
          public:
103
            MatchHelper(const Swap::Type type,
104
                        const Real npv,
105
                        const Real delta,
106
                        const Real gamma,
107
                        ext::shared_ptr<Gaussian1dModel> model,
108
                        ext::shared_ptr<SwapIndex> indexBase,
109
                        const Date& expiry,
110
                        const Real maxMaturity,
111
                        const Real h)
112
0
            : type_(type), mdl_(std::move(model)), indexBase_(std::move(indexBase)),
113
0
              expiry_(expiry), maxMaturity_(maxMaturity), npv_(npv), delta_(delta), gamma_(gamma),
114
0
              h_(h) {}
115
116
            Real NPV(const ext::shared_ptr<VanillaSwap>& swap,
117
                     Real fixedRate,
118
                     Real nominal,
119
                     Real y,
120
0
                     int type) const {
121
0
                Real npv = 0.0;
122
0
                for (const auto& i : swap->fixedLeg()) {
123
0
                    ext::shared_ptr<FixedRateCoupon> c =
124
0
                        ext::dynamic_pointer_cast<FixedRateCoupon>(i);
125
0
                    npv -=
126
0
                        fixedRate * c->accrualPeriod() * nominal *
127
0
                        mdl_->zerobond(c->date(), expiry_, y,
128
0
                                       indexBase_->discountingTermStructure());
129
0
                }
130
0
                for (const auto& i : swap->floatingLeg()) {
131
0
                    ext::shared_ptr<IborCoupon> c = ext::dynamic_pointer_cast<IborCoupon>(i);
132
0
                    npv +=
133
0
                        mdl_->forwardRate(c->fixingDate(), expiry_, y,
134
0
                                          c->iborIndex()) *
135
0
                        c->accrualPeriod() * nominal *
136
0
                        mdl_->zerobond(c->date(), expiry_, y,
137
0
                                       indexBase_->discountingTermStructure());
138
0
                }
139
0
                return (Real)type * npv;
140
0
            }
141
142
0
            Real value(const Array& v) const override {
143
0
                Array vals = values(v);
144
0
                Real res = 0.0;
145
0
                for (Real val : vals) {
146
0
                    res += val * val;
147
0
                }
148
0
                return std::sqrt(res / vals.size());
149
0
            }
150
151
0
            Array values(const Array& v) const override {
152
                // transformations
153
0
                int type = type_; // start with same type as non standard
154
                                  // underlying (1 means payer, -1 receiver)
155
0
                Real nominal = std::fabs(v[0]);
156
0
                if (v[0] < 0.0)
157
0
                    type *= -1;
158
0
                Real maturity = std::min(std::fabs(v[1]), maxMaturity_);
159
160
0
                Real fixedRate = v[2]; // allow for negative rates explicitly
161
                // (though it might not be reasonable for calibration depending
162
                // on the model to calibrate and the market instrument quotation)
163
0
                Size years = (Size)std::floor(maturity);
164
0
                maturity -= (Real)years;
165
0
                maturity *= 12.0;
166
0
                Size months = (Size)std::floor(maturity);
167
0
                Real alpha = 1.0 - (maturity - (Real)months);
168
0
                if (years == 0 && months == 0) {
169
0
                    months = 1;  // ensure a maturity of at least one month ...
170
0
                    alpha = 1.0; // ... but in this case only look at the lower
171
                                 // maturity swap
172
0
                }
173
                // maturity -= (Real)months; maturity *= 365.25;
174
                // Size days = (Size)std::floor(maturity);
175
                // Real alpha = 1.0-(maturity-(Real)days);
176
                // generate swap
177
0
                Period lowerPeriod =
178
0
                    years * Years + months * Months;           //+days*Days;
179
0
                Period upperPeriod = lowerPeriod + 1 * Months; // 1*Days;
180
0
                ext::shared_ptr<SwapIndex> tmpIndexLower, tmpIndexUpper;
181
0
                tmpIndexLower = indexBase_->clone(lowerPeriod);
182
0
                tmpIndexUpper = indexBase_->clone(upperPeriod);
183
0
                ext::shared_ptr<VanillaSwap> swapLower =
184
0
                    tmpIndexLower->underlyingSwap(expiry_);
185
0
                ext::shared_ptr<VanillaSwap> swapUpper =
186
0
                    tmpIndexUpper->underlyingSwap(expiry_);
187
                // compute npv, delta, gamma
188
0
                Real npvm =
189
0
                    alpha * NPV(swapLower, fixedRate, nominal, -h_, type) +
190
0
                    (1.0 - alpha) *
191
0
                        NPV(swapUpper, fixedRate, nominal, -h_, type);
192
0
                Real npv =
193
0
                    alpha * NPV(swapLower, fixedRate, nominal, 0.0, type) +
194
0
                    (1.0 - alpha) *
195
0
                        NPV(swapUpper, fixedRate, nominal, 0.0, type);
196
0
                Real npvu =
197
0
                    alpha * NPV(swapLower, fixedRate, nominal, h_, type) +
198
0
                    (1.0 - alpha) *
199
0
                        NPV(swapUpper, fixedRate, nominal, h_, type);
200
0
                Real delta = (npvu - npvm) / (2.0 * h_);
201
0
                Real gamma = (npvu - 2.0 * npv + npvm) / (h_ * h_);
202
203
                // debug output global standard underlying npv
204
                // Real xtmp = -5.0;
205
                // std::cout << "globalStandardNpv;";
206
                // while (xtmp <= 5.0 + QL_EPSILON) {
207
                //     std::cout << alpha *NPV(swapLower, fixedRate, nominal, xtmp,
208
                //                             type) +
209
                //                      (1.0 - alpha) * NPV(swapUpper, fixedRate,
210
                //                                          nominal, xtmp, type)
211
                //               << ";";
212
                //     xtmp += 0.1;
213
                // }
214
                // std::cout << std::endl;
215
                // end debug output
216
217
                // return target function values
218
0
                Array res(3);
219
0
                res[0] = (npv - npv_) / delta_;
220
0
                res[1] = (delta - delta_) / delta_;
221
0
                res[2] = (gamma - gamma_) / gamma_;
222
0
                return res;
223
0
            }
224
225
            const Swap::Type type_;
226
            const ext::shared_ptr<Gaussian1dModel> mdl_;
227
            const ext::shared_ptr<SwapIndex> indexBase_;
228
            const Date expiry_;
229
            const Real maxMaturity_;
230
            const Real npv_, delta_, gamma_, h_;
231
        };
232
    };
233
}
234
235
#endif