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