Skip to content

Commit

Permalink
Add DatedSwapRateHelper
Browse files Browse the repository at this point in the history
Also, move common functionality into SwapRateHelperBase to avoid
duplication. Reuse it between vanilla and overnight swaps.
  • Loading branch information
eltoder committed Feb 20, 2024
1 parent 72012a7 commit c2662ad
Show file tree
Hide file tree
Showing 9 changed files with 313 additions and 283 deletions.
1 change: 1 addition & 0 deletions ql/instruments/fixedvsfloatingswap.hpp
Expand Up @@ -90,6 +90,7 @@ namespace QuantLib {

const Leg& fixedLeg() const;
const Leg& floatingLeg() const;
virtual Date latestRelevantDate() const = 0;
//@}

//! \name Results
Expand Down
6 changes: 6 additions & 0 deletions ql/instruments/overnightindexedswap.hpp
Expand Up @@ -103,6 +103,12 @@ namespace QuantLib {
floatingSchedule().tenor().frequency());
}

Date latestRelevantDate() const override {
Date lastPaymentDate = std::max(overnightLeg().back()->date(),
fixedLeg().back()->date());
return std::max(maturityDate(), lastPaymentDate);
}

const std::vector<Real>& overnightNominals() const { return floatingNominals(); }
const Schedule& overnightSchedule() const { return floatingSchedule(); }
const ext::shared_ptr<OvernightIndex>& overnightIndex() const { return overnightIndex_; }
Expand Down
4 changes: 4 additions & 0 deletions ql/instruments/vanillaswap.cpp
Expand Up @@ -79,4 +79,8 @@ namespace QuantLib {
}
}

Date VanillaSwap::latestRelevantDate() const {
auto lastCoupon = ext::dynamic_pointer_cast<IborCoupon>(floatingLeg().back());
return std::max(maturityDate(), lastCoupon->fixingEndDate());
}
}
1 change: 1 addition & 0 deletions ql/instruments/vanillaswap.hpp
Expand Up @@ -76,6 +76,7 @@ namespace QuantLib {
ext::optional<BusinessDayConvention> paymentConvention = ext::nullopt,
ext::optional<bool> useIndexedCoupons = ext::nullopt);

