Skip to content

Commit 41747b9

Browse files
committed
Merge branch 'bugfix/QPR-13768' into 'master'
QPR-13768 use forward schedule, fix settlement days default Closes QPR-13768 See merge request qs/oreplus!3181
2 parents b302987 + 58e4faa commit 41747b9

5 files changed

Lines changed: 26 additions & 17 deletions

File tree

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
#TradeId,TradeType,Maturity,MaturityTime,NPV,NpvCurrency,NPV(Base),BaseCurrency,Notional,NotionalCurrency,Notional(Base),NettingSet,CounterParty
2-
Cap_USD_SOFR,Swap,2025-03-21,1.003324,292.453738,USD,292.453738,USD,100000000.00,USD,100000000.00,DUMMY_NS,DUMMY_CP
3-
Swaption_USD_SOFR,Swaption,2046-01-12,21.817022,248320.967264,USD,248320.967264,USD,100000000.00,USD,100000000.00,DUMMY_NS,DUMMY_CP
2+
Cap_USD_SOFR,Swap,2025-03-21,1.003324,292.449762,USD,292.449762,USD,100000000.00,USD,100000000.00,DUMMY_NS,DUMMY_CP
3+
Swaption_USD_SOFR,Swaption,2046-01-12,21.817022,248320.968781,USD,248320.968781,USD,100000000.00,USD,100000000.00,DUMMY_NS,DUMMY_CP

OREData/ored/marketdata/capfloorvolcurve.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,13 @@ CapFloorVolCurve::CapFloorVolCurve(
114114
QL_FAIL("Unexpected type (" << static_cast<int>(config->type()) << ") for cap floor config "
115115
<< config->curveID());
116116
}
117-
// Turn on or off extrapolation
118-
capletVol_->enableExtrapolation(config->extrapolate());
117+
118+
capletVol_->enableExtrapolation(true);
119119
}
120120

121+
// force bootstrap so that errors are thrown during the build, not later
122+
capletVol_->volatility(QL_EPSILON, capletVol_->minStrike());
123+
121124
if (buildCalibrationInfo) {
122125
this->buildCalibrationInfo(asof, curveConfigs, config, iborIndex);
123126
}
@@ -130,8 +133,6 @@ CapFloorVolCurve::CapFloorVolCurve(
130133
QL_FAIL("cap/floor vol curve building failed: unknown error");
131134
}
132135

133-
// force bootstrap so that errors are thrown during the build, not later
134-
capletVol_->volatility(QL_EPSILON, capletVol_->minStrike());
135136
}
136137

