Coverage Report

Created: 2025-08-11 06:28

/src/quantlib/ql/cashflows/conundrumpricer.hpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 Copyright (C) 2006 Giorgio Facchinetti
3
 Copyright (C) 2006 Mario Pucci
4
 Copyright (C) 2023 Andre Miemiec
5
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
 <http://quantlib.org/license.shtml>.
15
16
17
 This program is distributed in the hope that it will be useful, but
18
 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19
 or FITNESS FOR A PARTICULAR PURPOSE. See the license for more details. */
20
21
/*! \file conundrumpricer.hpp
22
    \brief CMS-coupon pricer
23
*/
24
25
#ifndef quantlib_conundrum_pricer_hpp
26
#define quantlib_conundrum_pricer_hpp
27
28
#include <ql/cashflows/couponpricer.hpp>
29
#include <ql/instruments/payoffs.hpp>
30
31
namespace QuantLib {
32
33
    class CmsCoupon;
34
    class YieldTermStructure;
35
    class Quote;
36
37
    class VanillaOptionPricer {
38
      public:
39
0
        virtual ~VanillaOptionPricer() = default;
40
        virtual Real operator()(Real strike,
41
                                Option::Type optionType,
42
                                Real deflator) const = 0;
43
    };
44
45
    class MarketQuotedOptionPricer : public VanillaOptionPricer {
46
      public:
47
        MarketQuotedOptionPricer(
48
                Rate forwardValue,
49
                Date expiryDate,
50
                const Period& swapTenor,
51
                const ext::shared_ptr<SwaptionVolatilityStructure>&
52
                                                         volatilityStructure);
53
54
        Real operator()(Real strike, Option::Type optionType, Real deflator) const override;
55
56
      private:
57
        Rate forwardValue_;
58
        Date expiryDate_;
59
        Period swapTenor_;
60
        ext::shared_ptr<SwaptionVolatilityStructure> volatilityStructure_;
61
        ext::shared_ptr<SmileSection> smile_;
62
    };
63
64
    class GFunction {
65
      public:
66
0
        virtual ~GFunction() = default;
67
        virtual Real operator()(Real x) = 0;
68
        virtual Real firstDerivative(Real x) = 0;
69
        virtual Real secondDerivative(Real x) = 0;
70
    };
71
72
    class GFunctionFactory {
73
      public:
74
        enum YieldCurveModel { Standard,
75
                               ExactYield,
76
                               ParallelShifts,
77
                               NonParallelShifts
78
        };
79
80
        GFunctionFactory() = delete;
81
82
        static ext::shared_ptr<GFunction>
83
        newGFunctionStandard(Size q,
84
                             Real delta,
85
                             Size swapLength);
86
        static ext::shared_ptr<GFunction>
87
        newGFunctionExactYield(const CmsCoupon& coupon);
88
        static ext::shared_ptr<GFunction>
89
        newGFunctionWithShifts(const CmsCoupon& coupon,
90
                               const Handle<Quote>& meanReversion);
91
      private:
92
        class GFunctionStandard : public GFunction {
93
          public:
94
            GFunctionStandard(Size q,
95
                              Real delta,
96
                              Size swapLength)
97
0
            : q_(q), delta_(delta), swapLength_(swapLength) {}
98
            Real operator()(Real x) override;
99
            Real firstDerivative(Real x) override;
100
            Real secondDerivative(Real x) override;
101
102
          protected:
103
            /* number of period per year */
104
            const int q_;
105
            /* fraction of a period between the swap start date and
106
               the pay date  */
107
            Real delta_;
108
            /* length of swap*/
109
            Size swapLength_;
110
        };
111
112
        class GFunctionExactYield : public GFunction {
113
          public:
114
            GFunctionExactYield(const CmsCoupon& coupon);
115
            Real operator()(Real x) override;
116
            Real firstDerivative(Real x) override;
117
            Real secondDerivative(Real x) override;
118
119
          protected:
120
            /* fraction of a period between the swap start date and
121
               the pay date  */
122
            Real delta_;
123
            /* accruals fraction*/
124
            std::vector<Time> accruals_;
125
        };
126
127
        class GFunctionWithShifts : public GFunction {
128
129
            Time swapStartTime_;
130
131
            Time shapedPaymentTime_;
132
            std::vector<Time> shapedSwapPaymentTimes_;
133
134
            std::vector<Time> accruals_;
135
            std::vector<Real> swapPaymentDiscounts_;
136
            Real discountAtStart_, discountRatio_;
137
138
            Real swapRateValue_;
139
            Handle<Quote> meanReversion_;
140
141
            Real calibratedShift_ = 0.03, tmpRs_ = 10000000.0;
142
            const Real accuracy_ = 1.0e-14;
143
144
            //* function describing the non-parallel shape of the curve shift*/
145
            Real shapeOfShift(Real s) const;
146
            //* calibration of shift*/
147
            Real calibrationOfShift(Real Rs);
148
            Real functionZ(Real x);
149
            Real derRs_derX(Real x);
150
            Real derZ_derX(Real x);
151
            Real der2Rs_derX2(Real x);
152
            Real der2Z_derX2(Real x);
153
154
            class ObjectiveFunction {
155
                const GFunctionWithShifts& o_;
156
                Real Rs_;
157
                mutable Real derivative_;
158
                public:
159
0
                  virtual ~ObjectiveFunction() = default;
160
0
                  ObjectiveFunction(const GFunctionWithShifts& o, const Real Rs) : o_(o), Rs_(Rs) {}
161
                  virtual Real operator()(const Real& x) const;
162
                  Real derivative(const Real& x) const;
163
                  void setSwapRateValue(Real x);
164
0
                  const GFunctionWithShifts& gFunctionWithShifts() const { return o_; }
165
            };
166
167
            ext::shared_ptr<ObjectiveFunction> objectiveFunction_;
168
          public:
169
            GFunctionWithShifts(const CmsCoupon& coupon, Handle<Quote> meanReversion);
170
            Real operator()(Real x) override;
171
            Real firstDerivative(Real x) override;
172
            Real secondDerivative(Real x) override;
173
        };
174
175
    };
176
177
    inline std::ostream& operator<<(std::ostream& out,
178
0
                                    GFunctionFactory::YieldCurveModel type) {
179
0
        switch (type) {
180
0
          case GFunctionFactory::Standard:
181
0
            return out << "Standard";
182
0
          case GFunctionFactory::ExactYield:
183
0
            return out << "ExactYield";
184
0
          case GFunctionFactory::ParallelShifts:
185
0
            return out << "ParallelShifts";
186
0
          case GFunctionFactory::NonParallelShifts:
187
0
            return out << "NonParallelShifts";
188
0
          default:
189
0
            QL_FAIL("unknown option type");
190
0
        }
191
0
    }
192
193
    //! CMS-coupon pricer
194
    /*! Base class for the pricing of a CMS coupon via static replication
195
        as in Hagan's "Conundrums..." article
196
    */
197
    class HaganPricer: public CmsCouponPricer, public MeanRevertingPricer {
198
      public:
199
        /* */
200
        Real swapletPrice() const override = 0;
201
        Rate swapletRate() const override;
202
        Real capletPrice(Rate effectiveCap) const override;
203
        Rate capletRate(Rate effectiveCap) const override;
204
        Real floorletPrice(Rate effectiveFloor) const override;
205
        Rate floorletRate(Rate effectiveFloor) const override;
206
        /* */
207
        Real meanReversion() const override;
208
0
        void setMeanReversion(const Handle<Quote>& meanReversion) override {
209
0
            unregisterWith(meanReversion_);
210
0
            meanReversion_ = meanReversion;
211
0
            registerWith(meanReversion_);
212
0
            update();
213
0
        };
214
215
      protected:
216
        HaganPricer(const Handle<SwaptionVolatilityStructure>& swaptionVol,
217
                    GFunctionFactory::YieldCurveModel modelOfYieldCurve,
218
                    Handle<Quote> meanReversion);
219
        void initialize(const FloatingRateCoupon& coupon) override;
220
221
        virtual Real optionletPrice(Option::Type optionType,
222
                                    Real strike) const = 0;
223
224
        ext::shared_ptr<YieldTermStructure> rateCurve_;
225
        GFunctionFactory::YieldCurveModel modelOfYieldCurve_;
226
        ext::shared_ptr<GFunction> gFunction_;
227
        const CmsCoupon* coupon_;
228
        Date paymentDate_, fixingDate_;
229
        Rate swapRateValue_;
230
        DiscountFactor discount_;
231
        Real annuity_;
232
        Real gearing_;
233
        Spread spread_;
234
        Real spreadLegValue_;
235
        Rate cutoffForCaplet_ = 2, cutoffForFloorlet_ = 0;
236
        Handle<Quote> meanReversion_;
237
        Period swapTenor_;
238
        ext::shared_ptr<VanillaOptionPricer> vanillaOptionPricer_;
239
    };
240
241
242
    //! CMS-coupon pricer
243
    /*! Prices a cms coupon via static replication as in Hagan's
244
        "Conundrums..." article via numerical integration based on
245
        prices of vanilla swaptions
246
    */
247
    class NumericHaganPricer : public HaganPricer {
248
      public:
249
        NumericHaganPricer(
250
            const Handle<SwaptionVolatilityStructure>& swaptionVol,
251
            GFunctionFactory::YieldCurveModel modelOfYieldCurve,
252
            const Handle<Quote>& meanReversion,
253
            Rate lowerLimit = 0.0,
254
            Rate upperLimit = 1.0,
255
            Real precision = 1.0e-6,
256
            Real hardUpperLimit = QL_MAX_REAL);
257
258
0
        Real upperLimit() const { return upperLimit_; }
259
0
        Real lowerLimit() const { return lowerLimit_; }
260
0
        Real stdDeviations() const { return stdDeviationsForUpperLimit_; }
261
262
        // private:
263
        class Function {
264
          public:
265
0
            virtual ~Function() = default;
266
            virtual Real operator()(Real x) const = 0;
267
        };
268
269
        class ConundrumIntegrand : public Function {
270
            friend class NumericHaganPricer;
271
          public:
272
            ConundrumIntegrand(ext::shared_ptr<VanillaOptionPricer> o,
273
                               const ext::shared_ptr<YieldTermStructure>& rateCurve,
274
                               ext::shared_ptr<GFunction> gFunction,
275
                               Date fixingDate,
276
                               Date paymentDate,
277
                               Real annuity,
278
                               Real forwardValue,
279
                               Real strike,
280
                               Option::Type optionType);
281
            Real operator()(Real x) const override;
282
283
          protected:
284
            Real functionF(Real x) const;
285
            Real firstDerivativeOfF(Real x) const;
286
            Real secondDerivativeOfF(Real x) const;
287
288
            Real strike() const;
289
            Real annuity() const;
290
            Date fixingDate() const;
291
            void setStrike(Real strike);
292
293
            const ext::shared_ptr<VanillaOptionPricer> vanillaOptionPricer_;
294
            const Real forwardValue_, annuity_;
295
            const Date fixingDate_, paymentDate_;
296
            Real strike_;
297
            const Option::Type optionType_;
298
            ext::shared_ptr<GFunction> gFunction_;
299
        };
300
301
        Real integrate(Real a,
302
                       Real b,
303
                       const ConundrumIntegrand& Integrand) const;
304
        Real optionletPrice(Option::Type optionType, Rate strike) const override;
305
        Real swapletPrice() const override;
306
        Real resetUpperLimit(Real stdDeviationsForUpperLimit) const;
307
        Real resetLowerLimit(Real stdDeviationsForLowerLimit) const;
308
        Real refineIntegration(Real integralValue, const ConundrumIntegrand& integrand) const;
309
310
        mutable Real lowerLimit_, stdDeviationsForLowerLimit_, upperLimit_, stdDeviationsForUpperLimit_;
311
        const Real  requiredStdDeviations_ = 8, precision_,
312
                                refiningIntegrationTolerance_ = .0001;
313
        const Real hardUpperLimit_;
314
    };
315
316
    //! CMS-coupon pricer
317
    class AnalyticHaganPricer : public HaganPricer {
318
      public:
319
        AnalyticHaganPricer(
320
            const Handle<SwaptionVolatilityStructure>& swaptionVol,
321
            GFunctionFactory::YieldCurveModel modelOfYieldCurve,
322
            const Handle<Quote>& meanReversion);
323
      protected:
324
        Real optionletPrice(Option::Type optionType, Real strike) const override;
325
        Real swapletPrice() const override;
326
    };
327
328
}
329
330
331
#endif