| 1 | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| 2 | |
| 3 | /* |
| 4 | Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl |
| 5 | Copyright (C) 2003, 2004, 2005, 2006, 2007 StatPro Italia srl |
| 6 | Copyright (C) 2007 Ferdinando Ametrano |
| 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 | <http://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/cashflows/cashflows.hpp> |
| 23 | #include <ql/cashflows/cashflowvectors.hpp> |
| 24 | #include <ql/cashflows/couponpricer.hpp> |
| 25 | #include <ql/cashflows/fixedratecoupon.hpp> |
| 26 | #include <ql/indexes/iborindex.hpp> |
| 27 | #include <ql/instruments/fixedvsfloatingswap.hpp> |
| 28 | #include <ql/termstructures/yieldtermstructure.hpp> |
| 29 | #include <utility> |
| 30 | |
| 31 | namespace QuantLib { |
| 32 | |
| 33 | FixedVsFloatingSwap::FixedVsFloatingSwap(Type type, |
| 34 | std::vector<Real> fixedNominals, |
| 35 | Schedule fixedSchedule, |
| 36 | Rate fixedRate, |
| 37 | DayCounter fixedDayCount, |
| 38 | std::vector<Real> floatingNominals, |
| 39 | Schedule floatingSchedule, |
| 40 | ext::shared_ptr<IborIndex> iborIndex, |
| 41 | Spread spread, |
| 42 | DayCounter floatingDayCount, |
| 43 | ext::optional<BusinessDayConvention> paymentConvention, |
| 44 | Natural paymentLag, |
| 45 | const Calendar& paymentCalendar) |
| 46 | : Swap(2), type_(type), fixedNominals_(std::move(fixedNominals)), fixedSchedule_(std::move(fixedSchedule)), |
| 47 | fixedRate_(fixedRate), fixedDayCount_(std::move(fixedDayCount)), |
| 48 | floatingNominals_(std::move(floatingNominals)), floatingSchedule_(std::move(floatingSchedule)), |
| 49 | iborIndex_(std::move(iborIndex)), spread_(spread), floatingDayCount_(std::move(floatingDayCount)) { |
| 50 | |
| 51 | QL_REQUIRE(iborIndex_, "null floating index provided" ); |
| 52 | |
| 53 | if (fixedDayCount_ == DayCounter()) |
| 54 | fixedDayCount_ = iborIndex_->dayCounter(); |
| 55 | |
| 56 | if (paymentConvention) // NOLINT(readability-implicit-bool-conversion) |
| 57 | paymentConvention_ = *paymentConvention; |
| 58 | else |
| 59 | paymentConvention_ = floatingSchedule_.businessDayConvention(); |
| 60 | |
| 61 | legs_[0] = FixedRateLeg(fixedSchedule_) |
| 62 | .withNotionals(fixedNominals_) |
| 63 | .withCouponRates(fixedRate_, paymentDayCounter: fixedDayCount_) |
| 64 | .withPaymentAdjustment(paymentConvention_) |
| 65 | .withPaymentLag(lag: paymentLag) |
| 66 | .withPaymentCalendar(paymentCalendar.empty() ? |
| 67 | fixedSchedule_.calendar() : |
| 68 | paymentCalendar); |
| 69 | |
| 70 | // legs_[1] to be built by derived class constructor |
| 71 | |
| 72 | switch (type_) { |
| 73 | case Payer: |
| 74 | payer_[0] = -1.0; |
| 75 | payer_[1] = +1.0; |
| 76 | break; |
| 77 | case Receiver: |
| 78 | payer_[0] = +1.0; |
| 79 | payer_[1] = -1.0; |
| 80 | break; |
| 81 | default: |
| 82 | QL_FAIL("Unknown vanilla-swap type" ); |
| 83 | } |
| 84 | |
| 85 | |
| 86 | // These bools tell us if we can support the old methods nominal() and nominals(). |
| 87 | // There might be false negatives (i.e., if we pass constant vectors of different lengths |
| 88 | // as fixedNominals and floatingNominals) but we're going to assume that whoever uses the |
| 89 | // constructor with two vectors is mostly going to use the new methods instead. |
| 90 | sameNominals_ = std::equal(first1: fixedNominals_.begin(), last1: fixedNominals_.end(), |
| 91 | first2: floatingNominals_.begin(), last2: floatingNominals_.end()); |
| 92 | if (!sameNominals_) { |
| 93 | constantNominals_ = false; |
| 94 | } else { |
| 95 | constantNominals_ = true; |
| 96 | Real front = fixedNominals_[0]; |
| 97 | for (auto x : fixedNominals_) { |
| 98 | if (x != front) { |
| 99 | constantNominals_ = false; |
| 100 | break; |
| 101 | } |
| 102 | } |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | void FixedVsFloatingSwap::setupArguments(PricingEngine::arguments* args) const { |
| 107 | |
| 108 | Swap::setupArguments(args); |
| 109 | |
| 110 | auto* arguments = dynamic_cast<FixedVsFloatingSwap::arguments*>(args); |
| 111 | |
| 112 | if (arguments == nullptr) // it's a swap engine... |
| 113 | return; |
| 114 | |
| 115 | arguments->type = type_; |
| 116 | |
| 117 | if (constantNominals_) |
| 118 | arguments->nominal = nominal(); |
| 119 | else |
| 120 | arguments->nominal = Null<Real>(); |
| 121 | |
| 122 | const Leg& fixedCoupons = fixedLeg(); |
| 123 | Size n = fixedCoupons.size(); |
| 124 | |
| 125 | arguments->fixedResetDates = arguments->fixedPayDates = std::vector<Date>(n); |
| 126 | arguments->fixedNominals = arguments->fixedCoupons = std::vector<Real>(n); |
| 127 | |
| 128 | for (Size i=0; i<n; ++i) { |
| 129 | auto coupon = ext::dynamic_pointer_cast<FixedRateCoupon>(r: fixedCoupons[i]); |
| 130 | |
| 131 | arguments->fixedPayDates[i] = coupon->date(); |
| 132 | arguments->fixedResetDates[i] = coupon->accrualStartDate(); |
| 133 | arguments->fixedCoupons[i] = coupon->amount(); |
| 134 | arguments->fixedNominals[i] = coupon->nominal(); |
| 135 | } |
| 136 | |
| 137 | setupFloatingArguments(arguments); |
| 138 | } |
| 139 | |
| 140 | Rate FixedVsFloatingSwap::fairRate() const { |
| 141 | calculate(); |
| 142 | QL_REQUIRE(fairRate_ != Null<Rate>(), "result not available" ); |
| 143 | return fairRate_; |
| 144 | } |
| 145 | |
| 146 | Spread FixedVsFloatingSwap::fairSpread() const { |
| 147 | calculate(); |
| 148 | QL_REQUIRE(fairSpread_ != Null<Spread>(), "result not available" ); |
| 149 | return fairSpread_; |
| 150 | } |
| 151 | |
| 152 | Real FixedVsFloatingSwap::fixedLegBPS() const { |
| 153 | calculate(); |
| 154 | QL_REQUIRE(legBPS_[0] != Null<Real>(), "result not available" ); |
| 155 | return legBPS_[0]; |
| 156 | } |
| 157 | |
| 158 | Real FixedVsFloatingSwap::floatingLegBPS() const { |
| 159 | calculate(); |
| 160 | QL_REQUIRE(legBPS_[1] != Null<Real>(), "result not available" ); |
| 161 | return legBPS_[1]; |
| 162 | } |
| 163 | |
| 164 | Real FixedVsFloatingSwap::fixedLegNPV() const { |
| 165 | calculate(); |
| 166 | QL_REQUIRE(legNPV_[0] != Null<Real>(), "result not available" ); |
| 167 | return legNPV_[0]; |
| 168 | } |
| 169 | |
| 170 | Real FixedVsFloatingSwap::floatingLegNPV() const { |
| 171 | calculate(); |
| 172 | QL_REQUIRE(legNPV_[1] != Null<Real>(), "result not available" ); |
| 173 | return legNPV_[1]; |
| 174 | } |
| 175 | |
| 176 | void FixedVsFloatingSwap::setupExpired() const { |
| 177 | Swap::setupExpired(); |
| 178 | legBPS_[0] = legBPS_[1] = 0.0; |
| 179 | fairRate_ = Null<Rate>(); |
| 180 | fairSpread_ = Null<Spread>(); |
| 181 | } |
| 182 | |
| 183 | void FixedVsFloatingSwap::fetchResults(const PricingEngine::results* r) const { |
| 184 | static const Spread basisPoint = 1.0e-4; |
| 185 | |
| 186 | Swap::fetchResults(r); |
| 187 | |
| 188 | const auto* results = dynamic_cast<const FixedVsFloatingSwap::results*>(r); |
| 189 | if (results != nullptr) { // might be a swap engine, so no error is thrown |
| 190 | fairRate_ = results->fairRate; |
| 191 | fairSpread_ = results->fairSpread; |
| 192 | } else { |
| 193 | fairRate_ = Null<Rate>(); |
| 194 | fairSpread_ = Null<Spread>(); |
| 195 | } |
| 196 | |
| 197 | if (fairRate_ == Null<Rate>()) { |
| 198 | // calculate it from other results |
| 199 | if (legBPS_[0] != Null<Real>()) |
| 200 | fairRate_ = fixedRate_ - NPV_/(legBPS_[0]/basisPoint); |
| 201 | } |
| 202 | if (fairSpread_ == Null<Spread>()) { |
| 203 | // ditto |
| 204 | if (legBPS_[1] != Null<Real>()) |
| 205 | fairSpread_ = spread_ - NPV_/(legBPS_[1]/basisPoint); |
| 206 | } |
| 207 | } |
| 208 | |
| 209 | void FixedVsFloatingSwap::arguments::validate() const { |
| 210 | Swap::arguments::validate(); |
| 211 | QL_REQUIRE(fixedNominals.size() == fixedPayDates.size(), |
| 212 | "number of fixed nominals different from " |
| 213 | "number of fixed payment dates" ); |
| 214 | QL_REQUIRE(fixedResetDates.size() == fixedPayDates.size(), |
| 215 | "number of fixed start dates different from " |
| 216 | "number of fixed payment dates" ); |
| 217 | QL_REQUIRE(fixedPayDates.size() == fixedCoupons.size(), |
| 218 | "number of fixed payment dates different from " |
| 219 | "number of fixed coupon amounts" ); |
| 220 | QL_REQUIRE(floatingNominals.size() == floatingPayDates.size(), |
| 221 | "number of floating nominals different from " |
| 222 | "number of floating payment dates" ); |
| 223 | QL_REQUIRE(floatingResetDates.size() == floatingPayDates.size(), |
| 224 | "number of floating start dates different from " |
| 225 | "number of floating payment dates" ); |
| 226 | QL_REQUIRE(floatingFixingDates.size() == floatingPayDates.size(), |
| 227 | "number of floating fixing dates different from " |
| 228 | "number of floating payment dates" ); |
| 229 | QL_REQUIRE(floatingAccrualTimes.size() == floatingPayDates.size(), |
| 230 | "number of floating accrual Times different from " |
| 231 | "number of floating payment dates" ); |
| 232 | QL_REQUIRE(floatingSpreads.size() == floatingPayDates.size(), |
| 233 | "number of floating spreads different from " |
| 234 | "number of floating payment dates" ); |
| 235 | QL_REQUIRE(floatingPayDates.size() == floatingCoupons.size(), |
| 236 | "number of floating payment dates different from " |
| 237 | "number of floating coupon amounts" ); |
| 238 | } |
| 239 | |
| 240 | void FixedVsFloatingSwap::results::reset() { |
| 241 | Swap::results::reset(); |
| 242 | fairRate = Null<Rate>(); |
| 243 | fairSpread = Null<Spread>(); |
| 244 | } |
| 245 | |
| 246 | } |
| 247 | |