413 {
414
415
416
418
419 auto floatingLegData = QuantLib::ext::dynamic_pointer_cast<CommodityFloatingLegData>(
data.concreteLegData());
420 QL_REQUIRE(floatingLegData,
"Wrong LegType: expected CommodityFloating but got " <<
data.legType());
421
422
423
424 string commName = floatingLegData->name();
425 Calendar commCal = WeekendsOnly();
426 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
427 QuantLib::ext::shared_ptr<CommodityFutureConvention> commFutureConv;
428 boost::optional<pair<Calendar, Real>> offPeakPowerData;
429 bool balanceOfTheMonth = false;
430 if (conventions->has(commName)) {
431 QuantLib::ext::shared_ptr<Convention> commConv = conventions->get(commName);
432
433
434 if (auto c = QuantLib::ext::dynamic_pointer_cast<CommodityForwardConvention>(commConv)) {
435 if (c->advanceCalendar() != NullCalendar()) {
436 commCal = c->advanceCalendar();
437 }
438 }
439
440
441 commFutureConv = QuantLib::ext::dynamic_pointer_cast<CommodityFutureConvention>(commConv);
442 if (commFutureConv) {
443 balanceOfTheMonth = commFutureConv->balanceOfTheMonth();
444 commCal = commFutureConv->calendar();
445 if (const auto& oppid = commFutureConv->offPeakPowerIndexData()) {
446 offPeakPowerData = make_pair(oppid->peakCalendar(), oppid->offPeakHours());
447 }
448 }
449 }
450
451
453
454
455 QuantLib::ext::shared_ptr<FutureExpiryCalculator> feCalc;
457
458
459 QL_REQUIRE(commFutureConv, "Expected to have a commodity future convention for commodity " << commName);
460
461 feCalc = QuantLib::ext::make_shared<ConventionsBasedFutureExpiry>(*commFutureConv);
462
463
464
465 if (commFutureConv->isAveraging()) {
466 QL_REQUIRE(floatingLegData->isAveraged(),
467 "The future, " << commName << ", is averaging but the leg is not.");
469 }
470 }
471
472
473 Handle<PriceTermStructure> priceCurve = engineFactory->market()->commodityPriceCurve(commName, configuration);
476
477
479 vector<Real> quantities =
480 buildScheduledVector(floatingLegData->quantities(), floatingLegData->quantityDates(), schedule);
481
482
483 vector<Real> spreads =
buildScheduledVector(floatingLegData->spreads(), floatingLegData->spreadDates(), schedule);
484 vector<Real> gearings =
486
487
489 if (!floatingLegData->pricingDates().empty()) {
490 pricingDates = parseVectorOfValues<Date>(floatingLegData->pricingDates(), &parseDate);
491 }
492
493
495 BusinessDayConvention paymentConvention =
497 Calendar paymentCalendar =
499 Calendar pricingCalendar;
500
501
502 if (floatingLegData->pricingCalendar().empty() && floatingLegData->isAveraged() && balanceOfTheMonth &&
503 commFutureConv) {
504 pricingCalendar = commFutureConv->balanceOfTheMonthPricingCalendar();
505 } else if (floatingLegData->pricingCalendar().empty()) {
506 pricingCalendar = commCal;
507 } else {
508 pricingCalendar =
parseCalendar(floatingLegData->pricingCalendar());
509 }
510
511
512 vector<Date> paymentDates;
513
514
515 Schedule paymentSchedule =
makeSchedule(
data.paymentSchedule(), openEndDateReplacement);
516
517 if (!paymentSchedule.empty()) {
518 paymentDates = paymentSchedule.dates();
519 }
else if (!
data.paymentDates().empty()) {
520 BusinessDayConvention paymentDatesConvention =
522 Calendar paymentDatesCalendar =
524 paymentDates = parseVectorOfValues<Date>(
data.paymentDates(), &parseDate);
525 for (Size i = 0; i < paymentDates.size(); i++)
526 paymentDates[i] = paymentDatesCalendar.adjust(paymentDates[i], paymentDatesConvention);
527 }
528
529
530 auto hoursPerDay = floatingLegData->hoursPerDay();
531 if ((floatingLegData->commodityQuantityFrequency() == CommodityQuantityFrequency::PerHour ||
532 floatingLegData->commodityQuantityFrequency() == CommodityQuantityFrequency::PerHourAndCalendarDay) &&
533 hoursPerDay == Null<Natural>()) {
534 QL_REQUIRE(commFutureConv,
535 "Commodity floating leg commodity frequency set to PerHour / PerHourAndCalendarDay but"
536 << " no HoursPerDay provided in floating leg data and no commodity future convention for "
537 << commName);
538 hoursPerDay = commFutureConv->hoursPerDay();
539 QL_REQUIRE(commFutureConv,
540 "Commodity floating leg commodity frequency set to PerHour / PerHourAndCalendarDay but"
541 << " no HoursPerDay provided in floating leg data and commodity future convention for "
542 << commName << " does not provide it.");
543 }
544
545
546 std::string daylightSavingLocation;
547 if (floatingLegData->commodityQuantityFrequency() == CommodityQuantityFrequency::PerHourAndCalendarDay) {
548 QL_REQUIRE(
549 commFutureConv,
550 "Commodity floating leg commodity frequency set to PerHourAndCalendarDay, need commodity convention for "
551 << commName);
552 daylightSavingLocation = commFutureConv->savingsTime();
553 }
554 QuantLib::ext::shared_ptr<FxIndex> fxIndex;
555
556 Leg leg;
557
558 bool isCashFlowAveraged =
559 floatingLegData->isAveraged() && !
allAveraging_ && floatingLegData->lastNDays() == Null<Natural>();
560
561
562 auto dailyExpOffset = floatingLegData->dailyExpiryOffset();
563 if (dailyExpOffset != Null<Natural>() && dailyExpOffset > 0) {
564 QL_REQUIRE(commFutureConv, "A positive DailyExpiryOffset has been provided but no commodity"
565 << " future convention given for " << commName);
566 QL_REQUIRE(commFutureConv->contractFrequency() == Daily,
567 "A positive DailyExpiryOffset has been"
568 << " provided but the commodity contract frequency is not Daily ("
569 << commFutureConv->contractFrequency() << ")");
570 }
571
572 if (!floatingLegData->fxIndex().empty()) {
573 auto underlyingCcy = priceCurve->currency().code();
574 auto npvCurrency =
data.currency();
575 if (underlyingCcy != npvCurrency)
576 fxIndex =
buildFxIndex(floatingLegData->fxIndex(), npvCurrency, underlyingCcy, engineFactory->market(),
578 }
579
580 if (isCashFlowAveraged) {
582 CommodityIndexedAverageCashFlow::PaymentTiming::InArrears;
584 paymentTiming = CommodityIndexedAverageCashFlow::PaymentTiming::InAdvance;
586 paymentTiming = CommodityIndexedAverageCashFlow::PaymentTiming::InArrears;
588 QL_FAIL("CommodityLegBuilder: CommodityPayRelativeTo 'FutureExpiryDate' not allowed for average cashflow.");
589 } else {
590 QL_FAIL("CommodityLegBuilder: CommodityPayRelativeTo " << floatingLegData->commodityPayRelativeTo()
591 << " not handled. This is an internal error.");
592 }
593
619 } else {
622 paymentTiming = CommodityIndexedCashFlow::PaymentTiming::InAdvance;
624 paymentTiming = CommodityIndexedCashFlow::PaymentTiming::InArrears;
626 paymentTiming = CommodityIndexedCashFlow::PaymentTiming::RelativeToExpiry;
627 } else {
628 QL_FAIL("CommodityLegBuilder: CommodityPayRelativeTo " << floatingLegData->commodityPayRelativeTo()
629 << " not handled. This is an internal error.");
630 }
631
642 .
inArrears(floatingLegData->isInArrears())
656
657
658 updateQuantities(leg,
allAveraging_, floatingLegData->commodityQuantityFrequency(), schedule,
659 floatingLegData->excludePeriodStart(), floatingLegData->includePeriodEnd(), commFutureConv,
660 feCalc, hoursPerDay, floatingLegData->useBusinessDays(), daylightSavingLocation, commName,
661 floatingLegData->unrealisedQuantity(), offPeakPowerData);
662
663
664 auto lastNDays = floatingLegData->lastNDays();
665 if (lastNDays != Null<Natural>() && lastNDays > 1) {
666 if (commFutureConv) {
667 if (lastNDays > 31) {
668 WLOG(
"LastNDays (" << lastNDays <<
") should not be greater than 31. " <<
669 "Proceed as if it is not set.");
670 } else if (commFutureConv->isAveraging()) {
671 WLOG(
"Commodity future convention for " << commName <<
" is averaging so LastNDays (" <<
672 lastNDays << ") is ignored. Proceed as if it is not set.");
673 } else {
674 DLOG(
"Amending cashflows to account for LastNDays (" << lastNDays <<
").");
675 const auto& cal = commFutureConv->calendar();
676 for (auto& cf : leg) {
677 auto ccf = QuantLib::ext::dynamic_pointer_cast<CommodityIndexedCashFlow>(cf);
678 const Date& endDate = ccf->pricingDate();
679 Date startDate = cal.advance(endDate, -static_cast<Integer>(lastNDays) + 1, Days, Preceding);
680 TLOG(
"Creating cashflow averaging over period [" << io::iso_date(startDate) <<
681 "," << io::iso_date(endDate) << "]");
682 cf = QuantLib::ext::make_shared<CommodityIndexedAverageCashFlow>(ccf->periodQuantity(), startDate,
683 endDate, ccf->date(), ccf->index(), cal, ccf->spread(), ccf->gearing(),
684 ccf->useFuturePrice(), 0, 0, feCalc, true, false);
685 }
686 }
687 } else {
688 WLOG(
"Need a commodity future convention for " << commName <<
" when LastNDays (" << lastNDays <<
689 ") is set and greater than 1. Proceed as if it is not set.");
690 }
691 }
692 }
693
694 if (fxIndex) {
695
696 for (auto cf : leg) {
697 auto cacf = QuantLib::ext::dynamic_pointer_cast<CommodityCashFlow>(cf);
698 QL_REQUIRE(cacf, "Commodity Indexed averaged cashflow is required to compute daily converted average.");
699 for (auto kv : cacf->indices()) {
700 if (!fxIndex->fixingCalendar().isBusinessDay(
701 kv.first)) {
702
703
704 Date adjustedFixingDate = fxIndex->fixingCalendar().adjust(kv.first, Preceding);
705 requiredFixings.
addFixingDate(adjustedFixingDate, floatingLegData->fxIndex());
706 } else
707 requiredFixings.
addFixingDate(kv.first, floatingLegData->fxIndex());
708 }
709 }
710 } else {
711
712 applyIndexing(leg, data, engineFactory, requiredFixings, openEndDateReplacement, useXbsCurves);
713 }
714
716 return leg;
717}
CommodityIndexedAverageLeg & useFuturePrice(bool flag=false)
CommodityIndexedAverageLeg & withPaymentLag(QuantLib::Natural paymentLag)
CommodityIndexedAverageLeg & includeEndDate(bool flag=true)
CommodityIndexedAverageLeg & excludeStartDate(bool flag=true)
CommodityIndexedAverageLeg & withFutureMonthOffset(QuantLib::Natural futureMonthOffset)
CommodityIndexedAverageLeg & withGearings(QuantLib::Real gearing)
CommodityIndexedAverageLeg & paymentTiming(CommodityIndexedAverageCashFlow::PaymentTiming paymentTiming)
CommodityIndexedAverageLeg & withDeliveryDateRoll(QuantLib::Natural deliveryDateRoll)
CommodityIndexedAverageLeg & withQuantityFrequency(CommodityQuantityFrequency quantityFrequency)
CommodityIndexedAverageLeg & withPaymentConvention(QuantLib::BusinessDayConvention paymentConvention)
CommodityIndexedAverageLeg & withPaymentDates(const std::vector< QuantLib::Date > &paymentDates)
CommodityIndexedAverageLeg & withDailyExpiryOffset(QuantLib::Natural dailyExpiryOffset)
CommodityIndexedAverageLeg & withQuantities(QuantLib::Real quantity)
CommodityIndexedAverageLeg & withPaymentCalendar(const QuantLib::Calendar &paymentCalendar)
CommodityIndexedAverageLeg & withHoursPerDay(QuantLib::Natural hoursPerDay)
CommodityIndexedAverageLeg & useBusinessDays(bool flag=true)
CommodityIndexedAverageLeg & withFutureExpiryCalculator(const ext::shared_ptr< FutureExpiryCalculator > &calc=nullptr)
CommodityIndexedAverageLeg & withPricingCalendar(const QuantLib::Calendar &pricingCalendar)
CommodityIndexedAverageLeg & withSpreads(QuantLib::Real spread)
CommodityIndexedAverageLeg & unrealisedQuantity(bool flag=false)
CommodityIndexedAverageLeg & withOffPeakPowerData(const boost::optional< std::pair< QuantLib::Calendar, QuantLib::Real > > &offPeakPowerData)
CommodityIndexedAverageLeg & withFxIndex(const ext::shared_ptr< FxIndex > &fxIndex)
CommodityIndexedAverageLeg & payAtMaturity(bool flag=false)
CommodityIndexedLeg & excludeStartDate(bool flag=true)
CommodityIndexedLeg & withPricingLagCalendar(const QuantLib::Calendar &pricingLagCalendar)
CommodityIndexedLeg & paymentTiming(CommodityIndexedCashFlow::PaymentTiming paymentTiming)
CommodityIndexedLeg & payAtMaturity(bool flag=false)
CommodityIndexedLeg & withIsAveraging(const bool isAveraging)
CommodityIndexedLeg & includeEndDate(bool flag=true)
CommodityIndexedLeg & withFutureMonthOffset(QuantLib::Natural futureMonthOffset)
CommodityIndexedLeg & withQuantities(QuantLib::Real quantity)
CommodityIndexedLeg & withPaymentDates(const std::vector< QuantLib::Date > &paymentDates)
CommodityIndexedLeg & withFxIndex(const ext::shared_ptr< FxIndex > &fxIndex)
CommodityIndexedLeg & withFutureExpiryCalculator(const ext::shared_ptr< FutureExpiryCalculator > &calc=nullptr)
CommodityIndexedLeg & withPaymentLag(QuantLib::Natural paymentLag)
CommodityIndexedLeg & inArrears(bool flag=true)
CommodityIndexedLeg & withGearings(QuantLib::Real gearing)
CommodityIndexedLeg & withDailyExpiryOffset(QuantLib::Natural dailyExpiryOffset)
CommodityIndexedLeg & withPaymentCalendar(const QuantLib::Calendar &paymentCalendar)
CommodityIndexedLeg & withPricingDates(const std::vector< QuantLib::Date > &pricingDates)
CommodityIndexedLeg & withSpreads(QuantLib::Real spread)
CommodityIndexedLeg & useFuturePrice(bool flag=false)
CommodityIndexedLeg & withPricingCalendar(const QuantLib::Calendar &pricingCalendar)
CommodityIndexedLeg & withPaymentConvention(QuantLib::BusinessDayConvention paymentConvention)
CommodityIndexedLeg & withPricingLag(QuantLib::Natural pricingLag)
CommodityIndexedLeg & useFutureExpiryDate(bool flag=true)
void addFixingDate(const QuantLib::Date &fixingDate, const std::string &indexName, const QuantLib::Date &payDate=Date::maxDate(), const bool alwaysAddIfPaysOnSettlement=false, const bool mandatoryFixing=true)
Calendar parseCalendar(const string &s)
Convert text to QuantLib::Calendar.
BusinessDayConvention parseBusinessDayConvention(const string &s)
Convert text to QuantLib::BusinessDayConvention.
PaymentLag parsePaymentLag(const string &s)
Convert text to PaymentLag.
#define DLOG(text)
Logging Macro (Level = Debug)
#define WLOG(text)
Logging Macro (Level = Warning)
#define TLOG(text)
Logging Macro (Level = Data)
set< Date > pricingDates(const Date &s, const Date &e, const Calendar &pricingCalendar, bool excludeStart, bool includeEnd, bool useBusinessDays)
@ CalculationPeriodStartDate
@ CalculationPeriodEndDate
void applyIndexing(Leg &leg, const LegData &data, const QuantLib::ext::shared_ptr< EngineFactory > &engineFactory, RequiredFixings &requiredFixings, const QuantLib::Date &openEndDateReplacement, const bool useXbsCurves)
void addToRequiredFixings(const QuantLib::Leg &leg, const QuantLib::ext::shared_ptr< FixingDateGetter > &fixingDateGetter)
vector< T > buildScheduledVector(const vector< T > &values, const vector< string > &dates, const Schedule &schedule, const bool checkAllValuesAppearInResult=false)
QuantLib::ext::shared_ptr< QuantExt::CommodityIndex > parseCommodityIndex(const string &name, bool hasPrefix, const Handle< PriceTermStructure > &ts, const Calendar &cal, const bool enforceFutureIndex)
boost::variant< QuantLib::Period, QuantLib::Natural > PaymentLag
QuantLib::ext::shared_ptr< QuantExt::FxIndex > buildFxIndex(const string &fxIndex, const string &domestic, const string &foreign, const QuantLib::ext::shared_ptr< Market > &market, const string &configuration, bool useXbsCurves)
Schedule makeSchedule(const ScheduleDates &data)