/src/quantlib/ql/pricingengines/swap/discretizedswap.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2001, 2002, 2003 Sadruddin Rejeb |
5 | | Copyright (C) 2004, 2007 StatPro Italia srl |
6 | | Copyright (C) 2022 Ralf Konrad Eckel |
7 | | |
8 | | This file is part of QuantLib, a free-software/open-source library |
9 | | for financial quantitative analysts and developers - http://quantlib.org/ |
10 | | |
11 | | QuantLib is free software: you can redistribute it and/or modify it |
12 | | under the terms of the QuantLib license. You should have received a |
13 | | copy of the license along with this program; if not, please email |
14 | | <quantlib-dev@lists.sf.net>. The license is also available online at |
15 | | <https://www.quantlib.org/license.shtml>. |
16 | | |
17 | | This program is distributed in the hope that it will be useful, but WITHOUT |
18 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
19 | | FOR A PARTICULAR PURPOSE. See the license for more details. |
20 | | */ |
21 | | |
22 | | #include <ql/pricingengines/swap/discretizedswap.hpp> |
23 | | #include <ql/settings.hpp> |
24 | | #include <utility> |
25 | | |
26 | | namespace QuantLib { |
27 | | namespace { |
28 | | inline bool isResetTimeInPast(const Time& resetTime, |
29 | | const Time& payTime, |
30 | 0 | const bool& includeTodaysCashFlows) { |
31 | 0 | return (resetTime < 0.0) && |
32 | 0 | ((payTime > 0.0) || (includeTodaysCashFlows && (payTime == 0.0))); |
33 | 0 | } |
34 | | } |
35 | | |
36 | | DiscretizedSwap::DiscretizedSwap(const VanillaSwap::arguments& args, |
37 | | const Date& referenceDate, |
38 | | const DayCounter& dayCounter) |
39 | 0 | : DiscretizedSwap( |
40 | 0 | args, |
41 | 0 | referenceDate, |
42 | 0 | dayCounter, |
43 | 0 | std::vector<CouponAdjustment>(args.fixedPayDates.size(), CouponAdjustment::pre), |
44 | 0 | std::vector<CouponAdjustment>(args.floatingPayDates.size(), CouponAdjustment::pre)) {} |
45 | | |
46 | | DiscretizedSwap::DiscretizedSwap(const VanillaSwap::arguments& args, |
47 | | const Date& referenceDate, |
48 | | const DayCounter& dayCounter, |
49 | | std::vector<CouponAdjustment> fixedCouponAdjustments, |
50 | | std::vector<CouponAdjustment> floatingCouponAdjustments) |
51 | 0 | : arguments_(args), fixedCouponAdjustments_(std::move(fixedCouponAdjustments)), |
52 | 0 | floatingCouponAdjustments_(std::move(floatingCouponAdjustments)) { |
53 | 0 | QL_REQUIRE( |
54 | 0 | fixedCouponAdjustments_.size() == arguments_.fixedPayDates.size(), |
55 | 0 | "The fixed coupon adjustments must have the same size as the number of fixed coupons."); |
56 | 0 | QL_REQUIRE(floatingCouponAdjustments_.size() == arguments_.floatingPayDates.size(), |
57 | 0 | "The floating coupon adjustments must have the same size as the number of " |
58 | 0 | "floating coupons."); |
59 | | |
60 | | // NOLINTNEXTLINE(readability-implicit-bool-conversion) |
61 | 0 | auto includeTodaysCashFlows = Settings::instance().includeTodaysCashFlows() && |
62 | 0 | *Settings::instance().includeTodaysCashFlows(); // NOLINT(bugprone-unchecked-optional-access) |
63 | |
|
64 | 0 | auto nrOfFixedCoupons = args.fixedResetDates.size(); |
65 | 0 | fixedResetTimes_.resize(nrOfFixedCoupons); |
66 | 0 | fixedPayTimes_.resize(nrOfFixedCoupons); |
67 | 0 | fixedResetTimeIsInPast_.resize(nrOfFixedCoupons); |
68 | 0 | for (Size i = 0; i < nrOfFixedCoupons; ++i) { |
69 | 0 | auto resetTime = dayCounter.yearFraction(referenceDate, args.fixedResetDates[i]); |
70 | 0 | auto payTime = dayCounter.yearFraction(referenceDate, args.fixedPayDates[i]); |
71 | 0 | auto resetIsInPast = isResetTimeInPast(resetTime, payTime, includeTodaysCashFlows); |
72 | |
|
73 | 0 | fixedResetTimes_[i] = resetTime; |
74 | 0 | fixedPayTimes_[i] = payTime; |
75 | 0 | fixedResetTimeIsInPast_[i] = resetIsInPast; |
76 | 0 | if (resetIsInPast) |
77 | 0 | fixedCouponAdjustments_[i] = CouponAdjustment::post; |
78 | 0 | } |
79 | |
|
80 | 0 | auto nrOfFloatingCoupons = args.floatingResetDates.size(); |
81 | 0 | floatingResetTimes_.resize(nrOfFloatingCoupons); |
82 | 0 | floatingPayTimes_.resize(nrOfFloatingCoupons); |
83 | 0 | floatingResetTimeIsInPast_.resize(nrOfFloatingCoupons); |
84 | 0 | for (Size i = 0; i < nrOfFloatingCoupons; ++i) { |
85 | 0 | auto resetTime = dayCounter.yearFraction(referenceDate, args.floatingResetDates[i]); |
86 | 0 | auto payTime = dayCounter.yearFraction(referenceDate, args.floatingPayDates[i]); |
87 | 0 | auto resetIsInPast = isResetTimeInPast(resetTime, payTime, includeTodaysCashFlows); |
88 | |
|
89 | 0 | floatingResetTimes_[i] = resetTime; |
90 | 0 | floatingPayTimes_[i] = payTime; |
91 | 0 | floatingResetTimeIsInPast_[i] = resetIsInPast; |
92 | 0 | if (resetIsInPast) |
93 | 0 | floatingCouponAdjustments_[i] = CouponAdjustment::post; |
94 | 0 | } |
95 | 0 | } |
96 | | |
97 | 0 | void DiscretizedSwap::reset(Size size) { |
98 | 0 | values_ = Array(size, 0.0); |
99 | 0 | adjustValues(); |
100 | 0 | } |
101 | | |
102 | 0 | std::vector<Time> DiscretizedSwap::mandatoryTimes() const { |
103 | 0 | std::vector<Time> times; |
104 | 0 | for (Real t : fixedResetTimes_) { |
105 | 0 | if (t >= 0.0) |
106 | 0 | times.push_back(t); |
107 | 0 | } |
108 | 0 | for (Real t : fixedPayTimes_) { |
109 | 0 | if (t >= 0.0) |
110 | 0 | times.push_back(t); |
111 | 0 | } |
112 | 0 | for (Real t : floatingResetTimes_) { |
113 | 0 | if (t >= 0.0) |
114 | 0 | times.push_back(t); |
115 | 0 | } |
116 | 0 | for (Real t : floatingPayTimes_) { |
117 | 0 | if (t >= 0.0) |
118 | 0 | times.push_back(t); |
119 | 0 | } |
120 | 0 | return times; |
121 | 0 | } |
122 | | |
123 | 0 | void DiscretizedSwap::preAdjustValuesImpl() { |
124 | | // floating payments |
125 | 0 | for (Size i = 0; i < floatingResetTimes_.size(); i++) { |
126 | 0 | Time t = floatingResetTimes_[i]; |
127 | 0 | if (floatingCouponAdjustments_[i] == CouponAdjustment::pre && t >= 0.0 && isOnTime(t)) { |
128 | 0 | addFloatingCoupon(i); |
129 | 0 | } |
130 | 0 | } |
131 | | // fixed payments |
132 | 0 | for (Size i = 0; i < fixedResetTimes_.size(); i++) { |
133 | 0 | Time t = fixedResetTimes_[i]; |
134 | 0 | if (fixedCouponAdjustments_[i] == CouponAdjustment::pre && t >= 0.0 && isOnTime(t)) { |
135 | 0 | addFixedCoupon(i); |
136 | 0 | } |
137 | 0 | } |
138 | 0 | } |
139 | | |
140 | 0 | void DiscretizedSwap::postAdjustValuesImpl() { |
141 | | // floating payments |
142 | 0 | for (Size i = 0; i < floatingResetTimes_.size(); i++) { |
143 | 0 | Time t = floatingResetTimes_[i]; |
144 | 0 | if (floatingCouponAdjustments_[i] == CouponAdjustment::post && t >= 0.0 && isOnTime(t)) { |
145 | 0 | addFloatingCoupon(i); |
146 | 0 | } |
147 | 0 | } |
148 | | // fixed payments |
149 | 0 | for (Size i = 0; i < fixedResetTimes_.size(); i++) { |
150 | 0 | Time t = fixedResetTimes_[i]; |
151 | 0 | if (fixedCouponAdjustments_[i] == CouponAdjustment::post && t >= 0.0 && isOnTime(t)) { |
152 | 0 | addFixedCoupon(i); |
153 | 0 | } |
154 | 0 | } |
155 | | |
156 | | // fixed coupons whose reset time is in the past won't be managed |
157 | | // in preAdjustValues() |
158 | 0 | for (Size i = 0; i < fixedPayTimes_.size(); i++) { |
159 | 0 | Time t = fixedPayTimes_[i]; |
160 | 0 | if (fixedResetTimeIsInPast_[i] && isOnTime(t)) { |
161 | 0 | Real fixedCoupon = arguments_.fixedCoupons[i]; |
162 | 0 | if (arguments_.type == Swap::Payer) |
163 | 0 | values_ -= fixedCoupon; |
164 | 0 | else |
165 | 0 | values_ += fixedCoupon; |
166 | 0 | } |
167 | 0 | } |
168 | | |
169 | | // the same applies to floating payments whose rate is already fixed |
170 | 0 | for (Size i = 0; i < floatingPayTimes_.size(); i++) { |
171 | 0 | Time t = floatingPayTimes_[i]; |
172 | 0 | if (floatingResetTimeIsInPast_[i] && isOnTime(t)) { |
173 | 0 | Real currentFloatingCoupon = arguments_.floatingCoupons[i]; |
174 | 0 | QL_REQUIRE(currentFloatingCoupon != Null<Real>(), |
175 | 0 | "current floating coupon not given"); |
176 | 0 | if (arguments_.type == Swap::Payer) |
177 | 0 | values_ += currentFloatingCoupon; |
178 | 0 | else |
179 | 0 | values_ -= currentFloatingCoupon; |
180 | 0 | } |
181 | 0 | } |
182 | 0 | } |
183 | | |
184 | 0 | void DiscretizedSwap::addFixedCoupon(Size i) { |
185 | 0 | DiscretizedDiscountBond bond; |
186 | 0 | bond.initialize(method(), fixedPayTimes_[i]); |
187 | 0 | bond.rollback(time_); |
188 | |
|
189 | 0 | Real fixedCoupon = arguments_.fixedCoupons[i]; |
190 | 0 | for (Size j = 0; j < values_.size(); j++) { |
191 | 0 | Real coupon = fixedCoupon * bond.values()[j]; |
192 | 0 | if (arguments_.type == Swap::Payer) |
193 | 0 | values_[j] -= coupon; |
194 | 0 | else |
195 | 0 | values_[j] += coupon; |
196 | 0 | } |
197 | 0 | } |
198 | | |
199 | 0 | void DiscretizedSwap::addFloatingCoupon(Size i) { |
200 | 0 | DiscretizedDiscountBond bond; |
201 | 0 | bond.initialize(method(), floatingPayTimes_[i]); |
202 | 0 | bond.rollback(time_); |
203 | |
|
204 | 0 | QL_REQUIRE(arguments_.nominal != Null<Real>(), |
205 | 0 | "non-constant nominals are not supported yet"); |
206 | | |
207 | 0 | Real nominal = arguments_.nominal; |
208 | 0 | Time T = arguments_.floatingAccrualTimes[i]; |
209 | 0 | Spread spread = arguments_.floatingSpreads[i]; |
210 | 0 | Real accruedSpread = nominal * T * spread; |
211 | 0 | for (Size j = 0; j < values_.size(); j++) { |
212 | 0 | Real coupon = nominal * (1.0 - bond.values()[j]) + accruedSpread * bond.values()[j]; |
213 | 0 | if (arguments_.type == Swap::Payer) |
214 | 0 | values_[j] += coupon; |
215 | 0 | else |
216 | 0 | values_[j] -= coupon; |
217 | 0 | } |
218 | 0 | } |
219 | | } |