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 
11  This file is part of QuantLib, a free-software/open-source library
12  for financial quantitative analysts and developers - http://quantlib.org/
13 
14  QuantLib is free software: you can redistribute it and/or modify it
15  under the terms of the QuantLib license.  You should have received a
16  copy of the license along with this program; if not, please email
17  <quantlib-dev@lists.sf.net>. The license is also available online at
18  <http://quantlib.org/license.shtml>.
19 
20  This program is distributed in the hope that it will be useful, but WITHOUT
21  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
22  FOR A PARTICULAR PURPOSE.  See the license for more details.
23 */
24 
25 #include <ql/instruments/bond.hpp>
26 #include <ql/cashflows/cashflows.hpp>
27 #include <ql/cashflows/floatingratecoupon.hpp>
28 #include <ql/math/solvers1d/brent.hpp>
29 #include <ql/cashflows/simplecashflow.hpp>
30 #include <ql/pricingengines/bond/discountingbondengine.hpp>
31 #include <ql/pricingengines/bond/bondfunctions.hpp>
32 
33 namespace QuantLib {
34 
Bond(Natural settlementDays,const Calendar & calendar,const Date & issueDate,const Leg & coupons)35     Bond::Bond(Natural settlementDays,
36                const Calendar& calendar,
37                const Date& issueDate,
38                const Leg& coupons)
39     : settlementDays_(settlementDays), calendar_(calendar),
40       cashflows_(coupons), 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()) {
47                 QL_REQUIRE(issueDate_<cashflows_[0]->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 
55             addRedemptionsToCashflows();
56         }
57 
58         registerWith(Settings::instance().evaluationDate());
59         for (Leg::const_iterator c = cashflows_.begin(); c != cashflows_.end();
60              ++c)
61             registerWith(*c);
62     }
63 
Bond(Natural settlementDays,const Calendar & calendar,Real faceAmount,const Date & maturityDate,const Date & issueDate,const Leg & cashflows)64     Bond::Bond(Natural settlementDays,
65                const Calendar& calendar,
66                Real faceAmount,
67                const Date& maturityDate,
68                const Date& issueDate,
69                const Leg& cashflows)
70     : settlementDays_(settlementDays), calendar_(calendar),
71       cashflows_(cashflows), maturityDate_(maturityDate),
72       issueDate_(issueDate) {
73 
74         if (!cashflows.empty()) {
75 
76             std::sort(cashflows_.begin(), cashflows_.end()-1,
77                       earlier_than<ext::shared_ptr<CashFlow> >());
78 
79             if (maturityDate_ == Date())
80                 maturityDate_ = CashFlows::maturityDate(cashflows);
81 
82             if (issueDate_ != Date()) {
83                 QL_REQUIRE(issueDate_<cashflows_[0]->date(),
84                            "issue date (" << issueDate_ <<
85                            ") must be earlier than first payment date (" <<
86                            cashflows_[0]->date() << ")");
87             }
88 
89             notionals_.resize(2);
90             notionalSchedule_.resize(2);
91 
92             notionalSchedule_[0] = Date();
93             notionals_[0] = faceAmount;
94 
95             notionalSchedule_[1] = maturityDate_;
96             notionals_[1] = 0.0;
97 
98             redemptions_.push_back(cashflows.back());
99         }
100 
101         registerWith(Settings::instance().evaluationDate());
102         for (Leg::const_iterator c = cashflows_.begin(); c != cashflows_.end();
103              ++c)
104             registerWith(*c);
105     }
106 
isExpired() const107     bool Bond::isExpired() const {
108         // this is the Instrument interface, so it doesn't use
109         // BondFunctions, and includeSettlementDateFlows is true
110         // (unless QL_TODAY_PAYMENTS will set it to false later on)
111         return CashFlows::isExpired(cashflows_,
112                                     true,
113                                     Settings::instance().evaluationDate());
114     }
115 
notional(Date d) const116     Real Bond::notional(Date d) const {
117         if (d == Date())
118             d = settlementDate();
119 
120         if (d > notionalSchedule_.back()) {
121             // after maturity
122             return 0.0;
123         }
124 
125         // After the check above, d is between the schedule
126         // boundaries.  We search starting from the second notional
127         // date, since the first is null.  After the call to
128         // lower_bound, *i is the earliest date which is greater or
129         // equal than d.  Its index is greater or equal to 1.
130         std::vector<Date>::const_iterator i =
131             std::lower_bound(notionalSchedule_.begin()+1,
132                              notionalSchedule_.end(), d);
133         Size index = std::distance(notionalSchedule_.begin(), i);
134 
135         if (d < notionalSchedule_[index]) {
136             // no doubt about what to return
137             return notionals_[index-1];
138         } else {
139             // d is equal to a redemption date.
140             // As per bond conventions, the payment has occurred;
141             // the bond already changed notional.
142             return notionals_[index];
143         }
144     }
145 
redemption() const146     const ext::shared_ptr<CashFlow>& Bond::redemption() const {
147         QL_REQUIRE(redemptions_.size() == 1,
148                    "multiple redemption cash flows given");
149         return redemptions_.back();
150     }
151 
startDate() const152     Date Bond::startDate() const {
153         return BondFunctions::startDate(*this);
154     }
155 
maturityDate() const156     Date Bond::maturityDate() const {
157         if (maturityDate_!=Null<Date>())
158             return maturityDate_;
159         else
160             return BondFunctions::maturityDate(*this);
161     }
162 
isTradable(Date d) const163     bool Bond::isTradable(Date d) const {
164         return BondFunctions::isTradable(*this, d);
165     }
166 
settlementDate(Date d) const167     Date Bond::settlementDate(Date d) const {
168         if (d==Date())
169             d = Settings::instance().evaluationDate();
170 
171         // usually, the settlement is at T+n...
172         Date settlement = calendar_.advance(d, settlementDays_, Days);
173         // ...but the bond won't be traded until the issue date (if given.)
174         if (issueDate_ == Date())
175             return settlement;
176         else
177             return std::max(settlement, issueDate_);
178     }
179 
cleanPrice() const180     Real Bond::cleanPrice() const {
181         return dirtyPrice() - accruedAmount(settlementDate());
182     }
183 
dirtyPrice() const184     Real Bond::dirtyPrice() const {
185         Real currentNotional = notional(settlementDate());
186         if (currentNotional == 0.0)
187             return 0.0;
188         else
189             return settlementValue()*100.0/currentNotional;
190     }
191 
settlementValue() const192     Real Bond::settlementValue() const {
193         calculate();
194         QL_REQUIRE(settlementValue_ != Null<Real>(),
195                    "settlement value not provided");
196         return settlementValue_;
197     }
198 
settlementValue(Real cleanPrice) const199     Real Bond::settlementValue(Real cleanPrice) const {
200         Real dirtyPrice = cleanPrice + accruedAmount(settlementDate());
201         return dirtyPrice / 100.0 * notional(settlementDate());
202     }
203 
yield(const DayCounter & dc,Compounding comp,Frequency freq,Real accuracy,Size maxEvaluations,Real guess,Bond::Price::Type priceType) const204     Rate Bond::yield(const DayCounter& dc,
205                      Compounding comp,
206                      Frequency freq,
207                      Real accuracy,
208                      Size maxEvaluations,
209                      Real guess,
210                      Bond::Price::Type priceType) const {
211         Real currentNotional = notional(settlementDate());
212         if (currentNotional == 0.0)
213             return 0.0;
214 
215         Real price = priceType == Bond::Price::Clean ? cleanPrice() : dirtyPrice();
216 
217         return BondFunctions::yield(*this, price, dc, comp, freq,
218                                     settlementDate(),
219                                     accuracy, maxEvaluations,
220                                     guess, priceType);
221     }
222 
cleanPrice(Rate y,const DayCounter & dc,Compounding comp,Frequency freq,Date settlement) const223     Real Bond::cleanPrice(Rate y,
224                           const DayCounter& dc,
225                           Compounding comp,
226                           Frequency freq,
227                           Date settlement) const {
228         return BondFunctions::cleanPrice(*this, y, dc, comp, freq, settlement);
229     }
230 
dirtyPrice(Rate y,const DayCounter & dc,Compounding comp,Frequency freq,Date settlement) const231     Real Bond::dirtyPrice(Rate y,
232                           const DayCounter& dc,
233                           Compounding comp,
234                           Frequency freq,
235                           Date settlement) const {
236         Real currentNotional = notional(settlement);
237         if (currentNotional == 0.0)
238             return 0.0;
239 
240         return BondFunctions::cleanPrice(*this, y, dc, comp, freq, settlement)
241             + accruedAmount(settlement);
242     }
243 
yield(Real price,const DayCounter & dc,Compounding comp,Frequency freq,Date settlement,Real accuracy,Size maxEvaluations,Real guess,Bond::Price::Type priceType) const244     Rate Bond::yield(Real price,
245                      const DayCounter& dc,
246                      Compounding comp,
247                      Frequency freq,
248                      Date settlement,
249                      Real accuracy,
250                      Size maxEvaluations,
251                      Real guess,
252                      Bond::Price::Type priceType) const {
253         Real currentNotional = notional(settlement);
254         if (currentNotional == 0.0)
255             return 0.0;
256 
257         return BondFunctions::yield(*this, price, dc, comp, freq,
258                                     settlement, accuracy, maxEvaluations,
259                                     guess, priceType);
260     }
261 
accruedAmount(Date settlement) const262     Real Bond::accruedAmount(Date settlement) const {
263         Real currentNotional = notional(settlement);
264         if (currentNotional == 0.0)
265             return 0.0;
266 
267         return BondFunctions::accruedAmount(*this, settlement);
268     }
269 
nextCouponRate(Date settlement) const270     Rate Bond::nextCouponRate(Date settlement) const {
271         return BondFunctions::nextCouponRate(*this, settlement);
272     }
273 
previousCouponRate(Date settlement) const274     Rate Bond::previousCouponRate(Date settlement) const {
275         return BondFunctions::previousCouponRate(*this, settlement);
276     }
277 
nextCashFlowDate(Date settlement) const278     Date Bond::nextCashFlowDate(Date settlement) const {
279         return BondFunctions::nextCashFlowDate(*this, settlement);
280     }
281 
previousCashFlowDate(Date settlement) const282     Date Bond::previousCashFlowDate(Date settlement) const {
283         return BondFunctions::previousCashFlowDate(*this, settlement);
284     }
285 
setupExpired() const286     void Bond::setupExpired() const {
287         Instrument::setupExpired();
288         settlementValue_ = 0.0;
289     }
290 
setupArguments(PricingEngine::arguments * args) const291     void Bond::setupArguments(PricingEngine::arguments* args) const {
292         Bond::arguments* arguments = dynamic_cast<Bond::arguments*>(args);
293         QL_REQUIRE(arguments != 0, "wrong argument type");
294 
295         arguments->settlementDate = settlementDate();
296         arguments->cashflows = cashflows_;
297         arguments->calendar = calendar_;
298     }
299 
fetchResults(const PricingEngine::results * r) const300     void Bond::fetchResults(const PricingEngine::results* r) const {
301 
302         Instrument::fetchResults(r);
303 
304         const Bond::results* results =
305             dynamic_cast<const Bond::results*>(r);
306         QL_ENSURE(results != 0, "wrong result type");
307 
308         settlementValue_ = results->settlementValue;
309     }
310 
addRedemptionsToCashflows(const std::vector<Real> & redemptions)311     void Bond::addRedemptionsToCashflows(const std::vector<Real>& redemptions) {
312         // First, we gather the notional information from the cashflows
313         calculateNotionalsFromCashflows();
314         // Then, we create the redemptions based on the notional
315         // information and we add them to the cashflows vector after
316         // the coupons.
317         redemptions_.clear();
318         for (Size i=1; i<notionalSchedule_.size(); ++i) {
319             Real R = i < redemptions.size() ? redemptions[i] :
320                      !redemptions.empty()   ? redemptions.back() :
321                                               100.0;
322             Real amount = (R/100.0)*(notionals_[i-1]-notionals_[i]);
323             ext::shared_ptr<CashFlow> payment;
324             if (i < notionalSchedule_.size()-1)
325                 payment.reset(new AmortizingPayment(amount,
326                                                     notionalSchedule_[i]));
327             else
328                 payment.reset(new Redemption(amount, notionalSchedule_[i]));
329             cashflows_.push_back(payment);
330             redemptions_.push_back(payment);
331         }
332         // stable_sort now moves the redemptions to the right places
333         // while ensuring that they follow coupons with the same date.
334         std::stable_sort(cashflows_.begin(), cashflows_.end(),
335                          earlier_than<ext::shared_ptr<CashFlow> >());
336     }
337 
setSingleRedemption(Real notional,Real redemption,const Date & date)338     void Bond::setSingleRedemption(Real notional,
339                                    Real redemption,
340                                    const Date& date) {
341 
342         ext::shared_ptr<CashFlow> redemptionCashflow(
343                          new Redemption(notional*redemption/100.0, date));
344         setSingleRedemption(notional, redemptionCashflow);
345     }
346 
setSingleRedemption(Real notional,const ext::shared_ptr<CashFlow> & redemption)347     void Bond::setSingleRedemption(Real notional,
348                                    const ext::shared_ptr<CashFlow>& redemption) {
349         notionals_.resize(2);
350         notionalSchedule_.resize(2);
351         redemptions_.clear();
352 
353         notionalSchedule_[0] = Date();
354         notionals_[0] = notional;
355 
356         notionalSchedule_[1] = redemption->date();
357         notionals_[1] = 0.0;
358 
359         cashflows_.push_back(redemption);
360         redemptions_.push_back(redemption);
361     }
362 
deepUpdate()363     void Bond::deepUpdate() {
364         for (Size k = 0; k < cashflows_.size(); ++k) {
365             ext::shared_ptr<LazyObject> f =
366                 ext::dynamic_pointer_cast<LazyObject>(cashflows_[k]);
367             if (f != 0)
368                 f->update();
369         }
370         update();
371     }
372 
calculateNotionalsFromCashflows()373     void Bond::calculateNotionalsFromCashflows() {
374         notionalSchedule_.clear();
375         notionals_.clear();
376 
377         Date lastPaymentDate = Date();
378         notionalSchedule_.push_back(Date());
379         for (Size i=0; i<cashflows_.size(); ++i) {
380             ext::shared_ptr<Coupon> coupon =
381                 ext::dynamic_pointer_cast<Coupon>(cashflows_[i]);
382             if (!coupon)
383                 continue;
384 
385             Real notional = coupon->nominal();
386             // we add the notional only if it is the first one...
387             if (notionals_.empty()) {
388                 notionals_.push_back(coupon->nominal());
389                 lastPaymentDate = coupon->date();
390             } else if (!close(notional, notionals_.back())) {
391                 // ...or if it has changed.
392                 QL_REQUIRE(notional < notionals_.back(),
393                            "increasing coupon notionals");
394                 notionals_.push_back(coupon->nominal());
395                 // in this case, we also add the last valid date for
396                 // the previous one...
397                 notionalSchedule_.push_back(lastPaymentDate);
398                 // ...and store the candidate for this one.
399                 lastPaymentDate = coupon->date();
400             } else {
401                 // otherwise, we just extend the valid range of dates
402                 // for the current notional.
403                 lastPaymentDate = coupon->date();
404             }
405         }
406         QL_REQUIRE(!notionals_.empty(), "no coupons provided");
407         notionals_.push_back(0.0);
408         notionalSchedule_.push_back(lastPaymentDate);
409     }
410 
411 
validate() const412     void Bond::arguments::validate() const {
413         QL_REQUIRE(settlementDate != Date(), "no settlement date provided");
414         QL_REQUIRE(!cashflows.empty(), "no cash flow provided");
415         for (Size i=0; i<cashflows.size(); ++i)
416             QL_REQUIRE(cashflows[i], "null cash flow provided");
417     }
418 
419 }
420