/src/quantlib/ql/termstructures/yield/ratehelpers.cpp
Line | Count | Source (jump to first uncovered line) |
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/optional.hpp> |
32 | | #include <ql/pricingengines/swap/discountingswapengine.hpp> |
33 | | #include <ql/quote.hpp> |
34 | | #include <ql/termstructures/yield/ratehelpers.hpp> |
35 | | #include <ql/time/asx.hpp> |
36 | | #include <ql/time/calendars/jointcalendar.hpp> |
37 | | #include <ql/time/imm.hpp> |
38 | | #include <ql/utilities/null_deleter.hpp> |
39 | | #include <utility> |
40 | | |
41 | | namespace QuantLib { |
42 | | |
43 | | namespace { |
44 | | |
45 | 0 | void CheckDate(const Date& date, const Futures::Type type) { |
46 | 0 | switch (type) { |
47 | 0 | case Futures::IMM: |
48 | 0 | QL_REQUIRE(IMM::isIMMdate(date, false), date << " is not a valid IMM date"); |
49 | 0 | break; |
50 | 0 | case Futures::ASX: |
51 | 0 | QL_REQUIRE(ASX::isASXdate(date, false), date << " is not a valid ASX date"); |
52 | 0 | break; |
53 | 0 | case Futures::Custom: |
54 | 0 | break; |
55 | 0 | default: |
56 | 0 | QL_FAIL("unknown futures type (" << type << ')'); |
57 | 0 | } |
58 | 0 | } |
59 | | |
60 | | Time DetermineYearFraction(const Date& earliestDate, |
61 | | const Date& maturityDate, |
62 | 0 | const DayCounter& dayCounter) { |
63 | 0 | return dayCounter.yearFraction(earliestDate, maturityDate, |
64 | 0 | earliestDate, maturityDate); |
65 | 0 | } |
66 | | |
67 | | } // namespace |
68 | | |
69 | | FuturesRateHelper::FuturesRateHelper(const std::variant<Real, Handle<Quote>>& price, |
70 | | const Date& iborStartDate, |
71 | | Natural lengthInMonths, |
72 | | const Calendar& calendar, |
73 | | BusinessDayConvention convention, |
74 | | bool endOfMonth, |
75 | | const DayCounter& dayCounter, |
76 | | const std::variant<Real, Handle<Quote>>& convAdj, |
77 | | Futures::Type type) |
78 | 0 | : RateHelper(price), convAdj_(handleFromVariant(convAdj)) { |
79 | 0 | CheckDate(iborStartDate, type); |
80 | |
|
81 | 0 | earliestDate_ = iborStartDate; |
82 | 0 | maturityDate_ = |
83 | 0 | calendar.advance(iborStartDate, lengthInMonths * Months, convention, endOfMonth); |
84 | 0 | yearFraction_ = DetermineYearFraction(earliestDate_, maturityDate_, dayCounter); |
85 | 0 | pillarDate_ = latestDate_ = latestRelevantDate_ = maturityDate_; |
86 | |
|
87 | 0 | registerWith(convAdj_); |
88 | 0 | } |
89 | | |
90 | | FuturesRateHelper::FuturesRateHelper(const std::variant<Real, Handle<Quote>>& price, |
91 | | const Date& iborStartDate, |
92 | | const Date& iborEndDate, |
93 | | const DayCounter& dayCounter, |
94 | | const std::variant<Real, Handle<Quote>>& convAdj, |
95 | | Futures::Type type) |
96 | 0 | : RateHelper(price), convAdj_(handleFromVariant(convAdj)) { |
97 | 0 | CheckDate(iborStartDate, type); |
98 | |
|
99 | 0 | const auto determineMaturityDate = |
100 | 0 | [&iborStartDate, &iborEndDate](const auto nextDateCalculator) -> Date { |
101 | 0 | Date maturityDate; |
102 | 0 | if (iborEndDate == Date()) { |
103 | | // advance 3 months |
104 | 0 | maturityDate = nextDateCalculator(iborStartDate); |
105 | 0 | maturityDate = nextDateCalculator(maturityDate); |
106 | 0 | maturityDate = nextDateCalculator(maturityDate); |
107 | 0 | } else { |
108 | 0 | QL_REQUIRE(iborEndDate > iborStartDate, |
109 | 0 | "end date (" << iborEndDate << ") must be greater than start date (" |
110 | 0 | << iborStartDate << ')'); |
111 | 0 | maturityDate = iborEndDate; |
112 | 0 | } |
113 | 0 | return maturityDate; |
114 | 0 | }; Unexecuted instantiation: ratehelpers.cpp:QuantLib::Date QuantLib::FuturesRateHelper::FuturesRateHelper(std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Date const&, QuantLib::Date const&, QuantLib::DayCounter const&, std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Futures::Type)::$_0::operator()<QuantLib::FuturesRateHelper::$_0(std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Date const&, QuantLib::Date const&, QuantLib::DayCounter const&, std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Futures::Type)::$_1>(QuantLib::FuturesRateHelper::$_0(std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Date const&, QuantLib::Date const&, QuantLib::DayCounter const&, std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Futures::Type)::$_1) const Unexecuted instantiation: ratehelpers.cpp:QuantLib::Date QuantLib::FuturesRateHelper::FuturesRateHelper(std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Date const&, QuantLib::Date const&, QuantLib::DayCounter const&, std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Futures::Type)::$_0::operator()<QuantLib::FuturesRateHelper::$_0(std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Date const&, QuantLib::Date const&, QuantLib::DayCounter const&, std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Futures::Type)::$_2>(QuantLib::FuturesRateHelper::$_0(std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Date const&, QuantLib::Date const&, QuantLib::DayCounter const&, std::__1::variant<double, QuantLib::Handle<QuantLib::Quote> > const&, QuantLib::Futures::Type)::$_2) const |
115 | |
|
116 | 0 | switch (type) { |
117 | 0 | case Futures::IMM: |
118 | 0 | maturityDate_ = determineMaturityDate( |
119 | 0 | [](const Date date) -> Date { return IMM::nextDate(date, false); }); |
120 | 0 | break; |
121 | 0 | case Futures::ASX: |
122 | 0 | maturityDate_ = determineMaturityDate( |
123 | 0 | [](const Date date) -> Date { return ASX::nextDate(date, false); }); |
124 | 0 | break; |
125 | 0 | case Futures::Custom: |
126 | 0 | maturityDate_ = iborEndDate; |
127 | 0 | break; |
128 | 0 | default: |
129 | 0 | QL_FAIL("unsupported futures type (" << type << ')'); |
130 | 0 | } |
131 | 0 | earliestDate_ = iborStartDate; |
132 | 0 | yearFraction_ = DetermineYearFraction(earliestDate_, maturityDate_, dayCounter); |
133 | 0 | pillarDate_ = latestDate_ = latestRelevantDate_ = maturityDate_; |
134 | |
|
135 | 0 | registerWith(convAdj_); |
136 | 0 | } |
137 | | |
138 | | FuturesRateHelper::FuturesRateHelper(const std::variant<Real, Handle<Quote>>& price, |
139 | | const Date& iborStartDate, |
140 | | const ext::shared_ptr<IborIndex>& index, |
141 | | const std::variant<Real, Handle<Quote>>& convAdj, |
142 | | Futures::Type type) |
143 | 0 | : RateHelper(price), convAdj_(handleFromVariant(convAdj)) { |
144 | 0 | CheckDate(iborStartDate, type); |
145 | |
|
146 | 0 | earliestDate_ = iborStartDate; |
147 | 0 | const Calendar& cal = index->fixingCalendar(); |
148 | 0 | maturityDate_ = |
149 | 0 | cal.advance(iborStartDate, index->tenor(), index->businessDayConvention()); |
150 | 0 | yearFraction_ = DetermineYearFraction(earliestDate_, maturityDate_, index->dayCounter()); |
151 | 0 | pillarDate_ = latestDate_ = latestRelevantDate_ = maturityDate_; |
152 | |
|
153 | 0 | registerWith(convAdj_); |
154 | 0 | } |
155 | | |
156 | 0 | Real FuturesRateHelper::impliedQuote() const { |
157 | 0 | QL_REQUIRE(termStructure_ != nullptr, "term structure not set"); |
158 | 0 | Rate forwardRate = (termStructure_->discount(earliestDate_) / |
159 | 0 | termStructure_->discount(maturityDate_) - 1.0) / yearFraction_; |
160 | | // Convexity, as FRA/futures adjustment, has been used in the |
161 | | // past to take into account futures margining vs FRA. |
162 | | // Therefore, there's no requirement for it to be non-negative. |
163 | 0 | Rate futureRate = forwardRate + convexityAdjustment(); |
164 | 0 | return 100.0 * (1.0 - futureRate); |
165 | 0 | } |
166 | | |
167 | 0 | Real FuturesRateHelper::convexityAdjustment() const { |
168 | 0 | return convAdj_.empty() ? 0.0 : convAdj_->value(); |
169 | 0 | } |
170 | | |
171 | 0 | void FuturesRateHelper::accept(AcyclicVisitor& v) { |
172 | 0 | auto* v1 = dynamic_cast<Visitor<FuturesRateHelper>*>(&v); |
173 | 0 | if (v1 != nullptr) |
174 | 0 | v1->visit(*this); |
175 | 0 | else |
176 | 0 | RateHelper::accept(v); |
177 | 0 | } |
178 | | |
179 | | DepositRateHelper::DepositRateHelper(const std::variant<Rate, Handle<Quote>>& rate, |
180 | | const Period& tenor, |
181 | | Natural fixingDays, |
182 | | const Calendar& calendar, |
183 | | BusinessDayConvention convention, |
184 | | bool endOfMonth, |
185 | | const DayCounter& dayCounter) |
186 | 0 | : RelativeDateRateHelper(rate) { |
187 | 0 | iborIndex_ = ext::make_shared<IborIndex>("no-fix", // never take fixing into account |
188 | 0 | tenor, fixingDays, |
189 | 0 | Currency(), calendar, convention, |
190 | 0 | endOfMonth, dayCounter, termStructureHandle_); |
191 | 0 | DepositRateHelper::initializeDates(); |
192 | 0 | } |
193 | | |
194 | | DepositRateHelper::DepositRateHelper(const std::variant<Rate, Handle<Quote>>& rate, |
195 | | const ext::shared_ptr<IborIndex>& i) |
196 | 0 | : RelativeDateRateHelper(rate) { |
197 | 0 | iborIndex_ = i->clone(termStructureHandle_); |
198 | 0 | DepositRateHelper::initializeDates(); |
199 | 0 | } |
200 | | |
201 | | DepositRateHelper::DepositRateHelper(const std::variant<Rate, Handle<Quote>>& rate, |
202 | | Date fixingDate, |
203 | | const ext::shared_ptr<IborIndex>& i) |
204 | 0 | : RelativeDateRateHelper(rate, false), fixingDate_(fixingDate) { |
205 | 0 | iborIndex_ = i->clone(termStructureHandle_); |
206 | 0 | DepositRateHelper::initializeDates(); |
207 | 0 | } |
208 | | |
209 | 0 | Real DepositRateHelper::impliedQuote() const { |
210 | 0 | QL_REQUIRE(termStructure_ != nullptr, "term structure not set"); |
211 | | // the forecast fixing flag is set to true because |
212 | | // we do not want to take fixing into account |
213 | 0 | return iborIndex_->fixing(fixingDate_, true); |
214 | 0 | } |
215 | | |
216 | 0 | void DepositRateHelper::setTermStructure(YieldTermStructure* t) { |
217 | | // do not set the relinkable handle as an observer - |
218 | | // force recalculation when needed---the index is not lazy |
219 | 0 | bool observer = false; |
220 | |
|
221 | 0 | ext::shared_ptr<YieldTermStructure> temp(t, null_deleter()); |
222 | 0 | termStructureHandle_.linkTo(temp, observer); |
223 | |
|
224 | 0 | RelativeDateRateHelper::setTermStructure(t); |
225 | 0 | } |
226 | | |
227 | 0 | void DepositRateHelper::initializeDates() { |
228 | 0 | if (updateDates_) { |
229 | | // if the evaluation date is not a business day |
230 | | // then move to the next business day |
231 | 0 | Date referenceDate = |
232 | 0 | iborIndex_->fixingCalendar().adjust(evaluationDate_); |
233 | 0 | earliestDate_ = iborIndex_->valueDate(referenceDate); |
234 | 0 | fixingDate_ = iborIndex_->fixingDate(earliestDate_); |
235 | 0 | } else { |
236 | 0 | earliestDate_ = iborIndex_->valueDate(fixingDate_); |
237 | 0 | } |
238 | 0 | maturityDate_ = iborIndex_->maturityDate(earliestDate_); |
239 | 0 | pillarDate_ = latestDate_ = latestRelevantDate_ = maturityDate_; |
240 | 0 | } |
241 | | |
242 | 0 | void DepositRateHelper::accept(AcyclicVisitor& v) { |
243 | 0 | auto* v1 = dynamic_cast<Visitor<DepositRateHelper>*>(&v); |
244 | 0 | if (v1 != nullptr) |
245 | 0 | v1->visit(*this); |
246 | 0 | else |
247 | 0 | RateHelper::accept(v); |
248 | 0 | } |
249 | | |
250 | | |
251 | | FraRateHelper::FraRateHelper(const std::variant<Rate, Handle<Quote>>& rate, |
252 | | Natural monthsToStart, |
253 | | Natural monthsToEnd, |
254 | | Natural fixingDays, |
255 | | const Calendar& calendar, |
256 | | BusinessDayConvention convention, |
257 | | bool endOfMonth, |
258 | | const DayCounter& dayCounter, |
259 | | Pillar::Choice pillarChoice, |
260 | | Date customPillarDate, |
261 | | bool useIndexedCoupon) |
262 | 0 | : FraRateHelper(rate, monthsToStart*Months, monthsToEnd-monthsToStart, fixingDays, calendar, |
263 | 0 | convention, endOfMonth, dayCounter, pillarChoice, customPillarDate, useIndexedCoupon) { |
264 | 0 | QL_REQUIRE(monthsToEnd>monthsToStart, |
265 | 0 | "monthsToEnd (" << monthsToEnd << |
266 | 0 | ") must be grater than monthsToStart (" << monthsToStart << |
267 | 0 | ")"); |
268 | 0 | } |
269 | | |
270 | | FraRateHelper::FraRateHelper(const std::variant<Rate, Handle<Quote>>& rate, |
271 | | Natural monthsToStart, |
272 | | const ext::shared_ptr<IborIndex>& i, |
273 | | Pillar::Choice pillarChoice, |
274 | | Date customPillarDate, |
275 | | bool useIndexedCoupon) |
276 | 0 | : FraRateHelper(rate, monthsToStart*Months, i, pillarChoice, customPillarDate, useIndexedCoupon) |
277 | 0 | {} |
278 | | |
279 | | FraRateHelper::FraRateHelper(const std::variant<Rate, Handle<Quote>>& rate, |
280 | | Period periodToStart, |
281 | | Natural lengthInMonths, |
282 | | Natural fixingDays, |
283 | | const Calendar& calendar, |
284 | | BusinessDayConvention convention, |
285 | | bool endOfMonth, |
286 | | const DayCounter& dayCounter, |
287 | | Pillar::Choice pillarChoice, |
288 | | Date customPillarDate, |
289 | | bool useIndexedCoupon) |
290 | 0 | : RelativeDateRateHelper(rate), periodToStart_(periodToStart), |
291 | 0 | pillarChoice_(pillarChoice), useIndexedCoupon_(useIndexedCoupon) { |
292 | | // no way to take fixing into account, |
293 | | // even if we would like to for FRA over today |
294 | 0 | iborIndex_ = ext::make_shared<IborIndex>("no-fix", // correct family name would be needed |
295 | 0 | lengthInMonths*Months, |
296 | 0 | fixingDays, |
297 | 0 | Currency(), calendar, convention, |
298 | 0 | endOfMonth, dayCounter, termStructureHandle_); |
299 | 0 | pillarDate_ = customPillarDate; |
300 | 0 | FraRateHelper::initializeDates(); |
301 | 0 | } |
302 | | |
303 | | FraRateHelper::FraRateHelper(const std::variant<Rate, Handle<Quote>>& rate, |
304 | | Period periodToStart, |
305 | | const ext::shared_ptr<IborIndex>& i, |
306 | | Pillar::Choice pillarChoice, |
307 | | Date customPillarDate, |
308 | | bool useIndexedCoupon) |
309 | 0 | : RelativeDateRateHelper(rate), periodToStart_(periodToStart), |
310 | 0 | pillarChoice_(pillarChoice), useIndexedCoupon_(useIndexedCoupon) { |
311 | | // take fixing into account |
312 | 0 | iborIndex_ = i->clone(termStructureHandle_); |
313 | | // We want to be notified of changes of fixings, but we don't |
314 | | // want notifications from termStructureHandle_ (they would |
315 | | // interfere with bootstrapping.) |
316 | 0 | iborIndex_->unregisterWith(termStructureHandle_); |
317 | 0 | registerWith(iborIndex_); |
318 | 0 | pillarDate_ = customPillarDate; |
319 | 0 | FraRateHelper::initializeDates(); |
320 | 0 | } |
321 | | |
322 | | FraRateHelper::FraRateHelper(const std::variant<Rate, Handle<Quote>>& rate, |
323 | | Natural immOffsetStart, |
324 | | Natural immOffsetEnd, |
325 | | const ext::shared_ptr<IborIndex>& i, |
326 | | Pillar::Choice pillarChoice, |
327 | | Date customPillarDate, |
328 | | bool useIndexedCoupon) |
329 | 0 | : RelativeDateRateHelper(rate), immOffsetStart_(immOffsetStart), immOffsetEnd_(immOffsetEnd), |
330 | 0 | pillarChoice_(pillarChoice), useIndexedCoupon_(useIndexedCoupon) { |
331 | | // take fixing into account |
332 | 0 | iborIndex_ = i->clone(termStructureHandle_); |
333 | | // see above |
334 | 0 | iborIndex_->unregisterWith(termStructureHandle_); |
335 | 0 | registerWith(iborIndex_); |
336 | 0 | pillarDate_ = customPillarDate; |
337 | 0 | FraRateHelper::initializeDates(); |
338 | 0 | } |
339 | | |
340 | | FraRateHelper::FraRateHelper(const std::variant<Rate, Handle<Quote>>& rate, |
341 | | Date startDate, |
342 | | Date endDate, |
343 | | const ext::shared_ptr<IborIndex>& i, |
344 | | Pillar::Choice pillarChoice, |
345 | | Date customPillarDate, |
346 | | bool useIndexedCoupon) |
347 | 0 | : RelativeDateRateHelper(rate, false), pillarChoice_(pillarChoice), |
348 | 0 | useIndexedCoupon_(useIndexedCoupon) { |
349 | | // take fixing into account |
350 | 0 | iborIndex_ = i->clone(termStructureHandle_); |
351 | | // see above |
352 | 0 | iborIndex_->unregisterWith(termStructureHandle_); |
353 | 0 | registerWith(iborIndex_); |
354 | 0 | earliestDate_ = startDate; |
355 | 0 | maturityDate_ = endDate; |
356 | 0 | pillarDate_ = customPillarDate; |
357 | 0 | FraRateHelper::initializeDates(); |
358 | 0 | } |
359 | | |
360 | 0 | Real FraRateHelper::impliedQuote() const { |
361 | 0 | QL_REQUIRE(termStructure_ != nullptr, "term structure not set"); |
362 | 0 | if (useIndexedCoupon_) |
363 | 0 | return iborIndex_->fixing(fixingDate_, true); |
364 | 0 | else |
365 | 0 | return (termStructure_->discount(earliestDate_) / |
366 | 0 | termStructure_->discount(maturityDate_) - |
367 | 0 | 1.0) / |
368 | 0 | spanningTime_; |
369 | 0 | } |
370 | | |
371 | 0 | void FraRateHelper::setTermStructure(YieldTermStructure* t) { |
372 | | // do not set the relinkable handle as an observer - |
373 | | // force recalculation when needed---the index is not lazy |
374 | 0 | bool observer = false; |
375 | |
|
376 | 0 | ext::shared_ptr<YieldTermStructure> temp(t, null_deleter()); |
377 | 0 | termStructureHandle_.linkTo(temp, observer); |
378 | |
|
379 | 0 | RelativeDateRateHelper::setTermStructure(t); |
380 | 0 | } |
381 | | |
382 | | namespace { |
383 | 0 | Date nthImmDate(const Date& asof, const Size n) { |
384 | 0 | Date imm = asof; |
385 | 0 | for (Size i = 0; i < n; ++i) { |
386 | 0 | imm = IMM::nextDate(imm, true); |
387 | 0 | } |
388 | 0 | return imm; |
389 | 0 | } |
390 | | } |
391 | | |
392 | 0 | void FraRateHelper::initializeDates() { |
393 | 0 | if (updateDates_) { |
394 | | // if the evaluation date is not a business day |
395 | | // then move to the next business day |
396 | 0 | Date referenceDate = |
397 | 0 | iborIndex_->fixingCalendar().adjust(evaluationDate_); |
398 | 0 | Date spotDate = iborIndex_->fixingCalendar().advance( |
399 | 0 | referenceDate, iborIndex_->fixingDays()*Days); |
400 | 0 | if (periodToStart_) { // NOLINT(readability-implicit-bool-conversion) |
401 | 0 | earliestDate_ = iborIndex_->fixingCalendar().advance( |
402 | 0 | spotDate, *periodToStart_, iborIndex_->businessDayConvention(), |
403 | 0 | iborIndex_->endOfMonth()); |
404 | | // maturity date is calculated from spot date |
405 | 0 | maturityDate_ = iborIndex_->fixingCalendar().advance( |
406 | 0 | spotDate, *periodToStart_ + iborIndex_->tenor(), iborIndex_->businessDayConvention(), |
407 | 0 | iborIndex_->endOfMonth()); |
408 | |
|
409 | 0 | } else if ((immOffsetStart_) && (immOffsetEnd_)) { // NOLINT(readability-implicit-bool-conversion) |
410 | 0 | earliestDate_ = iborIndex_->fixingCalendar().adjust(nthImmDate(spotDate, *immOffsetStart_)); |
411 | 0 | maturityDate_ = iborIndex_->fixingCalendar().adjust(nthImmDate(spotDate, *immOffsetEnd_)); |
412 | 0 | } else { |
413 | 0 | QL_FAIL("neither periodToStart nor immOffsetStart/End given"); |
414 | 0 | } |
415 | 0 | } |
416 | | |
417 | 0 | if (useIndexedCoupon_) |
418 | | // latest relevant date is calculated from earliestDate_ |
419 | 0 | latestRelevantDate_ = iborIndex_->maturityDate(earliestDate_); |
420 | 0 | else { |
421 | 0 | latestRelevantDate_ = maturityDate_; |
422 | 0 | spanningTime_ = iborIndex_->dayCounter().yearFraction(earliestDate_, maturityDate_); |
423 | 0 | } |
424 | |
|
425 | 0 | switch (pillarChoice_) { |
426 | 0 | case Pillar::MaturityDate: |
427 | 0 | pillarDate_ = maturityDate_; |
428 | 0 | break; |
429 | 0 | case Pillar::LastRelevantDate: |
430 | 0 | pillarDate_ = latestRelevantDate_; |
431 | 0 | break; |
432 | 0 | case Pillar::CustomDate: |
433 | | // pillarDate_ already assigned at construction time |
434 | 0 | QL_REQUIRE(pillarDate_ >= earliestDate_, |
435 | 0 | "pillar date (" << pillarDate_ << ") must be later " |
436 | 0 | "than or equal to the instrument's earliest date (" << |
437 | 0 | earliestDate_ << ")"); |
438 | 0 | QL_REQUIRE(pillarDate_ <= latestRelevantDate_, |
439 | 0 | "pillar date (" << pillarDate_ << ") must be before " |
440 | 0 | "or equal to the instrument's latest relevant date (" << |
441 | 0 | latestRelevantDate_ << ")"); |
442 | 0 | break; |
443 | 0 | default: |
444 | 0 | QL_FAIL("unknown Pillar::Choice(" << Integer(pillarChoice_) << ")"); |
445 | 0 | } |
446 | | |
447 | 0 | latestDate_ = pillarDate_; // backward compatibility |
448 | |
|
449 | 0 | fixingDate_ = iborIndex_->fixingDate(earliestDate_); |
450 | 0 | } |
451 | | |
452 | 0 | void FraRateHelper::accept(AcyclicVisitor& v) { |
453 | 0 | auto* v1 = dynamic_cast<Visitor<FraRateHelper>*>(&v); |
454 | 0 | if (v1 != nullptr) |
455 | 0 | v1->visit(*this); |
456 | 0 | else |
457 | 0 | RateHelper::accept(v); |
458 | 0 | } |
459 | | |
460 | | |
461 | | SwapRateHelper::SwapRateHelper(const std::variant<Rate, Handle<Quote>>& rate, |
462 | | const ext::shared_ptr<SwapIndex>& swapIndex, |
463 | | Handle<Quote> spread, |
464 | | const Period& fwdStart, |
465 | | Handle<YieldTermStructure> discount, |
466 | | Pillar::Choice pillarChoice, |
467 | | Date customPillarDate, |
468 | | bool endOfMonth, |
469 | | const ext::optional<bool>& useIndexedCoupons) |
470 | 0 | : SwapRateHelper(rate, swapIndex->tenor(), swapIndex->fixingCalendar(), |
471 | 0 | swapIndex->fixedLegTenor().frequency(), swapIndex->fixedLegConvention(), |
472 | 0 | swapIndex->dayCounter(), swapIndex->iborIndex(), std::move(spread), fwdStart, |
473 | 0 | std::move(discount), Null<Natural>(), pillarChoice, customPillarDate, endOfMonth, |
474 | 0 | useIndexedCoupons) {} |
475 | | |
476 | | SwapRateHelper::SwapRateHelper(const std::variant<Rate, Handle<Quote>>& rate, |
477 | | const Period& tenor, |
478 | | Calendar calendar, |
479 | | Frequency fixedFrequency, |
480 | | BusinessDayConvention fixedConvention, |
481 | | DayCounter fixedDayCount, |
482 | | const ext::shared_ptr<IborIndex>& iborIndex, |
483 | | Handle<Quote> spread, |
484 | | const Period& fwdStart, |
485 | | Handle<YieldTermStructure> discount, |
486 | | Natural settlementDays, |
487 | | Pillar::Choice pillarChoice, |
488 | | Date customPillarDate, |
489 | | bool endOfMonth, |
490 | | const ext::optional<bool>& useIndexedCoupons, |
491 | | const ext::optional<BusinessDayConvention>& floatConvention) |
492 | 0 | : RelativeDateRateHelper(rate), settlementDays_(settlementDays), tenor_(tenor), |
493 | 0 | pillarChoice_(pillarChoice), calendar_(std::move(calendar)), |
494 | 0 | fixedConvention_(fixedConvention), fixedFrequency_(fixedFrequency), |
495 | 0 | fixedDayCount_(std::move(fixedDayCount)), spread_(std::move(spread)), endOfMonth_(endOfMonth), |
496 | 0 | fwdStart_(fwdStart), discountHandle_(std::move(discount)), |
497 | 0 | useIndexedCoupons_(useIndexedCoupons), floatConvention_(floatConvention) { |
498 | 0 | initialize(iborIndex, customPillarDate); |
499 | 0 | } |
500 | | |
501 | | SwapRateHelper::SwapRateHelper(const std::variant<Rate, Handle<Quote>>& rate, |
502 | | const Date& startDate, |
503 | | const Date& endDate, |
504 | | Calendar calendar, |
505 | | Frequency fixedFrequency, |
506 | | BusinessDayConvention fixedConvention, |
507 | | DayCounter fixedDayCount, |
508 | | const ext::shared_ptr<IborIndex>& iborIndex, |
509 | | Handle<Quote> spread, |
510 | | Handle<YieldTermStructure> discount, |
511 | | Pillar::Choice pillarChoice, |
512 | | Date customPillarDate, |
513 | | bool endOfMonth, |
514 | | const ext::optional<bool>& useIndexedCoupons, |
515 | | const ext::optional<BusinessDayConvention>& floatConvention) |
516 | 0 | : RelativeDateRateHelper(rate, false), startDate_(startDate), endDate_(endDate), |
517 | 0 | pillarChoice_(pillarChoice), calendar_(std::move(calendar)), |
518 | 0 | fixedConvention_(fixedConvention), fixedFrequency_(fixedFrequency), |
519 | 0 | fixedDayCount_(std::move(fixedDayCount)), spread_(std::move(spread)), endOfMonth_(endOfMonth), |
520 | 0 | discountHandle_(std::move(discount)), useIndexedCoupons_(useIndexedCoupons), |
521 | 0 | floatConvention_(floatConvention) { |
522 | 0 | QL_REQUIRE(fixedFrequency != Once, |
523 | 0 | "fixedFrequency == Once is not supported when passing explicit " |
524 | 0 | "startDate and endDate"); |
525 | 0 | initialize(iborIndex, customPillarDate); |
526 | 0 | } |
527 | | |
528 | | void SwapRateHelper::initialize(const ext::shared_ptr<IborIndex>& iborIndex, |
529 | 0 | Date customPillarDate) { |
530 | | // take fixing into account |
531 | 0 | iborIndex_ = iborIndex->clone(termStructureHandle_); |
532 | | // We want to be notified of changes of fixings, but we don't |
533 | | // want notifications from termStructureHandle_ (they would |
534 | | // interfere with bootstrapping.) |
535 | 0 | iborIndex_->unregisterWith(termStructureHandle_); |
536 | |
|
537 | 0 | registerWith(iborIndex_); |
538 | 0 | registerWith(spread_); |
539 | 0 | registerWith(discountHandle_); |
540 | |
|
541 | 0 | pillarDate_ = customPillarDate; |
542 | 0 | SwapRateHelper::initializeDates(); |
543 | 0 | } |
544 | | |
545 | 0 | void SwapRateHelper::initializeDates() { |
546 | | |
547 | | // 1. do not pass the spread here, as it might be a Quote |
548 | | // i.e. it can dynamically change |
549 | | // 2. input discount curve Handle might be empty now but it could |
550 | | // be assigned a curve later; use a RelinkableHandle here |
551 | 0 | auto tmp = MakeVanillaSwap(tenor_, iborIndex_, 0.0, fwdStart_) |
552 | 0 | .withSettlementDays(settlementDays_) // resets effectiveDate |
553 | 0 | .withEffectiveDate(startDate_) |
554 | 0 | .withTerminationDate(endDate_) |
555 | 0 | .withDiscountingTermStructure(discountRelinkableHandle_) |
556 | 0 | .withFixedLegDayCount(fixedDayCount_) |
557 | 0 | .withFixedLegTenor(fixedFrequency_ == Once ? tenor_ : Period(fixedFrequency_)) |
558 | 0 | .withFixedLegConvention(fixedConvention_) |
559 | 0 | .withFixedLegTerminationDateConvention(fixedConvention_) |
560 | 0 | .withFixedLegCalendar(calendar_) |
561 | 0 | .withFixedLegEndOfMonth(endOfMonth_) |
562 | 0 | .withFloatingLegCalendar(calendar_) |
563 | 0 | .withFloatingLegEndOfMonth(endOfMonth_) |
564 | 0 | .withIndexedCoupons(useIndexedCoupons_); |
565 | 0 | if (floatConvention_) { |
566 | 0 | tmp.withFloatingLegConvention(*floatConvention_) |
567 | 0 | .withFloatingLegTerminationDateConvention(*floatConvention_); |
568 | 0 | } |
569 | 0 | swap_ = tmp; |
570 | |
|
571 | 0 | simplifyNotificationGraph(*swap_, true); |
572 | |
|
573 | 0 | earliestDate_ = swap_->startDate(); |
574 | 0 | maturityDate_ = swap_->maturityDate(); |
575 | |
|
576 | 0 | ext::shared_ptr<IborCoupon> lastCoupon = |
577 | 0 | ext::dynamic_pointer_cast<IborCoupon>(swap_->floatingLeg().back()); |
578 | 0 | latestRelevantDate_ = std::max(maturityDate_, lastCoupon->fixingEndDate()); |
579 | |
|
580 | 0 | switch (pillarChoice_) { |
581 | 0 | case Pillar::MaturityDate: |
582 | 0 | pillarDate_ = maturityDate_; |
583 | 0 | break; |
584 | 0 | case Pillar::LastRelevantDate: |
585 | 0 | pillarDate_ = latestRelevantDate_; |
586 | 0 | break; |
587 | 0 | case Pillar::CustomDate: |
588 | | // pillarDate_ already assigned at construction time |
589 | 0 | QL_REQUIRE(pillarDate_ >= earliestDate_, |
590 | 0 | "pillar date (" << pillarDate_ << ") must be later " |
591 | 0 | "than or equal to the instrument's earliest date (" << |
592 | 0 | earliestDate_ << ")"); |
593 | 0 | QL_REQUIRE(pillarDate_ <= latestRelevantDate_, |
594 | 0 | "pillar date (" << pillarDate_ << ") must be before " |
595 | 0 | "or equal to the instrument's latest relevant date (" << |
596 | 0 | latestRelevantDate_ << ")"); |
597 | 0 | break; |
598 | 0 | default: |
599 | 0 | QL_FAIL("unknown Pillar::Choice(" << Integer(pillarChoice_) << ")"); |
600 | 0 | } |
601 | | |
602 | 0 | latestDate_ = pillarDate_; // backward compatibility |
603 | |
|
604 | 0 | } |
605 | | |
606 | 0 | void SwapRateHelper::setTermStructure(YieldTermStructure* t) { |
607 | | // do not set the relinkable handle as an observer - |
608 | | // force recalculation when needed |
609 | 0 | bool observer = false; |
610 | |
|
611 | 0 | ext::shared_ptr<YieldTermStructure> temp(t, null_deleter()); |
612 | 0 | termStructureHandle_.linkTo(temp, observer); |
613 | |
|
614 | 0 | if (discountHandle_.empty()) |
615 | 0 | discountRelinkableHandle_.linkTo(temp, observer); |
616 | 0 | else |
617 | 0 | discountRelinkableHandle_.linkTo(*discountHandle_, observer); |
618 | |
|
619 | 0 | RelativeDateRateHelper::setTermStructure(t); |
620 | 0 | } |
621 | | |
622 | 0 | Real SwapRateHelper::impliedQuote() const { |
623 | 0 | QL_REQUIRE(termStructure_ != nullptr, "term structure not set"); |
624 | | // we didn't register as observers - force calculation |
625 | 0 | swap_->deepUpdate(); |
626 | | // weak implementation... to be improved |
627 | 0 | static const Spread basisPoint = 1.0e-4; |
628 | 0 | Real floatingLegNPV = swap_->floatingLegNPV(); |
629 | 0 | Spread spread = spread_.empty() ? 0.0 : spread_->value(); |
630 | 0 | Real spreadNPV = swap_->floatingLegBPS()/basisPoint*spread; |
631 | 0 | Real totNPV = - (floatingLegNPV+spreadNPV); |
632 | 0 | Real result = totNPV/(swap_->fixedLegBPS()/basisPoint); |
633 | 0 | return result; |
634 | 0 | } |
635 | | |
636 | 0 | void SwapRateHelper::accept(AcyclicVisitor& v) { |
637 | 0 | auto* v1 = dynamic_cast<Visitor<SwapRateHelper>*>(&v); |
638 | 0 | if (v1 != nullptr) |
639 | 0 | v1->visit(*this); |
640 | 0 | else |
641 | 0 | RateHelper::accept(v); |
642 | 0 | } |
643 | | |
644 | | BMASwapRateHelper::BMASwapRateHelper(const Handle<Quote>& liborFraction, |
645 | | const Period& tenor, |
646 | | Natural settlementDays, |
647 | | Calendar calendar, |
648 | | // bma leg |
649 | | const Period& bmaPeriod, |
650 | | BusinessDayConvention bmaConvention, |
651 | | DayCounter bmaDayCount, |
652 | | ext::shared_ptr<BMAIndex> bmaIndex, |
653 | | // libor leg |
654 | | ext::shared_ptr<IborIndex> iborIndex) |
655 | 0 | : RelativeDateRateHelper(liborFraction), tenor_(tenor), settlementDays_(settlementDays), |
656 | 0 | calendar_(std::move(calendar)), bmaPeriod_(bmaPeriod), bmaConvention_(bmaConvention), |
657 | 0 | bmaDayCount_(std::move(bmaDayCount)), bmaIndex_(std::move(bmaIndex)), |
658 | 0 | iborIndex_(std::move(iborIndex)) { |
659 | 0 | registerWith(iborIndex_); |
660 | 0 | registerWith(bmaIndex_); |
661 | 0 | BMASwapRateHelper::initializeDates(); |
662 | 0 | } |
663 | | |
664 | 0 | void BMASwapRateHelper::initializeDates() { |
665 | | // if the evaluation date is not a business day |
666 | | // then move to the next business day |
667 | 0 | JointCalendar jc(calendar_, |
668 | 0 | iborIndex_->fixingCalendar()); |
669 | 0 | Date referenceDate = jc.adjust(evaluationDate_); |
670 | 0 | earliestDate_ = |
671 | 0 | calendar_.advance(referenceDate, settlementDays_ * Days, Following); |
672 | |
|
673 | 0 | Date maturity = earliestDate_ + tenor_; |
674 | | |
675 | | // dummy BMA index with curve/swap arguments |
676 | 0 | ext::shared_ptr<BMAIndex> clonedIndex(new BMAIndex(termStructureHandle_)); |
677 | |
|
678 | 0 | Schedule bmaSchedule = |
679 | 0 | MakeSchedule().from(earliestDate_).to(maturity) |
680 | 0 | .withTenor(bmaPeriod_) |
681 | 0 | .withCalendar(bmaIndex_->fixingCalendar()) |
682 | 0 | .withConvention(bmaConvention_) |
683 | 0 | .backwards(); |
684 | |
|
685 | 0 | Schedule liborSchedule = |
686 | 0 | MakeSchedule().from(earliestDate_).to(maturity) |
687 | 0 | .withTenor(iborIndex_->tenor()) |
688 | 0 | .withCalendar(iborIndex_->fixingCalendar()) |
689 | 0 | .withConvention(iborIndex_->businessDayConvention()) |
690 | 0 | .endOfMonth(iborIndex_->endOfMonth()) |
691 | 0 | .backwards(); |
692 | |
|
693 | 0 | swap_ = ext::make_shared<BMASwap>(Swap::Payer, 100.0, |
694 | 0 | liborSchedule, |
695 | 0 | 0.75, // arbitrary |
696 | 0 | 0.0, |
697 | 0 | iborIndex_, |
698 | 0 | iborIndex_->dayCounter(), |
699 | 0 | bmaSchedule, |
700 | 0 | clonedIndex, |
701 | 0 | bmaDayCount_); |
702 | 0 | swap_->setPricingEngine(ext::shared_ptr<PricingEngine>(new |
703 | 0 | DiscountingSwapEngine(iborIndex_->forwardingTermStructure()))); |
704 | |
|
705 | 0 | Date d = calendar_.adjust(swap_->maturityDate(), Following); |
706 | 0 | Weekday w = d.weekday(); |
707 | 0 | Date nextWednesday = (w >= 4) ? |
708 | 0 | d + (11 - w) * Days : |
709 | 0 | d + (4 - w) * Days; |
710 | 0 | latestDate_ = clonedIndex->valueDate( |
711 | 0 | clonedIndex->fixingCalendar().adjust(nextWednesday)); |
712 | 0 | } |
713 | | |
714 | 0 | void BMASwapRateHelper::setTermStructure(YieldTermStructure* t) { |
715 | | // do not set the relinkable handle as an observer - |
716 | | // force recalculation when needed |
717 | 0 | bool observer = false; |
718 | |
|
719 | 0 | ext::shared_ptr<YieldTermStructure> temp(t, null_deleter()); |
720 | 0 | termStructureHandle_.linkTo(temp, observer); |
721 | |
|
722 | 0 | RelativeDateRateHelper::setTermStructure(t); |
723 | 0 | } |
724 | | |
725 | 0 | Real BMASwapRateHelper::impliedQuote() const { |
726 | 0 | QL_REQUIRE(termStructure_ != nullptr, "term structure not set"); |
727 | | // we didn't register as observers - force calculation |
728 | 0 | swap_->deepUpdate(); |
729 | 0 | return swap_->fairLiborFraction(); |
730 | 0 | } |
731 | | |
732 | 0 | void BMASwapRateHelper::accept(AcyclicVisitor& v) { |
733 | 0 | auto* v1 = dynamic_cast<Visitor<BMASwapRateHelper>*>(&v); |
734 | 0 | if (v1 != nullptr) |
735 | 0 | v1->visit(*this); |
736 | 0 | else |
737 | 0 | RateHelper::accept(v); |
738 | 0 | } |
739 | | |
740 | | FxSwapRateHelper::FxSwapRateHelper(const Handle<Quote>& fwdPoint, |
741 | | Handle<Quote> spotFx, |
742 | | const Period& tenor, |
743 | | Natural fixingDays, |
744 | | Calendar calendar, |
745 | | BusinessDayConvention convention, |
746 | | bool endOfMonth, |
747 | | bool isFxBaseCurrencyCollateralCurrency, |
748 | | Handle<YieldTermStructure> coll, |
749 | | Calendar tradingCalendar) |
750 | 0 | : RelativeDateRateHelper(fwdPoint), spot_(std::move(spotFx)), tenor_(tenor), |
751 | 0 | fixingDays_(fixingDays), cal_(std::move(calendar)), conv_(convention), eom_(endOfMonth), |
752 | 0 | isFxBaseCurrencyCollateralCurrency_(isFxBaseCurrencyCollateralCurrency), |
753 | 0 | collHandle_(std::move(coll)), tradingCalendar_(std::move(tradingCalendar)) { |
754 | 0 | registerWith(spot_); |
755 | 0 | registerWith(collHandle_); |
756 | |
|
757 | 0 | if (tradingCalendar_.empty()) |
758 | 0 | jointCalendar_ = cal_; |
759 | 0 | else |
760 | 0 | jointCalendar_ = JointCalendar(tradingCalendar_, cal_, |
761 | 0 | JoinHolidays); |
762 | 0 | FxSwapRateHelper::initializeDates(); |
763 | 0 | } |
764 | | |
765 | | FxSwapRateHelper::FxSwapRateHelper(const Handle<Quote>& fwdPoint, |
766 | | Handle<Quote> spotFx, |
767 | | const Date& startDate, |
768 | | const Date& endDate, |
769 | | bool isFxBaseCurrencyCollateralCurrency, |
770 | | Handle<YieldTermStructure> coll) |
771 | 0 | : RelativeDateRateHelper(fwdPoint, false), spot_(std::move(spotFx)), |
772 | 0 | isFxBaseCurrencyCollateralCurrency_(isFxBaseCurrencyCollateralCurrency), |
773 | 0 | collHandle_(std::move(coll)) { |
774 | 0 | registerWith(spot_); |
775 | 0 | registerWith(collHandle_); |
776 | 0 | earliestDate_ = startDate; |
777 | 0 | latestDate_ = endDate; |
778 | 0 | } |
779 | | |
780 | 0 | void FxSwapRateHelper::initializeDates() { |
781 | 0 | if (!updateDates_) return; |
782 | | // if the evaluation date is not a business day |
783 | | // then move to the next business day |
784 | 0 | Date refDate = cal_.adjust(evaluationDate_); |
785 | 0 | earliestDate_ = cal_.advance(refDate, fixingDays_*Days); |
786 | |
|
787 | 0 | if (!tradingCalendar_.empty()) { |
788 | | // check if fx trade can be settled in US, if not, adjust it |
789 | 0 | earliestDate_ = jointCalendar_.adjust(earliestDate_); |
790 | 0 | latestDate_ = jointCalendar_.advance(earliestDate_, tenor_, |
791 | 0 | conv_, eom_); |
792 | 0 | } else { |
793 | 0 | latestDate_ = cal_.advance(earliestDate_, tenor_, conv_, eom_); |
794 | 0 | } |
795 | 0 | } |
796 | | |
797 | 0 | Real FxSwapRateHelper::impliedQuote() const { |
798 | 0 | QL_REQUIRE(termStructure_ != nullptr, "term structure not set"); |
799 | | |
800 | 0 | QL_REQUIRE(!collHandle_.empty(), "collateral term structure not set"); |
801 | | |
802 | 0 | DiscountFactor d1 = collHandle_->discount(earliestDate_); |
803 | 0 | DiscountFactor d2 = collHandle_->discount(latestDate_); |
804 | 0 | Real collRatio = d1 / d2; |
805 | 0 | d1 = termStructureHandle_->discount(earliestDate_); |
806 | 0 | d2 = termStructureHandle_->discount(latestDate_); |
807 | 0 | Real ratio = d1 / d2; |
808 | 0 | Real spot = spot_->value(); |
809 | 0 | if (isFxBaseCurrencyCollateralCurrency_) { |
810 | 0 | return (ratio/collRatio-1)*spot; |
811 | 0 | } else { |
812 | 0 | return (collRatio/ratio-1)*spot; |
813 | 0 | } |
814 | 0 | } |
815 | | |
816 | 0 | void FxSwapRateHelper::setTermStructure(YieldTermStructure* t) { |
817 | | // do not set the relinkable handle as an observer - |
818 | | // force recalculation when needed |
819 | 0 | bool observer = false; |
820 | |
|
821 | 0 | ext::shared_ptr<YieldTermStructure> temp(t, null_deleter()); |
822 | 0 | termStructureHandle_.linkTo(temp, observer); |
823 | |
|
824 | 0 | collRelinkableHandle_.linkTo(*collHandle_, observer); |
825 | |
|
826 | 0 | RelativeDateRateHelper::setTermStructure(t); |
827 | 0 | } |
828 | | |
829 | 0 | void FxSwapRateHelper::accept(AcyclicVisitor& v) { |
830 | 0 | auto* v1 = dynamic_cast<Visitor<FxSwapRateHelper>*>(&v); |
831 | 0 | if (v1 != nullptr) |
832 | 0 | v1->visit(*this); |
833 | 0 | else |
834 | 0 | RateHelper::accept(v); |
835 | 0 | } |
836 | | |
837 | | } |