/src/quantlib/ql/instruments/bonds/amortizingfixedratebond.cpp
Line | Count | Source |
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 | | <https://www.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 | | |
25 | | namespace QuantLib { |
26 | | |
27 | | AmortizingFixedRateBond::AmortizingFixedRateBond( |
28 | | Natural settlementDays, |
29 | | const std::vector<Real>& notionals, |
30 | | 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 | | Integer paymentLag) |
41 | 69.9k | : Bond(settlementDays, schedule.calendar(), issueDate), |
42 | 69.9k | frequency_(schedule.tenor().frequency()), |
43 | 69.9k | dayCounter_(accrualDayCounter) { |
44 | | |
45 | 69.9k | maturityDate_ = schedule.endDate(); |
46 | | |
47 | 69.9k | cashflows_ = FixedRateLeg(std::move(schedule)) |
48 | 69.9k | .withNotionals(notionals) |
49 | 69.9k | .withCouponRates(coupons, accrualDayCounter) |
50 | 69.9k | .withPaymentAdjustment(paymentConvention) |
51 | 69.9k | .withExCouponPeriod(exCouponPeriod, |
52 | 69.9k | exCouponCalendar, |
53 | 69.9k | exCouponConvention, |
54 | 69.9k | exCouponEndOfMonth) |
55 | 69.9k | .withPaymentLag(paymentLag); |
56 | | |
57 | 69.9k | addRedemptionsToCashflows(redemptions); |
58 | | |
59 | 69.9k | QL_ENSURE(!cashflows().empty(), "bond with no cashflows!"); |
60 | 69.9k | } Unexecuted instantiation: QuantLib::AmortizingFixedRateBond::AmortizingFixedRateBond(unsigned int, std::__1::vector<double, std::__1::allocator<double> > const&, QuantLib::Schedule, std::__1::vector<double, std::__1::allocator<double> > const&, QuantLib::DayCounter const&, QuantLib::BusinessDayConvention, QuantLib::Date const&, QuantLib::Period const&, QuantLib::Calendar const&, QuantLib::BusinessDayConvention, bool, std::__1::vector<double, std::__1::allocator<double> > const&, int) QuantLib::AmortizingFixedRateBond::AmortizingFixedRateBond(unsigned int, std::__1::vector<double, std::__1::allocator<double> > const&, QuantLib::Schedule, std::__1::vector<double, std::__1::allocator<double> > const&, QuantLib::DayCounter const&, QuantLib::BusinessDayConvention, QuantLib::Date const&, QuantLib::Period const&, QuantLib::Calendar const&, QuantLib::BusinessDayConvention, bool, std::__1::vector<double, std::__1::allocator<double> > const&, int) Line | Count | Source | 41 | 69.9k | : Bond(settlementDays, schedule.calendar(), issueDate), | 42 | 69.9k | frequency_(schedule.tenor().frequency()), | 43 | 69.9k | dayCounter_(accrualDayCounter) { | 44 | | | 45 | 69.9k | maturityDate_ = schedule.endDate(); | 46 | | | 47 | 69.9k | cashflows_ = FixedRateLeg(std::move(schedule)) | 48 | 69.9k | .withNotionals(notionals) | 49 | 69.9k | .withCouponRates(coupons, accrualDayCounter) | 50 | 69.9k | .withPaymentAdjustment(paymentConvention) | 51 | 69.9k | .withExCouponPeriod(exCouponPeriod, | 52 | 69.9k | exCouponCalendar, | 53 | 69.9k | exCouponConvention, | 54 | 69.9k | exCouponEndOfMonth) | 55 | 69.9k | .withPaymentLag(paymentLag); | 56 | | | 57 | 69.9k | addRedemptionsToCashflows(redemptions); | 58 | | | 59 | | QL_ENSURE(!cashflows().empty(), "bond with no cashflows!"); | 60 | 69.9k | } |
|
61 | | |
62 | | |
63 | | Schedule sinkingSchedule(const Date& startDate, |
64 | | const Period& bondLength, |
65 | | const Frequency& frequency, |
66 | 69.9k | const Calendar& paymentCalendar) { |
67 | 69.9k | Date maturityDate = startDate + bondLength; |
68 | 69.9k | Schedule retVal(startDate, maturityDate, Period(frequency), |
69 | 69.9k | paymentCalendar, Unadjusted, Unadjusted, |
70 | 69.9k | DateGeneration::Backward, false); |
71 | 69.9k | return retVal; |
72 | 69.9k | } |
73 | | |
74 | | namespace { |
75 | | |
76 | 139k | std::pair<Integer,Integer> daysMinMax(const Period& p) { |
77 | 139k | switch (p.units()) { |
78 | 0 | case Days: |
79 | 0 | return std::make_pair(p.length(), p.length()); |
80 | 0 | case Weeks: |
81 | 0 | return std::make_pair(7*p.length(), 7*p.length()); |
82 | 69.9k | case Months: |
83 | 69.9k | return std::make_pair(28*p.length(), 31*p.length()); |
84 | 69.9k | case Years: |
85 | 69.9k | return std::make_pair(365*p.length(), 366*p.length()); |
86 | 0 | default: |
87 | 0 | QL_FAIL("unknown time unit (" << Integer(p.units()) << ")"); |
88 | 139k | } |
89 | 139k | } |
90 | | |
91 | | bool isSubPeriod(const Period& subPeriod, |
92 | | const Period& superPeriod, |
93 | 69.9k | Integer& numSubPeriods) { |
94 | | |
95 | 69.9k | std::pair<Integer, Integer> superDays(daysMinMax(superPeriod)); |
96 | 69.9k | std::pair<Integer, Integer> subDays(daysMinMax(subPeriod)); |
97 | | |
98 | | //obtain the approximate time ratio |
99 | 69.9k | Real minPeriodRatio = |
100 | 69.9k | ((Real)superDays.first)/((Real)subDays.second); |
101 | 69.9k | Real maxPeriodRatio = |
102 | 69.9k | ((Real)superDays.second)/((Real)subDays.first); |
103 | 69.9k | auto lowRatio = static_cast<Integer>(std::floor(minPeriodRatio)); |
104 | 69.9k | auto highRatio = static_cast<Integer>(std::ceil(maxPeriodRatio)); |
105 | | |
106 | 69.9k | try { |
107 | 559k | for(Integer i=lowRatio; i <= highRatio; ++i) { |
108 | 559k | Period testPeriod = subPeriod * i; |
109 | 559k | if(testPeriod == superPeriod) { |
110 | 69.9k | numSubPeriods = i; |
111 | 69.9k | return true; |
112 | 69.9k | } |
113 | 559k | } |
114 | 69.9k | } catch(Error&) { |
115 | 0 | return false; |
116 | 0 | } |
117 | | |
118 | 0 | return false; |
119 | 69.9k | } |
120 | | |
121 | | } |
122 | | |
123 | | std::vector<Real> sinkingNotionals(const Period& bondLength, |
124 | | const Frequency& sinkingFrequency, |
125 | | Rate couponRate, |
126 | 69.9k | Real initialNotional) { |
127 | 69.9k | Integer nPeriods; |
128 | 69.9k | QL_REQUIRE(isSubPeriod(Period(sinkingFrequency), bondLength, nPeriods), |
129 | 69.9k | "Bond frequency is incompatible with the maturity tenor"); |
130 | | |
131 | 69.9k | std::vector<Real> notionals(nPeriods+1); |
132 | 69.9k | notionals.front() = initialNotional; |
133 | 69.9k | Real coupon = couponRate / static_cast<Real>(sinkingFrequency); |
134 | 69.9k | Real compoundedInterest = 1.0; |
135 | 69.9k | Real totalValue = std::pow(1.0+coupon, nPeriods); |
136 | 25.1M | for (Size i = 0; i < (Size)nPeriods-1; ++i) { |
137 | 25.0M | compoundedInterest *= (1.0 + coupon); |
138 | 25.0M | Real currentNotional = 0.0; |
139 | 25.0M | if(coupon < 1.0e-12) { |
140 | 13.9M | currentNotional = initialNotional*(1.0 - (i+1.0)/nPeriods); |
141 | 13.9M | } else { |
142 | 11.1M | currentNotional = |
143 | 11.1M | initialNotional*(compoundedInterest - (compoundedInterest-1.0)/(1.0 - 1.0/totalValue)); |
144 | 11.1M | } |
145 | 25.0M | notionals[i+1] = currentNotional; |
146 | 25.0M | } |
147 | 69.9k | notionals.back() = 0.0; |
148 | | |
149 | 69.9k | return notionals; |
150 | 69.9k | } |
151 | | |
152 | | } |