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

lballabio / QuantLib / 14910176578

08 May 2025 03:28PM UTC coverage: 73.315% (+0.02%) from 73.3%
14910176578

Pull #2195

github

web-flow
Merge 3a61f499c into 5d972fb7b
Pull Request #2195: Added `Handle<Quote>` for spread in `OISRateHelper`

32 of 33 new or added lines in 2 files covered. (96.97%)

277 existing lines in 25 files now uncovered.

56277 of 76761 relevant lines covered (73.31%)

8687029.35 hits per line

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

83.44
/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 <algorithm>
27
#include <utility>
28

29
namespace QuantLib {
30

31
    namespace {
32

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

50
        bool allowsEndOfMonth(const Period& tenor) {
110,052✔
51
            return (tenor.units() == Months || tenor.units() == Years)
69,026✔
52
                && tenor >= 1*Months;
160,335✔
53
        }
54

55
    }
56

57

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

70
        if (tenor && !allowsEndOfMonth(*tenor))
766✔
UNCOV
71
            endOfMonth_ = false;
×
72
        else
73
            endOfMonth_ = endOfMonth;
766✔
74

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

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

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

116
        QL_REQUIRE(effectiveDate < terminationDate,
110,047✔
117
                   "effective date (" << effectiveDate
118
                   << ") later than or equal to termination date ("
119
                   << terminationDate << ")");
120

121
        if (tenor.length()==0)
110,047✔
122
            rule_ = DateGeneration::Zero;
5✔
123
        else
124
            QL_REQUIRE(tenor.length()>0,
110,042✔
125
                       "non positive tenor (" << tenor << ") not allowed");
126

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

186

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

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

200
          case DateGeneration::Backward:
89,779✔
201

202
            dates_.push_back(terminationDate);
89,779✔
203

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

213
            exitDate = effectiveDate;
89,779✔
214
            if (firstDate_ != Date())
89,779✔
215
                exitDate = firstDate_;
10✔
216

217
            for (;;) {
218
                Date temp = nullCalendar.advance(seed,
4,726,510✔
219
                    -periods*(*tenor_), convention, *endOfMonth_);
4,726,510✔
220
                if (temp < exitDate) {
4,726,510✔
221
                    if (firstDate_ != Date() &&
89,789✔
222
                        (calendar_.adjust(dates_.back(),convention)!=
10✔
223
                         calendar_.adjust(firstDate_,convention))) {
89,785✔
224
                        dates_.push_back(firstDate_);
4✔
225
                        isRegular_.push_back(false);
4✔
226
                    }
227
                    break;
89,779✔
228
                } else {
229
                    // skip dates that would result in duplicates
230
                    // after adjustment
231
                    if (calendar_.adjust(dates_.back(),convention)!=
4,636,731✔
232
                        calendar_.adjust(temp,convention)) {
9,273,462✔
233
                        dates_.push_back(temp);
3,618,245✔
234
                        isRegular_.push_back(true);
3,618,245✔
235
                    }
236
                    ++periods;
4,636,731✔
237
                }
238
            }
4,636,731✔
239

240
            if (calendar_.adjust(dates_.back(),convention)!=
89,779✔
241
                calendar_.adjust(effectiveDate,convention)) {
179,558✔
242
                dates_.push_back(effectiveDate);
10,973✔
243
                isRegular_.push_back(false);
10,973✔
244
            }
245
            std::reverse(dates_.begin(), dates_.end());
89,779✔
246
            std::reverse(isRegular_.begin(), isRegular_.end());
89,779✔
247
            break;
248

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

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

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

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

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

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

346
            break;
347

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

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

362
        // first date not adjusted for old CDS schedules
363
        if (convention != Unadjusted && *rule_ != DateGeneration::OldCDS)
110,047✔
364
            dates_.front() = calendar_.adjust(dates_.front(), convention);
94,860✔
365

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

377
        if (*endOfMonth_ && calendar_.isEndOfMonth(seed)) {
110,361✔
378
            // adjust to end of month
379
            for (Size i=1; i<dates_.size()-1; ++i)
224✔
380
                dates_[i] = calendar_.adjust(Date::endOfMonth(dates_[i]), convention);
202✔
381
        } else {
382
            for (Size i=1; i<dates_.size()-1; ++i)
3,870,295✔
383
                dates_[i] = calendar_.adjust(dates_[i], convention);
3,760,270✔
384
        }
385

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

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

420
    Schedule Schedule::after(const Date& truncationDate) const {
28✔
421
        Schedule result = *this;
28✔
422

423
        QL_REQUIRE(truncationDate < result.dates_.back(),
28✔
424
            "truncation date " << truncationDate <<
425
            " must be before the last schedule date " <<
426
            result.dates_.back());
427
        if (truncationDate > result.dates_[0]) {
28✔
428
            // remove earlier dates
429
            while (result.dates_[0] < truncationDate) {
1,226✔
430
                result.dates_.erase(result.dates_.begin());
1,201✔
431
                if (!result.isRegular_.empty())
432
                    result.isRegular_.erase(result.isRegular_.begin());
1,201✔
433
            }
434

435
            // add truncationDate if missing
436
            if (truncationDate != result.dates_.front()) {
25✔
437
                result.dates_.insert(result.dates_.begin(), truncationDate);
1✔
438
                result.isRegular_.insert(result.isRegular_.begin(), false);
1✔
439
                result.terminationDateConvention_ = Unadjusted;
1✔
440
            }
441
            else {
442
                result.terminationDateConvention_ = convention_;
24✔
443
            }
444

445
            if (result.nextToLastDate_ <= truncationDate)
25✔
446
                result.nextToLastDate_ = Date();
25✔
447
            if (result.firstDate_ <= truncationDate)
25✔
448
                result.firstDate_ = Date();
25✔
449
        }
450

451
        return result;
28✔
452
    }
×
453

454
    Schedule Schedule::until(const Date& truncationDate) const {
138✔
455
        Schedule result = *this;
138✔
456

457
        QL_REQUIRE(truncationDate>result.dates_[0],
138✔
458
                   "truncation date " << truncationDate <<
459
                   " must be later than schedule first date " <<
460
                   result.dates_[0]);
461
        if (truncationDate<result.dates_.back()) {
138✔
462
            // remove later dates
463
            while (result.dates_.back()>truncationDate) {
1,459✔
464
                result.dates_.pop_back();
465
                if(!result.isRegular_.empty())
466
                    result.isRegular_.pop_back();
467
            }
468

469
            // add truncationDate if missing
470
            if (truncationDate!=result.dates_.back()) {
40✔
471
                result.dates_.push_back(truncationDate);
14✔
472
                result.isRegular_.push_back(false);
14✔
473
                result.terminationDateConvention_ = Unadjusted;
14✔
474
            } else {
475
                result.terminationDateConvention_ = convention_;
26✔
476
            }
477

478
            if (result.nextToLastDate_>=truncationDate)
40✔
479
                result.nextToLastDate_ = Date();
×
480
            if (result.firstDate_>=truncationDate)
40✔
481
                result.firstDate_ = Date();
×
482
        }
483

484
        return result;
138✔
485
    }
×
486

487
    std::vector<Date>::const_iterator
488
    Schedule::lower_bound(const Date& refDate) const {
×
489
        Date d = (refDate==Date() ?
×
490
                  Settings::instance().evaluationDate() :
×
491
                  refDate);
×
492
        return std::lower_bound(dates_.begin(), dates_.end(), d);
×
493
    }
494

495
    Date Schedule::nextDate(const Date& refDate) const {
×
496
        auto res = lower_bound(refDate);
×
497
        if (res!=dates_.end())
×
498
            return *res;
×
499
        else
500
            return {};
×
501
    }
502

503
    Date Schedule::previousDate(const Date& refDate) const {
×
504
        auto res = lower_bound(refDate);
×
505
        if (res!=dates_.begin())
×
506
            return *(--res);
×
507
        else
508
            return {};
×
509
    }
510

511
    bool Schedule::hasIsRegular() const { return !isRegular_.empty(); }
412,910✔
512

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

522
    const std::vector<bool>& Schedule::isRegular() const {
6✔
523
        QL_REQUIRE(!isRegular_.empty(), "full interface (isRegular) not available");
×
524
        return isRegular_;
6✔
525
    }
526

527
    MakeSchedule& MakeSchedule::from(const Date& effectiveDate) {
21,951✔
528
        effectiveDate_ = effectiveDate;
21,951✔
529
        return *this;
21,951✔
530
    }
531

532
    MakeSchedule& MakeSchedule::to(const Date& terminationDate) {
21,951✔
533
        terminationDate_ = terminationDate;
21,951✔
534
        return *this;
21,951✔
535
    }
536

537
    MakeSchedule& MakeSchedule::withTenor(const Period& tenor) {
15,538✔
538
        tenor_ = tenor;
539
        return *this;
15,538✔
540
    }
541

542
    MakeSchedule& MakeSchedule::withFrequency(Frequency frequency) {
6,413✔
543
        tenor_ = Period(frequency);
6,413✔
544
        return *this;
6,413✔
545
    }
546

547
    MakeSchedule& MakeSchedule::withCalendar(const Calendar& calendar) {
21,934✔
548
        calendar_ = calendar;
549
        return *this;
21,934✔
550
    }
551

552
    MakeSchedule& MakeSchedule::withConvention(BusinessDayConvention conv) {
21,911✔
553
        convention_ = conv;
21,911✔
554
        return *this;
21,911✔
555
    }
556

557
    MakeSchedule& MakeSchedule::withTerminationDateConvention(
311✔
558
                                                BusinessDayConvention conv) {
559
        terminationDateConvention_ = conv;
311✔
560
        return *this;
311✔
561
    }
562

563
    MakeSchedule& MakeSchedule::withRule(DateGeneration::Rule r) {
340✔
564
        rule_ = r;
340✔
565
        return *this;
340✔
566
    }
567

568
    MakeSchedule& MakeSchedule::forwards() {
6,266✔
569
        rule_ = DateGeneration::Forward;
6,266✔
570
        return *this;
6,266✔
571
    }
572

573
    MakeSchedule& MakeSchedule::backwards() {
15,290✔
574
        rule_ = DateGeneration::Backward;
15,290✔
575
        return *this;
15,290✔
576
    }
577

578
    MakeSchedule& MakeSchedule::endOfMonth(bool flag) {
766✔
579
        endOfMonth_ = flag;
766✔
580
        return *this;
766✔
581
    }
582

583
    MakeSchedule& MakeSchedule::withFirstDate(const Date& d) {
13✔
584
        firstDate_ = d;
13✔
585
        return *this;
13✔
586
    }
587

588
    MakeSchedule& MakeSchedule::withNextToLastDate(const Date& d) {
9✔
589
        nextToLastDate_ = d;
9✔
590
        return *this;
9✔
591
    }
592

593
    MakeSchedule::operator Schedule() const {
21,951✔
594
        // check for mandatory arguments
595
        QL_REQUIRE(effectiveDate_ != Date(), "effective date not provided");
21,951✔
596
        QL_REQUIRE(terminationDate_ != Date(), "termination date not provided");
21,951✔
597
        QL_REQUIRE(tenor_, "tenor/frequency not provided");
21,951✔
598

599
        // set dynamic defaults:
600
        BusinessDayConvention convention;
601
        // if a convention was set, we use it.
602
        if (convention_) { // NOLINT(readability-implicit-bool-conversion)
21,951✔
603
            convention = *convention_;
21,911✔
604
        } else {
605
            if (!calendar_.empty()) {
40✔
606
                // ...if we set a calendar, we probably want it to be used;
607
                convention = Following;
608
            } else {
609
                // if not, we don't care.
610
                convention = Unadjusted;
611
            }
612
        }
613

614
        BusinessDayConvention terminationDateConvention;
615
        // if set explicitly, we use it;
616
        if (terminationDateConvention_) { // NOLINT(readability-implicit-bool-conversion)
21,951✔
617
            terminationDateConvention = *terminationDateConvention_;
311✔
618
        } else {
619
            // Unadjusted as per ISDA specification
620
            terminationDateConvention = convention;
621
        }
622

623
        Calendar calendar = calendar_;
624
        // if no calendar was set...
625
        if (calendar.empty()) {
21,951✔
626
            // ...we use a null one.
627
            calendar = NullCalendar();
34✔
628
        }
629

630
        return Schedule(effectiveDate_, terminationDate_, *tenor_, calendar,
21,951✔
631
                        convention, terminationDateConvention,
632
                        rule_, endOfMonth_, firstDate_, nextToLastDate_);
65,853✔
633
    }
634

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

652
}
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

© 2025 Coveralls, Inc