@@ -893,6 +893,18 @@ Leg makeFixedLeg(const LegData& data, const QuantLib::Date& openEndDateReplaceme
893893 Schedule schedule = makeSchedule (data.schedule (), openEndDateReplacement);
894894 QL_REQUIRE (schedule.size () > 1 , " FixedLeg:Schedule must have at least 2 dates." );
895895
896+ // Get explicit payment dates which in most cases should be empty
897+ vector<Date> paymentDates;
898+ if (!data.paymentDates ().empty ()) {
899+ BusinessDayConvention paymentDatesConvention =
900+ data.paymentConvention ().empty () ? Unadjusted : parseBusinessDayConvention (data.paymentConvention ());
901+ Calendar paymentDatesCalendar =
902+ data.paymentCalendar ().empty () ? NullCalendar () : parseCalendar (data.paymentCalendar ());
903+ paymentDates = parseVectorOfValues<Date>(data.paymentDates (), &parseDate);
904+ for (Size i = 0 ; i < paymentDates.size (); i++)
905+ paymentDates[i] = paymentDatesCalendar.adjust (paymentDates[i], paymentDatesConvention);
906+ }
907+
896908 DayCounter dc = parseDayCounter (data.dayCounter ());
897909 BusinessDayConvention bdc = parseBusinessDayConvention (data.paymentConvention ());
898910
@@ -912,9 +924,9 @@ Leg makeFixedLeg(const LegData& data, const QuantLib::Date& openEndDateReplaceme
912924 .withPaymentAdjustment (bdc)
913925 .withPaymentLag (boost::apply_visitor (PaymentLagInteger (), paymentLag))
914926 .withPaymentCalendar (paymentCalendar)
915- .withLastPeriodDayCounter (data. lastPeriodDayCounter (). empty ()
916- ? DayCounter ()
917- : parseDayCounter (data. lastPeriodDayCounter ()) );
927+ .withLastPeriodDayCounter (
928+ data. lastPeriodDayCounter (). empty () ? DayCounter () : parseDayCounter (data. lastPeriodDayCounter ()) )
929+ . withPaymentDates (paymentDates );
918930 return leg;
919931
920932}
@@ -977,6 +989,19 @@ Leg makeIborLeg(const LegData& data, const boost::shared_ptr<IborIndex>& index,
977989 QL_REQUIRE (floatData, " Wrong LegType, expected Floating, got " << data.legType ());
978990
979991 Schedule schedule = makeSchedule (data.schedule (), openEndDateReplacement);
992+
993+ // Get explicit payment dates which in most cases should be empty
994+ vector<Date> paymentDates;
995+ if (!data.paymentDates ().empty ()) {
996+ BusinessDayConvention paymentDatesConvention =
997+ data.paymentConvention ().empty () ? Unadjusted : parseBusinessDayConvention (data.paymentConvention ());
998+ Calendar paymentDatesCalendar =
999+ data.paymentCalendar ().empty () ? NullCalendar () : parseCalendar (data.paymentCalendar ());
1000+ paymentDates = parseVectorOfValues<Date>(data.paymentDates (), &parseDate);
1001+ for (Size i = 0 ; i < paymentDates.size (); i++)
1002+ paymentDates[i] = paymentDatesCalendar.adjust (paymentDates[i], paymentDatesConvention);
1003+ }
1004+
9801005 Calendar paymentCalendar;
9811006 if (data.paymentCalendar ().empty ())
9821007 paymentCalendar = schedule.calendar ();
@@ -1079,7 +1104,8 @@ Leg makeIborLeg(const LegData& data, const boost::shared_ptr<IborIndex>& index,
10791104 .withFixingDays (fixingDays)
10801105 .inArrears (isInArrears)
10811106 .withGearings (gearings)
1082- .withPaymentLag (boost::apply_visitor (PaymentLagInteger (), paymentLag));
1107+ .withPaymentLag (boost::apply_visitor (PaymentLagInteger (), paymentLag))
1108+ .withPaymentDates (paymentDates);
10831109
10841110 // If no caps or floors or in arrears fixing, return the leg
10851111 if (!hasCapsFloors && !isInArrears)
@@ -1137,7 +1163,18 @@ Leg makeOISLeg(const LegData& data, const boost::shared_ptr<OvernightIndex>& ind
11371163 DayCounter dc = parseDayCounter (data.dayCounter ());
11381164 BusinessDayConvention bdc = parseBusinessDayConvention (data.paymentConvention ());
11391165 PaymentLag paymentLag = parsePaymentLag (data.paymentLag ());
1140- Calendar paymentCalendar;
1166+
1167+ // Get explicit payment dates which in most cases should be empty
1168+ vector<Date> paymentDates;
1169+ if (!data.paymentDates ().empty ()) {
1170+ BusinessDayConvention paymentDatesConvention =
1171+ data.paymentConvention ().empty () ? Unadjusted : parseBusinessDayConvention (data.paymentConvention ());
1172+ Calendar paymentDatesCalendar =
1173+ data.paymentCalendar ().empty () ? NullCalendar () : parseCalendar (data.paymentCalendar ());
1174+ paymentDates = parseVectorOfValues<Date>(data.paymentDates (), &parseDate);
1175+ for (Size i = 0 ; i < paymentDates.size (); i++)
1176+ paymentDates[i] = paymentDatesCalendar.adjust (paymentDates[i], paymentDatesConvention);
1177+ }
11411178
11421179 // try to set the rate computation period based on the schedule tenor
11431180 Period rateComputationPeriod = 0 * Days;
@@ -1146,6 +1183,7 @@ Leg makeOISLeg(const LegData& data, const boost::shared_ptr<OvernightIndex>& ind
11461183 else if (!tmp.dates ().empty () && !tmp.dates ().front ().tenor ().empty ())
11471184 rateComputationPeriod = parsePeriod (tmp.dates ().front ().tenor ());
11481185
1186+ Calendar paymentCalendar;
11491187 if (data.paymentCalendar ().empty ())
11501188 paymentCalendar = index->fixingCalendar ();
11511189 else
@@ -1201,10 +1239,25 @@ Leg makeOISLeg(const LegData& data, const boost::shared_ptr<OvernightIndex>& ind
12011239 .withLocalCapFloor (floatData->localCapFloor ())
12021240 .withAverageONIndexedCouponPricer (couponPricer)
12031241 .withCapFlooredAverageONIndexedCouponPricer (cfCouponPricer)
1204- .withTelescopicValueDates (floatData->telescopicValueDates ());
1242+ .withTelescopicValueDates (floatData->telescopicValueDates ())
1243+ .withPaymentDates (paymentDates);
12051244 return leg;
12061245
12071246 } else {
1247+
1248+ boost::shared_ptr<QuantExt::OvernightIndexedCouponPricer> couponPricer =
1249+ boost::make_shared<QuantExt::OvernightIndexedCouponPricer>();
1250+
1251+ boost::shared_ptr<QuantExt::CappedFlooredOvernightIndexedCouponPricer> cfCouponPricer;
1252+ if (attachPricer && (floatData->caps ().size () > 0 || floatData->floors ().size () > 0 )) {
1253+ auto builder = boost::dynamic_pointer_cast<CapFlooredOvernightIndexedCouponLegEngineBuilder>(
1254+ engineFactory->builder (" CapFlooredOvernightIndexedCouponLeg" ));
1255+ QL_REQUIRE (builder, " No builder found for CapFlooredOvernightIndexedCouponLeg" );
1256+ cfCouponPricer = boost::dynamic_pointer_cast<QuantExt::CappedFlooredOvernightIndexedCouponPricer>(
1257+ builder->engine (IndexNameTranslator::instance ().oreName (index->name ()), rateComputationPeriod));
1258+ QL_REQUIRE (cfCouponPricer, " internal error, could not cast to CapFlooredAverageONIndexedCouponPricer" );
1259+ }
1260+
12081261 Leg leg = QuantExt::OvernightLeg (schedule, index)
12091262 .withNotionals (notionals)
12101263 .withSpreads (spreads)
@@ -1228,23 +1281,16 @@ Leg makeOISLeg(const LegData& data, const boost::shared_ptr<OvernightIndex>& ind
12281281 schedule, Null<Real>()))
12291282 .withNakedOption (floatData->nakedOption ())
12301283 .withLocalCapFloor (floatData->localCapFloor ())
1231- .withTelescopicValueDates (floatData->telescopicValueDates ());
1284+ .withOvernightIndexedCouponPricer (couponPricer)
1285+ .withCapFlooredOvernightIndexedCouponPricer (cfCouponPricer)
1286+ .withTelescopicValueDates (floatData->telescopicValueDates ())
1287+ .withPaymentDates (paymentDates);
12321288
12331289 // If the overnight index is BRL CDI, we need a special coupon pricer
12341290 boost::shared_ptr<BRLCdi> brlCdiIndex = boost::dynamic_pointer_cast<BRLCdi>(index);
12351291 if (brlCdiIndex)
12361292 QuantExt::setCouponPricer (leg, boost::make_shared<BRLCdiCouponPricer>());
12371293
1238- // if we have caps / floors, we need a pricer for that
1239- if (attachPricer && (floatData->caps ().size () > 0 || floatData->floors ().size () > 0 )) {
1240- auto builder = boost::dynamic_pointer_cast<CapFlooredOvernightIndexedCouponLegEngineBuilder>(
1241- engineFactory->builder (" CapFlooredOvernightIndexedCouponLeg" ));
1242- QL_REQUIRE (builder, " No builder found for CapFlooredOvernightIndexedCouponLeg" );
1243- auto pricer =
1244- builder->engine (IndexNameTranslator::instance ().oreName (index->name ()), rateComputationPeriod);
1245- QuantExt::setCouponPricer (leg, pricer);
1246- }
1247-
12481294 return leg;
12491295 }
12501296}
0 commit comments