Coverage Report

Created: 2025-10-14 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/instruments/nonstandardswap.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2013, 2016 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
 <https://www.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
#include <ql/cashflows/capflooredcoupon.hpp>
21
#include <ql/cashflows/cashflows.hpp>
22
#include <ql/cashflows/cashflowvectors.hpp>
23
#include <ql/cashflows/cmscoupon.hpp>
24
#include <ql/cashflows/couponpricer.hpp>
25
#include <ql/cashflows/iborcoupon.hpp>
26
#include <ql/cashflows/simplecashflow.hpp>
27
#include <ql/indexes/iborindex.hpp>
28
#include <ql/indexes/swapindex.hpp>
29
#include <ql/instruments/nonstandardswap.hpp>
30
#include <ql/termstructures/yieldtermstructure.hpp>
31
#include <ql/optional.hpp>
32
#include <utility>
33
34
namespace QuantLib {
35
36
    NonstandardSwap::NonstandardSwap(const FixedVsFloatingSwap &fromVanilla)
37
0
        : Swap(2), type_(fromVanilla.type()),
38
0
          fixedNominal_(std::vector<Real>(fromVanilla.fixedLeg().size(),
39
0
                                          fromVanilla.nominal())),
40
0
          floatingNominal_(std::vector<Real>(fromVanilla.floatingLeg().size(),
41
0
                                             fromVanilla.nominal())),
42
0
          fixedSchedule_(fromVanilla.fixedSchedule()),
43
0
          fixedRate_(std::vector<Real>(fromVanilla.fixedLeg().size(),
44
0
                                       fromVanilla.fixedRate())),
45
0
          fixedDayCount_(fromVanilla.fixedDayCount()),
46
0
          floatingSchedule_(fromVanilla.floatingSchedule()),
47
0
          iborIndex_(fromVanilla.iborIndex()),
48
0
          spread_(std::vector<Real>(fromVanilla.floatingLeg().size(), fromVanilla.spread())),
49
0
          gearing_(std::vector<Real>(fromVanilla.floatingLeg().size(), 1.0)),
50
0
          singleSpreadAndGearing_(true),
51
0
          floatingDayCount_(fromVanilla.floatingDayCount()),
52
0
          paymentConvention_(fromVanilla.paymentConvention()),
53
0
          intermediateCapitalExchange_(false), finalCapitalExchange_(false) {
54
55
0
        init();
56
0
    }
Unexecuted instantiation: QuantLib::NonstandardSwap::NonstandardSwap(QuantLib::FixedVsFloatingSwap const&)
Unexecuted instantiation: QuantLib::NonstandardSwap::NonstandardSwap(QuantLib::FixedVsFloatingSwap const&)
57
58
    NonstandardSwap::NonstandardSwap(const Swap::Type type,
59
                                     std::vector<Real> fixedNominal,
60
                                     const std::vector<Real>& floatingNominal,
61
                                     Schedule fixedSchedule,
62
                                     std::vector<Real> fixedRate,
63
                                     DayCounter fixedDayCount,
64
                                     Schedule floatingSchedule,
65
                                     ext::shared_ptr<IborIndex> iborIndex,
66
                                     const Real gearing,
67
                                     const Spread spread,
68
                                     DayCounter floatingDayCount,
69
                                     const bool intermediateCapitalExchange,
70
                                     const bool finalCapitalExchange,
71
                                     ext::optional<BusinessDayConvention> paymentConvention)
72
0
    : Swap(2), type_(type), fixedNominal_(std::move(fixedNominal)),
73
0
      floatingNominal_(floatingNominal), fixedSchedule_(std::move(fixedSchedule)),
74
0
      fixedRate_(std::move(fixedRate)), fixedDayCount_(std::move(fixedDayCount)),
75
0
      floatingSchedule_(std::move(floatingSchedule)), iborIndex_(std::move(iborIndex)),
76
0
      spread_(std::vector<Real>(floatingNominal.size(), spread)),
77
0
      gearing_(std::vector<Real>(floatingNominal.size(), gearing)), singleSpreadAndGearing_(true),
78
0
      floatingDayCount_(std::move(floatingDayCount)),
79
0
      intermediateCapitalExchange_(intermediateCapitalExchange),
80
0
      finalCapitalExchange_(finalCapitalExchange) {
81
82
0
        if (paymentConvention) // NOLINT(readability-implicit-bool-conversion)
83
0
            paymentConvention_ = *paymentConvention;
84
0
        else
85
0
            paymentConvention_ = floatingSchedule_.businessDayConvention();
86
0
        init();
87
0
    }
Unexecuted instantiation: QuantLib::NonstandardSwap::NonstandardSwap(QuantLib::Swap::Type, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<double, std::__1::allocator<double> > const&, QuantLib::Schedule, std::__1::vector<double, std::__1::allocator<double> >, QuantLib::DayCounter, QuantLib::Schedule, boost::shared_ptr<QuantLib::IborIndex>, double, double, QuantLib::DayCounter, bool, bool, std::__1::optional<QuantLib::BusinessDayConvention>)
Unexecuted instantiation: QuantLib::NonstandardSwap::NonstandardSwap(QuantLib::Swap::Type, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<double, std::__1::allocator<double> > const&, QuantLib::Schedule, std::__1::vector<double, std::__1::allocator<double> >, QuantLib::DayCounter, QuantLib::Schedule, boost::shared_ptr<QuantLib::IborIndex>, double, double, QuantLib::DayCounter, bool, bool, std::__1::optional<QuantLib::BusinessDayConvention>)
88
89
    NonstandardSwap::NonstandardSwap(const Swap::Type type,
90
                                     std::vector<Real> fixedNominal,
91
                                     std::vector<Real> floatingNominal,
92
                                     Schedule fixedSchedule,
93
                                     std::vector<Real> fixedRate,
94
                                     DayCounter fixedDayCount,
95
                                     Schedule floatingSchedule,
96
                                     ext::shared_ptr<IborIndex> iborIndex,
97
                                     std::vector<Real> gearing,
98
                                     std::vector<Spread> spread,
99
                                     DayCounter floatingDayCount,
100
                                     const bool intermediateCapitalExchange,
101
                                     const bool finalCapitalExchange,
102
                                     ext::optional<BusinessDayConvention> paymentConvention)
103
0
    : Swap(2), type_(type), fixedNominal_(std::move(fixedNominal)),
104
0
      floatingNominal_(std::move(floatingNominal)), fixedSchedule_(std::move(fixedSchedule)),
105
0
      fixedRate_(std::move(fixedRate)), fixedDayCount_(std::move(fixedDayCount)),
106
0
      floatingSchedule_(std::move(floatingSchedule)), iborIndex_(std::move(iborIndex)),
107
0
      spread_(std::move(spread)), gearing_(std::move(gearing)), singleSpreadAndGearing_(false),
108
0
      floatingDayCount_(std::move(floatingDayCount)),
109
0
      intermediateCapitalExchange_(intermediateCapitalExchange),
110
0
      finalCapitalExchange_(finalCapitalExchange) {
111
112
0
        if (paymentConvention) // NOLINT(readability-implicit-bool-conversion)
113
0
            paymentConvention_ = *paymentConvention;
114
0
        else
115
0
            paymentConvention_ = floatingSchedule_.businessDayConvention();
116
0
        init();
117
0
    }
Unexecuted instantiation: QuantLib::NonstandardSwap::NonstandardSwap(QuantLib::Swap::Type, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<double, std::__1::allocator<double> >, QuantLib::Schedule, std::__1::vector<double, std::__1::allocator<double> >, QuantLib::DayCounter, QuantLib::Schedule, boost::shared_ptr<QuantLib::IborIndex>, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<double, std::__1::allocator<double> >, QuantLib::DayCounter, bool, bool, std::__1::optional<QuantLib::BusinessDayConvention>)
Unexecuted instantiation: QuantLib::NonstandardSwap::NonstandardSwap(QuantLib::Swap::Type, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<double, std::__1::allocator<double> >, QuantLib::Schedule, std::__1::vector<double, std::__1::allocator<double> >, QuantLib::DayCounter, QuantLib::Schedule, boost::shared_ptr<QuantLib::IborIndex>, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<double, std::__1::allocator<double> >, QuantLib::DayCounter, bool, bool, std::__1::optional<QuantLib::BusinessDayConvention>)
118
119
0
    void NonstandardSwap::init() {
120
121
0
        QL_REQUIRE(fixedNominal_.size() == fixedRate_.size(),
122
0
                   "Fixed nominal size ("
123
0
                       << fixedNominal_.size()
124
0
                       << ") does not match fixed rate size ("
125
0
                       << fixedRate_.size() << ")");
126
127
0
        QL_REQUIRE(fixedNominal_.size() == fixedSchedule_.size() - 1,
128
0
                   "Fixed nominal size (" << fixedNominal_.size()
129
0
                                          << ") does not match schedule size ("
130
0
                                          << fixedSchedule_.size() << ") - 1");
131
132
0
        QL_REQUIRE(floatingNominal_.size() == floatingSchedule_.size() - 1,
133
0
                   "Floating nominal size ("
134
0
                       << floatingNominal_.size()
135
0
                       << ") does not match schedule size ("
136
0
                       << floatingSchedule_.size() << ") - 1");
137
138
0
        QL_REQUIRE(floatingNominal_.size() == spread_.size(),
139
0
                   "Floating nominal size (" << floatingNominal_.size()
140
0
                                             << ") does not match spread size ("
141
0
                                             << spread_.size() << ")");
142
143
0
        QL_REQUIRE(floatingNominal_.size() == gearing_.size(),
144
0
                   "Floating nominal size ("
145
0
                       << floatingNominal_.size()
146
0
                       << ") does not match gearing size (" << gearing_.size()
147
0
                       << ")");
148
149
        // if the gearing is zero then the ibor leg will be set up with fixed
150
        // coupons which makes trouble here in this context. We therefore use
151
        // a dirty trick and enforce the gearing to be non zero.
152
0
        for (Real& i : gearing_) {
153
0
            if (close(i, 0.0))
154
0
                i = QL_EPSILON;
155
0
        }
156
157
0
        legs_[0] = FixedRateLeg(fixedSchedule_)
158
0
                       .withNotionals(fixedNominal_)
159
0
                       .withCouponRates(fixedRate_, fixedDayCount_)
160
0
                       .withPaymentAdjustment(paymentConvention_);
161
162
0
        legs_[1] = IborLeg(floatingSchedule_, iborIndex_)
163
0
                       .withNotionals(floatingNominal_)
164
0
                       .withPaymentDayCounter(floatingDayCount_)
165
0
                       .withPaymentAdjustment(paymentConvention_)
166
0
                       .withSpreads(spread_)
167
0
                       .withGearings(gearing_);
168
169
0
        if (intermediateCapitalExchange_) {
170
0
            for (Size i = 0; i < legs_[0].size() - 1; i++) {
171
0
                Real cap = fixedNominal_[i] - fixedNominal_[i + 1];
172
0
                if (!close(cap, 0.0)) {
173
0
                    auto it1 = legs_[0].begin();
174
0
                    std::advance(it1, i + 1);
175
0
                    legs_[0].insert(
176
0
                        it1, ext::shared_ptr<CashFlow>(
177
0
                                 new Redemption(cap, legs_[0][i]->date())));
178
0
                    auto it2 = fixedNominal_.begin();
179
0
                    std::advance(it2, i + 1);
180
0
                    fixedNominal_.insert(it2, fixedNominal_[i]);
181
0
                    auto it3 = fixedRate_.begin();
182
0
                    std::advance(it3, i + 1);
183
0
                    fixedRate_.insert(it3, 0.0);
184
0
                    i++;
185
0
                }
186
0
            }
187
0
            for (Size i = 0; i < legs_[1].size() - 1; i++) {
188
0
                Real cap = floatingNominal_[i] - floatingNominal_[i + 1];
189
0
                if (!close(cap, 0.0)) {
190
0
                    auto it1 = legs_[1].begin();
191
0
                    std::advance(it1, i + 1);
192
0
                    legs_[1].insert(
193
0
                        it1, ext::shared_ptr<CashFlow>(
194
0
                                 new Redemption(cap, legs_[1][i]->date())));
195
0
                    auto it2 = floatingNominal_.begin();
196
0
                    std::advance(it2, i + 1);
197
0
                    floatingNominal_.insert(it2, floatingNominal_[i]);
198
0
                    i++;
199
0
                }
200
0
            }
201
0
        }
202
203
0
        if (finalCapitalExchange_) {
204
0
            legs_[0].push_back(ext::shared_ptr<CashFlow>(
205
0
                new Redemption(fixedNominal_.back(), legs_[0].back()->date())));
206
0
            fixedNominal_.push_back(fixedNominal_.back());
207
0
            fixedRate_.push_back(0.0);
208
0
            legs_[1].push_back(ext::shared_ptr<CashFlow>(new Redemption(
209
0
                floatingNominal_.back(), legs_[1].back()->date())));
210
0
            floatingNominal_.push_back(floatingNominal_.back());
211
0
        }
212
213
0
        for (auto i = legs_[1].begin(); i < legs_[1].end(); ++i)
214
0
            registerWith(*i);
215
216
0
        switch (type_) {
217
0
        case Swap::Payer:
218
0
            payer_[0] = -1.0;
219
0
            payer_[1] = +1.0;
220
0
            break;
221
0
        case Swap::Receiver:
222
0
            payer_[0] = +1.0;
223
0
            payer_[1] = -1.0;
224
0
            break;
225
0
        default:
226
0
            QL_FAIL("Unknown nonstandard-swap type");
227
0
        }
228
0
    }
229
230
0
    void NonstandardSwap::setupArguments(PricingEngine::arguments *args) const {
231
232
0
        Swap::setupArguments(args);
233
234
0
        auto* arguments = dynamic_cast<NonstandardSwap::arguments*>(args);
235
236
0
        if (arguments == nullptr)
237
0
            return; // swap engine ...
238
239
0
        arguments->type = type_;
240
0
        arguments->fixedNominal = fixedNominal_;
241
0
        arguments->floatingNominal = floatingNominal_;
242
0
        arguments->fixedRate = fixedRate_;
243
244
0
        const Leg &fixedCoupons = fixedLeg();
245
246
0
        arguments->fixedResetDates = arguments->fixedPayDates =
247
0
            std::vector<Date>(fixedCoupons.size());
248
0
        arguments->fixedCoupons = std::vector<Real>(fixedCoupons.size());
249
0
        arguments->fixedIsRedemptionFlow =
250
0
            std::vector<bool>(fixedCoupons.size(), false);
251
252
0
        for (Size i = 0; i < fixedCoupons.size(); ++i) {
253
0
            ext::shared_ptr<FixedRateCoupon> coupon =
254
0
                ext::dynamic_pointer_cast<FixedRateCoupon>(fixedCoupons[i]);
255
0
            if (coupon != nullptr) {
256
0
                arguments->fixedPayDates[i] = coupon->date();
257
0
                arguments->fixedResetDates[i] = coupon->accrualStartDate();
258
0
                arguments->fixedCoupons[i] = coupon->amount();
259
0
            } else {
260
0
                ext::shared_ptr<CashFlow> cashflow =
261
0
                    ext::dynamic_pointer_cast<CashFlow>(fixedCoupons[i]);
262
0
                auto j =
263
0
                    std::find(arguments->fixedPayDates.begin(),
264
0
                              arguments->fixedPayDates.end(), cashflow->date());
265
0
                QL_REQUIRE(j != arguments->fixedPayDates.end(),
266
0
                           "nominal redemption on "
267
0
                               << cashflow->date()
268
0
                               << "has no corresponding coupon");
269
0
                Size jIdx = j - arguments->fixedPayDates.begin();
270
0
                arguments->fixedIsRedemptionFlow[i] = true;
271
0
                arguments->fixedCoupons[i] = cashflow->amount();
272
0
                arguments->fixedResetDates[i] =
273
0
                    arguments->fixedResetDates[jIdx];
274
0
                arguments->fixedPayDates[i] = cashflow->date();
275
0
            }
276
0
        }
277
278
0
        const Leg &floatingCoupons = floatingLeg();
279
280
0
        arguments->floatingResetDates = arguments->floatingPayDates =
281
0
            arguments->floatingFixingDates =
282
0
                std::vector<Date>(floatingCoupons.size());
283
0
        arguments->floatingAccrualTimes =
284
0
            std::vector<Time>(floatingCoupons.size());
285
0
        arguments->floatingSpreads =
286
0
            std::vector<Spread>(floatingCoupons.size());
287
0
        arguments->floatingGearings = std::vector<Real>(floatingCoupons.size());
288
0
        arguments->floatingCoupons = std::vector<Real>(floatingCoupons.size());
289
0
        arguments->floatingIsRedemptionFlow =
290
0
            std::vector<bool>(floatingCoupons.size(), false);
291
292
0
        for (Size i = 0; i < floatingCoupons.size(); ++i) {
293
0
            ext::shared_ptr<IborCoupon> coupon =
294
0
                ext::dynamic_pointer_cast<IborCoupon>(floatingCoupons[i]);
295
0
            if (coupon != nullptr) {
296
0
                arguments->floatingResetDates[i] = coupon->accrualStartDate();
297
0
                arguments->floatingPayDates[i] = coupon->date();
298
0
                arguments->floatingFixingDates[i] = coupon->fixingDate();
299
0
                arguments->floatingAccrualTimes[i] = coupon->accrualPeriod();
300
0
                arguments->floatingSpreads[i] = coupon->spread();
301
0
                arguments->floatingGearings[i] = coupon->gearing();
302
0
                try {
303
0
                    arguments->floatingCoupons[i] = coupon->amount();
304
0
                }
305
0
                catch (Error &) {
306
0
                    arguments->floatingCoupons[i] = Null<Real>();
307
0
                }
308
0
            } else {
309
0
                ext::shared_ptr<CashFlow> cashflow =
310
0
                    ext::dynamic_pointer_cast<CashFlow>(floatingCoupons[i]);
311
0
                auto j = std::find(
312
0
                    arguments->floatingPayDates.begin(),
313
0
                    arguments->floatingPayDates.end(), cashflow->date());
314
0
                QL_REQUIRE(j != arguments->floatingPayDates.end(),
315
0
                           "nominal redemption on "
316
0
                               << cashflow->date()
317
0
                               << "has no corresponding coupon");
318
0
                Size jIdx = j - arguments->floatingPayDates.begin();
319
0
                arguments->floatingIsRedemptionFlow[i] = true;
320
0
                arguments->floatingCoupons[i] = cashflow->amount();
321
0
                arguments->floatingResetDates[i] =
322
0
                    arguments->floatingResetDates[jIdx];
323
0
                arguments->floatingFixingDates[i] =
324
0
                    arguments->floatingFixingDates[jIdx];
325
0
                arguments->floatingAccrualTimes[i] = 0.0;
326
0
                arguments->floatingSpreads[i] = 0.0;
327
0
                arguments->floatingGearings[i] = 1.0;
328
0
                arguments->floatingPayDates[i] = cashflow->date();
329
0
            }
330
0
        }
331
332
0
        arguments->iborIndex = iborIndex();
333
0
    }
334
335
0
    void NonstandardSwap::setupExpired() const { Swap::setupExpired(); }
336
337
0
    void NonstandardSwap::fetchResults(const PricingEngine::results *r) const {
338
339
0
        Swap::fetchResults(r);
340
0
    }
341
342
0
    void NonstandardSwap::arguments::validate() const {
343
0
        Swap::arguments::validate();
344
0
        QL_REQUIRE(fixedNominal.size() == fixedPayDates.size(),
345
0
                   "number of fixed leg nominals plus redemption flows "
346
0
                   "different from number of payment dates");
347
0
        QL_REQUIRE(fixedRate.size() == fixedPayDates.size(),
348
0
                   "number of fixed rates plus redemption flows different from "
349
0
                   "number of payment dates");
350
0
        QL_REQUIRE(floatingNominal.size() == floatingPayDates.size(),
351
0
                   "number of float leg nominals different from number of "
352
0
                   "payment dates");
353
0
        QL_REQUIRE(fixedResetDates.size() == fixedPayDates.size(),
354
0
                   "number of fixed start dates different from "
355
0
                   "number of fixed payment dates");
356
0
        QL_REQUIRE(fixedPayDates.size() == fixedCoupons.size(),
357
0
                   "number of fixed payment dates different from "
358
0
                   "number of fixed coupon amounts");
359
0
        QL_REQUIRE(floatingResetDates.size() == floatingPayDates.size(),
360
0
                   "number of floating start dates different from "
361
0
                   "number of floating payment dates");
362
0
        QL_REQUIRE(floatingFixingDates.size() == floatingPayDates.size(),
363
0
                   "number of floating fixing dates different from "
364
0
                   "number of floating payment dates");
365
0
        QL_REQUIRE(floatingAccrualTimes.size() == floatingPayDates.size(),
366
0
                   "number of floating accrual Times different from "
367
0
                   "number of floating payment dates");
368
0
        QL_REQUIRE(floatingSpreads.size() == floatingPayDates.size(),
369
0
                   "number of floating spreads different from "
370
0
                   "number of floating payment dates");
371
0
        QL_REQUIRE(floatingPayDates.size() == floatingCoupons.size(),
372
0
                   "number of floating payment dates different from "
373
0
                   "number of floating coupon amounts");
374
0
    }
375
376
0
    void NonstandardSwap::results::reset() { Swap::results::reset(); }
377
}