diff --git a/QuantLib.vcxproj b/QuantLib.vcxproj index a4efc7bcc40..b6932c13874 100644 --- a/QuantLib.vcxproj +++ b/QuantLib.vcxproj @@ -923,6 +923,7 @@ + @@ -2183,6 +2184,7 @@ + diff --git a/QuantLib.vcxproj.filters b/QuantLib.vcxproj.filters index 6f8cacb3663..17f37995f21 100644 --- a/QuantLib.vcxproj.filters +++ b/QuantLib.vcxproj.filters @@ -828,6 +828,9 @@ instruments + + instruments + instruments @@ -4712,6 +4715,9 @@ instruments + + instruments + instruments diff --git a/ql/CMakeLists.txt b/ql/CMakeLists.txt index 34e7332439f..7c2609d323b 100644 --- a/ql/CMakeLists.txt +++ b/ql/CMakeLists.txt @@ -285,6 +285,7 @@ set(QL_SOURCES instruments/doublebarriertype.cpp instruments/equitytotalreturnswap.cpp instruments/europeanoption.cpp + instruments/fixedvsfloatingswap.cpp instruments/floatfloatswap.cpp instruments/floatfloatswaption.cpp instruments/forward.cpp @@ -1338,6 +1339,7 @@ set(QL_HEADERS instruments/equitytotalreturnswap.hpp instruments/europeanoption.hpp instruments/fixedratebondforward.hpp + instruments/fixedvsfloatingswap.hpp instruments/floatfloatswap.hpp instruments/floatfloatswaption.hpp instruments/forward.hpp diff --git a/ql/instruments/Makefile.am b/ql/instruments/Makefile.am index a223dbbf1d6..7c1021d17a2 100644 --- a/ql/instruments/Makefile.am +++ b/ql/instruments/Makefile.am @@ -33,6 +33,7 @@ this_include_HEADERS = \ equitytotalreturnswap.hpp \ europeanoption.hpp \ fixedratebondforward.hpp \ + fixedvsfloatingswap.hpp \ floatfloatswap.hpp \ floatfloatswaption.hpp \ forward.hpp \ @@ -100,6 +101,7 @@ cpp_files = \ doublebarriertype.cpp \ equitytotalreturnswap.cpp \ europeanoption.cpp \ + fixedvsfloatingswap.cpp \ floatfloatswap.cpp \ floatfloatswaption.cpp \ forward.cpp \ diff --git a/ql/instruments/all.hpp b/ql/instruments/all.hpp index 2c3821c0b79..cd72c9a66d3 100644 --- a/ql/instruments/all.hpp +++ b/ql/instruments/all.hpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/ql/instruments/fixedvsfloatingswap.cpp b/ql/instruments/fixedvsfloatingswap.cpp new file mode 100644 index 00000000000..719eacba5de --- /dev/null +++ b/ql/instruments/fixedvsfloatingswap.cpp @@ -0,0 +1,246 @@ +/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl + Copyright (C) 2003, 2004, 2005, 2006, 2007 StatPro Italia srl + Copyright (C) 2007 Ferdinando Ametrano + + This file is part of QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + + QuantLib is free software: you can redistribute it and/or modify it + under the terms of the QuantLib license. You should have received a + copy of the license along with this program; if not, please email + . The license is also available online at + . + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the license for more details. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace QuantLib { + + FixedVsFloatingSwap::FixedVsFloatingSwap(Type type, + std::vector fixedNominals, + Schedule fixedSchedule, + Rate fixedRate, + DayCounter fixedDayCount, + std::vector floatingNominals, + Schedule floatingSchedule, + ext::shared_ptr iborIndex, + Spread spread, + DayCounter floatingDayCount, + ext::optional paymentConvention, + Natural paymentLag, + const Calendar& paymentCalendar) + : Swap(2), type_(type), fixedNominals_(std::move(fixedNominals)), fixedSchedule_(std::move(fixedSchedule)), + fixedRate_(fixedRate), fixedDayCount_(std::move(fixedDayCount)), + floatingNominals_(std::move(floatingNominals)), floatingSchedule_(std::move(floatingSchedule)), + iborIndex_(std::move(iborIndex)), spread_(spread), floatingDayCount_(std::move(floatingDayCount)) { + + QL_REQUIRE(iborIndex_, "null floating index provided"); + + if (fixedDayCount_ == DayCounter()) + fixedDayCount_ = iborIndex_->dayCounter(); + + if (paymentConvention) // NOLINT(readability-implicit-bool-conversion) + paymentConvention_ = *paymentConvention; + else + paymentConvention_ = floatingSchedule_.businessDayConvention(); + + legs_[0] = FixedRateLeg(fixedSchedule_) + .withNotionals(fixedNominals_) + .withCouponRates(fixedRate_, fixedDayCount_) + .withPaymentAdjustment(paymentConvention_) + .withPaymentLag(paymentLag) + .withPaymentCalendar(paymentCalendar.empty() ? + fixedSchedule_.calendar() : + paymentCalendar); + + // legs_[1] to be built by derived class constructor + + switch (type_) { + case Payer: + payer_[0] = -1.0; + payer_[1] = +1.0; + break; + case Receiver: + payer_[0] = +1.0; + payer_[1] = -1.0; + break; + default: + QL_FAIL("Unknown vanilla-swap type"); + } + + + // These bools tell us if we can support the old methods nominal() and nominals(). + // There might be false negatives (i.e., if we pass constant vectors of different lengths + // as fixedNominals and floatingNominals) but we're going to assume that whoever uses the + // constructor with two vectors is mostly going to use the new methods instead. + sameNominals_ = std::equal(fixedNominals_.begin(), fixedNominals_.end(), + floatingNominals_.begin(), floatingNominals_.end()); + if (!sameNominals_) { + constantNominals_ = false; + } else { + constantNominals_ = true; + Real front = fixedNominals_[0]; + for (auto x : fixedNominals_) { + if (x != front) { + constantNominals_ = false; + break; + } + } + } + } + + void FixedVsFloatingSwap::setupArguments(PricingEngine::arguments* args) const { + + Swap::setupArguments(args); + + auto* arguments = dynamic_cast(args); + + if (arguments == nullptr) // it's a swap engine... + return; + + arguments->type = type_; + + if (constantNominals_) + arguments->nominal = nominal(); + else + arguments->nominal = Null(); + + const Leg& fixedCoupons = fixedLeg(); + Size n = fixedCoupons.size(); + + arguments->fixedResetDates = arguments->fixedPayDates = std::vector(n); + arguments->fixedNominals = arguments->fixedCoupons = std::vector(n); + + for (Size i=0; i(fixedCoupons[i]); + + arguments->fixedPayDates[i] = coupon->date(); + arguments->fixedResetDates[i] = coupon->accrualStartDate(); + arguments->fixedCoupons[i] = coupon->amount(); + arguments->fixedNominals[i] = coupon->nominal(); + } + + setupFloatingArguments(arguments); + } + + Rate FixedVsFloatingSwap::fairRate() const { + calculate(); + QL_REQUIRE(fairRate_ != Null(), "result not available"); + return fairRate_; + } + + Spread FixedVsFloatingSwap::fairSpread() const { + calculate(); + QL_REQUIRE(fairSpread_ != Null(), "result not available"); + return fairSpread_; + } + + Real FixedVsFloatingSwap::fixedLegBPS() const { + calculate(); + QL_REQUIRE(legBPS_[0] != Null(), "result not available"); + return legBPS_[0]; + } + + Real FixedVsFloatingSwap::floatingLegBPS() const { + calculate(); + QL_REQUIRE(legBPS_[1] != Null(), "result not available"); + return legBPS_[1]; + } + + Real FixedVsFloatingSwap::fixedLegNPV() const { + calculate(); + QL_REQUIRE(legNPV_[0] != Null(), "result not available"); + return legNPV_[0]; + } + + Real FixedVsFloatingSwap::floatingLegNPV() const { + calculate(); + QL_REQUIRE(legNPV_[1] != Null(), "result not available"); + return legNPV_[1]; + } + + void FixedVsFloatingSwap::setupExpired() const { + Swap::setupExpired(); + legBPS_[0] = legBPS_[1] = 0.0; + fairRate_ = Null(); + fairSpread_ = Null(); + } + + void FixedVsFloatingSwap::fetchResults(const PricingEngine::results* r) const { + static const Spread basisPoint = 1.0e-4; + + Swap::fetchResults(r); + + const auto* results = dynamic_cast(r); + if (results != nullptr) { // might be a swap engine, so no error is thrown + fairRate_ = results->fairRate; + fairSpread_ = results->fairSpread; + } else { + fairRate_ = Null(); + fairSpread_ = Null(); + } + + if (fairRate_ == Null()) { + // calculate it from other results + if (legBPS_[0] != Null()) + fairRate_ = fixedRate_ - NPV_/(legBPS_[0]/basisPoint); + } + if (fairSpread_ == Null()) { + // ditto + if (legBPS_[1] != Null()) + fairSpread_ = spread_ - NPV_/(legBPS_[1]/basisPoint); + } + } + + void FixedVsFloatingSwap::arguments::validate() const { + Swap::arguments::validate(); + QL_REQUIRE(fixedNominals.size() == fixedPayDates.size(), + "number of fixed nominals different from " + "number of fixed payment dates"); + QL_REQUIRE(fixedResetDates.size() == fixedPayDates.size(), + "number of fixed start dates different from " + "number of fixed payment dates"); + QL_REQUIRE(fixedPayDates.size() == fixedCoupons.size(), + "number of fixed payment dates different from " + "number of fixed coupon amounts"); + QL_REQUIRE(floatingNominals.size() == floatingPayDates.size(), + "number of floating nominals different from " + "number of floating payment dates"); + QL_REQUIRE(floatingResetDates.size() == floatingPayDates.size(), + "number of floating start dates different from " + "number of floating payment dates"); + QL_REQUIRE(floatingFixingDates.size() == floatingPayDates.size(), + "number of floating fixing dates different from " + "number of floating payment dates"); + QL_REQUIRE(floatingAccrualTimes.size() == floatingPayDates.size(), + "number of floating accrual Times different from " + "number of floating payment dates"); + QL_REQUIRE(floatingSpreads.size() == floatingPayDates.size(), + "number of floating spreads different from " + "number of floating payment dates"); + QL_REQUIRE(floatingPayDates.size() == floatingCoupons.size(), + "number of floating payment dates different from " + "number of floating coupon amounts"); + } + + void FixedVsFloatingSwap::results::reset() { + Swap::results::reset(); + fairRate = Null(); + fairSpread = Null(); + } + +} diff --git a/ql/instruments/fixedvsfloatingswap.hpp b/ql/instruments/fixedvsfloatingswap.hpp new file mode 100644 index 00000000000..71187063c17 --- /dev/null +++ b/ql/instruments/fixedvsfloatingswap.hpp @@ -0,0 +1,231 @@ +/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl + Copyright (C) 2003, 2004, 2005, 2006, 2007 StatPro Italia srl + Copyright (C) 2006, 2008 Ferdinando Ametrano + + This file is part of QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + + QuantLib is free software: you can redistribute it and/or modify it + under the terms of the QuantLib license. You should have received a + copy of the license along with this program; if not, please email + . The license is also available online at + . + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the license for more details. +*/ + +/*! \file fixedvsfloatingswap.hpp + \brief Fixed-rate vs floating-rate swap +*/ + +#ifndef quantlib_fixed_vs_floating_swap_hpp +#define quantlib_fixed_vs_floating_swap_hpp + +#include +#include +#include +#include + +namespace QuantLib { + + class IborIndex; + + //! Fixed vs floating swap + /*! \ingroup instruments + + If no payment convention is passed, the convention of the + floating-rate schedule is used. + + \warning if Settings::includeReferenceDateCashFlows() + is set to true, payments occurring at the + settlement date of the swap might be included in the + NPV and therefore affect the fair-rate and + fair-spread calculation. This might not be what you + want. + */ + class FixedVsFloatingSwap : public Swap { + public: + class arguments; + class results; + class engine; + FixedVsFloatingSwap(Type type, + std::vector fixedNominals, + Schedule fixedSchedule, + Rate fixedRate, + DayCounter fixedDayCount, + std::vector floatingNominals, + Schedule floatingSchedule, + ext::shared_ptr iborIndex, + Spread spread, + DayCounter floatingDayCount, + ext::optional paymentConvention = ext::nullopt, + Natural paymentLag = 0, + const Calendar& paymentCalendar = Calendar()); + //! \name Inspectors + //@{ + Type type() const; + + /*! This throws if the nominal is not constant across coupons. */ + Real nominal() const; + /*! This throws if the nominals are not the same for the two legs. */ + const std::vector& nominals() const; + + const std::vector& fixedNominals() const; + const Schedule& fixedSchedule() const; + Rate fixedRate() const; + const DayCounter& fixedDayCount() const; + + const std::vector& floatingNominals() const; + const Schedule& floatingSchedule() const; + const ext::shared_ptr& iborIndex() const; + Spread spread() const; + const DayCounter& floatingDayCount() const; + + BusinessDayConvention paymentConvention() const; + + const Leg& fixedLeg() const; + const Leg& floatingLeg() const; + //@} + + //! \name Results + //@{ + Real fixedLegBPS() const; + Real fixedLegNPV() const; + Rate fairRate() const; + + Real floatingLegBPS() const; + Real floatingLegNPV() const; + Spread fairSpread() const; + //@} + // other + void setupArguments(PricingEngine::arguments* args) const override; + void fetchResults(const PricingEngine::results*) const override; + + private: + void setupExpired() const override; + virtual void setupFloatingArguments(arguments* args) const = 0; + Type type_; + std::vector fixedNominals_; + Schedule fixedSchedule_; + Rate fixedRate_; + DayCounter fixedDayCount_; + std::vector floatingNominals_; + Schedule floatingSchedule_; + ext::shared_ptr iborIndex_; + Spread spread_; + DayCounter floatingDayCount_; + BusinessDayConvention paymentConvention_; + // results + mutable Rate fairRate_; + mutable Spread fairSpread_; + + bool constantNominals_, sameNominals_; + }; + + + //! %Arguments for simple swap calculation + class FixedVsFloatingSwap::arguments : public Swap::arguments { + public: + arguments() : nominal(Null()) {} + Type type = Receiver; + Real nominal; + + std::vector fixedNominals; + std::vector fixedResetDates; + std::vector fixedPayDates; + std::vector floatingNominals; + std::vector