Date latestRelevantDate() const override;
private:
void setupFloatingArguments(arguments* args) const override;
};
Expand Down
25 changes: 9 additions & 16 deletions ql/termstructures/bootstraphelper.hpp
Expand Up @@ -125,19 +125,19 @@ namespace QuantLib {
/*! Derived classes must takes care of rebuilding the date schedule when
the global evaluation date changes
*/
template <class TS>
class RelativeDateBootstrapHelper : public BootstrapHelper<TS> {
template <class TS, class Base = BootstrapHelper<TS>>
class RelativeDateBootstrapHelper : public Base {
public:
explicit RelativeDateBootstrapHelper(const Handle<Quote>& quote);
explicit RelativeDateBootstrapHelper(Real quote);
template <class... Args>
explicit RelativeDateBootstrapHelper(Args&&... args);
//! \name Observer interface
//@{
void update() override {
if (evaluationDate_ != Settings::instance().evaluationDate()) {
evaluationDate_ = Settings::instance().evaluationDate();
initializeDates();
}
BootstrapHelper<TS>::update();
Base::update();
}
//@}
protected:
Expand Down Expand Up @@ -210,17 +210,10 @@ namespace QuantLib {
QL_FAIL("not a bootstrap-helper visitor");
}

template <class TS>
RelativeDateBootstrapHelper<TS>::RelativeDateBootstrapHelper(
const Handle<Quote>& quote)
: BootstrapHelper<TS>(quote) {
this->registerWith(Settings::instance().evaluationDate());
evaluationDate_ = Settings::instance().evaluationDate();
}

template <class TS>
RelativeDateBootstrapHelper<TS>::RelativeDateBootstrapHelper(Real quote)
: BootstrapHelper<TS>(quote) {
template <class TS, class Base>
template <class... Args>
RelativeDateBootstrapHelper<TS, Base>::RelativeDateBootstrapHelper(Args&&... args)
: Base(std::forward<Args>(args)...) {
this->registerWith(Settings::instance().evaluationDate());
evaluationDate_ = Settings::instance().evaluationDate();
}
Expand Down
153 changes: 31 additions & 122 deletions ql/termstructures/yield/oisratehelper.cpp
Expand Up @@ -26,6 +26,22 @@
#include <utility>

namespace QuantLib {
namespace detail {
OISRateHelperBase::OISRateHelperBase(const Handle<Quote>& fixedRate,
const ext::shared_ptr<OvernightIndex>& overnightIndex,
Handle<YieldTermStructure> discountingCurve,
Pillar::Choice pillar,
Date customPillarDate)
: SwapRateHelperBase(fixedRate, overnightIndex, std::move(discountingCurve), pillar,
customPillarDate) {}

Real OISRateHelperBase::impliedQuote() const {
QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
// we didn't register as observers - force calculation
swap_->deepUpdate();
return swap_->fairRate();
}
}

OISRateHelper::OISRateHelper(Natural settlementDays,
const Period& tenor, // swap maturity
Expand All @@ -45,33 +61,20 @@ namespace QuantLib {
ext::optional<bool> endOfMonth,
ext::optional<Frequency> fixedPaymentFrequency,
Calendar fixedCalendar)
: RelativeDateRateHelper(fixedRate), pillarChoice_(pillar), settlementDays_(settlementDays), tenor_(tenor),
discountHandle_(std::move(discount)), telescopicValueDates_(telescopicValueDates),
: base_type(fixedRate, overnightIndex, std::move(discount), pillar, customPillarDate),
settlementDays_(settlementDays), tenor_(tenor), telescopicValueDates_(telescopicValueDates),
paymentLag_(paymentLag), paymentConvention_(paymentConvention),
paymentFrequency_(paymentFrequency), paymentCalendar_(std::move(paymentCalendar)),
forwardStart_(forwardStart), overnightSpread_(overnightSpread),
averagingMethod_(averagingMethod), endOfMonth_(endOfMonth),
fixedPaymentFrequency_(fixedPaymentFrequency), fixedCalendar_(std::move(fixedCalendar)) {

overnightIndex_ =
ext::dynamic_pointer_cast<OvernightIndex>(overnightIndex->clone(termStructureHandle_));
// We want to be notified of changes of fixings, but we don't
// want notifications from termStructureHandle_ (they would
// interfere with bootstrapping.)
overnightIndex_->unregisterWith(termStructureHandle_);

registerWith(overnightIndex_);
registerWith(discountHandle_);

pillarDate_ = customPillarDate;
OISRateHelper::initializeDates();
}

void OISRateHelper::initializeDates() {

// input discount curve Handle might be empty now but it could
// be assigned a curve later; use a RelinkableHandle here
MakeOIS tmp = MakeOIS(tenor_, overnightIndex_, 0.0, forwardStart_)
auto tmp = MakeOIS(tenor_, overnightIndex(), 0.0, forwardStart_)
.withDiscountingTermStructure(discountRelinkableHandle_)
.withSettlementDays(settlementDays_)
.withTelescopicValueDates(telescopicValueDates_)
Expand All @@ -90,77 +93,21 @@ namespace QuantLib {
if (!fixedCalendar_.empty()) {
tmp.withFixedLegCalendar(fixedCalendar_);
}
swap_ = tmp;

simplifyNotificationGraph(*swap_, true);

earliestDate_ = swap_->startDate();
maturityDate_ = swap_->maturityDate();

Date lastPaymentDate = std::max(swap_->overnightLeg().back()->date(),
swap_->fixedLeg().back()->date());
latestRelevantDate_ = std::max(maturityDate_, lastPaymentDate);

switch (pillarChoice_) {
case Pillar::MaturityDate:
pillarDate_ = maturityDate_;
break;
case Pillar::LastRelevantDate:
pillarDate_ = latestRelevantDate_;
break;
case Pillar::CustomDate:
// pillarDate_ already assigned at construction time
QL_REQUIRE(pillarDate_ >= earliestDate_,
"pillar date (" << pillarDate_ << ") must be later "
"than or equal to the instrument's earliest date (" <<
earliestDate_ << ")");
QL_REQUIRE(pillarDate_ <= latestRelevantDate_,
"pillar date (" << pillarDate_ << ") must be before "
"or equal to the instrument's latest relevant date (" <<
latestRelevantDate_ << ")");
break;
default:
QL_FAIL("unknown Pillar::Choice(" << Integer(pillarChoice_) << ")");
}

latestDate_ = std::max(swap_->maturityDate(), lastPaymentDate);
}

void OISRateHelper::setTermStructure(YieldTermStructure* t) {
// do not set the relinkable handle as an observer -
// force recalculation when needed
bool observer = false;

ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
termStructureHandle_.linkTo(temp, observer);

if (discountHandle_.empty())
discountRelinkableHandle_.linkTo(temp, observer);
else
discountRelinkableHandle_.linkTo(*discountHandle_, observer);

RelativeDateRateHelper::setTermStructure(t);
}

Real OISRateHelper::impliedQuote() const {
QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
// we didn't register as observers - force calculation
swap_->deepUpdate();
return swap_->fairRate();
setInstrument<OvernightIndexedSwap>(tmp);
}

void OISRateHelper::accept(AcyclicVisitor& v) {
auto* v1 = dynamic_cast<Visitor<OISRateHelper>*>(&v);
if (v1 != nullptr)
v1->visit(*this);
else
RateHelper::accept(v);
base_type::accept(v);
}

DatedOISRateHelper::DatedOISRateHelper(const Date& startDate,
const Date& endDate,
const Handle<Quote>& fixedRate,
const ext::shared_ptr<OvernightIndex>& overnightIndex,
const ext::shared_ptr<OvernightIndex>& index,
Handle<YieldTermStructure> discount,
bool telescopicValueDates,
RateAveraging::Type averagingMethod,
Expand All @@ -172,33 +119,23 @@ namespace QuantLib {
Spread overnightSpread,
ext::optional<bool> endOfMonth,
ext::optional<Frequency> fixedPaymentFrequency,
const Calendar& fixedCalendar)
: RateHelper(fixedRate), discountHandle_(std::move(discount)),
telescopicValueDates_(telescopicValueDates), averagingMethod_(averagingMethod) {

auto clonedOvernightIndex =
ext::dynamic_pointer_cast<OvernightIndex>(overnightIndex->clone(termStructureHandle_));
// We want to be notified of changes of fixings, but we don't
// want notifications from termStructureHandle_ (they would
// interfere with bootstrapping.)
clonedOvernightIndex->unregisterWith(termStructureHandle_);

registerWith(clonedOvernightIndex);
registerWith(discountHandle_);

const Calendar& fixedCalendar,
Pillar::Choice pillar,
Date customPillarDate)
: base_type(fixedRate, index, std::move(discount), pillar, customPillarDate) {
// input discount curve Handle might be empty now but it could
// be assigned a curve later; use a RelinkableHandle here
auto tmp = MakeOIS(Period(), clonedOvernightIndex, 0.0, forwardStart)
auto tmp = MakeOIS(Period(), overnightIndex(), 0.0, forwardStart)
.withDiscountingTermStructure(discountRelinkableHandle_)
.withEffectiveDate(startDate)
.withTerminationDate(endDate)
.withTelescopicValueDates(telescopicValueDates_)
.withTelescopicValueDates(telescopicValueDates)
.withPaymentLag(paymentLag)
.withPaymentAdjustment(paymentConvention)
.withPaymentFrequency(paymentFrequency)
.withPaymentCalendar(paymentCalendar)
.withOvernightLegSpread(overnightSpread)
.withAveragingMethod(averagingMethod_);
.withAveragingMethod(averagingMethod);
if (endOfMonth) {
tmp.withEndOfMonth(*endOfMonth);
}
Expand All @@ -208,43 +145,15 @@ namespace QuantLib {
if (!fixedCalendar.empty()) {
tmp.withFixedLegCalendar(fixedCalendar);
}
swap_ = tmp;

earliestDate_ = swap_->startDate();
Date lastPaymentDate = std::max(swap_->overnightLeg().back()->date(),
swap_->fixedLeg().back()->date());
latestDate_ = std::max(swap_->maturityDate(), lastPaymentDate);
}

void DatedOISRateHelper::setTermStructure(YieldTermStructure* t) {
// do not set the relinkable handle as an observer -
// force recalculation when needed
bool observer = false;

ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
termStructureHandle_.linkTo(temp, observer);

if (discountHandle_.empty())
discountRelinkableHandle_.linkTo(temp, observer);
else
discountRelinkableHandle_.linkTo(*discountHandle_, observer);

RateHelper::setTermStructure(t);
}

Real DatedOISRateHelper::impliedQuote() const {
QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
// we didn't register as observers - force calculation
swap_->deepUpdate();
return swap_->fairRate();
setInstrument<OvernightIndexedSwap>(tmp);
}

void DatedOISRateHelper::accept(AcyclicVisitor& v) {
auto* v1 = dynamic_cast<Visitor<DatedOISRateHelper>*>(&v);
if (v1 != nullptr)
v1->visit(*this);
else
RateHelper::accept(v);
base_type::accept(v);
}

}

0 comments on commit c2662ad

Please sign in to comment.