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

lballabio / QuantLib / 8467932009

28 Mar 2024 01:18PM UTC coverage: 72.497% (+0.07%) from 72.426%
8467932009

Pull #1593

github

web-flow
Merge 9b4efa33c into d6f6c13a5
Pull Request #1593: allow swaptions to take OvernightIndexedSwap

103 of 127 new or added lines in 13 files covered. (81.1%)

373 existing lines in 21 files now uncovered.

54966 of 75818 relevant lines covered (72.5%)

8708317.57 hits per line

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

83.66
/ql/time/schedule.cpp
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2

3
/*
4
 Copyright (C) 2006, 2007, 2008, 2010, 2011, 2015 Ferdinando Ametrano
5
 Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl
6
 Copyright (C) 2009, 2012 StatPro Italia srl
7

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

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

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

22
#include <ql/optional.hpp>
23
#include <ql/settings.hpp>
24
#include <ql/time/imm.hpp>
25
#include <ql/time/schedule.hpp>
26
#include <utility>
27

28
namespace QuantLib {
29

30
    namespace {
31

32
        Date nextTwentieth(const Date& d, DateGeneration::Rule rule) {
506✔
33
            Date result = Date(20, d.month(), d.year());
506✔
34
            if (result < d)
506✔
35
                result += 1*Months;
212✔
36
            if (rule == DateGeneration::TwentiethIMM ||
506✔
37
                rule == DateGeneration::OldCDS ||
38
                rule == DateGeneration::CDS ||
506✔
39
                rule == DateGeneration::CDS2015) {
40
                Month m = result.month();
506✔
41
                if (m % 3 != 0) { // not a main IMM nmonth
506✔
42
                    Integer skip = 3 - m%3;
156✔
43
                    result += skip*Months;
156✔
44
                }
45
            }
46
            return result;
506✔
47
        }
48

49
        bool allowsEndOfMonth(const Period& tenor) {
106,074✔
50
            return (tenor.units() == Months || tenor.units() == Years)
64,749✔
51
                && tenor >= 1*Months;
91,158✔
52
        }
53

54
    }
55

56

57
    Schedule::Schedule(const std::vector<Date>& dates,
886✔
58
                       Calendar calendar,
59
                       BusinessDayConvention convention,
60
                       const ext::optional<BusinessDayConvention>& terminationDateConvention,
61
                       const ext::optional<Period>& tenor,
62
                       const ext::optional<DateGeneration::Rule>& rule,
63
                       const ext::optional<bool>& endOfMonth,
64
                       std::vector<bool> isRegular)
886✔
65
    : tenor_(tenor), calendar_(std::move(calendar)), convention_(convention),
886✔
66
      terminationDateConvention_(terminationDateConvention), rule_(rule), dates_(dates),
1,772✔
67
      isRegular_(std::move(isRegular)) {
886✔
68

69
        if (tenor && !allowsEndOfMonth(*tenor))
891✔
70
            endOfMonth_ = false;
71
        else
72
            endOfMonth_ = endOfMonth;
886✔
73

74
        QL_REQUIRE(isRegular_.empty() || isRegular_.size() == dates.size() - 1,
5✔
75
                   "isRegular size (" << isRegular_.size()
76
                                      << ") must be zero or equal to the number of dates minus 1 ("
77
                                      << dates.size() - 1 << ")");
78
    }
886✔
79

80
    Schedule::Schedule(Date effectiveDate,
106,069✔
81
                       const Date& terminationDate,
82
                       const Period& tenor,
83
                       Calendar cal,
84
                       BusinessDayConvention convention,
85
                       BusinessDayConvention terminationDateConvention,
86
                       DateGeneration::Rule rule,
87
                       bool endOfMonth,
88
                       const Date& first,
89
                       const Date& nextToLast)
106,069✔
90
    : tenor_(tenor), calendar_(std::move(cal)), convention_(convention),
106,069✔
91
      terminationDateConvention_(terminationDateConvention), rule_(rule),
106,069✔
92
      endOfMonth_(allowsEndOfMonth(tenor) ? endOfMonth : false),
106,069✔
93
      firstDate_(first == effectiveDate ? Date() : first),
106,069✔
94
      nextToLastDate_(nextToLast == terminationDate ? Date() : nextToLast) {
106,069✔
95
        // sanity checks
96
        QL_REQUIRE(terminationDate != Date(), "null termination date");
106,069✔
97

98
        // in many cases (e.g. non-expired bonds) the effective date is not
99
        // really necessary. In these cases a decent placeholder is enough
100
        if (effectiveDate==Date() && first==Date()
106,069✔
101
                                  && rule==DateGeneration::Backward) {
106,069✔
102
            Date evalDate = Settings::instance().evaluationDate();
×
103
            QL_REQUIRE(evalDate < terminationDate, "null effective date");
×
104
            Natural y;
105
            if (nextToLast != Date()) {
×
106
                y = (nextToLast - evalDate)/366 + 1;
×
107
                effectiveDate = nextToLast - y*Years;
×
108
            } else {
109
                y = (terminationDate - evalDate)/366 + 1;
×
110
                effectiveDate = terminationDate - y*Years;
×
111
            }
112
        } else
113
            QL_REQUIRE(effectiveDate != Date(), "null effective date");
106,069✔
114

115
        QL_REQUIRE(effectiveDate < terminationDate,
106,069✔
116
                   "effective date (" << effectiveDate
117
                   << ") later than or equal to termination date ("
118
                   << terminationDate << ")");
119

120
        if (tenor.length()==0)
106,069✔
121
            rule_ = DateGeneration::Zero;
122
        else
123
            QL_REQUIRE(tenor.length()>0,
106,067✔
124
                       "non positive tenor (" << tenor << ") not allowed");
125

126
        if (firstDate_ != Date()) {
106,069✔
127
            switch (*rule_) {
19✔
128
              case DateGeneration::Backward:
19✔
129
              case DateGeneration::Forward:
130
                QL_REQUIRE(firstDate_ > effectiveDate &&
19✔
131
                           firstDate_ <= terminationDate,
132
                           "first date (" << firstDate_ <<
133
                           ") out of effective-termination date range (" <<
134
                           effectiveDate << ", " << terminationDate << "]");
135
                // we should ensure that the above condition is still
136
                // verified after adjustment
137
                break;
138
              case DateGeneration::ThirdWednesday:
×
139
                  QL_REQUIRE(IMM::isIMMdate(firstDate_, false),
×
140
                             "first date (" << firstDate_ <<
141
                             ") is not an IMM date");
142
                break;
143
              case DateGeneration::Zero:
×
144
              case DateGeneration::Twentieth:
145
              case DateGeneration::TwentiethIMM:
146
              case DateGeneration::OldCDS:
147
              case DateGeneration::CDS:
148
              case DateGeneration::CDS2015:
149
                QL_FAIL("first date incompatible with " << *rule_ <<
×
150
                        " date generation rule");
151
              default:
×
152
                QL_FAIL("unknown rule (" << Integer(*rule_) << ")");
×
153
            }
154
        }
155
        if (nextToLastDate_ != Date()) {
106,069✔
156
            switch (*rule_) {
11✔
157
              case DateGeneration::Backward:
11✔
158
              case DateGeneration::Forward:
159
                QL_REQUIRE(nextToLastDate_ >= effectiveDate &&
11✔
160
                           nextToLastDate_ < terminationDate,
161
                           "next to last date (" << nextToLastDate_ <<
162
                           ") out of effective-termination date range [" <<
163
                           effectiveDate << ", " << terminationDate << ")");
164
                // we should ensure that the above condition is still
165
                // verified after adjustment
166
                break;
167
              case DateGeneration::ThirdWednesday:
×
168
                QL_REQUIRE(IMM::isIMMdate(nextToLastDate_, false),
×
169
                           "next-to-last date (" << nextToLastDate_ <<
170
                           ") is not an IMM date");
171
                break;
172
              case DateGeneration::Zero:
×
173
              case DateGeneration::Twentieth:
174
              case DateGeneration::TwentiethIMM:
175
              case DateGeneration::OldCDS:
176
              case DateGeneration::CDS:
177
              case DateGeneration::CDS2015:
178
                QL_FAIL("next to last date incompatible with " << *rule_ <<
×
179
                        " date generation rule");
180
              default:
×
181
                QL_FAIL("unknown rule (" << Integer(*rule_) << ")");
×
182
            }
183
        }
184

185

186
        // calendar needed for endOfMonth adjustment
187
        Calendar nullCalendar = NullCalendar();
106,069✔
188
        Integer periods = 1;
189
        Date seed, exitDate;
106,069✔
190
        switch (*rule_) {
106,069✔
191

192
          case DateGeneration::Zero:
193
            tenor_ = 0*Years;
194
            dates_.push_back(effectiveDate);
2✔
195
            dates_.push_back(terminationDate);
2✔
196
            isRegular_.push_back(true);
2✔
197
            break;
198

199
          case DateGeneration::Backward:
85,804✔
200

201
            dates_.push_back(terminationDate);
85,804✔
202

203
            seed = terminationDate;
85,804✔
204
            if (nextToLastDate_ != Date()) {
85,804✔
205
                dates_.push_back(nextToLastDate_);
11✔
206
                Date temp = nullCalendar.advance(seed,
22✔
207
                    -periods*(*tenor_), convention, *endOfMonth_);
11✔
208
                isRegular_.push_back(temp == nextToLastDate_);
11✔
209
                seed = nextToLastDate_;
11✔
210
            }
211

212
            exitDate = effectiveDate;
85,804✔
213
            if (firstDate_ != Date())
85,804✔
214
                exitDate = firstDate_;
10✔
215

216
            for (;;) {
217
                Date temp = nullCalendar.advance(seed,
3,934,658✔
218
                    -periods*(*tenor_), convention, *endOfMonth_);
3,934,658✔
219
                if (temp < exitDate) {
3,934,658✔
220
                    if (firstDate_ != Date() &&
85,814✔
221
                        (calendar_.adjust(dates_.back(),convention)!=
10✔
222
                         calendar_.adjust(firstDate_,convention))) {
85,810✔
223
                        dates_.push_back(firstDate_);
4✔
224
                        isRegular_.push_back(false);
4✔
225
                    }
226
                    break;
85,804✔
227
                } else {
228
                    // skip dates that would result in duplicates
229
                    // after adjustment
230
                    if (calendar_.adjust(dates_.back(),convention)!=
3,848,854✔
231
                        calendar_.adjust(temp,convention)) {
7,697,708✔
232
                        dates_.push_back(temp);
3,068,133✔
233
                        isRegular_.push_back(true);
3,068,133✔
234
                    }
235
                    ++periods;
3,848,854✔
236
                }
237
            }
3,848,854✔
238

239
            if (calendar_.adjust(dates_.back(),convention)!=
85,804✔
240
                calendar_.adjust(effectiveDate,convention)) {
171,608✔
241
                dates_.push_back(effectiveDate);
10,584✔
242
                isRegular_.push_back(false);
10,584✔
243
            }
244
            std::reverse(dates_.begin(), dates_.end());
85,804✔
245
            std::reverse(isRegular_.begin(), isRegular_.end());
85,804✔
246
            break;
247

248
          case DateGeneration::Twentieth:
418✔
249
          case DateGeneration::TwentiethIMM:
250
          case DateGeneration::ThirdWednesday:
251
          case DateGeneration::ThirdWednesdayInclusive:
252
          case DateGeneration::OldCDS:
253
          case DateGeneration::CDS:
254
          case DateGeneration::CDS2015:
255
            QL_REQUIRE(!*endOfMonth_,
418✔
256
                       "endOfMonth convention incompatible with " << *rule_ <<
257
                       " date generation rule");
258
          // fall through
259
          case DateGeneration::Forward:
260

261
            if (*rule_ == DateGeneration::CDS || *rule_ == DateGeneration::CDS2015) {
40,304✔
262
                Date prev20th = previousTwentieth(effectiveDate, *rule_);
306✔
263
                if (calendar_.adjust(prev20th, convention) > effectiveDate) {
306✔
264
                    dates_.push_back(prev20th - 3 * Months);
20✔
265
                    isRegular_.push_back(true);
20✔
266
                }
267
                dates_.push_back(prev20th);
306✔
268
            } else {
269
                dates_.push_back(effectiveDate);
19,957✔
270
            }
271

272
            seed = dates_.back();
20,263✔
273

274
            if (firstDate_!=Date()) {
20,263✔
275
                dates_.push_back(firstDate_);
9✔
276
                Date temp = nullCalendar.advance(seed, periods*(*tenor_),
9✔
277
                                                 convention, *endOfMonth_);
9✔
278
                if (temp!=firstDate_)
9✔
279
                    isRegular_.push_back(false);
4✔
280
                else
281
                    isRegular_.push_back(true);
5✔
282
                seed = firstDate_;
9✔
283
            } else if (*rule_ == DateGeneration::Twentieth ||
40,508✔
284
                       *rule_ == DateGeneration::TwentiethIMM ||
20,254✔
285
                       *rule_ == DateGeneration::OldCDS ||
20,211✔
286
                       *rule_ == DateGeneration::CDS ||
40,398✔
287
                       *rule_ == DateGeneration::CDS2015) {
19,922✔
288
                Date next20th = nextTwentieth(effectiveDate, *rule_);
416✔
289
                if (*rule_ == DateGeneration::OldCDS) {
416✔
290
                    // distance rule inforced in natural days
291
                    static const Date::serial_type stubDays = 30;
292
                    if (next20th - effectiveDate < stubDays) {
67✔
293
                        // +1 will skip this one and get the next
294
                        next20th = nextTwentieth(next20th + 1, *rule_);
44✔
295
                    }
296
                }
297
                if (next20th != effectiveDate) {
416✔
298
                    dates_.push_back(next20th);
363✔
299
                    isRegular_.push_back(*rule_ == DateGeneration::CDS || *rule_ == DateGeneration::CDS2015);
591✔
300
                    seed = next20th;
363✔
301
                }
302
            }
303

304
            exitDate = terminationDate;
20,263✔
305
            if (nextToLastDate_ != Date())
20,263✔
UNCOV
306
                exitDate = nextToLastDate_;
×
307
            for (;;) {
308
                Date temp = nullCalendar.advance(seed, periods*(*tenor_),
260,699✔
309
                                                 convention, *endOfMonth_);
260,699✔
310
                if (temp > exitDate) {
260,699✔
311
                    if (nextToLastDate_ != Date() &&
20,263✔
UNCOV
312
                        (calendar_.adjust(dates_.back(),convention)!=
×
313
                         calendar_.adjust(nextToLastDate_,convention))) {
20,263✔
UNCOV
314
                        dates_.push_back(nextToLastDate_);
×
315
                        isRegular_.push_back(false);
×
316
                    }
317
                    break;
20,263✔
318
                } else {
319
                    // skip dates that would result in duplicates
320
                    // after adjustment
321
                    if (calendar_.adjust(dates_.back(),convention)!=
240,436✔
322
                        calendar_.adjust(temp,convention)) {
480,872✔
323
                        dates_.push_back(temp);
240,271✔
324
                        isRegular_.push_back(true);
240,271✔
325
                    }
326
                    ++periods;
240,436✔
327
                }
328
            }
240,436✔
329

330
            if (calendar_.adjust(dates_.back(),terminationDateConvention)!=
20,263✔
331
                calendar_.adjust(terminationDate,terminationDateConvention)) {
40,526✔
332
                if (*rule_ == DateGeneration::Twentieth ||
376✔
333
                    *rule_ == DateGeneration::TwentiethIMM ||
188✔
334
                    *rule_ == DateGeneration::OldCDS ||
145✔
335
                    *rule_ == DateGeneration::CDS ||
333✔
336
                    *rule_ == DateGeneration::CDS2015) {
144✔
337
                    dates_.push_back(nextTwentieth(terminationDate, *rule_));
46✔
338
                    isRegular_.push_back(true);
46✔
339
                } else {
340
                    dates_.push_back(terminationDate);
142✔
341
                    isRegular_.push_back(false);
142✔
342
                }
343
            }
344

345
            break;
346

UNCOV
347
          default:
×
348
            QL_FAIL("unknown rule (" << Integer(*rule_) << ")");
×
349
        }
350

351
        // adjustments
352
        if (*rule_==DateGeneration::ThirdWednesday)
106,069✔
UNCOV
353
            for (Size i=1; i<dates_.size()-1; ++i)
×
354
                dates_[i] = Date::nthWeekday(3, Wednesday,
×
355
                                             dates_[i].month(),
356
                                             dates_[i].year());
357
        else if (*rule_ == DateGeneration::ThirdWednesdayInclusive)
106,069✔
358
            for (auto& date : dates_)
8✔
359
                date = Date::nthWeekday(3, Wednesday, date.month(), date.year());
6✔
360

361
        // first date not adjusted for old CDS schedules
362
        if (convention != Unadjusted && *rule_ != DateGeneration::OldCDS)
197,083✔
363
            dates_.front() = calendar_.adjust(dates_.front(), convention);
90,947✔
364

365
        // termination date is NOT adjusted as per ISDA
366
        // specifications, unless otherwise specified in the
367
        // confirmation of the deal or unless we're creating a CDS
368
        // schedule
369
        if (terminationDateConvention != Unadjusted 
370
            && *rule_ != DateGeneration::CDS 
89,887✔
371
            && *rule_ != DateGeneration::CDS2015) {
195,955✔
372
            dates_.back() = calendar_.adjust(dates_.back(), 
89,886✔
373
                                             terminationDateConvention);
374
        }
375

376
        if (*endOfMonth_ && calendar_.isEndOfMonth(seed)) {
106,069✔
377
            // adjust to end of month
378
            if (convention == Unadjusted) {
19✔
379
                for (Size i=1; i<dates_.size()-1; ++i)
147✔
380
                    dates_[i] = Date::endOfMonth(dates_[i]);
134✔
381
            } else {
382
                for (Size i=1; i<dates_.size()-1; ++i)
41✔
383
                    dates_[i] = calendar_.endOfMonth(dates_[i]);
35✔
384
            }
385
        } else {
386
            for (Size i=1; i<dates_.size()-1; ++i)
3,319,397✔
387
                dates_[i] = calendar_.adjust(dates_[i], convention);
3,213,347✔
388
        }
389

390
        // Final safety checks to remove extra next-to-last date, if
391
        // necessary.  It can happen to be equal or later than the end
392
        // date due to EOM adjustments (see the Schedule test suite
393
        // for an example).
394
        if (dates_.size() >= 2 && dates_[dates_.size()-2] >= dates_.back()) {
106,069✔
395
            // there might be two dates only, then isRegular_ has size one
396
            if (isRegular_.size() >= 2) {
3✔
397
                isRegular_[isRegular_.size() - 2] =
3✔
398
                    (dates_[dates_.size() - 2] == dates_.back());
3✔
399
            }
400
            dates_[dates_.size() - 2] = dates_.back();
3✔
401
            dates_.pop_back();
402
            isRegular_.pop_back();
403
        }
404
        if (dates_.size() >= 2 && dates_[1] <= dates_.front()) {
106,069✔
UNCOV
405
            isRegular_[1] =
×
406
                (dates_[1] == dates_.front());
×
407
            dates_[1] = dates_.front();
×
408
            dates_.erase(dates_.begin());
409
            isRegular_.erase(isRegular_.begin());
410
        }
411

412
        QL_ENSURE(dates_.size()>1,
106,069✔
413
            "degenerate single date (" << dates_[0] << ") schedule" <<
414
            "\n seed date: " << seed <<
415
            "\n exit date: " << exitDate <<
416
            "\n effective date: " << effectiveDate <<
417
            "\n first date: " << first <<
418
            "\n next to last date: " << nextToLast <<
419
            "\n termination date: " << terminationDate <<
420
            "\n generation rule: " << *rule_ <<
421
            "\n end of month: " << *endOfMonth_);
422
    }
106,069✔
423

424
    Schedule Schedule::after(const Date& truncationDate) const {
2✔
425
        Schedule result = *this;
2✔
426

427
        QL_REQUIRE(truncationDate < result.dates_.back(),
2✔
428
            "truncation date " << truncationDate <<
429
            " must be before the last schedule date " <<
430
            result.dates_.back());
431
        if (truncationDate > result.dates_[0]) {
2✔
432
            // remove earlier dates
433
            while (result.dates_[0] < truncationDate) {
29✔
434
                result.dates_.erase(result.dates_.begin());
27✔
435
                if (!result.isRegular_.empty())
436
                    result.isRegular_.erase(result.isRegular_.begin());
27✔
437
            }
438

439
            // add truncationDate if missing
440
            if (truncationDate != result.dates_.front()) {
2✔
441
                result.dates_.insert(result.dates_.begin(), truncationDate);
1✔
442
                result.isRegular_.insert(result.isRegular_.begin(), false);
1✔
443
                result.terminationDateConvention_ = Unadjusted;
444
            }
445
            else {
446
                result.terminationDateConvention_ = convention_;
447
            }
448

449
            if (result.nextToLastDate_ <= truncationDate)
2✔
450
                result.nextToLastDate_ = Date();
2✔
451
            if (result.firstDate_ <= truncationDate)
2✔
452
                result.firstDate_ = Date();
2✔
453
        }
454

455
        return result;
2✔
UNCOV
456
    }
×
457

458
    Schedule Schedule::until(const Date& truncationDate) const {
16✔
459
        Schedule result = *this;
16✔
460

461
        QL_REQUIRE(truncationDate>result.dates_[0],
16✔
462
                   "truncation date " << truncationDate <<
463
                   " must be later than schedule first date " <<
464
                   result.dates_[0]);
465
        if (truncationDate<result.dates_.back()) {
16✔
466
            // remove later dates
467
            while (result.dates_.back()>truncationDate) {
162✔
468
                result.dates_.pop_back();
469
                if(!result.isRegular_.empty())
470
                    result.isRegular_.pop_back();
471
            }
472

473
            // add truncationDate if missing
474
            if (truncationDate!=result.dates_.back()) {
16✔
475
                result.dates_.push_back(truncationDate);
14✔
476
                result.isRegular_.push_back(false);
14✔
477
                result.terminationDateConvention_ = Unadjusted;
478
            } else {
479
                result.terminationDateConvention_ = convention_;
480
            }
481

482
            if (result.nextToLastDate_>=truncationDate)
16✔
UNCOV
483
                result.nextToLastDate_ = Date();
×
484
            if (result.firstDate_>=truncationDate)
16✔
UNCOV
485
                result.firstDate_ = Date();
×
486
        }
487

488
        return result;
16✔
UNCOV
489
    }
×
490

491
    std::vector<Date>::const_iterator
UNCOV
492
    Schedule::lower_bound(const Date& refDate) const {
×
493
        Date d = (refDate==Date() ?
×
494
                  Settings::instance().evaluationDate() :
×
495
                  refDate);
×
496
        return std::lower_bound(dates_.begin(), dates_.end(), d);
×
497
    }
498

UNCOV
499
    Date Schedule::nextDate(const Date& refDate) const {
×
500
        auto res = lower_bound(refDate);
×
501
        if (res!=dates_.end())
×
502
            return *res;
×
503
        else
UNCOV
504
            return {};
×
505
    }
506

UNCOV
507
    Date Schedule::previousDate(const Date& refDate) const {
×
508
        auto res = lower_bound(refDate);
×
509
        if (res!=dates_.begin())
×
510
            return *(--res);
×
511
        else
UNCOV
512
            return {};
×
513
    }
514

515
    bool Schedule::hasIsRegular() const { return !isRegular_.empty(); }
412,956✔
516

517
    bool Schedule::isRegular(Size i) const {
205,821✔
518
        QL_REQUIRE(hasIsRegular(),
205,821✔
519
                   "full interface (isRegular) not available");
520
        QL_REQUIRE(i<=isRegular_.size() && i>0,
205,821✔
521
                   "index (" << i << ") must be in [1, " <<
522
                   isRegular_.size() <<"]");
523
        return isRegular_[i-1];
205,821✔
524
    }
525

526
    const std::vector<bool>& Schedule::isRegular() const {
6✔
UNCOV
527
        QL_REQUIRE(!isRegular_.empty(), "full interface (isRegular) not available");
×
528
        return isRegular_;
6✔
529
    }
530

531
    MakeSchedule& MakeSchedule::from(const Date& effectiveDate) {
18,030✔
532
        effectiveDate_ = effectiveDate;
18,030✔
533
        return *this;
18,030✔
534
    }
535

536
    MakeSchedule& MakeSchedule::to(const Date& terminationDate) {
18,030✔
537
        terminationDate_ = terminationDate;
18,030✔
538
        return *this;
18,030✔
539
    }
540

541
    MakeSchedule& MakeSchedule::withTenor(const Period& tenor) {
11,627✔
542
        tenor_ = tenor;
543
        return *this;
11,627✔
544
    }
545

546
    MakeSchedule& MakeSchedule::withFrequency(Frequency frequency) {
6,403✔
547
        tenor_ = Period(frequency);
6,403✔
548
        return *this;
6,403✔
549
    }
550

551
    MakeSchedule& MakeSchedule::withCalendar(const Calendar& calendar) {
18,015✔
552
        calendar_ = calendar;
553
        return *this;
18,015✔
554
    }
555

556
    MakeSchedule& MakeSchedule::withConvention(BusinessDayConvention conv) {
17,999✔
557
        convention_ = conv;
558
        return *this;
17,999✔
559
    }
560

561
    MakeSchedule& MakeSchedule::withTerminationDateConvention(
311✔
562
                                                BusinessDayConvention conv) {
563
        terminationDateConvention_ = conv;
564
        return *this;
311✔
565
    }
566

567
    MakeSchedule& MakeSchedule::withRule(DateGeneration::Rule r) {
338✔
568
        rule_ = r;
338✔
569
        return *this;
338✔
570
    }
571

572
    MakeSchedule& MakeSchedule::forwards() {
6,265✔
573
        rule_ = DateGeneration::Forward;
6,265✔
574
        return *this;
6,265✔
575
    }
576

577
    MakeSchedule& MakeSchedule::backwards() {
11,390✔
578
        rule_ = DateGeneration::Backward;
11,390✔
579
        return *this;
11,390✔
580
    }
581

582
    MakeSchedule& MakeSchedule::endOfMonth(bool flag) {
628✔
583
        endOfMonth_ = flag;
628✔
584
        return *this;
628✔
585
    }
586

587
    MakeSchedule& MakeSchedule::withFirstDate(const Date& d) {
13✔
588
        firstDate_ = d;
13✔
589
        return *this;
13✔
590
    }
591

592
    MakeSchedule& MakeSchedule::withNextToLastDate(const Date& d) {
9✔
593
        nextToLastDate_ = d;
9✔
594
        return *this;
9✔
595
    }
596

597
    MakeSchedule::operator Schedule() const {
18,030✔
598
        // check for mandatory arguments
599
        QL_REQUIRE(effectiveDate_ != Date(), "effective date not provided");
18,030✔
600
        QL_REQUIRE(terminationDate_ != Date(), "termination date not provided");
18,030✔
601
        QL_REQUIRE(tenor_, "tenor/frequency not provided");
18,030✔
602

603
        // set dynamic defaults:
604
        BusinessDayConvention convention;
605
        // if a convention was set, we use it.
606
        if (convention_) { // NOLINT(readability-implicit-bool-conversion)
18,030✔
607
            convention = *convention_;
17,999✔
608
        } else {
609
            if (!calendar_.empty()) {
31✔
610
                // ...if we set a calendar, we probably want it to be used;
611
                convention = Following;
612
            } else {
613
                // if not, we don't care.
614
                convention = Unadjusted;
615
            }
616
        }
617

618
        BusinessDayConvention terminationDateConvention;
619
        // if set explicitly, we use it;
620
        if (terminationDateConvention_) { // NOLINT(readability-implicit-bool-conversion)
18,030✔
621
            terminationDateConvention = *terminationDateConvention_;
311✔
622
        } else {
623
            // Unadjusted as per ISDA specification
624
            terminationDateConvention = convention;
625
        }
626

627
        Calendar calendar = calendar_;
628
        // if no calendar was set...
629
        if (calendar.empty()) {
18,030✔
630
            // ...we use a null one.
631
            calendar = NullCalendar();
30✔
632
        }
633

634
        return Schedule(effectiveDate_, terminationDate_, *tenor_, calendar,
18,030✔
635
                        convention, terminationDateConvention,
636
                        rule_, endOfMonth_, firstDate_, nextToLastDate_);
72,120✔
637
    }
638

639
    Date previousTwentieth(const Date& d, DateGeneration::Rule rule) {
579✔
640
        Date result = Date(20, d.month(), d.year());
579✔
641
        if (result > d)
579✔
642
            result -= 1 * Months;
241✔
643
        if (rule == DateGeneration::TwentiethIMM ||
579✔
644
            rule == DateGeneration::OldCDS ||
645
            rule == DateGeneration::CDS ||
579✔
646
            rule == DateGeneration::CDS2015) {
647
            Month m = result.month();
579✔
648
            if (m % 3 != 0) { // not a main IMM nmonth
579✔
649
                Integer skip = m % 3;
650
                result -= skip * Months;
315✔
651
            }
652
        }
653
        return result;
579✔
654
    }
655

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

© 2026 Coveralls, Inc