1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2008 Simon Ibbotson
5
6 This file is part of QuantLib, a free-software/open-source library
7 for financial quantitative analysts and developers - http://quantlib.org/
8
9 QuantLib is free software: you can redistribute it and/or modify it
10 under the terms of the QuantLib license. You should have received a
11 copy of the license along with this program; if not, please email
12 <quantlib-dev@lists.sf.net>. The license is also available online at
13 <http://quantlib.org/license.shtml>.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the license for more details.
18*/
19
20#include <ql/instruments/bonds/amortizingfixedratebond.hpp>
21#include <ql/cashflows/cashflowvectors.hpp>
22#include <ql/cashflows/simplecashflow.hpp>
23#include <ql/time/schedule.hpp>
24
25namespace QuantLib {
26
27 AmortizingFixedRateBond::AmortizingFixedRateBond(
28 Natural settlementDays,
29 const std::vector<Real>& notionals,
30 const Schedule& schedule,
31 const std::vector<Rate>& coupons,
32 const DayCounter& accrualDayCounter,
33 BusinessDayConvention paymentConvention,
34 const Date& issueDate,
35 const Period& exCouponPeriod,
36 const Calendar& exCouponCalendar,
37 const BusinessDayConvention exCouponConvention,
38 bool exCouponEndOfMonth,
39 const std::vector<Real>& redemptions,
40 Natural paymentLag)
41 : Bond(settlementDays, schedule.calendar(), issueDate),
42 frequency_(schedule.tenor().frequency()),
43 dayCounter_(accrualDayCounter) {
44
45 maturityDate_ = schedule.endDate();
46
47 cashflows_ = FixedRateLeg(schedule)
48 .withNotionals(notionals)
49 .withCouponRates(coupons, paymentDayCounter: accrualDayCounter)
50 .withPaymentAdjustment(paymentConvention)
51 .withExCouponPeriod(exCouponPeriod,
52 exCouponCalendar,
53 exCouponConvention,
54 endOfMonth: exCouponEndOfMonth)
55 .withPaymentLag(lag: paymentLag);
56
57 addRedemptionsToCashflows(redemptions);
58
59 QL_ENSURE(!cashflows().empty(), "bond with no cashflows!");
60 }
61
62
63 AmortizingFixedRateBond::AmortizingFixedRateBond(
64 Natural settlementDays,
65 const Calendar& calendar,
66 Real initialFaceAmount,
67 const Date& startDate,
68 const Period& bondTenor,
69 const Frequency& sinkingFrequency,
70 const Rate coupon,
71 const DayCounter& accrualDayCounter,
72 BusinessDayConvention paymentConvention,
73 const Date& issueDate)
74 : Bond(settlementDays, calendar, issueDate),
75 frequency_(sinkingFrequency),
76 dayCounter_(accrualDayCounter) {
77
78 QL_REQUIRE(bondTenor.length() > 0,
79 "bond tenor must be positive. "
80 << bondTenor << " is not allowed.");
81 maturityDate_ = startDate + bondTenor;
82
83 cashflows_ =
84 FixedRateLeg(sinkingSchedule(startDate, bondLength: bondTenor,
85 frequency: sinkingFrequency, paymentCalendar: calendar))
86 .withNotionals(sinkingNotionals(bondLength: bondTenor,
87 frequency: sinkingFrequency, couponRate: coupon,
88 initialNotional: initialFaceAmount))
89 .withCouponRates(coupon, paymentDayCounter: accrualDayCounter)
90 .withPaymentAdjustment(paymentConvention);
91
92 addRedemptionsToCashflows();
93 }
94
95
96 AmortizingFixedRateBond::AmortizingFixedRateBond(
97 Natural settlementDays,
98 const std::vector<Real>& notionals,
99 const Schedule& schedule,
100 const std::vector<InterestRate>& coupons,
101 BusinessDayConvention paymentConvention,
102 const Date& issueDate,
103 const Calendar& paymentCalendar,
104 const Period& exCouponPeriod,
105 const Calendar& exCouponCalendar,
106 const BusinessDayConvention exCouponConvention,
107 bool exCouponEndOfMonth)
108 : Bond(settlementDays,
109 paymentCalendar==Calendar() ? schedule.calendar() : paymentCalendar,
110 issueDate),
111 frequency_(schedule.tenor().frequency()),
112 dayCounter_(coupons[0].dayCounter()) {
113
114 maturityDate_ = schedule.endDate();
115
116 cashflows_ = FixedRateLeg(schedule)
117 .withNotionals(notionals)
118 .withCouponRates(coupons)
119 .withPaymentAdjustment(paymentConvention)
120 .withExCouponPeriod(exCouponPeriod,
121 exCouponCalendar,
122 exCouponConvention,
123 endOfMonth: exCouponEndOfMonth);
124
125 addRedemptionsToCashflows();
126
127 QL_ENSURE(!cashflows().empty(), "bond with no cashflows!");
128 }
129
130
131 Schedule sinkingSchedule(const Date& startDate,
132 const Period& bondLength,
133 const Frequency& frequency,
134 const Calendar& paymentCalendar) {
135 Date maturityDate = startDate + bondLength;
136 Schedule retVal(startDate, maturityDate, Period(frequency),
137 paymentCalendar, Unadjusted, Unadjusted,
138 DateGeneration::Backward, false);
139 return retVal;
140 }
141
142 namespace {
143
144 std::pair<Integer,Integer> daysMinMax(const Period& p) {
145 switch (p.units()) {
146 case Days:
147 return std::make_pair(x: p.length(), y: p.length());
148 case Weeks:
149 return std::make_pair(x: 7*p.length(), y: 7*p.length());
150 case Months:
151 return std::make_pair(x: 28*p.length(), y: 31*p.length());
152 case Years:
153 return std::make_pair(x: 365*p.length(), y: 366*p.length());
154 default:
155 QL_FAIL("unknown time unit (" << Integer(p.units()) << ")");
156 }
157 }
158
159 bool isSubPeriod(const Period& subPeriod,
160 const Period& superPeriod,
161 Integer& numSubPeriods) {
162
163 std::pair<Integer, Integer> superDays(daysMinMax(p: superPeriod));
164 std::pair<Integer, Integer> subDays(daysMinMax(p: subPeriod));
165
166 //obtain the approximate time ratio
167 Real minPeriodRatio =
168 ((Real)superDays.first)/((Real)subDays.second);
169 Real maxPeriodRatio =
170 ((Real)superDays.second)/((Real)subDays.first);
171 auto lowRatio = static_cast<Integer>(std::floor(x: minPeriodRatio));
172 auto highRatio = static_cast<Integer>(std::ceil(x: maxPeriodRatio));
173
174 try {
175 for(Integer i=lowRatio; i <= highRatio; ++i) {
176 Period testPeriod = subPeriod * i;
177 if(testPeriod == superPeriod) {
178 numSubPeriods = i;
179 return true;
180 }
181 }
182 } catch(Error&) {
183 return false;
184 }
185
186 return false;
187 }
188
189 }
190
191 std::vector<Real> sinkingNotionals(const Period& bondLength,
192 const Frequency& sinkingFrequency,
193 Rate couponRate,
194 Real initialNotional) {
195 Integer nPeriods;
196 QL_REQUIRE(isSubPeriod(Period(sinkingFrequency), bondLength, nPeriods),
197 "Bond frequency is incompatible with the maturity tenor");
198
199 std::vector<Real> notionals(nPeriods+1);
200 notionals.front() = initialNotional;
201 Real coupon = couponRate / static_cast<Real>(sinkingFrequency);
202 Real compoundedInterest = 1.0;
203 Real totalValue = std::pow(x: 1.0+coupon, y: nPeriods);
204 for (Size i = 0; i < (Size)nPeriods-1; ++i) {
205 compoundedInterest *= (1.0 + coupon);
206 Real currentNotional = 0.0;
207 if(coupon < 1.0e-12) {
208 currentNotional = initialNotional*(1.0 - (i+1.0)/nPeriods);
209 } else {
210 currentNotional =
211 initialNotional*(compoundedInterest - (compoundedInterest-1.0)/(1.0 - 1.0/totalValue));
212 }
213 notionals[i+1] = currentNotional;
214 }
215 notionals.back() = 0.0;
216
217 return notionals;
218 }
219
220}
221

source code of quantlib/ql/instruments/bonds/amortizingfixedratebond.cpp