Skip to content

Commit

Permalink
Add SwapRateHelper constructor with fixed dates (lballabio#2106)
Browse files Browse the repository at this point in the history
  • Loading branch information
lballabio authored Oct 28, 2024
2 parents a63045e + 90c9cba commit a6378c5
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 2 deletions.
3 changes: 2 additions & 1 deletion ql/instruments/makevanillaswap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,8 @@ namespace QuantLib {
MakeVanillaSwap&
MakeVanillaSwap::withTerminationDate(const Date& terminationDate) {
terminationDate_ = terminationDate;
swapTenor_ = Period();
if (terminationDate != Date())
swapTenor_ = Period();
return *this;
}

Expand Down
33 changes: 32 additions & 1 deletion ql/termstructures/yield/ratehelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,11 @@ namespace QuantLib {
fixedDayCount_(std::move(fixedDayCount)), spread_(std::move(spread)), endOfMonth_(endOfMonth),
fwdStart_(fwdStart), discountHandle_(std::move(discount)),
useIndexedCoupons_(useIndexedCoupons) {
initialize(iborIndex, customPillarDate);
}

void SwapRateHelper::initialize(const ext::shared_ptr<IborIndex>& iborIndex,
Date customPillarDate) {
// take fixing into account
iborIndex_ = iborIndex->clone(termStructureHandle_);
// We want to be notified of changes of fixings, but we don't
Expand Down Expand Up @@ -603,14 +607,41 @@ namespace QuantLib {
std::move(fixedDayCount), iborIndex, std::move(spread), fwdStart, std::move(discount), settlementDays,
pillarChoice, customPillarDate, endOfMonth, useIndexedCoupons) {}

SwapRateHelper::SwapRateHelper(const Handle<Quote>& rate,
const Date& startDate,
const Date& endDate,
Calendar calendar,
Frequency fixedFrequency,
BusinessDayConvention fixedConvention,
DayCounter fixedDayCount,
const ext::shared_ptr<IborIndex>& iborIndex,
Handle<Quote> spread,
Handle<YieldTermStructure> discount,
Pillar::Choice pillarChoice,
Date customPillarDate,
bool endOfMonth,
const ext::optional<bool>& useIndexedCoupons)
: RelativeDateRateHelper(rate, false), startDate_(startDate), endDate_(endDate),
pillarChoice_(pillarChoice), calendar_(std::move(calendar)),
fixedConvention_(fixedConvention), fixedFrequency_(fixedFrequency),
fixedDayCount_(std::move(fixedDayCount)), spread_(std::move(spread)), endOfMonth_(endOfMonth),
discountHandle_(std::move(discount)), useIndexedCoupons_(useIndexedCoupons) {
QL_REQUIRE(fixedFrequency != Once,
"fixedFrequency == Once is not supported when passing explicit "
"startDate and endDate");
initialize(iborIndex, customPillarDate);
}

void SwapRateHelper::initializeDates() {

// 1. do not pass the spread here, as it might be a Quote
// i.e. it can dynamically change
// 2. input discount curve Handle might be empty now but it could
// be assigned a curve later; use a RelinkableHandle here
swap_ = MakeVanillaSwap(tenor_, iborIndex_, 0.0, fwdStart_)
.withSettlementDays(settlementDays_)
.withSettlementDays(settlementDays_) // resets effectiveDate
.withEffectiveDate(startDate_)
.withTerminationDate(endDate_)
.withDiscountingTermStructure(discountRelinkableHandle_)
.withFixedLegDayCount(fixedDayCount_)
.withFixedLegTenor(fixedFrequency_ == Once ? tenor_ : Period(fixedFrequency_))
Expand Down
20 changes: 20 additions & 0 deletions ql/termstructures/yield/ratehelpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,23 @@ namespace QuantLib {
Date customPillarDate = Date(),
bool endOfMonth = false,
const ext::optional<bool>& useIndexedCoupons = ext::nullopt);
SwapRateHelper(const Handle<Quote>& rate,
const Date& startDate,
const Date& endDate,
Calendar calendar,
// fixed leg
Frequency fixedFrequency,
BusinessDayConvention fixedConvention,
DayCounter fixedDayCount,
// floating leg
const ext::shared_ptr<IborIndex>& iborIndex,
Handle<Quote> spread = {},
// exogenous discounting curve
Handle<YieldTermStructure> discountingCurve = {},
Pillar::Choice pillar = Pillar::LastRelevantDate,
Date customPillarDate = Date(),
bool endOfMonth = false,
const ext::optional<bool>& useIndexedCoupons = ext::nullopt);
//! \name RateHelper interface
//@{
Real impliedQuote() const override;
Expand All @@ -330,9 +347,12 @@ namespace QuantLib {
void accept(AcyclicVisitor&) override;
//@}
protected:
void initialize(const ext::shared_ptr<IborIndex>& iborIndex,
Date customPillarDate);
void initializeDates() override;
Natural settlementDays_;
Period tenor_;
Date startDate_, endDate_;
Pillar::Choice pillarChoice_;
Calendar calendar_;
BusinessDayConvention fixedConvention_;
Expand Down
63 changes: 63 additions & 0 deletions test-suite/piecewiseyieldcurve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1563,6 +1563,69 @@ BOOST_AUTO_TEST_CASE(testSwapHelpersWithOnceFrequency) {
}


BOOST_AUTO_TEST_CASE(testDatedSwapHelpers) {
BOOST_TEST_MESSAGE("Testing dated swap rate helpers...");

Date today { 28, October, 2024 };
Settings::instance().evaluationDate() = today;

std::tuple<Date, Date, Rate> swapData[] = {
{{1, November, 2024}, {1, November, 2025}, 4.54 },
{{15, October, 2024}, {15, October, 2026}, 4.63 },
{{28, October, 2024}, {1, November, 2029}, 4.99 },
{{4, November, 2024}, {4, November, 2034}, 5.47 },
{{11, October, 2024}, {11, October, 2044}, 5.89 }
};

auto euribor6m = ext::make_shared<Euribor6M>();
euribor6m->addFixing({9, October, 2024}, 0.0447);
euribor6m->addFixing({11, October, 2024}, 0.045);
euribor6m->addFixing({24, October, 2024}, 0.0442);

auto calendar = TARGET();
auto fixedLegFrequency = Annual;
auto fixedLegConvention = Unadjusted;
auto fixedLegDayCounter = Thirty360(Thirty360::BondBasis);

std::vector<ext::shared_ptr<RateHelper>> helpers;
for (auto [start, end, q] : swapData) {
Handle<Quote> r(ext::make_shared<SimpleQuote>(q/100));
helpers.push_back(ext::make_shared<SwapRateHelper>(
r, start, end,
calendar,
fixedLegFrequency, fixedLegConvention,
fixedLegDayCounter, euribor6m));
}

auto curve = ext::make_shared<PiecewiseYieldCurve<ZeroYield, Linear>>(
today, helpers, Actual365Fixed());
Handle<YieldTermStructure> h(curve);
euribor6m = ext::make_shared<Euribor6M>(h);

for (auto [start, end, q] : swapData) {
VanillaSwap swap = MakeVanillaSwap(Period(), euribor6m, 0.0)
.withEffectiveDate(start)
.withTerminationDate(end)
.withFixedLegDayCount(fixedLegDayCounter)
.withFixedLegTenor(Period(fixedLegFrequency))
.withFixedLegConvention(fixedLegConvention)
.withFixedLegTerminationDateConvention(fixedLegConvention);

Rate expectedRate = q/100,
estimatedRate = swap.fairRate();
Spread error = std::fabs(expectedRate-estimatedRate);
Real tolerance = 1e-9;
if (error > tolerance) {
BOOST_ERROR("swap from " << start << " to " << end << ":\n"
<< std::setprecision(8)
<< "\n estimated rate: " << io::rate(estimatedRate)
<< "\n expected rate: " << io::rate(expectedRate)
<< "\n error: " << io::rate(error)
<< "\n tolerance: " << io::rate(tolerance));
}
}
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit a6378c5

Please sign in to comment.