@@ -1126,55 +1126,128 @@ void YieldCurve::buildDiscountCurve() {
11261126
11271127void 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