137138
void CapFloorVolCurve::buildProxyCurve(

QuantExt/qle/instruments/makeoiscapfloor.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ MakeOISCapFloor::MakeOISCapFloor(CapFloor::Type type, const Period& tenor, const
2626
const Period& rateComputationPeriod, Rate strike,
2727
const QuantLib::Handle<QuantLib::YieldTermStructure>& discountCurve)
2828
: type_(type), tenor_(tenor), index_(index), rateComputationPeriod_(rateComputationPeriod), strike_(strike),
29-
nominal_(1.0), settlementDays_(2), calendar_(index->fixingCalendar()), convention_(ModifiedFollowing),
29+
nominal_(1.0), settlementDays_(0), calendar_(index->fixingCalendar()), convention_(ModifiedFollowing),
3030
rule_(DateGeneration::Backward), dayCounter_(index->dayCounter()), telescopicValueDates_(false),
3131
discountCurve_(discountCurve) {}
3232

@@ -43,8 +43,8 @@ MakeOISCapFloor::operator Leg() const {
4343

4444
Date endDate = calendar.adjust(startDate + tenor_, ModifiedFollowing);
4545

46-
Schedule schedule(startDate, endDate, rateComputationPeriod_, calendar, ModifiedFollowing, ModifiedFollowing,
47-
DateGeneration::Backward, false);
46+
Schedule schedule(startDate, endDate, rateComputationPeriod_, calendar, ModifiedFollowing, ModifiedFollowing, rule_,
47+
false);
4848

4949
// determine atm strike if required
5050
Real effectiveStrike = strike_;

QuantExt/qle/termstructures/oiscapfloorhelper.cpp

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,12 @@ void OISCapFloorHelper::initializeDates() {
7777
Rate dummyStrike = strike_ == Null<Real>() ? 0.01 : strike_;
7878
capFloor_ = MakeOISCapFloor(capFloorType, tenor_, index_, rateComputationPeriod_, dummyStrike)
7979
.withEffectiveDate(effectiveDate_)
80-
.withTelescopicValueDates(true);
80+
.withTelescopicValueDates(true)
81+
.withRule(DateGeneration::Rule::Forward);
8182
capFloorCopy_ = MakeOISCapFloor(capFloorType, tenor_, index_, rateComputationPeriod_, dummyStrike)
8283
.withEffectiveDate(effectiveDate_)
83-
.withTelescopicValueDates(true);
84+
.withTelescopicValueDates(true)
85+
.withRule(DateGeneration::Rule::Forward);
8486

8587
QL_REQUIRE(!capFloor_.empty(), "OISCapFloorHelper: got empty leg.");
8688

@@ -107,21 +109,26 @@ void OISCapFloorHelper::setTermStructure(OptionletVolatilityStructure* ovts) {
107109
Rate atm = CashFlows::atmRate(getOisCapFloorUnderlying(capFloor_), **discountHandle_, false);
108110
CapFloor::Type capFloorType = type_ == CapFloorHelper::Cap ? CapFloor::Cap : CapFloor::Floor;
109111
capFloor_ = MakeOISCapFloor(capFloorType, tenor_, index_, rateComputationPeriod_, atm)
112+
.withEffectiveDate(effectiveDate_)
110113
.withTelescopicValueDates(true)
111-
.withEffectiveDate(effectiveDate_);
114+
.withRule(DateGeneration::Rule::Forward);
112115
capFloorCopy_ = MakeOISCapFloor(capFloorType, tenor_, index_, rateComputationPeriod_, atm)
116+
.withEffectiveDate(effectiveDate_)
113117
.withTelescopicValueDates(true)
114-
.withEffectiveDate(effectiveDate_);
118+
.withRule(DateGeneration::Rule::Forward);
115119
} else if (type_ == CapFloorHelper::Automatic && quoteType_ != CapFloorHelper::Premium) {
116120
// If the helper is set to automatically choose the underlying instrument type, do it now based on the ATM rate
117121
Rate atm = CashFlows::atmRate(getOisCapFloorUnderlying(capFloor_), **discountHandle_, false);
118122
CapFloor::Type capFloorType = atm > strike_ ? CapFloor::Floor : CapFloor::Cap;
119123
capFloor_ = MakeOISCapFloor(capFloorType, tenor_, index_, rateComputationPeriod_, strike_)
124+
.withEffectiveDate(effectiveDate_)
120125
.withTelescopicValueDates(true)
121-
.withEffectiveDate(effectiveDate_);
126+
.withRule(DateGeneration::Rule::Forward);
122127
capFloorCopy_ = MakeOISCapFloor(capFloorType, tenor_, index_, rateComputationPeriod_, strike_)
123-
.withTelescopicValueDates(true)
124-
.withEffectiveDate(effectiveDate_);
128+
.withEffectiveDate(effectiveDate_)
129+
.withTelescopicValueDates(true)
130+
.withRule(DateGeneration::Rule::Forward);
131+
125132
for (auto const& c : capFloor_) {
126133
auto cpn = QuantLib::ext::dynamic_pointer_cast<Coupon>(c);
127134
}

QuantExt/qle/termstructures/optionletstripper.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,8 @@ void OptionletStripper::populateDates() const {
161161
MakeOISCapFloor(CapFloor::Cap, capFloorLengths_[i], QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(index_),
162162
rateComputationPeriod_, 0.04)
163163
.withTelescopicValueDates(true)
164-
.withSettlementDays(onCapSettlementDays_);
164+
.withSettlementDays(onCapSettlementDays_)
165+
.withRule(DateGeneration::Rule::Forward);
165166
auto lastCoupon = QuantLib::ext::dynamic_pointer_cast<CappedFlooredOvernightIndexedCoupon>(dummyCap.back());
166167
QL_REQUIRE(lastCoupon, "OptionletStripper::populateDates(): expected CappedFlooredOvernightIndexedCoupon");
167168
optionletDates_[i] = std::max(referenceDate + 1, lastCoupon->underlying()->fixingDates().front());

0 commit comments

Comments
 (0)