Skip to content

Commit 7242d36

Browse files
pcaspersjenkins
authored andcommitted
QPR-9557 enable priorities in curve builder
1 parent 65e9ef6 commit 7242d36

1 file changed

Lines changed: 89 additions & 16 deletions

File tree

OREData/ored/marketdata/yieldcurve.cpp

Lines changed: 89 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,55 +1126,128 @@ void YieldCurve::buildDiscountCurve() {
11261126

11271127
void YieldCurve::buildBootstrappedCurve() {
11281128

1129-
/* Loop over segments and add helpers. */
1130-
vector<boost::shared_ptr<RateHelper>> instruments;
1131-
for (Size i = 0; i < curveSegments_.size(); i++) {
1129+
QL_REQUIRE(!curveSegments_.empty(), "no curve segments defined.");
1130+
1131+
/* Loop over segments and add helpers for each segment. */
1132+
1133+
DLOG("Building instrument sets for yield curve segments 0..." << curveSegments_.size() - 1);
1134+
1135+
std::vector<vector<boost::shared_ptr<RateHelper>>> instrumentsPerSegment(curveSegments_.size());
1136+
1137+
for (Size i = 0; i < curveSegments_.size(); ++i) {
11321138
switch (curveSegments_[i]->type()) {
11331139
case YieldCurveSegment::Type::Deposit:
1134-
addDeposits(curveSegments_[i], instruments);
1140+
addDeposits(curveSegments_[i], instrumentsPerSegment[i]);
11351141
break;
11361142
case YieldCurveSegment::Type::FRA:
1137-
addFras(curveSegments_[i], instruments);
1143+
addFras(curveSegments_[i], instrumentsPerSegment[i]);
11381144
break;
11391145
case YieldCurveSegment::Type::Future:
1140-
addFutures(curveSegments_[i], instruments);
1146+
addFutures(curveSegments_[i], instrumentsPerSegment[i]);
11411147
break;
11421148
case YieldCurveSegment::Type::OIS:
1143-
addOISs(curveSegments_[i], instruments);
1149+
addOISs(curveSegments_[i], instrumentsPerSegment[i]);
11441150
break;
11451151
case YieldCurveSegment::Type::Swap:
1146-
addSwaps(curveSegments_[i], instruments);
1152+
addSwaps(curveSegments_[i], instrumentsPerSegment[i]);
11471153
break;
11481154
case YieldCurveSegment::Type::AverageOIS:
1149-
addAverageOISs(curveSegments_[i], instruments);
1155+
addAverageOISs(curveSegments_[i], instrumentsPerSegment[i]);
11501156
break;
11511157
case YieldCurveSegment::Type::TenorBasis:
1152-
addTenorBasisSwaps(curveSegments_[i], instruments);
1158+
addTenorBasisSwaps(curveSegments_[i], instrumentsPerSegment[i]);
11531159
break;
11541160
case YieldCurveSegment::Type::TenorBasisTwo:
1155-
addTenorBasisTwoSwaps(curveSegments_[i], instruments);
1161+
addTenorBasisTwoSwaps(curveSegments_[i], instrumentsPerSegment[i]);
11561162
break;
11571163
case YieldCurveSegment::Type::BMABasis:
1158-
addBMABasisSwaps(curveSegments_[i], instruments);
1164+
addBMABasisSwaps(curveSegments_[i], instrumentsPerSegment[i]);
11591165
break;
11601166
case YieldCurveSegment::Type::FXForward:
1161-
addFXForwards(curveSegments_[i], instruments);
1167+
addFXForwards(curveSegments_[i], instrumentsPerSegment[i]);
11621168
break;
11631169
case YieldCurveSegment::Type::CrossCcyBasis:
1164-
addCrossCcyBasisSwaps(curveSegments_[i], instruments);
1170+
addCrossCcyBasisSwaps(curveSegments_[i], instrumentsPerSegment[i]);
11651171
break;
11661172
case YieldCurveSegment::Type::CrossCcyFixFloat:
1167-
addCrossCcyFixFloatSwaps(curveSegments_[i], instruments);
1173+
addCrossCcyFixFloatSwaps(curveSegments_[i], instrumentsPerSegment[i]);
11681174
break;
11691175
default:
11701176
QL_FAIL("Yield curve segment type not recognized.");
11711177
break;
11721178
}
11731179
}
11741180

1175-
DLOG("Bootstrapping with " << instruments.size() << " instruments");
1181+
/* If we have two instruments with identical pillar dates wthin a segment, remove the earlier one */
1182+
1183+
for (Size i = 0; i < curveSegments_.size(); ++i) {
1184+
for (auto it = instrumentsPerSegment[i].begin(); it != instrumentsPerSegment[i].end(); ++it) {
1185+
if (std::next(it, 1) != instrumentsPerSegment[i].end() && (*it)->pillarDate() == (*std::next(it, 1))->pillarDate()) {
1186+
it = instrumentsPerSegment[i].erase(it);
1187+
DLOG("Removing instrument with pillar date "
1188+
<< (*it)->pillarDate() << " in segment #" << i
1189+
<< " because the next instrument in the same segment has the same pillar date");
1190+
} else
1191+
++it;
1192+
}
1193+
}
1194+
1195+
/* Determine min and max pillar date in each segment */
1196+
1197+
std::vector<std::pair<Date, Date>> minMaxDatePerSegment(curveSegments_.size(),
1198+
std::make_pair(Date::maxDate(), Date::minDate()));
1199+
for (Size i = 0; i < curveSegments_.size(); ++i) {
1200+
if (!instrumentsPerSegment[i].empty()) {
1201+
auto [minIt, maxIt] =
1202+
std::minmax_element(instrumentsPerSegment[i].begin(), instrumentsPerSegment[i].end(),
1203+
[](const boost::shared_ptr<RateHelper>& h, const boost::shared_ptr<RateHelper>& j) {
1204+
return h->pillarDate() < h->pillarDate();
1205+
});
1206+
minMaxDatePerSegment[i] = std::make_pair((*minIt)->pillarDate(), (*maxIt)->pillarDate());
1207+
}
1208+
}
1209+
1210+
/* If there are two segments with different priorities and overlapping instruments, remove instruments as
1211+
* appropriate */
1212+
1213+
for (Size i = 0; i < curveSegments_.size(); ++i) {
1214+
for (Size j = 0; j < curveSegments_.size(); ++j) {
1215+
if (curveSegments_[i]->priority() > curveSegments_[j]->priority()) {
1216+
for (auto it = instrumentsPerSegment[i].begin(); it != instrumentsPerSegment[i].end();) {
1217+
if ((*it)->pillarDate() > minMaxDatePerSegment[j].first - curveSegments_[i]->minDistance() &&
1218+
(*it)->pillarDate() + curveSegments_[i]->minDistance() < minMaxDatePerSegment[j].second) {
1219+
DLOG("Removing instrument with pillar date "
1220+
<< (*it)->pillarDate() << " in segment #" << i << " (priority "
1221+
<< curveSegments_[i]->priority() << ") because it overlaps with segment #" << j
1222+
<< " (priority " << curveSegments_[j]->priority() << ") spanning ["
1223+
<< minMaxDatePerSegment[j].first << ", " << minMaxDatePerSegment[j].second
1224+
<< "], where we take a min distance of " << curveSegments_[i]->minDistance()
1225+
<< " calendar days defined for segment #" << i << " into account.");
1226+
it = instrumentsPerSegment[i].erase(it);
1227+
} else
1228+
++it;
1229+
}
1230+
}
1231+
}
1232+
}
1233+
1234+
/* Set mixed interpolation size using the specified cutoff for the number of segments */
1235+
1236+
mixedInterpolationSize_ = 0;
1237+
for (Size i = 0; i < curveConfig_->mixedInterpolationCutoff(); ++i)
1238+
mixedInterpolationSize_ += instrumentsPerSegment[i].size();
1239+
1240+
/* Now put all remaining instruments into a single vector. */
1241+
1242+
vector<boost::shared_ptr<RateHelper>> instruments;
1243+
for (Size i = 0; i < curveSegments_.size(); ++i) {
1244+
instruments.insert(instruments.end(), instrumentsPerSegment[i].begin(), instrumentsPerSegment[i].end());
1245+
DLOG("Adding " << instrumentsPerSegment[i].size() << " instruments for segment #" << i);
1246+
}
11761247

11771248
/* Build the bootstrapped curve from the instruments. */
1249+
1250+
DLOG("Bootstrapping with " << instruments.size() << " instruments");
11781251
QL_REQUIRE(instruments.size() > 0,
11791252
"Empty instrument list for date = " << io::iso_date(asofDate_) << " and curve = " << curveSpec_.name());
11801253
p_ = piecewisecurve(instruments);

0 commit comments

Comments
 (0)