| 1 | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| 2 | |
| 3 | /* |
| 4 | Copyright (C) 2013, 2016 Peter Caspers |
| 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/cashflows/capflooredcoupon.hpp> |
| 21 | #include <ql/cashflows/cashflows.hpp> |
| 22 | #include <ql/cashflows/cashflowvectors.hpp> |
| 23 | #include <ql/cashflows/cmscoupon.hpp> |
| 24 | #include <ql/cashflows/couponpricer.hpp> |
| 25 | #include <ql/cashflows/iborcoupon.hpp> |
| 26 | #include <ql/cashflows/simplecashflow.hpp> |
| 27 | #include <ql/indexes/iborindex.hpp> |
| 28 | #include <ql/indexes/swapindex.hpp> |
| 29 | #include <ql/instruments/nonstandardswap.hpp> |
| 30 | #include <ql/termstructures/yieldtermstructure.hpp> |
| 31 | #include <ql/optional.hpp> |
| 32 | #include <utility> |
| 33 | |
| 34 | namespace QuantLib { |
| 35 | |
| 36 | NonstandardSwap::NonstandardSwap(const VanillaSwap &fromVanilla) |
| 37 | : Swap(2), type_(fromVanilla.type()), |
| 38 | fixedNominal_(std::vector<Real>(fromVanilla.fixedLeg().size(), |
| 39 | fromVanilla.nominal())), |
| 40 | floatingNominal_(std::vector<Real>(fromVanilla.floatingLeg().size(), |
| 41 | fromVanilla.nominal())), |
| 42 | fixedSchedule_(fromVanilla.fixedSchedule()), |
| 43 | fixedRate_(std::vector<Real>(fromVanilla.fixedLeg().size(), |
| 44 | fromVanilla.fixedRate())), |
| 45 | fixedDayCount_(fromVanilla.fixedDayCount()), |
| 46 | floatingSchedule_(fromVanilla.floatingSchedule()), |
| 47 | iborIndex_(fromVanilla.iborIndex()), |
| 48 | spread_(std::vector<Real>(fromVanilla.floatingLeg().size(), fromVanilla.spread())), |
| 49 | gearing_(std::vector<Real>(fromVanilla.floatingLeg().size(), 1.0)), |
| 50 | singleSpreadAndGearing_(true), |
| 51 | floatingDayCount_(fromVanilla.floatingDayCount()), |
| 52 | paymentConvention_(fromVanilla.paymentConvention()), |
| 53 | intermediateCapitalExchange_(false), finalCapitalExchange_(false) { |
| 54 | |
| 55 | init(); |
| 56 | } |
| 57 | |
| 58 | NonstandardSwap::NonstandardSwap(const Swap::Type type, |
| 59 | std::vector<Real> fixedNominal, |
| 60 | const std::vector<Real>& floatingNominal, |
| 61 | Schedule fixedSchedule, |
| 62 | std::vector<Real> fixedRate, |
| 63 | DayCounter fixedDayCount, |
| 64 | Schedule floatingSchedule, |
| 65 | ext::shared_ptr<IborIndex> iborIndex, |
| 66 | const Real gearing, |
| 67 | const Spread spread, |
| 68 | DayCounter floatingDayCount, |
| 69 | const bool intermediateCapitalExchange, |
| 70 | const bool finalCapitalExchange, |
| 71 | ext::optional<BusinessDayConvention> paymentConvention) |
| 72 | : Swap(2), type_(type), fixedNominal_(std::move(fixedNominal)), |
| 73 | floatingNominal_(floatingNominal), fixedSchedule_(std::move(fixedSchedule)), |
| 74 | fixedRate_(std::move(fixedRate)), fixedDayCount_(std::move(fixedDayCount)), |
| 75 | floatingSchedule_(std::move(floatingSchedule)), iborIndex_(std::move(iborIndex)), |
| 76 | spread_(std::vector<Real>(floatingNominal.size(), spread)), |
| 77 | gearing_(std::vector<Real>(floatingNominal.size(), gearing)), singleSpreadAndGearing_(true), |
| 78 | floatingDayCount_(std::move(floatingDayCount)), |
| 79 | intermediateCapitalExchange_(intermediateCapitalExchange), |
| 80 | finalCapitalExchange_(finalCapitalExchange) { |
| 81 | |
| 82 | if (paymentConvention) // NOLINT(readability-implicit-bool-conversion) |
| 83 | paymentConvention_ = *paymentConvention; |
| 84 | else |
| 85 | paymentConvention_ = floatingSchedule_.businessDayConvention(); |
| 86 | init(); |
| 87 | } |
| 88 | |
| 89 | NonstandardSwap::NonstandardSwap(const Swap::Type type, |
| 90 | std::vector<Real> fixedNominal, |
| 91 | std::vector<Real> floatingNominal, |
| 92 | Schedule fixedSchedule, |
| 93 | std::vector<Real> fixedRate, |
| 94 | DayCounter fixedDayCount, |
| 95 | Schedule floatingSchedule, |
| 96 | ext::shared_ptr<IborIndex> iborIndex, |
| 97 | std::vector<Real> gearing, |
| 98 | std::vector<Spread> spread, |
| 99 | DayCounter floatingDayCount, |
| 100 | const bool intermediateCapitalExchange, |
| 101 | const bool finalCapitalExchange, |
| 102 | ext::optional<BusinessDayConvention> paymentConvention) |
| 103 | : Swap(2), type_(type), fixedNominal_(std::move(fixedNominal)), |
| 104 | floatingNominal_(std::move(floatingNominal)), fixedSchedule_(std::move(fixedSchedule)), |
| 105 | fixedRate_(std::move(fixedRate)), fixedDayCount_(std::move(fixedDayCount)), |
| 106 | floatingSchedule_(std::move(floatingSchedule)), iborIndex_(std::move(iborIndex)), |
| 107 | spread_(std::move(spread)), gearing_(std::move(gearing)), singleSpreadAndGearing_(false), |
| 108 | floatingDayCount_(std::move(floatingDayCount)), |
| 109 | intermediateCapitalExchange_(intermediateCapitalExchange), |
| 110 | finalCapitalExchange_(finalCapitalExchange) { |
| 111 | |
| 112 | if (paymentConvention) // NOLINT(readability-implicit-bool-conversion) |
| 113 | paymentConvention_ = *paymentConvention; |
| 114 | else |
| 115 | paymentConvention_ = floatingSchedule_.businessDayConvention(); |
| 116 | init(); |
| 117 | } |
| 118 | |
| 119 | void NonstandardSwap::init() { |
| 120 | |
| 121 | QL_REQUIRE(fixedNominal_.size() == fixedRate_.size(), |
| 122 | "Fixed nominal size (" |
| 123 | << fixedNominal_.size() |
| 124 | << ") does not match fixed rate size (" |
| 125 | << fixedRate_.size() << ")" ); |
| 126 | |
| 127 | QL_REQUIRE(fixedNominal_.size() == fixedSchedule_.size() - 1, |
| 128 | "Fixed nominal size (" << fixedNominal_.size() |
| 129 | << ") does not match schedule size (" |
| 130 | << fixedSchedule_.size() << ") - 1" ); |
| 131 | |
| 132 | QL_REQUIRE(floatingNominal_.size() == floatingSchedule_.size() - 1, |
| 133 | "Floating nominal size (" |
| 134 | << floatingNominal_.size() |
| 135 | << ") does not match schedule size (" |
| 136 | << floatingSchedule_.size() << ") - 1" ); |
| 137 | |
| 138 | QL_REQUIRE(floatingNominal_.size() == spread_.size(), |
| 139 | "Floating nominal size (" << floatingNominal_.size() |
| 140 | << ") does not match spread size (" |
| 141 | << spread_.size() << ")" ); |
| 142 | |
| 143 | QL_REQUIRE(floatingNominal_.size() == gearing_.size(), |
| 144 | "Floating nominal size (" |
| 145 | << floatingNominal_.size() |
| 146 | << ") does not match gearing size (" << gearing_.size() |
| 147 | << ")" ); |
| 148 | |
| 149 | // if the gearing is zero then the ibor leg will be set up with fixed |
| 150 | // coupons which makes trouble here in this context. We therefore use |
| 151 | // a dirty trick and enforce the gearing to be non zero. |
| 152 | for (Real& i : gearing_) { |
| 153 | if (close(x: i, y: 0.0)) |
| 154 | i = QL_EPSILON; |
| 155 | } |
| 156 | |
| 157 | legs_[0] = FixedRateLeg(fixedSchedule_) |
| 158 | .withNotionals(fixedNominal_) |
| 159 | .withCouponRates(fixedRate_, paymentDayCounter: fixedDayCount_) |
| 160 | .withPaymentAdjustment(paymentConvention_); |
| 161 | |
| 162 | legs_[1] = IborLeg(floatingSchedule_, iborIndex_) |
| 163 | .withNotionals(notionals: floatingNominal_) |
| 164 | .withPaymentDayCounter(floatingDayCount_) |
| 165 | .withPaymentAdjustment(paymentConvention_) |
| 166 | .withSpreads(spreads: spread_) |
| 167 | .withGearings(gearings: gearing_); |
| 168 | |
| 169 | if (intermediateCapitalExchange_) { |
| 170 | for (Size i = 0; i < legs_[0].size() - 1; i++) { |
| 171 | Real cap = fixedNominal_[i] - fixedNominal_[i + 1]; |
| 172 | if (!close(x: cap, y: 0.0)) { |
| 173 | auto it1 = legs_[0].begin(); |
| 174 | std::advance(i&: it1, n: i + 1); |
| 175 | legs_[0].insert( |
| 176 | position: it1, x: ext::shared_ptr<CashFlow>( |
| 177 | new Redemption(cap, legs_[0][i]->date()))); |
| 178 | auto it2 = fixedNominal_.begin(); |
| 179 | std::advance(i&: it2, n: i + 1); |
| 180 | fixedNominal_.insert(position: it2, x: fixedNominal_[i]); |
| 181 | auto it3 = fixedRate_.begin(); |
| 182 | std::advance(i&: it3, n: i + 1); |
| 183 | fixedRate_.insert(position: it3, x: 0.0); |
| 184 | i++; |
| 185 | } |
| 186 | } |
| 187 | for (Size i = 0; i < legs_[1].size() - 1; i++) { |
| 188 | Real cap = floatingNominal_[i] - floatingNominal_[i + 1]; |
| 189 | if (!close(x: cap, y: 0.0)) { |
| 190 | auto it1 = legs_[1].begin(); |
| 191 | std::advance(i&: it1, n: i + 1); |
| 192 | legs_[1].insert( |
| 193 | position: it1, x: ext::shared_ptr<CashFlow>( |
| 194 | new Redemption(cap, legs_[1][i]->date()))); |
| 195 | auto it2 = floatingNominal_.begin(); |
| 196 | std::advance(i&: it2, n: i + 1); |
| 197 | floatingNominal_.insert(position: it2, x: floatingNominal_[i]); |
| 198 | i++; |
| 199 | } |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | if (finalCapitalExchange_) { |
| 204 | legs_[0].push_back(x: ext::shared_ptr<CashFlow>( |
| 205 | new Redemption(fixedNominal_.back(), legs_[0].back()->date()))); |
| 206 | fixedNominal_.push_back(x: fixedNominal_.back()); |
| 207 | fixedRate_.push_back(x: 0.0); |
| 208 | legs_[1].push_back(x: ext::shared_ptr<CashFlow>(new Redemption( |
| 209 | floatingNominal_.back(), legs_[1].back()->date()))); |
| 210 | floatingNominal_.push_back(x: floatingNominal_.back()); |
| 211 | } |
| 212 | |
| 213 | for (Leg::const_iterator i = legs_[1].begin(); i < legs_[1].end(); ++i) |
| 214 | registerWith(h: *i); |
| 215 | |
| 216 | switch (type_) { |
| 217 | case Swap::Payer: |
| 218 | payer_[0] = -1.0; |
| 219 | payer_[1] = +1.0; |
| 220 | break; |
| 221 | case Swap::Receiver: |
| 222 | payer_[0] = +1.0; |
| 223 | payer_[1] = -1.0; |
| 224 | break; |
| 225 | default: |
| 226 | QL_FAIL("Unknown nonstandard-swap type" ); |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | void NonstandardSwap::setupArguments(PricingEngine::arguments *args) const { |
| 231 | |
| 232 | Swap::setupArguments(args); |
| 233 | |
| 234 | auto* arguments = dynamic_cast<NonstandardSwap::arguments*>(args); |
| 235 | |
| 236 | if (arguments == nullptr) |
| 237 | return; // swap engine ... |
| 238 | |
| 239 | arguments->type = type_; |
| 240 | arguments->fixedNominal = fixedNominal_; |
| 241 | arguments->floatingNominal = floatingNominal_; |
| 242 | arguments->fixedRate = fixedRate_; |
| 243 | |
| 244 | const Leg &fixedCoupons = fixedLeg(); |
| 245 | |
| 246 | arguments->fixedResetDates = arguments->fixedPayDates = |
| 247 | std::vector<Date>(fixedCoupons.size()); |
| 248 | arguments->fixedCoupons = std::vector<Real>(fixedCoupons.size()); |
| 249 | arguments->fixedIsRedemptionFlow = |
| 250 | std::vector<bool>(fixedCoupons.size(), false); |
| 251 | |
| 252 | for (Size i = 0; i < fixedCoupons.size(); ++i) { |
| 253 | ext::shared_ptr<FixedRateCoupon> coupon = |
| 254 | ext::dynamic_pointer_cast<FixedRateCoupon>(r: fixedCoupons[i]); |
| 255 | if (coupon != nullptr) { |
| 256 | arguments->fixedPayDates[i] = coupon->date(); |
| 257 | arguments->fixedResetDates[i] = coupon->accrualStartDate(); |
| 258 | arguments->fixedCoupons[i] = coupon->amount(); |
| 259 | } else { |
| 260 | ext::shared_ptr<CashFlow> cashflow = |
| 261 | ext::dynamic_pointer_cast<CashFlow>(r: fixedCoupons[i]); |
| 262 | std::vector<Date>::const_iterator j = |
| 263 | std::find(first: arguments->fixedPayDates.begin(), |
| 264 | last: arguments->fixedPayDates.end(), val: cashflow->date()); |
| 265 | QL_REQUIRE(j != arguments->fixedPayDates.end(), |
| 266 | "nominal redemption on " |
| 267 | << cashflow->date() |
| 268 | << "has no corresponding coupon" ); |
| 269 | Size jIdx = j - arguments->fixedPayDates.begin(); |
| 270 | arguments->fixedIsRedemptionFlow[i] = true; |
| 271 | arguments->fixedCoupons[i] = cashflow->amount(); |
| 272 | arguments->fixedResetDates[i] = |
| 273 | arguments->fixedResetDates[jIdx]; |
| 274 | arguments->fixedPayDates[i] = cashflow->date(); |
| 275 | } |
| 276 | } |
| 277 | |
| 278 | const Leg &floatingCoupons = floatingLeg(); |
| 279 | |
| 280 | arguments->floatingResetDates = arguments->floatingPayDates = |
| 281 | arguments->floatingFixingDates = |
| 282 | std::vector<Date>(floatingCoupons.size()); |
| 283 | arguments->floatingAccrualTimes = |
| 284 | std::vector<Time>(floatingCoupons.size()); |
| 285 | arguments->floatingSpreads = |
| 286 | std::vector<Spread>(floatingCoupons.size()); |
| 287 | arguments->floatingGearings = std::vector<Real>(floatingCoupons.size()); |
| 288 | arguments->floatingCoupons = std::vector<Real>(floatingCoupons.size()); |
| 289 | arguments->floatingIsRedemptionFlow = |
| 290 | std::vector<bool>(floatingCoupons.size(), false); |
| 291 | |
| 292 | for (Size i = 0; i < floatingCoupons.size(); ++i) { |
| 293 | ext::shared_ptr<IborCoupon> coupon = |
| 294 | ext::dynamic_pointer_cast<IborCoupon>(r: floatingCoupons[i]); |
| 295 | if (coupon != nullptr) { |
| 296 | arguments->floatingResetDates[i] = coupon->accrualStartDate(); |
| 297 | arguments->floatingPayDates[i] = coupon->date(); |
| 298 | arguments->floatingFixingDates[i] = coupon->fixingDate(); |
| 299 | arguments->floatingAccrualTimes[i] = coupon->accrualPeriod(); |
| 300 | arguments->floatingSpreads[i] = coupon->spread(); |
| 301 | arguments->floatingGearings[i] = coupon->gearing(); |
| 302 | try { |
| 303 | arguments->floatingCoupons[i] = coupon->amount(); |
| 304 | } |
| 305 | catch (Error &) { |
| 306 | arguments->floatingCoupons[i] = Null<Real>(); |
| 307 | } |
| 308 | } else { |
| 309 | ext::shared_ptr<CashFlow> cashflow = |
| 310 | ext::dynamic_pointer_cast<CashFlow>(r: floatingCoupons[i]); |
| 311 | std::vector<Date>::const_iterator j = std::find( |
| 312 | first: arguments->floatingPayDates.begin(), |
| 313 | last: arguments->floatingPayDates.end(), val: cashflow->date()); |
| 314 | QL_REQUIRE(j != arguments->floatingPayDates.end(), |
| 315 | "nominal redemption on " |
| 316 | << cashflow->date() |
| 317 | << "has no corresponding coupon" ); |
| 318 | Size jIdx = j - arguments->floatingPayDates.begin(); |
| 319 | arguments->floatingIsRedemptionFlow[i] = true; |
| 320 | arguments->floatingCoupons[i] = cashflow->amount(); |
| 321 | arguments->floatingResetDates[i] = |
| 322 | arguments->floatingResetDates[jIdx]; |
| 323 | arguments->floatingFixingDates[i] = |
| 324 | arguments->floatingFixingDates[jIdx]; |
| 325 | arguments->floatingAccrualTimes[i] = 0.0; |
| 326 | arguments->floatingSpreads[i] = 0.0; |
| 327 | arguments->floatingGearings[i] = 1.0; |
| 328 | arguments->floatingPayDates[i] = cashflow->date(); |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | arguments->iborIndex = iborIndex(); |
| 333 | } |
| 334 | |
| 335 | void NonstandardSwap::setupExpired() const { Swap::setupExpired(); } |
| 336 | |
| 337 | void NonstandardSwap::fetchResults(const PricingEngine::results *r) const { |
| 338 | |
| 339 | Swap::fetchResults(r); |
| 340 | } |
| 341 | |
| 342 | void NonstandardSwap::arguments::validate() const { |
| 343 | Swap::arguments::validate(); |
| 344 | QL_REQUIRE(fixedNominal.size() == fixedPayDates.size(), |
| 345 | "number of fixed leg nominals plus redemption flows " |
| 346 | "different from number of payment dates" ); |
| 347 | QL_REQUIRE(fixedRate.size() == fixedPayDates.size(), |
| 348 | "number of fixed rates plus redemption flows different from " |
| 349 | "number of payment dates" ); |
| 350 | QL_REQUIRE(floatingNominal.size() == floatingPayDates.size(), |
| 351 | "number of float leg nominals different from number of " |
| 352 | "payment dates" ); |
| 353 | QL_REQUIRE(fixedResetDates.size() == fixedPayDates.size(), |
| 354 | "number of fixed start dates different from " |
| 355 | "number of fixed payment dates" ); |
| 356 | QL_REQUIRE(fixedPayDates.size() == fixedCoupons.size(), |
| 357 | "number of fixed payment dates different from " |
| 358 | "number of fixed coupon amounts" ); |
| 359 | QL_REQUIRE(floatingResetDates.size() == floatingPayDates.size(), |
| 360 | "number of floating start dates different from " |
| 361 | "number of floating payment dates" ); |
| 362 | QL_REQUIRE(floatingFixingDates.size() == floatingPayDates.size(), |
| 363 | "number of floating fixing dates different from " |
| 364 | "number of floating payment dates" ); |
| 365 | QL_REQUIRE(floatingAccrualTimes.size() == floatingPayDates.size(), |
| 366 | "number of floating accrual Times different from " |
| 367 | "number of floating payment dates" ); |
| 368 | QL_REQUIRE(floatingSpreads.size() == floatingPayDates.size(), |
| 369 | "number of floating spreads different from " |
| 370 | "number of floating payment dates" ); |
| 371 | QL_REQUIRE(floatingPayDates.size() == floatingCoupons.size(), |
| 372 | "number of floating payment dates different from " |
| 373 | "number of floating coupon amounts" ); |
| 374 | } |
| 375 | |
| 376 | void NonstandardSwap::results::reset() { Swap::results::reset(); } |
| 377 | } |
| 378 | |