1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl
5 Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 StatPro Italia srl
6 Copyright (C) 2007, 2008, 2009, 2015 Ferdinando Ametrano
7 Copyright (C) 2007, 2009 Roland Lichters
8 Copyright (C) 2015 Maddalena Zanzi
9 Copyright (C) 2015 Paolo Mazzocchi
10 Copyright (C) 2018 Matthias Lungwitz
11
12 This file is part of QuantLib, a free-software/open-source library
13 for financial quantitative analysts and developers - http://quantlib.org/
14
15 QuantLib is free software: you can redistribute it and/or modify it
16 under the terms of the QuantLib license. You should have received a
17 copy of the license along with this program; if not, please email
18 <quantlib-dev@lists.sf.net>. The license is also available online at
19 <http://quantlib.org/license.shtml>.
20
21 This program is distributed in the hope that it will be useful, but WITHOUT
22 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
23 FOR A PARTICULAR PURPOSE. See the license for more details.
24*/
25
26#include <ql/cashflows/iborcoupon.hpp>
27#include <ql/currency.hpp>
28#include <ql/indexes/swapindex.hpp>
29#include <ql/instruments/makevanillaswap.hpp>
30#include <ql/instruments/simplifynotificationgraph.hpp>
31#include <ql/pricingengines/swap/discountingswapengine.hpp>
32#include <ql/quote.hpp>
33#include <ql/termstructures/yield/ratehelpers.hpp>
34#include <ql/time/asx.hpp>
35#include <ql/time/calendars/jointcalendar.hpp>
36#include <ql/time/calendars/unitedstates.hpp>
37#include <ql/time/imm.hpp>
38#include <ql/utilities/null_deleter.hpp>
39#include <ql/optional.hpp>
40#include <utility>
41
42namespace QuantLib {
43
44 FuturesRateHelper::FuturesRateHelper(const Handle<Quote>& price,
45 const Date& iborStartDate,
46 Natural lengthInMonths,
47 const Calendar& calendar,
48 BusinessDayConvention convention,
49 bool endOfMonth,
50 const DayCounter& dayCounter,
51 Handle<Quote> convAdj,
52 Futures::Type type)
53 : RateHelper(price), convAdj_(std::move(convAdj)) {
54 switch (type) {
55 case Futures::IMM:
56 QL_REQUIRE(IMM::isIMMdate(iborStartDate, false),
57 iborStartDate << " is not a valid IMM date");
58 break;
59 case Futures::ASX:
60 QL_REQUIRE(ASX::isASXdate(iborStartDate, false),
61 iborStartDate << " is not a valid ASX date");
62 break;
63 default:
64 QL_FAIL("unknown futures type (" << Integer(type) << ")");
65 }
66 earliestDate_ = iborStartDate;
67 maturityDate_ = calendar.advance(date: iborStartDate, period: lengthInMonths*Months,
68 convention, endOfMonth);
69 yearFraction_ = dayCounter.yearFraction(d1: earliestDate_, d2: maturityDate_);
70 pillarDate_ = latestDate_ = latestRelevantDate_ = maturityDate_;
71
72 registerWith(h: convAdj_);
73 }
74
75 FuturesRateHelper::FuturesRateHelper(Real price,
76 const Date& iborStartDate,
77 Natural lengthInMonths,
78 const Calendar& calendar,
79 BusinessDayConvention convention,
80 bool endOfMonth,
81 const DayCounter& dayCounter,
82 Rate convAdj,
83 Futures::Type type)
84 : RateHelper(price),
85 convAdj_(Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(convAdj))))
86 {
87 switch (type) {
88 case Futures::IMM:
89 QL_REQUIRE(IMM::isIMMdate(iborStartDate, false),
90 iborStartDate << " is not a valid IMM date");
91 break;
92 case Futures::ASX:
93 QL_REQUIRE(ASX::isASXdate(iborStartDate, false),
94 iborStartDate << " is not a valid ASX date");
95 break;
96 default:
97 QL_FAIL("unknown futures type (" << Integer(type) << ")");
98 }
99 earliestDate_ = iborStartDate;
100 maturityDate_ = calendar.advance(date: iborStartDate, period: lengthInMonths*Months,
101 convention, endOfMonth);
102 yearFraction_ = dayCounter.yearFraction(d1: earliestDate_, d2: maturityDate_);
103 pillarDate_ = latestDate_ = latestRelevantDate_ = maturityDate_;
104 }
105
106 FuturesRateHelper::FuturesRateHelper(const Handle<Quote>& price,
107 const Date& iborStartDate,
108 const Date& iborEndDate,
109 const DayCounter& dayCounter,
110 Handle<Quote> convAdj,
111 Futures::Type type)
112 : RateHelper(price), convAdj_(std::move(convAdj)) {
113 switch (type) {
114 case Futures::IMM:
115 QL_REQUIRE(IMM::isIMMdate(iborStartDate, false),
116 iborStartDate << " is not a valid IMM date");
117 if (iborEndDate == Date()) {
118 // advance 3 months
119 maturityDate_ = IMM::nextDate(d: iborStartDate, mainCycle: false);
120 maturityDate_ = IMM::nextDate(d: maturityDate_, mainCycle: false);
121 maturityDate_ = IMM::nextDate(d: maturityDate_, mainCycle: false);
122 }
123 else {
124 QL_REQUIRE(iborEndDate>iborStartDate,
125 "end date (" << iborEndDate <<
126 ") must be greater than start date (" <<
127 iborStartDate << ")");
128 maturityDate_ = iborEndDate;
129 }
130 break;
131 case Futures::ASX:
132 QL_REQUIRE(ASX::isASXdate(iborStartDate, false),
133 iborStartDate << " is not a valid ASX date");
134 if (iborEndDate == Date()) {
135 // advance 3 months
136 maturityDate_ = ASX::nextDate(d: iborStartDate, mainCycle: false);
137 maturityDate_ = ASX::nextDate(d: maturityDate_, mainCycle: false);
138 maturityDate_ = ASX::nextDate(d: maturityDate_, mainCycle: false);
139 }
140 else {
141 QL_REQUIRE(iborEndDate>iborStartDate,
142 "end date (" << iborEndDate <<
143 ") must be greater than start date (" <<
144 iborStartDate << ")");
145 maturityDate_ = iborEndDate;
146 }
147 break;
148 default:
149 QL_FAIL("unknown futures type (" << Integer(type) << ")");
150 }
151 earliestDate_ = iborStartDate;
152 yearFraction_ = dayCounter.yearFraction(d1: earliestDate_, d2: maturityDate_);
153 pillarDate_ = latestDate_ = latestRelevantDate_ = maturityDate_;
154
155 registerWith(h: convAdj_);
156 }
157
158 FuturesRateHelper::FuturesRateHelper(Real price,
159 const Date& iborStartDate,
160 const Date& iborEndDate,
161 const DayCounter& dayCounter,
162 Rate convAdj,
163 Futures::Type type)
164 : RateHelper(price),
165 convAdj_(Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(convAdj))))
166 {
167 switch (type) {
168 case Futures::IMM:
169 QL_REQUIRE(IMM::isIMMdate(iborStartDate, false),
170 iborStartDate << " is not a valid IMM date");
171 if (iborEndDate == Date()) {
172 // advance 3 months
173 maturityDate_ = IMM::nextDate(d: iborStartDate, mainCycle: false);
174 maturityDate_ = IMM::nextDate(d: maturityDate_, mainCycle: false);
175 maturityDate_ = IMM::nextDate(d: maturityDate_, mainCycle: false);
176 }
177 else {
178 QL_REQUIRE(iborEndDate>iborStartDate,
179 "end date (" << iborEndDate <<
180 ") must be greater than start date (" <<
181 iborStartDate << ")");
182 maturityDate_ = iborEndDate;
183 }
184 break;
185 case Futures::ASX:
186 QL_REQUIRE(ASX::isASXdate(iborStartDate, false),
187 iborStartDate << " is not a valid ASX date");
188 if (iborEndDate == Date()) {
189 // advance 3 months
190 maturityDate_ = ASX::nextDate(d: iborStartDate, mainCycle: false);
191 maturityDate_ = ASX::nextDate(d: maturityDate_, mainCycle: false);
192 maturityDate_ = ASX::nextDate(d: maturityDate_, mainCycle: false);
193 }
194 else {
195 QL_REQUIRE(iborEndDate>iborStartDate,
196 "end date (" << iborEndDate <<
197 ") must be greater than start date (" <<
198 iborStartDate << ")");
199 maturityDate_ = iborEndDate;
200 }
201 break;
202 default:
203 QL_FAIL("unknown futures type (" << Integer(type) << ")");
204 }
205 earliestDate_ = iborStartDate;
206 yearFraction_ = dayCounter.yearFraction(d1: earliestDate_, d2: maturityDate_);
207 pillarDate_ = latestDate_ = latestRelevantDate_ = maturityDate_;
208 }
209
210 FuturesRateHelper::FuturesRateHelper(const Handle<Quote>& price,
211 const Date& iborStartDate,
212 const ext::shared_ptr<IborIndex>& i,
213 const Handle<Quote>& convAdj,
214 Futures::Type type)
215 : RateHelper(price), convAdj_(convAdj) {
216 switch (type) {
217 case Futures::IMM:
218 QL_REQUIRE(IMM::isIMMdate(iborStartDate, false),
219 iborStartDate << " is not a valid IMM date");
220 break;
221 case Futures::ASX:
222 QL_REQUIRE(ASX::isASXdate(iborStartDate, false),
223 iborStartDate << " is not a valid ASX date");
224 break;
225 default:
226 QL_FAIL("unknown futures type (" << Integer(type) << ")");
227 }
228 earliestDate_ = iborStartDate;
229 const Calendar& cal = i->fixingCalendar();
230 maturityDate_ = cal.advance(date: iborStartDate, period: i->tenor(),
231 convention: i->businessDayConvention());
232 yearFraction_ = i->dayCounter().yearFraction(d1: earliestDate_,
233 d2: maturityDate_);
234 pillarDate_ = latestDate_ = latestRelevantDate_ = maturityDate_;
235
236 registerWith(h: convAdj);
237 }
238
239 FuturesRateHelper::FuturesRateHelper(Real price,
240 const Date& iborStartDate,
241 const ext::shared_ptr<IborIndex>& i,
242 Rate convAdj,
243 Futures::Type type)
244 : RateHelper(price),
245 convAdj_(Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(convAdj))))
246 {
247 switch (type) {
248 case Futures::IMM:
249 QL_REQUIRE(IMM::isIMMdate(iborStartDate, false),
250 iborStartDate << " is not a valid IMM date");
251 break;
252 case Futures::ASX:
253 QL_REQUIRE(ASX::isASXdate(iborStartDate, false),
254 iborStartDate << " is not a valid ASX date");
255 break;
256 default:
257 QL_FAIL("unknown futures type (" << Integer(type) << ")");
258 }
259 earliestDate_ = iborStartDate;
260 const Calendar& cal = i->fixingCalendar();
261 maturityDate_ = cal.advance(date: iborStartDate, period: i->tenor(),
262 convention: i->businessDayConvention());
263 yearFraction_ = i->dayCounter().yearFraction(d1: earliestDate_,
264 d2: maturityDate_);
265 pillarDate_ = latestDate_ = latestRelevantDate_ = maturityDate_;
266 }
267
268 Real FuturesRateHelper::impliedQuote() const {
269 QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
270 Rate forwardRate = (termStructure_->discount(d: earliestDate_) /
271 termStructure_->discount(d: maturityDate_) - 1.0) / yearFraction_;
272 Rate convAdj = convAdj_.empty() ? 0.0 : convAdj_->value();
273 // Convexity, as FRA/futures adjustment, has been used in the
274 // past to take into account futures margining vs FRA.
275 // Therefore, there's no requirement for it to be non-negative.
276 Rate futureRate = forwardRate + convAdj;
277 return 100.0 * (1.0 - futureRate);
278 }
279
280 Real FuturesRateHelper::convexityAdjustment() const {
281 return convAdj_.empty() ? 0.0 : convAdj_->value();
282 }
283
284 void FuturesRateHelper::accept(AcyclicVisitor& v) {
285 auto* v1 = dynamic_cast<Visitor<FuturesRateHelper>*>(&v);
286 if (v1 != nullptr)
287 v1->visit(*this);
288 else
289 RateHelper::accept(v);
290 }
291
292 DepositRateHelper::DepositRateHelper(const Handle<Quote>& rate,
293 const Period& tenor,
294 Natural fixingDays,
295 const Calendar& calendar,
296 BusinessDayConvention convention,
297 bool endOfMonth,
298 const DayCounter& dayCounter)
299 : RelativeDateRateHelper(rate) {
300 iborIndex_ = ext::make_shared<IborIndex>(args: "no-fix", // never take fixing into account
301 args: tenor, args&: fixingDays,
302 args: Currency(), args: calendar, args&: convention,
303 args&: endOfMonth, args: dayCounter, args&: termStructureHandle_);
304 DepositRateHelper::initializeDates();
305 }
306
307 DepositRateHelper::DepositRateHelper(Rate rate,
308 const Period& tenor,
309 Natural fixingDays,
310 const Calendar& calendar,
311 BusinessDayConvention convention,
312 bool endOfMonth,
313 const DayCounter& dayCounter)
314 : RelativeDateRateHelper(rate) {
315 iborIndex_ = ext::make_shared<IborIndex>(args: "no-fix", // never take fixing into account
316 args: tenor, args&: fixingDays,
317 args: Currency(), args: calendar, args&: convention,
318 args&: endOfMonth, args: dayCounter, args&: termStructureHandle_);
319 DepositRateHelper::initializeDates();
320 }
321
322 DepositRateHelper::DepositRateHelper(const Handle<Quote>& rate,
323 const ext::shared_ptr<IborIndex>& i)
324 : RelativeDateRateHelper(rate) {
325 iborIndex_ = i->clone(forwarding: termStructureHandle_);
326 DepositRateHelper::initializeDates();
327 }
328
329 DepositRateHelper::DepositRateHelper(Rate rate,
330 const ext::shared_ptr<IborIndex>& i)
331 : RelativeDateRateHelper(rate) {
332 iborIndex_ = i->clone(forwarding: termStructureHandle_);
333 DepositRateHelper::initializeDates();
334 }
335
336 Real DepositRateHelper::impliedQuote() const {
337 QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
338 // the forecast fixing flag is set to true because
339 // we do not want to take fixing into account
340 return iborIndex_->fixing(fixingDate: fixingDate_, forecastTodaysFixing: true);
341 }
342
343 void DepositRateHelper::setTermStructure(YieldTermStructure* t) {
344 // do not set the relinkable handle as an observer -
345 // force recalculation when needed---the index is not lazy
346 bool observer = false;
347
348 ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
349 termStructureHandle_.linkTo(h: temp, registerAsObserver: observer);
350
351 RelativeDateRateHelper::setTermStructure(t);
352 }
353
354 void DepositRateHelper::initializeDates() {
355 // if the evaluation date is not a business day
356 // then move to the next business day
357 Date referenceDate =
358 iborIndex_->fixingCalendar().adjust(evaluationDate_);
359 earliestDate_ = iborIndex_->valueDate(fixingDate: referenceDate);
360 fixingDate_ = iborIndex_->fixingDate(valueDate: earliestDate_);
361 maturityDate_ = iborIndex_->maturityDate(valueDate: earliestDate_);
362 pillarDate_ = latestDate_ = latestRelevantDate_ = maturityDate_;
363 }
364
365 void DepositRateHelper::accept(AcyclicVisitor& v) {
366 auto* v1 = dynamic_cast<Visitor<DepositRateHelper>*>(&v);
367 if (v1 != nullptr)
368 v1->visit(*this);
369 else
370 RateHelper::accept(v);
371 }
372
373
374 FraRateHelper::FraRateHelper(const Handle<Quote>& rate,
375 Natural monthsToStart,
376 Natural monthsToEnd,
377 Natural fixingDays,
378 const Calendar& calendar,
379 BusinessDayConvention convention,
380 bool endOfMonth,
381 const DayCounter& dayCounter,
382 Pillar::Choice pillarChoice,
383 Date customPillarDate,
384 bool useIndexedCoupon)
385 : RelativeDateRateHelper(rate), periodToStart_(monthsToStart*Months),
386 pillarChoice_(pillarChoice), useIndexedCoupon_(useIndexedCoupon) {
387 QL_REQUIRE(monthsToEnd>monthsToStart,
388 "monthsToEnd (" << monthsToEnd <<
389 ") must be grater than monthsToStart (" << monthsToStart <<
390 ")");
391 // no way to take fixing into account,
392 // even if we would like to for FRA over today
393 iborIndex_ = ext::make_shared<IborIndex>(args: "no-fix", // correct family name would be needed
394 args: (monthsToEnd-monthsToStart)*Months,
395 args&: fixingDays,
396 args: Currency(), args: calendar, args&: convention,
397 args&: endOfMonth, args: dayCounter, args&: termStructureHandle_);
398 pillarDate_ = customPillarDate;
399 FraRateHelper::initializeDates();
400 }
401
402 FraRateHelper::FraRateHelper(Rate rate,
403 Natural monthsToStart,
404 Natural monthsToEnd,
405 Natural fixingDays,
406 const Calendar& calendar,
407 BusinessDayConvention convention,
408 bool endOfMonth,
409 const DayCounter& dayCounter,
410 Pillar::Choice pillarChoice,
411 Date customPillarDate,
412 bool useIndexedCoupon)
413 : RelativeDateRateHelper(rate), periodToStart_(monthsToStart*Months),
414 pillarChoice_(pillarChoice), useIndexedCoupon_(useIndexedCoupon) {
415 QL_REQUIRE(monthsToEnd>monthsToStart,
416 "monthsToEnd (" << monthsToEnd <<
417 ") must be grater than monthsToStart (" << monthsToStart <<
418 ")");
419 // no way to take fixing into account,
420 // even if we would like to for FRA over today
421 iborIndex_ = ext::make_shared<IborIndex>(args: "no-fix", // correct family name would be needed
422 args: (monthsToEnd-monthsToStart)*Months,
423 args&: fixingDays,
424 args: Currency(), args: calendar, args&: convention,
425 args&: endOfMonth, args: dayCounter, args&: termStructureHandle_);
426 pillarDate_ = customPillarDate;
427 FraRateHelper::initializeDates();
428 }
429
430 FraRateHelper::FraRateHelper(const Handle<Quote>& rate,
431 Natural monthsToStart,
432 const ext::shared_ptr<IborIndex>& i,
433 Pillar::Choice pillarChoice,
434 Date customPillarDate,
435 bool useIndexedCoupon)
436 : RelativeDateRateHelper(rate), periodToStart_(monthsToStart*Months),
437 pillarChoice_(pillarChoice), useIndexedCoupon_(useIndexedCoupon) {
438 // take fixing into account
439 iborIndex_ = i->clone(forwarding: termStructureHandle_);
440 // We want to be notified of changes of fixings, but we don't
441 // want notifications from termStructureHandle_ (they would
442 // interfere with bootstrapping.)
443 iborIndex_->unregisterWith(h: termStructureHandle_);
444 registerWith(h: iborIndex_);
445 pillarDate_ = customPillarDate;
446 FraRateHelper::initializeDates();
447 }
448
449 FraRateHelper::FraRateHelper(Rate rate,
450 Natural monthsToStart,
451 const ext::shared_ptr<IborIndex>& i,
452 Pillar::Choice pillarChoice,
453 Date customPillarDate,
454 bool useIndexedCoupon)
455 : RelativeDateRateHelper(rate), periodToStart_(monthsToStart*Months),
456 pillarChoice_(pillarChoice), useIndexedCoupon_(useIndexedCoupon) {
457 // take fixing into account
458 iborIndex_ = i->clone(forwarding: termStructureHandle_);
459 // see above
460 iborIndex_->unregisterWith(h: termStructureHandle_);
461 registerWith(h: iborIndex_);
462 pillarDate_ = customPillarDate;
463 FraRateHelper::initializeDates();
464 }
465
466 FraRateHelper::FraRateHelper(const Handle<Quote>& rate,
467 Period periodToStart,
468 Natural lengthInMonths,
469 Natural fixingDays,
470 const Calendar& calendar,
471 BusinessDayConvention convention,
472 bool endOfMonth,
473 const DayCounter& dayCounter,
474 Pillar::Choice pillarChoice,
475 Date customPillarDate,
476 bool useIndexedCoupon)
477 : RelativeDateRateHelper(rate), periodToStart_(periodToStart),
478 pillarChoice_(pillarChoice), useIndexedCoupon_(useIndexedCoupon) {
479 // no way to take fixing into account,
480 // even if we would like to for FRA over today
481 iborIndex_ = ext::make_shared<IborIndex>(args: "no-fix", // correct family name would be needed
482 args: lengthInMonths*Months,
483 args&: fixingDays,
484 args: Currency(), args: calendar, args&: convention,
485 args&: endOfMonth, args: dayCounter, args&: termStructureHandle_);
486 pillarDate_ = customPillarDate;
487 FraRateHelper::initializeDates();
488 }
489
490 FraRateHelper::FraRateHelper(Rate rate,
491 Period periodToStart,
492 Natural lengthInMonths,
493 Natural fixingDays,
494 const Calendar& calendar,
495 BusinessDayConvention convention,
496 bool endOfMonth,
497 const DayCounter& dayCounter,
498 Pillar::Choice pillarChoice,
499 Date customPillarDate,
500 bool useIndexedCoupon)
501 : RelativeDateRateHelper(rate), periodToStart_(periodToStart),
502 pillarChoice_(pillarChoice), useIndexedCoupon_(useIndexedCoupon) {
503 // no way to take fixing into account,
504 // even if we would like to for FRA over today
505 iborIndex_ = ext::make_shared<IborIndex>(args: "no-fix", // correct family name would be needed
506 args: lengthInMonths*Months,
507 args&: fixingDays,
508 args: Currency(), args: calendar, args&: convention,
509 args&: endOfMonth, args: dayCounter, args&: termStructureHandle_);
510 pillarDate_ = customPillarDate;
511 FraRateHelper::initializeDates();
512 }
513
514 FraRateHelper::FraRateHelper(const Handle<Quote>& rate,
515 Period periodToStart,
516 const ext::shared_ptr<IborIndex>& i,
517 Pillar::Choice pillarChoice,
518 Date customPillarDate,
519 bool useIndexedCoupon)
520 : RelativeDateRateHelper(rate), periodToStart_(periodToStart),
521 pillarChoice_(pillarChoice), useIndexedCoupon_(useIndexedCoupon) {
522 // take fixing into account
523 iborIndex_ = i->clone(forwarding: termStructureHandle_);
524 // see above
525 iborIndex_->unregisterWith(h: termStructureHandle_);
526 registerWith(h: iborIndex_);
527 pillarDate_ = customPillarDate;
528 FraRateHelper::initializeDates();
529 }
530
531 FraRateHelper::FraRateHelper(Rate rate,
532 Period periodToStart,
533 const ext::shared_ptr<IborIndex>& i,
534 Pillar::Choice pillarChoice,
535 Date customPillarDate,
536 bool useIndexedCoupon)
537 : RelativeDateRateHelper(rate), periodToStart_(periodToStart),
538 pillarChoice_(pillarChoice), useIndexedCoupon_(useIndexedCoupon) {
539 // take fixing into account
540 iborIndex_ = i->clone(forwarding: termStructureHandle_);
541 // see above
542 iborIndex_->unregisterWith(h: termStructureHandle_);
543 registerWith(h: iborIndex_);
544 pillarDate_ = customPillarDate;
545 FraRateHelper::initializeDates();
546 }
547
548 FraRateHelper::FraRateHelper(const Handle<Quote>& rate,
549 Natural immOffsetStart,
550 Natural immOffsetEnd,
551 const ext::shared_ptr<IborIndex>& i,
552 Pillar::Choice pillarChoice,
553 Date customPillarDate,
554 bool useIndexedCoupon)
555 : RelativeDateRateHelper(rate), immOffsetStart_(immOffsetStart), immOffsetEnd_(immOffsetEnd),
556 pillarChoice_(pillarChoice), useIndexedCoupon_(useIndexedCoupon) {
557 // take fixing into account
558 iborIndex_ = i->clone(forwarding: termStructureHandle_);
559 // see above
560 iborIndex_->unregisterWith(h: termStructureHandle_);
561 registerWith(h: iborIndex_);
562 pillarDate_ = customPillarDate;
563 FraRateHelper::initializeDates();
564 }
565
566 FraRateHelper::FraRateHelper(Rate rate,
567 Natural immOffsetStart,
568 Natural immOffsetEnd,
569 const ext::shared_ptr<IborIndex>& i,
570 Pillar::Choice pillarChoice,
571 Date customPillarDate,
572 bool useIndexedCoupon)
573 : RelativeDateRateHelper(rate), immOffsetStart_(immOffsetStart), immOffsetEnd_(immOffsetEnd),
574 pillarChoice_(pillarChoice), useIndexedCoupon_(useIndexedCoupon) {
575 // take fixing into account
576 iborIndex_ = i->clone(forwarding: termStructureHandle_);
577 // see above
578 iborIndex_->unregisterWith(h: termStructureHandle_);
579 registerWith(h: iborIndex_);
580 pillarDate_ = customPillarDate;
581 FraRateHelper::initializeDates();
582 }
583
584 Real FraRateHelper::impliedQuote() const {
585 QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
586 if (useIndexedCoupon_)
587 return iborIndex_->fixing(fixingDate: fixingDate_, forecastTodaysFixing: true);
588 else
589 return (termStructure_->discount(d: earliestDate_) /
590 termStructure_->discount(d: maturityDate_) -
591 1.0) /
592 spanningTime_;
593 }
594
595 void FraRateHelper::setTermStructure(YieldTermStructure* t) {
596 // do not set the relinkable handle as an observer -
597 // force recalculation when needed---the index is not lazy
598 bool observer = false;
599
600 ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
601 termStructureHandle_.linkTo(h: temp, registerAsObserver: observer);
602
603 RelativeDateRateHelper::setTermStructure(t);
604 }
605
606 namespace {
607 Date nthImmDate(const Date& asof, const Size n) {
608 Date imm = asof;
609 for (Size i = 0; i < n; ++i) {
610 imm = IMM::nextDate(d: imm, mainCycle: true);
611 }
612 return imm;
613 }
614 }
615
616 void FraRateHelper::initializeDates() {
617 // if the evaluation date is not a business day
618 // then move to the next business day
619 Date referenceDate =
620 iborIndex_->fixingCalendar().adjust(evaluationDate_);
621 Date spotDate = iborIndex_->fixingCalendar().advance(
622 date: referenceDate, period: iborIndex_->fixingDays()*Days);
623 if (periodToStart_) { // NOLINT(readability-implicit-bool-conversion)
624 earliestDate_ = iborIndex_->fixingCalendar().advance(
625 date: spotDate, period: *periodToStart_, convention: iborIndex_->businessDayConvention(),
626 endOfMonth: iborIndex_->endOfMonth());
627 // maturity date is calculated from spot date
628 maturityDate_ = iborIndex_->fixingCalendar().advance(
629 date: spotDate, period: *periodToStart_ + iborIndex_->tenor(), convention: iborIndex_->businessDayConvention(),
630 endOfMonth: iborIndex_->endOfMonth());
631
632 } else if ((immOffsetStart_) && (immOffsetEnd_)) { // NOLINT(readability-implicit-bool-conversion)
633 earliestDate_ = iborIndex_->fixingCalendar().adjust(nthImmDate(asof: spotDate, n: *immOffsetStart_));
634 maturityDate_ = iborIndex_->fixingCalendar().adjust(nthImmDate(asof: spotDate, n: *immOffsetEnd_));
635 } else {
636 QL_FAIL("neither periodToStart nor immOffsetStart/End given");
637 }
638
639 if (useIndexedCoupon_)
640 // latest relevant date is calculated from earliestDate_
641 latestRelevantDate_ = iborIndex_->maturityDate(valueDate: earliestDate_);
642 else {
643 latestRelevantDate_ = maturityDate_;
644 spanningTime_ = iborIndex_->dayCounter().yearFraction(d1: earliestDate_, d2: maturityDate_);
645 }
646
647 switch (pillarChoice_) {
648 case Pillar::MaturityDate:
649 pillarDate_ = maturityDate_;
650 break;
651 case Pillar::LastRelevantDate:
652 pillarDate_ = latestRelevantDate_;
653 break;
654 case Pillar::CustomDate:
655 // pillarDate_ already assigned at construction time
656 QL_REQUIRE(pillarDate_ >= earliestDate_,
657 "pillar date (" << pillarDate_ << ") must be later "
658 "than or equal to the instrument's earliest date (" <<
659 earliestDate_ << ")");
660 QL_REQUIRE(pillarDate_ <= latestRelevantDate_,
661 "pillar date (" << pillarDate_ << ") must be before "
662 "or equal to the instrument's latest relevant date (" <<
663 latestRelevantDate_ << ")");
664 break;
665 default:
666 QL_FAIL("unknown Pillar::Choice(" << Integer(pillarChoice_) << ")");
667 }
668
669 latestDate_ = pillarDate_; // backward compatibility
670
671 fixingDate_ = iborIndex_->fixingDate(valueDate: earliestDate_);
672 }
673
674 void FraRateHelper::accept(AcyclicVisitor& v) {
675 auto* v1 = dynamic_cast<Visitor<FraRateHelper>*>(&v);
676 if (v1 != nullptr)
677 v1->visit(*this);
678 else
679 RateHelper::accept(v);
680 }
681
682
683 SwapRateHelper::SwapRateHelper(const Handle<Quote>& rate,
684 const ext::shared_ptr<SwapIndex>& swapIndex,
685 Handle<Quote> spread,
686 const Period& fwdStart,
687 Handle<YieldTermStructure> discount,
688 Pillar::Choice pillarChoice,
689 Date customPillarDate,
690 bool endOfMonth,
691 const ext::optional<bool>& useIndexedCoupons)
692 : RelativeDateRateHelper(rate), settlementDays_(Null<Natural>()), tenor_(swapIndex->tenor()),
693 pillarChoice_(pillarChoice), calendar_(swapIndex->fixingCalendar()),
694 fixedConvention_(swapIndex->fixedLegConvention()),
695 fixedFrequency_(swapIndex->fixedLegTenor().frequency()),
696 fixedDayCount_(swapIndex->dayCounter()), spread_(std::move(spread)), endOfMonth_(endOfMonth),
697 fwdStart_(fwdStart), discountHandle_(std::move(discount)), useIndexedCoupons_(useIndexedCoupons) {
698 // take fixing into account
699 iborIndex_ = swapIndex->iborIndex()->clone(forwarding: termStructureHandle_);
700 // We want to be notified of changes of fixings, but we don't
701 // want notifications from termStructureHandle_ (they would
702 // interfere with bootstrapping.)
703 iborIndex_->unregisterWith(h: termStructureHandle_);
704
705 registerWith(h: iborIndex_);
706 registerWith(h: spread_);
707 registerWith(h: discountHandle_);
708
709 pillarDate_ = customPillarDate;
710 SwapRateHelper::initializeDates();
711 }
712
713 SwapRateHelper::SwapRateHelper(const Handle<Quote>& rate,
714 const Period& tenor,
715 Calendar calendar,
716 Frequency fixedFrequency,
717 BusinessDayConvention fixedConvention,
718 DayCounter fixedDayCount,
719 const ext::shared_ptr<IborIndex>& iborIndex,
720 Handle<Quote> spread,
721 const Period& fwdStart,
722 Handle<YieldTermStructure> discount,
723 Natural settlementDays,
724 Pillar::Choice pillarChoice,
725 Date customPillarDate,
726 bool endOfMonth,
727 const ext::optional<bool>& useIndexedCoupons)
728 : RelativeDateRateHelper(rate), settlementDays_(settlementDays), tenor_(tenor),
729 pillarChoice_(pillarChoice), calendar_(std::move(calendar)),
730 fixedConvention_(fixedConvention), fixedFrequency_(fixedFrequency),
731 fixedDayCount_(std::move(fixedDayCount)), spread_(std::move(spread)), endOfMonth_(endOfMonth),
732 fwdStart_(fwdStart), discountHandle_(std::move(discount)),
733 useIndexedCoupons_(useIndexedCoupons) {
734
735 // take fixing into account
736 iborIndex_ = iborIndex->clone(forwarding: termStructureHandle_);
737 // We want to be notified of changes of fixings, but we don't
738 // want notifications from termStructureHandle_ (they would
739 // interfere with bootstrapping.)
740 iborIndex_->unregisterWith(h: termStructureHandle_);
741
742 registerWith(h: iborIndex_);
743 registerWith(h: spread_);
744 registerWith(h: discountHandle_);
745
746 pillarDate_ = customPillarDate;
747 SwapRateHelper::initializeDates();
748 }
749
750 SwapRateHelper::SwapRateHelper(Rate rate,
751 const ext::shared_ptr<SwapIndex>& swapIndex,
752 Handle<Quote> spread,
753 const Period& fwdStart,
754 Handle<YieldTermStructure> discount,
755 Pillar::Choice pillarChoice,
756 Date customPillarDate,
757 bool endOfMonth,
758 const ext::optional<bool>& useIndexedCoupons)
759 : RelativeDateRateHelper(rate), settlementDays_(Null<Natural>()), tenor_(swapIndex->tenor()),
760 pillarChoice_(pillarChoice), calendar_(swapIndex->fixingCalendar()),
761 fixedConvention_(swapIndex->fixedLegConvention()),
762 fixedFrequency_(swapIndex->fixedLegTenor().frequency()),
763 fixedDayCount_(swapIndex->dayCounter()), spread_(std::move(spread)), endOfMonth_(endOfMonth),
764 fwdStart_(fwdStart), discountHandle_(std::move(discount)),
765 useIndexedCoupons_(useIndexedCoupons) {
766 // take fixing into account
767 iborIndex_ = swapIndex->iborIndex()->clone(forwarding: termStructureHandle_);
768 // We want to be notified of changes of fixings, but we don't
769 // want notifications from termStructureHandle_ (they would
770 // interfere with bootstrapping.)
771 iborIndex_->unregisterWith(h: termStructureHandle_);
772
773 registerWith(h: iborIndex_);
774 registerWith(h: spread_);
775 registerWith(h: discountHandle_);
776
777 pillarDate_ = customPillarDate;
778 SwapRateHelper::initializeDates();
779 }
780
781 SwapRateHelper::SwapRateHelper(Rate rate,
782 const Period& tenor,
783 Calendar calendar,
784 Frequency fixedFrequency,
785 BusinessDayConvention fixedConvention,
786 DayCounter fixedDayCount,
787 const ext::shared_ptr<IborIndex>& iborIndex,
788 Handle<Quote> spread,
789 const Period& fwdStart,
790 Handle<YieldTermStructure> discount,
791 Natural settlementDays,
792 Pillar::Choice pillarChoice,
793 Date customPillarDate,
794 bool endOfMonth,
795 const ext::optional<bool>& useIndexedCoupons)
796 : RelativeDateRateHelper(rate), settlementDays_(settlementDays), tenor_(tenor),
797 pillarChoice_(pillarChoice), calendar_(std::move(calendar)),
798 fixedConvention_(fixedConvention), fixedFrequency_(fixedFrequency),
799 fixedDayCount_(std::move(fixedDayCount)), spread_(std::move(spread)), endOfMonth_(endOfMonth),
800 fwdStart_(fwdStart), discountHandle_(std::move(discount)),
801 useIndexedCoupons_(useIndexedCoupons) {
802
803 // take fixing into account
804 iborIndex_ = iborIndex->clone(forwarding: termStructureHandle_);
805 // We want to be notified of changes of fixings, but we don't
806 // want notifications from termStructureHandle_ (they would
807 // interfere with bootstrapping.)
808 iborIndex_->unregisterWith(h: termStructureHandle_);
809
810 registerWith(h: iborIndex_);
811 registerWith(h: spread_);
812 registerWith(h: discountHandle_);
813
814 pillarDate_ = customPillarDate;
815 SwapRateHelper::initializeDates();
816 }
817
818 void SwapRateHelper::initializeDates() {
819
820 // 1. do not pass the spread here, as it might be a Quote
821 // i.e. it can dinamically change
822 // 2. input discount curve Handle might be empty now but it could
823 // be assigned a curve later; use a RelinkableHandle here
824 swap_ = MakeVanillaSwap(tenor_, iborIndex_, 0.0, fwdStart_)
825 .withSettlementDays(settlementDays: settlementDays_)
826 .withDiscountingTermStructure(discountCurve: discountRelinkableHandle_)
827 .withFixedLegDayCount(dc: fixedDayCount_)
828 .withFixedLegTenor(t: Period(fixedFrequency_))
829 .withFixedLegConvention(bdc: fixedConvention_)
830 .withFixedLegTerminationDateConvention(bdc: fixedConvention_)
831 .withFixedLegCalendar(cal: calendar_)
832 .withFixedLegEndOfMonth(flag: endOfMonth_)
833 .withFloatingLegCalendar(cal: calendar_)
834 .withFloatingLegEndOfMonth(flag: endOfMonth_)
835 .withIndexedCoupons(b: useIndexedCoupons_);
836
837 simplifyNotificationGraph(swap&: *swap_, unregisterCoupons: true);
838
839 earliestDate_ = swap_->startDate();
840 maturityDate_ = swap_->maturityDate();
841
842 ext::shared_ptr<IborCoupon> lastCoupon =
843 ext::dynamic_pointer_cast<IborCoupon>(r: swap_->floatingLeg().back());
844 latestRelevantDate_ = std::max(a: maturityDate_, b: lastCoupon->fixingEndDate());
845
846 switch (pillarChoice_) {
847 case Pillar::MaturityDate:
848 pillarDate_ = maturityDate_;
849 break;
850 case Pillar::LastRelevantDate:
851 pillarDate_ = latestRelevantDate_;
852 break;
853 case Pillar::CustomDate:
854 // pillarDate_ already assigned at construction time
855 QL_REQUIRE(pillarDate_ >= earliestDate_,
856 "pillar date (" << pillarDate_ << ") must be later "
857 "than or equal to the instrument's earliest date (" <<
858 earliestDate_ << ")");
859 QL_REQUIRE(pillarDate_ <= latestRelevantDate_,
860 "pillar date (" << pillarDate_ << ") must be before "
861 "or equal to the instrument's latest relevant date (" <<
862 latestRelevantDate_ << ")");
863 break;
864 default:
865 QL_FAIL("unknown Pillar::Choice(" << Integer(pillarChoice_) << ")");
866 }
867
868 latestDate_ = pillarDate_; // backward compatibility
869
870 }
871
872 void SwapRateHelper::setTermStructure(YieldTermStructure* t) {
873 // do not set the relinkable handle as an observer -
874 // force recalculation when needed
875 bool observer = false;
876
877 ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
878 termStructureHandle_.linkTo(h: temp, registerAsObserver: observer);
879
880 if (discountHandle_.empty())
881 discountRelinkableHandle_.linkTo(h: temp, registerAsObserver: observer);
882 else
883 discountRelinkableHandle_.linkTo(h: *discountHandle_, registerAsObserver: observer);
884
885 RelativeDateRateHelper::setTermStructure(t);
886 }
887
888 Real SwapRateHelper::impliedQuote() const {
889 QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
890 // we didn't register as observers - force calculation
891 swap_->deepUpdate();
892 // weak implementation... to be improved
893 static const Spread basisPoint = 1.0e-4;
894 Real floatingLegNPV = swap_->floatingLegNPV();
895 Spread spread = spread_.empty() ? 0.0 : spread_->value();
896 Real spreadNPV = swap_->floatingLegBPS()/basisPoint*spread;
897 Real totNPV = - (floatingLegNPV+spreadNPV);
898 Real result = totNPV/(swap_->fixedLegBPS()/basisPoint);
899 return result;
900 }
901
902 void SwapRateHelper::accept(AcyclicVisitor& v) {
903 auto* v1 = dynamic_cast<Visitor<SwapRateHelper>*>(&v);
904 if (v1 != nullptr)
905 v1->visit(*this);
906 else
907 RateHelper::accept(v);
908 }
909
910 BMASwapRateHelper::BMASwapRateHelper(const Handle<Quote>& liborFraction,
911 const Period& tenor,
912 Natural settlementDays,
913 Calendar calendar,
914 // bma leg
915 const Period& bmaPeriod,
916 BusinessDayConvention bmaConvention,
917 DayCounter bmaDayCount,
918 ext::shared_ptr<BMAIndex> bmaIndex,
919 // libor leg
920 ext::shared_ptr<IborIndex> iborIndex)
921 : RelativeDateRateHelper(liborFraction), tenor_(tenor), settlementDays_(settlementDays),
922 calendar_(std::move(calendar)), bmaPeriod_(bmaPeriod), bmaConvention_(bmaConvention),
923 bmaDayCount_(std::move(bmaDayCount)), bmaIndex_(std::move(bmaIndex)),
924 iborIndex_(std::move(iborIndex)) {
925 registerWith(h: iborIndex_);
926 registerWith(h: bmaIndex_);
927 BMASwapRateHelper::initializeDates();
928 }
929
930 void BMASwapRateHelper::initializeDates() {
931 // if the evaluation date is not a business day
932 // then move to the next business day
933 JointCalendar jc(calendar_,
934 iborIndex_->fixingCalendar());
935 Date referenceDate = jc.adjust(evaluationDate_);
936 earliestDate_ =
937 calendar_.advance(date: referenceDate, period: settlementDays_ * Days, convention: Following);
938
939 Date maturity = earliestDate_ + tenor_;
940
941 // dummy BMA index with curve/swap arguments
942 ext::shared_ptr<BMAIndex> clonedIndex(new BMAIndex(termStructureHandle_));
943
944 Schedule bmaSchedule =
945 MakeSchedule().from(effectiveDate: earliestDate_).to(terminationDate: maturity)
946 .withTenor(bmaPeriod_)
947 .withCalendar(bmaIndex_->fixingCalendar())
948 .withConvention(bmaConvention_)
949 .backwards();
950
951 Schedule liborSchedule =
952 MakeSchedule().from(effectiveDate: earliestDate_).to(terminationDate: maturity)
953 .withTenor(iborIndex_->tenor())
954 .withCalendar(iborIndex_->fixingCalendar())
955 .withConvention(iborIndex_->businessDayConvention())
956 .endOfMonth(flag: iborIndex_->endOfMonth())
957 .backwards();
958
959 swap_ = ext::make_shared<BMASwap>(args: Swap::Payer, args: 100.0,
960 args&: liborSchedule,
961 args: 0.75, // arbitrary
962 args: 0.0,
963 args&: iborIndex_,
964 args: iborIndex_->dayCounter(),
965 args&: bmaSchedule,
966 args&: clonedIndex,
967 args&: bmaDayCount_);
968 swap_->setPricingEngine(ext::shared_ptr<PricingEngine>(new
969 DiscountingSwapEngine(iborIndex_->forwardingTermStructure())));
970
971 Date d = calendar_.adjust(swap_->maturityDate(), convention: Following);
972 Weekday w = d.weekday();
973 Date nextWednesday = (w >= 4) ?
974 d + (11 - w) * Days :
975 d + (4 - w) * Days;
976 latestDate_ = clonedIndex->valueDate(
977 fixingDate: clonedIndex->fixingCalendar().adjust(nextWednesday));
978 }
979
980 void BMASwapRateHelper::setTermStructure(YieldTermStructure* t) {
981 // do not set the relinkable handle as an observer -
982 // force recalculation when needed
983 bool observer = false;
984
985 ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
986 termStructureHandle_.linkTo(h: temp, registerAsObserver: observer);
987
988 RelativeDateRateHelper::setTermStructure(t);
989 }
990
991 Real BMASwapRateHelper::impliedQuote() const {
992 QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
993 // we didn't register as observers - force calculation
994 swap_->deepUpdate();
995 return swap_->fairLiborFraction();
996 }
997
998 void BMASwapRateHelper::accept(AcyclicVisitor& v) {
999 auto* v1 = dynamic_cast<Visitor<BMASwapRateHelper>*>(&v);
1000 if (v1 != nullptr)
1001 v1->visit(*this);
1002 else
1003 RateHelper::accept(v);
1004 }
1005
1006 FxSwapRateHelper::FxSwapRateHelper(const Handle<Quote>& fwdPoint,
1007 Handle<Quote> spotFx,
1008 const Period& tenor,
1009 Natural fixingDays,
1010 Calendar calendar,
1011 BusinessDayConvention convention,
1012 bool endOfMonth,
1013 bool isFxBaseCurrencyCollateralCurrency,
1014 Handle<YieldTermStructure> coll,
1015 Calendar tradingCalendar)
1016 : RelativeDateRateHelper(fwdPoint), spot_(std::move(spotFx)), tenor_(tenor),
1017 fixingDays_(fixingDays), cal_(std::move(calendar)), conv_(convention), eom_(endOfMonth),
1018 isFxBaseCurrencyCollateralCurrency_(isFxBaseCurrencyCollateralCurrency),
1019 collHandle_(std::move(coll)), tradingCalendar_(std::move(tradingCalendar)) {
1020 registerWith(h: spot_);
1021 registerWith(h: collHandle_);
1022
1023 if (tradingCalendar_.empty())
1024 jointCalendar_ = cal_;
1025 else
1026 jointCalendar_ = JointCalendar(tradingCalendar_, cal_,
1027 JoinHolidays);
1028 FxSwapRateHelper::initializeDates();
1029 }
1030
1031 void FxSwapRateHelper::initializeDates() {
1032 // if the evaluation date is not a business day
1033 // then move to the next business day
1034 Date refDate = cal_.adjust(evaluationDate_);
1035 earliestDate_ = cal_.advance(date: refDate, period: fixingDays_*Days);
1036
1037 if (!tradingCalendar_.empty()) {
1038 // check if fx trade can be settled in US, if not, adjust it
1039 earliestDate_ = jointCalendar_.adjust(earliestDate_);
1040 latestDate_ = jointCalendar_.advance(date: earliestDate_, period: tenor_,
1041 convention: conv_, endOfMonth: eom_);
1042 } else {
1043 latestDate_ = cal_.advance(date: earliestDate_, period: tenor_, convention: conv_, endOfMonth: eom_);
1044 }
1045 }
1046
1047 Real FxSwapRateHelper::impliedQuote() const {
1048 QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
1049
1050 QL_REQUIRE(!collHandle_.empty(), "collateral term structure not set");
1051
1052 DiscountFactor d1 = collHandle_->discount(d: earliestDate_);
1053 DiscountFactor d2 = collHandle_->discount(d: latestDate_);
1054 Real collRatio = d1 / d2;
1055 d1 = termStructureHandle_->discount(d: earliestDate_);
1056 d2 = termStructureHandle_->discount(d: latestDate_);
1057 Real ratio = d1 / d2;
1058 Real spot = spot_->value();
1059 if (isFxBaseCurrencyCollateralCurrency_) {
1060 return (ratio/collRatio-1)*spot;
1061 } else {
1062 return (collRatio/ratio-1)*spot;
1063 }
1064 }
1065
1066 void FxSwapRateHelper::setTermStructure(YieldTermStructure* t) {
1067 // do not set the relinkable handle as an observer -
1068 // force recalculation when needed
1069 bool observer = false;
1070
1071 ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
1072 termStructureHandle_.linkTo(h: temp, registerAsObserver: observer);
1073
1074 collRelinkableHandle_.linkTo(h: *collHandle_, registerAsObserver: observer);
1075
1076 RelativeDateRateHelper::setTermStructure(t);
1077 }
1078
1079 void FxSwapRateHelper::accept(AcyclicVisitor& v) {
1080 auto* v1 = dynamic_cast<Visitor<FxSwapRateHelper>*>(&v);
1081 if (v1 != nullptr)
1082 v1->visit(*this);
1083 else
1084 RateHelper::accept(v);
1085 }
1086
1087}
1088

source code of quantlib/ql/termstructures/yield/ratehelpers.cpp