Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
indexedcoupon.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2020 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
20
21#include <ql/patterns/visitor.hpp>
22#include <ql/time/daycounter.hpp>
23
24namespace QuantExt {
25
26IndexedCoupon::IndexedCoupon(const QuantLib::ext::shared_ptr<Coupon>& c, const Real qty, const QuantLib::ext::shared_ptr<Index>& index,
27 const Date& fixingDate)
28 : Coupon(c->date(), 0.0, c->accrualStartDate(), c->accrualEndDate(), c->referencePeriodStart(),
29 c->referencePeriodEnd(), c->exCouponDate()),
30 c_(c), qty_(qty), index_(index), fixingDate_(fixingDate), initialFixing_(Null<Real>()) {
31 QL_REQUIRE(index, "IndexedCoupon: index is null");
32 QL_REQUIRE(fixingDate != Date(), "IndexedCoupon: fixingDate is null");
33 registerWith(c);
34 registerWith(index);
35}
36
37IndexedCoupon::IndexedCoupon(const QuantLib::ext::shared_ptr<Coupon>& c, const Real qty, const Real initialFixing)
38 : Coupon(c->date(), c->nominal(), c->accrualStartDate(), c->accrualEndDate(), c->referencePeriodStart(),
39 c->referencePeriodEnd(), c->exCouponDate()),
40 c_(c), qty_(qty), initialFixing_(initialFixing) {
41 QL_REQUIRE(initialFixing != Null<Real>(), "IndexedCoupon: initial fixing is null");
42 registerWith(c);
43}
44
45void IndexedCoupon::update() { notifyObservers(); }
46
47Real IndexedCoupon::amount() const { return c_->amount() * multiplier(); }
48
49Real IndexedCoupon::accruedAmount(const Date& d) const { return c_->accruedAmount(d) * multiplier(); }
50
52
53Real IndexedCoupon::nominal() const { return c_->nominal() * multiplier(); }
54
55Real IndexedCoupon::rate() const { return c_->rate(); }
56
57DayCounter IndexedCoupon::dayCounter() const { return c_->dayCounter(); }
58
59QuantLib::ext::shared_ptr<Coupon> IndexedCoupon::underlying() const { return c_; }
60
61Real IndexedCoupon::quantity() const { return qty_; }
62
63const Date& IndexedCoupon::fixingDate() const { return fixingDate_; }
64
66
67QuantLib::ext::shared_ptr<Index> IndexedCoupon::index() const { return index_; }
68
69void IndexedCoupon::accept(AcyclicVisitor& v) {
70 Visitor<IndexedCoupon>* v1 = dynamic_cast<Visitor<IndexedCoupon>*>(&v);
71 if (v1 != 0)
72 v1->visit(*this);
73 else
74 Coupon::accept(v);
75}
76
77IndexWrappedCashFlow::IndexWrappedCashFlow(const QuantLib::ext::shared_ptr<CashFlow>& c, const Real qty,
78 const QuantLib::ext::shared_ptr<Index>& index, const Date& fixingDate)
79 : c_(c), qty_(qty), index_(index), fixingDate_(fixingDate), initialFixing_(Null<Real>()) {
80 QL_REQUIRE(index, "IndexWrappedCashFlow: index is null");
81 QL_REQUIRE(fixingDate != Date(), "IndexWrappedCashFlow: fixingDate is null");
82 registerWith(c);
83 registerWith(index);
84}
85
86IndexWrappedCashFlow::IndexWrappedCashFlow(const QuantLib::ext::shared_ptr<CashFlow>& c, const Real qty,
87 const Real initialFixing)
88 : c_(c), qty_(qty), initialFixing_(initialFixing) {
89 QL_REQUIRE(initialFixing != Null<Real>(), "IndexWrappedCashFlow: initial fixing is null");
90 registerWith(c);
91}
92
93void IndexWrappedCashFlow::update() { notifyObservers(); }
94
95Date IndexWrappedCashFlow::date() const { return c_->date(); }
96Real IndexWrappedCashFlow::amount() const { return c_->amount() * multiplier(); }
97
99 return index_ ? qty_ * index_->fixing(fixingDate_) : qty_ * initialFixing_;
100}
101
102QuantLib::ext::shared_ptr<CashFlow> IndexWrappedCashFlow::underlying() const { return c_; }
103
104Real IndexWrappedCashFlow::quantity() const { return qty_; }
105
106const Date& IndexWrappedCashFlow::fixingDate() const { return fixingDate_; }
107
109
110QuantLib::ext::shared_ptr<Index> IndexWrappedCashFlow::index() const { return index_; }
111
112void IndexWrappedCashFlow::accept(AcyclicVisitor& v) {
113 Visitor<IndexWrappedCashFlow>* v1 = dynamic_cast<Visitor<IndexWrappedCashFlow>*>(&v);
114 if (v1 != 0)
115 v1->visit(*this);
116 else
117 CashFlow::accept(v);
118}
119
120IndexedCouponLeg::IndexedCouponLeg(const Leg& underlyingLeg, const Real qty, const QuantLib::ext::shared_ptr<Index>& index)
121 : underlyingLeg_(underlyingLeg), qty_(qty), index_(index), initialFixing_(Null<Real>()),
122 initialNotionalFixing_(Null<Real>()), fixingDays_(0), fixingCalendar_(NullCalendar()),
123 fixingConvention_(Preceding), inArrearsFixing_(true) {
124 QL_REQUIRE(index, "IndexedCouponLeg: index required");
125}
126
128 initialFixing_ = initialFixing;
129 return *this;
130}
131
133 initialNotionalFixing_ = initialNotionalFixing;
134 return *this;
135}
136
138 valuationSchedule_ = valuationSchedule;
139 return *this;
140}
141
143 fixingDays_ = fixingDays;
144 return *this;
145}
146
148 fixingCalendar_ = fixingCalendar;
149 return *this;
150}
151
152IndexedCouponLeg& IndexedCouponLeg::withFixingConvention(const BusinessDayConvention& fixingConvention) {
153 fixingConvention_ = fixingConvention;
154 return *this;
155}
156
159 return *this;
160}
161
162IndexedCouponLeg::operator Leg() const {
163 Leg resultLeg;
164 resultLeg.reserve(underlyingLeg_.size());
165
166 for (Size i = 0; i < underlyingLeg_.size(); ++i) {
167 bool firstValuationDate = (i == 0);
168 Date fixingDate;
169
170 if (auto cpn = QuantLib::ext::dynamic_pointer_cast<Coupon>(underlyingLeg_[i])) {
171 if (valuationSchedule_.empty()) {
172 fixingDate = inArrearsFixing_ ? cpn->accrualEndDate() : cpn->accrualStartDate();
173 } else {
174 if (valuationSchedule_.size() == underlyingLeg_.size() + 1) {
175 // valuation schedule corresponds one to one to underlying schedule
176 fixingDate = inArrearsFixing_ ? valuationSchedule_.date(i + 1) : valuationSchedule_.date(i);
177 } else {
178 // look for the latest valuation date less or equal to the underlying accrual start date (if
179 // the indexing is using in advance fixing) resp. accrual end date (for in arrears fixing)
180 auto valDates = valuationSchedule_.dates();
181 Date refDate = inArrearsFixing_ ? cpn->accrualEndDate() : cpn->accrualStartDate();
182 Size index =
183 std::distance(valDates.begin(), std::upper_bound(valDates.begin(), valDates.end(), refDate));
184 QL_REQUIRE(index > 0, "IndexedCouponLeg: First valuation date ("
185 << valDates.front() << ") must be leq accrual "
186 << (inArrearsFixing_ ? "end" : "start") << " date ("
187 << cpn->accrualStartDate() << ") of the " << (i + 1)
188 << "th coupon in the underlying leg");
189 fixingDate = valuationSchedule_.date(index - 1);
190 firstValuationDate = (index == 1);
191 }
192 }
193
194 fixingDate = fixingCalendar_.advance(fixingDate, -static_cast<int>(fixingDays_), Days, fixingConvention_);
195 if (index_ != nullptr)
196 fixingDate = index_->fixingCalendar().adjust(fixingDate, Preceding);
197
198 if (firstValuationDate && initialFixing_ != Null<Real>()) {
199 resultLeg.push_back(QuantLib::ext::make_shared<IndexedCoupon>(cpn, qty_, initialFixing_));
200 } else {
201 resultLeg.push_back(QuantLib::ext::make_shared<IndexedCoupon>(cpn, qty_, index_, fixingDate));
202 }
203 } else if (auto csf = QuantLib::ext::dynamic_pointer_cast<CashFlow>(underlyingLeg_[i])) {
204 fixingDate = fixingCalendar_.advance(csf->date(), -static_cast<int>(fixingDays_), Days, fixingConvention_);
205 if (firstValuationDate && initialNotionalFixing_ != Null<Real>()) {
206 // use firstNotionalFixing if the first flow is a cashflow (not a coupon)
207 resultLeg.push_back(QuantLib::ext::make_shared<IndexWrappedCashFlow>(csf, qty_, initialNotionalFixing_));
208 } else if (!valuationSchedule_.empty() && valuationSchedule_.dates().size() > 0 &&
209 valuationSchedule_.date(0) == csf->date() && initialFixing_ != Null<Real>()) {
210 // use the initial fixing if the cashflow date equals the first date in the val schedule
211 resultLeg.push_back(QuantLib::ext::make_shared<IndexWrappedCashFlow>(csf, qty_, initialFixing_));
212 } else {
213 // use a flow with free fixing otherwise
214 resultLeg.push_back(QuantLib::ext::make_shared<IndexWrappedCashFlow>(csf, qty_, index_, fixingDate));
215 }
216 } else {
217 QL_FAIL("IndexedCouponLeg: coupon or cashflow required");
218 }
219 }
220
221 return resultLeg;
222}
223
224QuantLib::ext::shared_ptr<CashFlow> unpackIndexedCouponOrCashFlow(const QuantLib::ext::shared_ptr<CashFlow>& c) {
225 if (auto cpn = QuantLib::ext::dynamic_pointer_cast<Coupon>(c))
226 return unpackIndexedCoupon(cpn);
227 else
229}
230
231QuantLib::ext::shared_ptr<Coupon> unpackIndexedCoupon(const QuantLib::ext::shared_ptr<Coupon>& c) {
232 if (auto cpn = QuantLib::ext::dynamic_pointer_cast<IndexedCoupon>(c)) {
233 QuantLib::ext::shared_ptr<Coupon> unpacked_cpn = cpn->underlying();
234 return unpackIndexedCoupon(unpacked_cpn);
235 } else
236 return c;
237}
238
239QuantLib::ext::shared_ptr<CashFlow> unpackIndexWrappedCashFlow(const QuantLib::ext::shared_ptr<CashFlow>& c) {
240 if (auto cf = QuantLib::ext::dynamic_pointer_cast<IndexWrappedCashFlow>(c)) {
241 QuantLib::ext::shared_ptr<CashFlow> unpacked_cf = cf->underlying();
242 return unpackIndexWrappedCashFlow(unpacked_cf);
243 } else
244 return c;
245}
246
247Real getIndexedCouponOrCashFlowMultiplier(const QuantLib::ext::shared_ptr<CashFlow>& c) {
248 if (auto cpn = QuantLib::ext::dynamic_pointer_cast<IndexedCoupon>(c)) {
249 return cpn->multiplier() * getIndexedCouponOrCashFlowMultiplier(cpn->underlying());
250 } else if (auto cf = QuantLib::ext::dynamic_pointer_cast<IndexWrappedCashFlow>(c)) {
251 return cf->multiplier() * getIndexedCouponOrCashFlowMultiplier(cf->underlying());
252 } else {
253 return 1.0;
254 }
255}
256
257std::vector<std::tuple<Date, QuantLib::ext::shared_ptr<Index>, Real>>
258getIndexedCouponOrCashFlowFixingDetails(const QuantLib::ext::shared_ptr<CashFlow>& c) {
259 if (auto cpn = QuantLib::ext::dynamic_pointer_cast<IndexedCoupon>(c)) {
260 auto v = getIndexedCouponOrCashFlowFixingDetails(cpn->underlying());
261 v.push_back(std::make_tuple(cpn->fixingDate(), cpn->index(), cpn->multiplier()));
262 return v;
263 } else if (auto cf = QuantLib::ext::dynamic_pointer_cast<IndexWrappedCashFlow>(c)) {
264 auto v = getIndexedCouponOrCashFlowFixingDetails(cf->underlying());
265 v.push_back(std::make_tuple(cf->fixingDate(), cf->index(), cf->multiplier()));
266 return v;
267 } else {
268 return {};
269 }
270}
271
272} // namespace QuantExt
QuantLib::ext::shared_ptr< Index > index() const
QuantLib::ext::shared_ptr< Index > index_
Real amount() const override
void accept(AcyclicVisitor &) override
QuantLib::ext::shared_ptr< CashFlow > c_
QuantLib::ext::shared_ptr< CashFlow > underlying() const
IndexWrappedCashFlow(const QuantLib::ext::shared_ptr< CashFlow > &c, const Real qty, const QuantLib::ext::shared_ptr< Index > &index, const Date &fixingDate)
Date date() const override
const Date & fixingDate() const
QuantLib::ext::shared_ptr< Index > index() const
void update() override
QuantLib::ext::shared_ptr< Index > index_
Real amount() const override
void accept(AcyclicVisitor &) override
Real nominal() const override
DayCounter dayCounter() const override
QuantLib::ext::shared_ptr< Coupon > underlying() const
QuantLib::ext::shared_ptr< Coupon > c_
IndexedCoupon(const QuantLib::ext::shared_ptr< Coupon > &c, const Real qty, const QuantLib::ext::shared_ptr< Index > &index, const Date &fixingDate)
const Date & fixingDate() const
Real accruedAmount(const Date &d) const override
Real rate() const override
IndexedCouponLeg(const Leg &underlyingLeg, const Real qty, const QuantLib::ext::shared_ptr< Index > &index)
IndexedCouponLeg & withInitialNotionalFixing(const Real initialNotionalFixing)
BusinessDayConvention fixingConvention_
IndexedCouponLeg & withFixingCalendar(const Calendar &fixingCalendar)
IndexedCouponLeg & inArrearsFixing(const bool inArrearsFixing=true)
IndexedCouponLeg & withInitialFixing(const Real initialFixing)
IndexedCouponLeg & withFixingDays(const Size fixingDays)
IndexedCouponLeg & withValuationSchedule(const Schedule &valuationSchedule)
IndexedCouponLeg & withFixingConvention(const BusinessDayConvention &fixingConvention)
coupon with an indexed notional
std::vector< std::tuple< Date, QuantLib::ext::shared_ptr< Index >, Real > > getIndexedCouponOrCashFlowFixingDetails(const QuantLib::ext::shared_ptr< CashFlow > &c)
QuantLib::ext::shared_ptr< Coupon > unpackIndexedCoupon(const QuantLib::ext::shared_ptr< Coupon > &c)
QuantLib::ext::shared_ptr< CashFlow > unpackIndexWrappedCashFlow(const QuantLib::ext::shared_ptr< CashFlow > &c)
Real getIndexedCouponOrCashFlowMultiplier(const QuantLib::ext::shared_ptr< CashFlow > &c)
QuantLib::ext::shared_ptr< CashFlow > unpackIndexedCouponOrCashFlow(const QuantLib::ext::shared_ptr< CashFlow > &c)