• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lballabio / QuantLib / 30255006850

27 Jul 2026 09:41AM UTC coverage: 74.898% (-0.05%) from 74.944%
30255006850

push

github

lballabio
Avoid deprecated Ubuntu runner

60031 of 80150 relevant lines covered (74.9%)

8637062.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.31
/ql/instruments/capfloor.cpp
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2

3
/*
4
 Copyright (C) 2006, 2014 Ferdinando Ametrano
5
 Copyright (C) 2006 François du Vignaud
6
 Copyright (C) 2001, 2002, 2003 Sadruddin Rejeb
7
 Copyright (C) 2006, 2007 StatPro Italia srl
8
 Copyright (C) 2016 Paolo Mazzocchi
9

10
 This file is part of QuantLib, a free-software/open-source library
11
 for financial quantitative analysts and developers - http://quantlib.org/
12

13
 QuantLib is free software: you can redistribute it and/or modify it
14
 under the terms of the QuantLib license.  You should have received a
15
 copy of the license along with this program; if not, please email
16
 <quantlib-dev@lists.sf.net>. The license is also available online at
17
 <https://www.quantlib.org/license.shtml>.
18

19
 This program is distributed in the hope that it will be useful, but WITHOUT
20
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21
 FOR A PARTICULAR PURPOSE.  See the license for more details.
22
*/
23

24
#include <ql/any.hpp>
25
#include <ql/cashflows/cashflows.hpp>
26
#include <ql/instruments/capfloor.hpp>
27
#include <ql/math/solvers1d/newtonsafe.hpp>
28
#include <ql/pricingengines/capfloor/bacheliercapfloorengine.hpp>
29
#include <ql/pricingengines/capfloor/blackcapfloorengine.hpp>
30
#include <ql/quotes/simplequote.hpp>
31
#include <ql/termstructures/yieldtermstructure.hpp>
32
#include <ql/utilities/dataformatters.hpp>
33
#include <utility>
34

