QuantLib: a free/open-source library for quantitative finance
fully annotated source code - version 1.38
Loading...
Searching...
No Matches
bond.cpp
Go to the documentation of this file.
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2004 Jeff Yu
5 Copyright (C) 2004 M-Dimension Consulting Inc.
6 Copyright (C) 2005, 2006, 2007, 2008, 2010 StatPro Italia srl
7 Copyright (C) 2007, 2008, 2009 Ferdinando Ametrano
8 Copyright (C) 2007 Chiara Fornarola
9 Copyright (C) 2008 Simon Ibbotson
10 Copyright (C) 2022 Oleg Kulkov
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
33#include <ql/shared_ptr.hpp>
34#include <utility>
35
36namespace QuantLib {
37
38 Bond::Bond(Natural settlementDays, Calendar calendar, const Date& issueDate, const Leg& coupons)
39 : settlementDays_(settlementDays), calendar_(std::move(calendar)), cashflows_(coupons),
40 issueDate_(issueDate) {
41
42 if (!coupons.empty()) {
43 std::sort(cashflows_.begin(), cashflows_.end(),
44 earlier_than<ext::shared_ptr<CashFlow> >());
45
46 if (issueDate_ != Date()) {
48 "issue date (" << issueDate_ <<
49 ") must be earlier than first payment date (" <<
50 cashflows_[0]->date() << ")");
51 }
52
53 maturityDate_ = coupons.back()->date();
54
56 }
57
58 registerWith(Settings::instance().evaluationDate());
59 for (const auto& cashflow : cashflows_)
60 registerWith(cashflow);
61 }
62
63 Bond::Bond(Natural settlementDays,
64 Calendar calendar,
65 Real faceAmount,
66 const Date& maturityDate,
67 const Date& issueDate,
68 const Leg& cashflows)
69 : settlementDays_(settlementDays), calendar_(std::move(calendar)), cashflows_(cashflows),
70 maturityDate_(maturityDate), issueDate_(issueDate) {
71
72 if (!cashflows.empty()) {
73
74 std::sort(cashflows_.begin(), cashflows_.end()-1,
75 earlier_than<ext::shared_ptr<CashFlow> >());
76
77 if (maturityDate_ == Date())
79
80 if (issueDate_ != Date()) {
82 "issue date (" << issueDate_ <<
83 ") must be earlier than first payment date (" <<
84 cashflows_[0]->date() << ")");
85 }
86
87 notionals_.resize(2);
88 notionalSchedule_.resize(2);
89
91 notionals_[0] = faceAmount;
92
94 notionals_[1] = 0.0;
95
96 redemptions_.push_back(cashflows.back());
97 }
98
99 registerWith(Settings::instance().evaluationDate());
100 for (const auto& cashflow : cashflows_)
101 registerWith(cashflow);
102 }
103
104 bool Bond::isExpired() const {
105 // this is the Instrument interface, so it doesn't use
106 // BondFunctions, and includeSettlementDateFlows is true
107 // (unless QL_TODAY_PAYMENTS will set it to false later on)
109 true,
110 Settings::instance().evaluationDate());
111 }
112
114 if (d == Date())
115 d = settlementDate();
116
117 if (d > notionalSchedule_.back()) {
118 // after maturity
119 return 0.0;
120 }
121
122 // After the check above, d is between the schedule
123 // boundaries. We search starting from the second notional
124 // date, since the first is null. After the call to
125 // lower_bound, *i is the earliest date which is greater or
126 // equal than d. Its index is greater or equal to 1.
127 auto i = std::lower_bound(notionalSchedule_.begin() + 1, notionalSchedule_.end(), d);
128 Size index = std::distance(notionalSchedule_.begin(), i);
129
130 if (d < notionalSchedule_[index]) {
131 // no doubt about what to return
132 return notionals_[index-1];
133 } else {
134 // d is equal to a redemption date.
135 // As per bond conventions, the payment has occurred;
136 // the bond already changed notional.
137 return notionals_[index];
138 }
139 }
140
141 const ext::shared_ptr<CashFlow>& Bond::redemption() const {
142 QL_REQUIRE(redemptions_.size() == 1,
143 "multiple redemption cash flows given");
144 return redemptions_.back();
145 }
146
148 return BondFunctions::startDate(*this);
149 }
150
152 if (maturityDate_ != Date())
153 return maturityDate_;
154 else
155 return BondFunctions::maturityDate(*this);
156 }
157
158 bool Bond::isTradable(Date d) const {
159 return BondFunctions::isTradable(*this, d);
160 }
161
163 if (d==Date())
165
166 // usually, the settlement is at T+n...
167 Date settlement = calendar_.advance(d, settlementDays_, Days);
168 // ...but the bond won't be traded until the issue date (if given.)
169 if (issueDate_ == Date())
170 return settlement;
171 else
172 return std::max(settlement, issueDate_);
173 }
174
177 }
178
180 Real currentNotional = notional(settlementDate());
181 if (currentNotional == 0.0)
182 return 0.0;
183 else
184 return settlementValue()*100.0/currentNotional;
185 }
186
188 calculate();
190 "settlement value not provided");
191 return settlementValue_;
192 }
193
194 Real Bond::settlementValue(Real cleanPrice) const {
196 return dirtyPrice / 100.0 * notional(settlementDate());
197 }
198
200 Compounding comp,
201 Frequency freq,
202 Real accuracy,
203 Size maxEvaluations,
204 Real guess,
205 Bond::Price::Type priceType) const {
206 Real currentNotional = notional(settlementDate());
207 if (currentNotional == 0.0)
208 return 0.0;
209
210 Bond::Price price(priceType == Bond::Price::Clean ? cleanPrice() : dirtyPrice(), priceType);
211
212 return BondFunctions::yield(*this, price, dc, comp, freq,
214 accuracy, maxEvaluations,
215 guess);
216 }
217
219 const DayCounter& dc,
220 Compounding comp,
221 Frequency freq,
222 Date settlement) const {
223 return BondFunctions::cleanPrice(*this, y, dc, comp, freq, settlement);
224 }
225
227 const DayCounter& dc,
228 Compounding comp,
229 Frequency freq,
230 Date settlement) const {
231 Real currentNotional = notional(settlement);
232 if (currentNotional == 0.0)
233 return 0.0;
234
235 return BondFunctions::cleanPrice(*this, y, dc, comp, freq, settlement)
236 + accruedAmount(settlement);
237 }
238
240 const DayCounter& dc,
241 Compounding comp,
242 Frequency freq,
243 Date settlement,
244 Real accuracy,
245 Size maxEvaluations,
246 Real guess) const {
247 Real currentNotional = notional(settlement);
248 if (currentNotional == 0.0)
249 return 0.0;
250
251 return BondFunctions::yield(*this, price, dc, comp, freq,
252 settlement, accuracy, maxEvaluations,
253 guess);
254 }
255
256 Real Bond::accruedAmount(Date settlement) const {
257 Real currentNotional = notional(settlement);
258 if (currentNotional == 0.0)
259 return 0.0;
260
261 return BondFunctions::accruedAmount(*this, settlement);
262 }
263
264 Rate Bond::nextCouponRate(Date settlement) const {
265 return BondFunctions::nextCouponRate(*this, settlement);
266 }
267
269 return BondFunctions::previousCouponRate(*this, settlement);
270 }
271
272 Date Bond::nextCashFlowDate(Date settlement) const {
273 return BondFunctions::nextCashFlowDate(*this, settlement);
274 }
275
277 return BondFunctions::previousCashFlowDate(*this, settlement);
278 }
279
280 void Bond::setupExpired() const {
282 settlementValue_ = 0.0;
283 }
284
286 auto* arguments = dynamic_cast<Bond::arguments*>(args);
287 QL_REQUIRE(arguments != nullptr, "wrong argument type");
288
292 }
293
295
297
298 const auto* results = dynamic_cast<const Bond::results*>(r);
299 QL_ENSURE(results != nullptr, "wrong result type");
300
302 }
303
304 void Bond::addRedemptionsToCashflows(const std::vector<Real>& redemptions) {
305 // First, we gather the notional information from the cashflows
307 // Then, we create the redemptions based on the notional
308 // information and we add them to the cashflows vector after
309 // the coupons.
310 redemptions_.clear();
311 for (Size i=1; i<notionalSchedule_.size(); ++i) {
312 Real R = i < redemptions.size() ? redemptions[i] :
313 !redemptions.empty() ? redemptions.back() :
314 100.0;
315 Real amount = (R/100.0)*(notionals_[i-1]-notionals_[i]);
316 ext::shared_ptr<CashFlow> payment;
317 if (i < notionalSchedule_.size()-1)
318 payment = ext::make_shared<AmortizingPayment>(amount,
320 else
321 payment = ext::make_shared<Redemption>(amount, notionalSchedule_[i]);
322 cashflows_.push_back(payment);
323 redemptions_.push_back(payment);
324 }
325 // stable_sort now moves the redemptions to the right places
326 // while ensuring that they follow coupons with the same date.
327 std::stable_sort(cashflows_.begin(), cashflows_.end(),
328 earlier_than<ext::shared_ptr<CashFlow> >());
329 }
330
332 Real redemption,
333 const Date& date) {
334
335 ext::shared_ptr<CashFlow> redemptionCashflow(
336 new Redemption(notional*redemption/100.0, date));
337 setSingleRedemption(notional, redemptionCashflow);
338 }
339
341 const ext::shared_ptr<CashFlow>& redemption) {
342 notionals_.resize(2);
343 notionalSchedule_.resize(2);
344 redemptions_.clear();
345
346 notionalSchedule_[0] = Date();
347 notionals_[0] = notional;
348
349 notionalSchedule_[1] = redemption->date();
350 notionals_[1] = 0.0;
351
352 cashflows_.push_back(redemption);
353 redemptions_.push_back(redemption);
354 }
355
357 for (auto& cashflow : cashflows_) {
358 cashflow->deepUpdate();
359 }
360 update();
361 }
362
364 notionalSchedule_.clear();
365 notionals_.clear();
366
367 Date lastPaymentDate = Date();
368 notionalSchedule_.emplace_back();
369 for (auto& cashflow : cashflows_) {
370 ext::shared_ptr<Coupon> coupon = ext::dynamic_pointer_cast<Coupon>(cashflow);
371 if (!coupon)
372 continue;
373
374 Real notional = coupon->nominal();
375 // we add the notional only if it is the first one...
376 if (notionals_.empty()) {
377 notionals_.push_back(coupon->nominal());
378 lastPaymentDate = coupon->date();
379 } else if (!close(notional, notionals_.back())) {
380 // ...or if it has changed.
381 notionals_.push_back(coupon->nominal());
382 // in this case, we also add the last valid date for
383 // the previous one...
384 notionalSchedule_.push_back(lastPaymentDate);
385 // ...and store the candidate for this one.
386 lastPaymentDate = coupon->date();
387 } else {
388 // otherwise, we just extend the valid range of dates
389 // for the current notional.
390 lastPaymentDate = coupon->date();
391 }
392 }
393 QL_REQUIRE(!notionals_.empty(), "no coupons provided");
394 notionals_.push_back(0.0);
395 notionalSchedule_.push_back(lastPaymentDate);
396 }
397
398
400 QL_REQUIRE(settlementDate != Date(), "no settlement date provided");
401 QL_REQUIRE(!cashflows.empty(), "no cash flow provided");
402 for (const auto & cf: cashflows)
403 QL_REQUIRE(cf, "null cash flow provided");
404 }
405
406}
concrete bond class
bond functions
Brent 1-D solver.
Cash-flow analysis functions.
Bond price information.
Definition: bond.hpp:62
void validate() const override
Definition: bond.cpp:399
Calendar calendar_
Definition: bond.hpp:285
Real cleanPrice() const
theoretical clean price
Definition: bond.cpp:175
Leg redemptions_
Definition: bond.hpp:289
Real settlementValue_
Definition: bond.hpp:292
void setupArguments(PricingEngine::arguments *) const override
Definition: bond.cpp:285
Rate previousCouponRate(Date d=Date()) const
Previous coupon already paid at a given date.
Definition: bond.cpp:268
bool isExpired() const override
returns whether the instrument might have value greater than zero.
Definition: bond.cpp:104
void addRedemptionsToCashflows(const std::vector< Real > &redemptions=std::vector< Real >())
Definition: bond.cpp:304
Rate yield(const DayCounter &dc, Compounding comp, Frequency freq, Real accuracy=1.0e-8, Size maxEvaluations=100, Real guess=0.05, Bond::Price::Type priceType=Bond::Price::Clean) const
theoretical bond yield
Definition: bond.cpp:199
virtual Real accruedAmount(Date d=Date()) const
accrued amount at a given date
Definition: bond.cpp:256
void deepUpdate() override
Definition: bond.cpp:356
Date startDate() const
Definition: bond.cpp:147
const Leg & cashflows() const
Definition: bond.hpp:330
Date nextCashFlowDate(Date d=Date()) const
Definition: bond.cpp:272
Bond(Natural settlementDays, Calendar calendar, const Date &issueDate=Date(), const Leg &coupons=Leg())
constructor for amortizing or non-amortizing bonds.
Definition: bond.cpp:38
Real settlementValue() const
theoretical settlement value
Definition: bond.cpp:187
std::vector< Real > notionals_
Definition: bond.hpp:287
Real dirtyPrice() const
theoretical dirty price
Definition: bond.cpp:179
Natural settlementDays_
Definition: bond.hpp:284
Leg cashflows_
Definition: bond.hpp:288
const ext::shared_ptr< CashFlow > & redemption() const
Definition: bond.cpp:141
void setSingleRedemption(Real notional, Real redemption, const Date &date)
Definition: bond.cpp:331
Date previousCashFlowDate(Date d=Date()) const
Definition: bond.cpp:276
Date issueDate_
Definition: bond.hpp:291
Date maturityDate() const
Definition: bond.cpp:151
bool isTradable(Date d=Date()) const
Definition: bond.cpp:158
const Leg & redemptions() const
Definition: bond.hpp:334
void setupExpired() const override
Definition: bond.cpp:280
void fetchResults(const PricingEngine::results *) const override
Definition: bond.cpp:294
std::vector< Date > notionalSchedule_
Definition: bond.hpp:286
Date maturityDate_
Definition: bond.hpp:291
void calculateNotionalsFromCashflows()
Definition: bond.cpp:363
virtual Real notional(Date d=Date()) const
Definition: bond.cpp:113
Date settlementDate(Date d=Date()) const
Definition: bond.cpp:162
virtual Rate nextCouponRate(Date d=Date()) const
Definition: bond.cpp:264
calendar class
Definition: calendar.hpp:61
Date advance(const Date &, Integer n, TimeUnit unit, BusinessDayConvention convention=Following, bool endOfMonth=false) const
Definition: calendar.cpp:130
static Date maturityDate(const Leg &leg)
Definition: cashflows.cpp:52
static bool isExpired(const Leg &leg, bool includeSettlementDateFlows, Date settlementDate=Date())
Definition: cashflows.cpp:66
Concrete date class.
Definition: date.hpp:125
day counter class
Definition: daycounter.hpp:44
void calculate() const override
Definition: instrument.hpp:129
virtual void fetchResults(const PricingEngine::results *) const
Definition: instrument.hpp:155
virtual void setupExpired() const
Definition: instrument.hpp:140
void update() override
Definition: lazyobject.hpp:188
template class providing a null value for a given type.
Definition: null.hpp:59
std::pair< iterator, bool > registerWith(const ext::shared_ptr< Observable > &)
Definition: observable.hpp:226
Bond redemption.
DateProxy & evaluationDate()
the date at which pricing is to be performed.
Definition: settings.hpp:147
static Settings & instance()
access to the unique instance
Definition: singleton.hpp:104
discounting bond engine
#define QL_ENSURE(condition, message)
throw an error if the given post-condition is not verified
Definition: errors.hpp:130
#define QL_REQUIRE(condition, message)
throw an error if the given pre-condition is not verified
Definition: errors.hpp:117
Date d
Coupon paying a variable index-based rate.
Frequency
Frequency of events.
Definition: frequency.hpp:37
QL_REAL Real
real number
Definition: types.hpp:50
unsigned QL_INTEGER Natural
positive integer
Definition: types.hpp:43
Real Rate
interest rates
Definition: types.hpp:70
std::size_t Size
size of a container
Definition: types.hpp:58
Definition: any.hpp:37
Compounding
Interest rate coumpounding rule.
Definition: compounding.hpp:32
bool close(const Quantity &m1, const Quantity &m2, Size n)
Definition: quantity.cpp:163
std::vector< ext::shared_ptr< CashFlow > > Leg
Sequence of cash-flows.
Definition: cashflow.hpp:78
STL namespace.
ext::shared_ptr< YieldTermStructure > r
Maps shared_ptr to either the boost or std implementation.
Predetermined cash flow.
static Rate nextCouponRate(const Bond &bond, Date settlementDate=Date())
static Date startDate(const Bond &bond)
static bool isTradable(const Bond &bond, Date settlementDate=Date())
static Date maturityDate(const Bond &bond)
static Date previousCashFlowDate(const Bond &bond, Date refDate=Date())
static Date nextCashFlowDate(const Bond &bond, Date refDate=Date())
static Real accruedAmount(const Bond &bond, Date settlementDate=Date())
static Real cleanPrice(const Bond &bond, const YieldTermStructure &discountCurve, Date settlementDate=Date())
static Rate previousCouponRate(const Bond &bond, Date settlementDate=Date())
static Rate yield(const Bond &bond, Bond::Price price, const DayCounter &dayCounter, Compounding compounding, Frequency frequency, Date settlementDate=Date(), Real accuracy=1.0e-10, Size maxIterations=100, Rate guess=0.05)
compare two objects by date
Definition: comparison.hpp:130