35
namespace QuantLib {
36

37
    namespace {
38

39
        class ImpliedCapVolHelper {
40
          public:
41
            ImpliedCapVolHelper(const CapFloor&,
42
                                Handle<YieldTermStructure> discountCurve,
43
                                Real targetValue,
44
                                Real displacement,
45
                                VolatilityType type);
46
            Real operator()(Volatility x) const;
47
            Real derivative(Volatility x) const;
48
          private:
49
            ext::shared_ptr<PricingEngine> engine_;
50
            Handle<YieldTermStructure> discountCurve_;
51
            Real targetValue_;
52
            ext::shared_ptr<SimpleQuote> vol_;
53
            const Instrument::results* results_;
54
        };
55

56
        ImpliedCapVolHelper::ImpliedCapVolHelper(const CapFloor& cap,
756✔
57
                                                 Handle<YieldTermStructure> discountCurve,
58
                                                 Real targetValue,
59
                                                 Real displacement,
60
                                                 VolatilityType type)
756✔
61
        : discountCurve_(std::move(discountCurve)), targetValue_(targetValue),
756✔
62
          vol_(ext::make_shared<SimpleQuote>(-1.0)) {
756✔
63

64
            // vol_ is set an implausible value, so that calculation is forced
65
            // at first ImpliedCapVolHelper::operator()(Volatility x) call
66
            Handle<Quote> h(vol_);
756✔
67

68
            switch (type) {
756✔
69
            case ShiftedLognormal:
756✔
70
                engine_ = ext::shared_ptr<PricingEngine>(new
756✔
71
                    BlackCapFloorEngine(discountCurve_, h, Actual365Fixed(),
×
72
                                                                displacement));
3,024✔
73
                break;
756✔
74
            case Normal:
×
75
                engine_ = ext::shared_ptr<PricingEngine>(new
×
76
                    BachelierCapFloorEngine(discountCurve_, h, 
77
                                                            Actual365Fixed()));
×
78
                break;
×
79
            default:
×
80
                QL_FAIL("unknown VolatilityType (" << type << ")");
×
81
                break;
82
            }
83

84
            cap.setupArguments(engine_->getArguments());
756✔
85

86
            results_ =
756✔
87
                dynamic_cast<const Instrument::results*>(engine_->getResults());
756✔
88
        }
756✔
89

90
        Real ImpliedCapVolHelper::operator()(Volatility x) const {
5,941✔
91
            if (x!=vol_->value()) {
5,941✔
92
                vol_->setValue(x);
5,817✔
93
                engine_->calculate();
5,817✔
94
            }
95
            return results_->value-targetValue_;
5,941✔
96
        }
97

98
        Real ImpliedCapVolHelper::derivative(Volatility x) const {
3,933✔
99
            if (x!=vol_->value()) {
3,933✔
100
                vol_->setValue(x);
×
101
                engine_->calculate();
×
102
            }
103
            auto vega_ = results_->additionalResults.find("vega");
3,933✔
104
            QL_REQUIRE(vega_ != results_->additionalResults.end(),
3,933✔
105
                       "vega not provided");
106
            return std::any_cast<Real>(vega_->second);
3,933✔
107
        }
108
    }
109

110
    std::ostream& operator<<(std::ostream& out,
×
111
                             CapFloor::Type t) {
112
        switch (t) {
×
113
          case CapFloor::Cap:
×
114
            return out << "Cap";
×
115
          case CapFloor::Floor:
×
116
            return out << "Floor";
×
117
          case CapFloor::Collar:
×
118
            return out << "Collar";
×
119
          default:
×
120
            QL_FAIL("unknown CapFloor::Type (" << Integer(t) << ")");
×
121
        }
122
    }
123

124
    CapFloor::CapFloor(CapFloor::Type type,
7,380✔
125
                       Leg floatingLeg,
126
                       std::vector<Rate> capRates,
127
                       std::vector<Rate> floorRates)
×
128
    : type_(type), floatingLeg_(std::move(floatingLeg)), capRates_(std::move(capRates)),
7,380✔
129
      floorRates_(std::move(floorRates)) {
7,380✔
130
        if (type_ == Cap || type_ == Collar) {
7,380✔
131
            QL_REQUIRE(!capRates_.empty(), "no cap rates given");
4,199✔
132
            capRates_.reserve(floatingLeg_.size());
4,199✔
133
            while (capRates_.size() < floatingLeg_.size())
70,489✔
134
                capRates_.push_back(capRates_.back());
66,290✔
135
        }
136
        if (type_ == Floor || type_ == Collar) {
7,380✔
137
            QL_REQUIRE(!floorRates_.empty(), "no floor rates given");
4,184✔
138
            floorRates_.reserve(floatingLeg_.size());
4,184✔
139
            while (floorRates_.size() < floatingLeg_.size())
70,396✔
140
                floorRates_.push_back(floorRates_.back());
66,212✔
141
        }
142
        Leg::const_iterator i;
143
        for (i = floatingLeg_.begin(); i != floatingLeg_.end(); ++i)
132,464✔
144
            registerWith(*i);
250,168✔
145

146
        registerWith(Settings::instance().evaluationDate());
7,380✔
147
    }
7,380✔
148

149
    CapFloor::CapFloor(CapFloor::Type type, Leg floatingLeg, const std::vector<Rate>& strikes)
7,840✔
150
    : type_(type), floatingLeg_(std::move(floatingLeg)) {
7,840✔
151
        QL_REQUIRE(!strikes.empty(), "no strikes given");
7,840✔
152
        if (type_ == Cap) {
7,840✔
153
            capRates_ = strikes;
4,390✔
154
            capRates_.reserve(floatingLeg_.size());
4,390✔
155
            while (capRates_.size() < floatingLeg_.size())
215,619✔
156
                capRates_.push_back(capRates_.back());
211,229✔
157
        } else if (type_ == Floor) {
3,450✔
158
            floorRates_ = strikes;
3,450✔
159
            floorRates_.reserve(floatingLeg_.size());
3,450✔
160
            while (floorRates_.size() < floatingLeg_.size())
200,579✔
161
                floorRates_.push_back(floorRates_.back());
197,129✔
162
        } else
163
            QL_FAIL("only Cap/Floor types allowed in this constructor");
×
164

165
        Leg::const_iterator i;
166
        for (i = floatingLeg_.begin(); i != floatingLeg_.end(); ++i)
424,038✔
167
            registerWith(*i);
832,396✔
168

169
        registerWith(Settings::instance().evaluationDate());
7,840✔
170
    }
7,840✔
171

172
    bool CapFloor::isExpired() const {
116,773✔
173
        for (Size i=floatingLeg_.size(); i>0; --i)
116,773✔
174
            if (!floatingLeg_[i-1]->hasOccurred())
116,773✔
175
                return false;
176
        return true;
177
    }
178

179
    Date CapFloor::startDate() const {
×
180
        return CashFlows::startDate(floatingLeg_);
×
181
    }
182

183
    Date CapFloor::maturityDate() const {
×
184
        return CashFlows::maturityDate(floatingLeg_);
×
185
    }
186

187
    ext::shared_ptr<FloatingRateCoupon>
188
    CapFloor::lastFloatingRateCoupon() const {
549✔
189
        ext::shared_ptr<CashFlow> lastCF(floatingLeg_.back());
190
        ext::shared_ptr<FloatingRateCoupon> lastFloatingCoupon =
191
            ext::dynamic_pointer_cast<FloatingRateCoupon>(lastCF);
549✔
192
        return lastFloatingCoupon;
549✔
193
    }
194

195
    ext::shared_ptr<CapFloor> CapFloor::optionlet(const Size i) const {
×
196
        QL_REQUIRE(i < floatingLeg().size(),
×
197
                   io::ordinal(i+1) << " optionlet does not exist, only " <<
198
                   floatingLeg().size());
199
        Leg cf(1, floatingLeg()[i]);
×
200

201
        std::vector<Rate> cap, floor;
202
        if (type() == Cap || type() == Collar)
×
203
            cap.push_back(capRates()[i]);
×
204
        if (type() == Floor || type() == Collar)
×
205
            floor.push_back(floorRates()[i]);
×
206

207
        return ext::make_shared<CapFloor>(type(), cf, cap, floor);
×
208
    }
×
209

210
    void CapFloor::setupArguments(PricingEngine::arguments* args) const {
116,773✔
211
        auto* arguments = dynamic_cast<CapFloor::arguments*>(args);
116,773✔
212
        QL_REQUIRE(arguments != nullptr, "wrong argument type");
116,773✔
213

214
        Size n = floatingLeg_.size();
215

216
        arguments->startDates.resize(n);
116,773✔
217
        arguments->fixingDates.resize(n);
116,773✔
218
        arguments->endDates.resize(n);
116,773✔
219
        arguments->accrualTimes.resize(n);
116,773✔
220
        arguments->forwards.resize(n);
116,773✔
221
        arguments->nominals.resize(n);
116,773✔
222
        arguments->gearings.resize(n);
116,773✔
223
        arguments->capRates.resize(n);
116,773✔
224
        arguments->floorRates.resize(n);
116,773✔
225
        arguments->spreads.resize(n);
116,773✔
226
        arguments->indexes.resize(n);
116,773✔
227

228
        arguments->type = type_;
116,773✔
229

230
        Date today = Settings::instance().evaluationDate();
116,773✔
231

232
        for (Size i=0; i<n; ++i) {
1,369,986✔
233
            ext::shared_ptr<FloatingRateCoupon> coupon =
234
                ext::dynamic_pointer_cast<FloatingRateCoupon>(
235
                                                             floatingLeg_[i]);
1,253,213✔
236
            QL_REQUIRE(coupon, "non-FloatingRateCoupon given");
1,253,213✔
237
            arguments->startDates[i] = coupon->accrualStartDate();
1,253,213✔
238
            arguments->fixingDates[i] = coupon->fixingDate();
1,253,213✔
239
            arguments->endDates[i] = coupon->date();
1,253,213✔
240

241
            // this is passed explicitly for precision
242
            arguments->accrualTimes[i] = coupon->accrualPeriod();
1,253,213✔
243

244
            // this is passed explicitly for precision...
245
            if (arguments->endDates[i] >= today) { // ...but only if needed
1,253,213✔
246
                arguments->forwards[i] = coupon->adjustedFixing();
1,253,213✔
247
            } else {
248
                arguments->forwards[i] = Null<Rate>();
×
249
            }
250

251
            arguments->nominals[i] = coupon->nominal();
1,253,213✔
252
            Spread spread = coupon->spread();
1,253,213✔
253
            Real gearing = coupon->gearing();
1,253,213✔
254
            arguments->gearings[i] = gearing;
1,253,213✔
255
            arguments->spreads[i] = spread;
1,253,213✔
256

257
            if (type_ == Cap || type_ == Collar)
1,253,213✔
258
                arguments->capRates[i] = (capRates_[i]-spread)/gearing;
995,187✔
259
            else
260
                arguments->capRates[i] = Null<Rate>();
258,026✔
261

262
            if (type_ == Floor || type_ == Collar)
1,253,213✔
263
                arguments->floorRates[i] = (floorRates_[i]-spread)/gearing;
273,836✔
264
            else
265
                arguments->floorRates[i] = Null<Rate>();
979,377✔
266

267
            arguments->indexes[i] = coupon->index();
1,253,213✔
268
        }
269
    }
116,773✔
270

271
    void CapFloor::deepUpdate() {
×
272
        for (auto& i : floatingLeg_) {
×
273
            i->deepUpdate();
×
274
        }
275
        update();
×
276
    }
×
277

278
    void CapFloor::arguments::validate() const {
116,017✔
279
        QL_REQUIRE(endDates.size() == startDates.size(),
116,017✔
280
                   "number of start dates (" << startDates.size()
281
                   << ") different from that of end dates ("
282
                   << endDates.size() << ")");
283
        QL_REQUIRE(accrualTimes.size() == startDates.size(),
116,017✔
284
                   "number of start dates (" << startDates.size()
285
                   << ") different from that of accrual times ("
286
                   << accrualTimes.size() << ")");
287
        QL_REQUIRE(type == CapFloor::Floor ||
116,017✔
288
                   capRates.size() == startDates.size(),
289
                   "number of start dates (" << startDates.size()
290
                   << ") different from that of cap rates ("
291
                   << capRates.size() << ")");
292
        QL_REQUIRE(type == CapFloor::Cap ||
116,017✔
293
                   floorRates.size() == startDates.size(),
294
                   "number of start dates (" << startDates.size()
295
                   << ") different from that of floor rates ("
296
                   << floorRates.size() << ")");
297
        QL_REQUIRE(gearings.size() == startDates.size(),
116,017✔
298
                   "number of start dates (" << startDates.size()
299
                   << ") different from that of gearings ("
300
                   << gearings.size() << ")");
301
        QL_REQUIRE(spreads.size() == startDates.size(),
116,017✔
302
                   "number of start dates (" << startDates.size()
303
                   << ") different from that of spreads ("
304
                   << spreads.size() << ")");
305
        QL_REQUIRE(nominals.size() == startDates.size(),
116,017✔
306
                   "number of start dates (" << startDates.size()
307
                   << ") different from that of nominals ("
308
                   << nominals.size() << ")");
309
        QL_REQUIRE(forwards.size() == startDates.size(),
116,017✔
310
                   "number of start dates (" << startDates.size()
311
                   << ") different from that of forwards ("
312
                   << forwards.size() << ")");
313
    }
116,017✔
314

315
    Rate CapFloor::atmRate(const YieldTermStructure& discountCurve) const {
506✔
316
        bool includeSettlementDateFlows = false;
317
        Date settlementDate = discountCurve.referenceDate();
506✔
318
        return CashFlows::atmRate(floatingLeg_, discountCurve,
506✔
319
                                  includeSettlementDateFlows,
320
                                  settlementDate);
506✔
321
    }
322

323
    Volatility CapFloor::impliedVolatility(Real targetValue,
756✔
324
                                           const Handle<YieldTermStructure>& d,
325
                                           Volatility guess,
326
                                           Real accuracy,
327
                                           Natural maxEvaluations,
328
                                           Volatility minVol,
329
                                           Volatility maxVol,
330
                                           VolatilityType type,
331
                                           Real displacement) const {
332
        //calculate();
333
        QL_REQUIRE(!isExpired(), "instrument expired");
756✔
334

335
        ImpliedCapVolHelper f(*this, d, targetValue, displacement, type);
1,512✔
336
        //Brent solver;
337
        NewtonSafe solver;
338
        solver.setMaxEvaluations(maxEvaluations);
756✔
339
        return solver.solve(f, accuracy, guess, minVol, maxVol);
1,512✔
340
    }
756✔
341

342
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc