292 {
293
294 DLOG(
"TRS::build() called for id = " <<
id());
295
296
297
299
303
304
305
306 std::set<bool> fundingLegPayers;
307 std::set<std::string> fundingCurrencies;
308
311
313 fundingLegPayer = l.isPayer();
314 fundingCurrency = l.currency();
315 fundingLegPayers.insert(fundingLegPayer);
316 fundingCurrencies.insert(fundingCurrency);
317 }
318
319 QL_REQUIRE(fundingLegPayers.size() <= 1, "funding leg payer flags must match");
320 QL_REQUIRE(fundingCurrencies.size() <= 1, "funding leg currencies must match");
321
322
323
326 for (auto const& b : TrsUnderlyingBuilderFactory::instance().getBuilders()) {
327 b.second->updateUnderlying(engineFactory->referenceData(),
underlying_[i],
id());
328 }
329 }
330 }
331
332
333
339
342 }
343 }
344
345
349 }
350 }
351
352
354
355
356
357 DLOG(
"build valuation and payment dates vectors");
358
359 std::vector<Date> valuationDates, paymentDates;
360
362 QL_REQUIRE(schedule.dates().size() >= 2, "at least two dates required in return schedule");
363
366 ? Unadjusted
369
372 ? Unadjusted
375 Period plPeriod = boost::apply_visitor(PaymentLagPeriod(), paymentLag);
376
377 for (auto const& d : schedule.dates()) {
378 valuationDates.push_back(observationCalendar.advance(d, -observationLag, observationConvention));
379 if (d != schedule.dates().front())
380 paymentDates.push_back(paymentCalendar.advance(d, plPeriod, paymentConvention));
381 }
382
384 paymentDates.clear();
387 << valuationDates.size() << ") minus 1");
390 }
391
392 DLOG(
"valuation schedule:");
393 for (auto const& d : valuationDates)
395
396 DLOG(
"payment schedule:");
397 for (auto const& d : paymentDates)
399
400
401
402 std::map<std::string, double> indexNamesAndQty;
403 std::map<std::string, QuantLib::ext::shared_ptr<QuantExt::FxIndex>> initialFxIndices, fxIndices, fxIndicesDummy;
404
405
406
407 std::set<std::string> missingFxIndexPairs;
408
411 auto fxIndexAdditionalCashflows =
413 ? fxIndexReturn
416 missingFxIndexPairs);
417
419
420 std::vector<QuantLib::ext::shared_ptr<QuantLib::Index>> underlyingIndex(
underlying_.size(),
nullptr);
421 std::vector<Real> underlyingMultiplier(
underlying_.size(), Null<Real>());
422 std::vector<std::string> assetCurrency(
underlying_.size(), fundingCurrency);
423 std::vector<QuantLib::ext::shared_ptr<QuantExt::FxIndex>> fxIndexAsset(
underlying_.size(),
nullptr);
424
426
428
429 DLOG(
"build underlying index for underlying #" << (i + 1));
430
431 std::string localCreditRiskCurrency;
432 Date localMaturity;
433 std::map<std::string, double> localIndexNamesAndQuantities;
434 std::map<std::string, QuantLib::ext::shared_ptr<QuantExt::FxIndex>> localFxIndices = initialFxIndices;
435 Real dummyInitialPrice = 1.0;
436
437 std::vector<Leg> returnLegs;
438 auto builder = TrsUnderlyingBuilderFactory::instance().getBuilder(
440 builder->build(
id(),
underlying_[i], valuationDates, paymentDates, fundingCurrency, engineFactory,
441 underlyingIndex[i], underlyingMultiplier[i], localIndexNamesAndQuantities, localFxIndices,
442 underlying_.size() == 1 ? initialPrice : dummyInitialPrice, assetCurrency[i],
444 std::bind(&
TRS::getFxIndex,
this, std::placeholders::_1, std::placeholders::_2,
445 std::placeholders::_3, std::placeholders::_4, std::placeholders::_5,
446 std::ref(missingFxIndexPairs)),
448
450
451
452
455 }
else if (!localCreditRiskCurrency.empty() &&
creditRiskCurrency_ != localCreditRiskCurrency) {
458 localCreditRiskCurrency + "' in addition.")
460 }
461
462
463
465
466
467
468 DLOG(
"underlying #" << (i + 1) <<
" has asset ccy " << assetCurrency[i] <<
", funding ccy is "
470
472 assetCurrency[i], fundingCurrency, localFxIndices, missingFxIndexPairs);
473 DLOG(
"underlying #" << (i + 1) <<
" index (" << underlyingIndex[i]->
name() <<
") built.");
474 DLOG(
"underlying #" << (i + 1) <<
" multiplier is " << underlyingMultiplier[i]);
475
476
477
478 for (const auto& [indexName, qty] : localIndexNamesAndQuantities) {
480 }
481
482 fxIndices.insert(localFxIndices.begin(), localFxIndices.end());
483 }
484
485
486 bool assetClassIsUnique = true;
487 std::string assetClass;
489 auto it = u->additionalData().find("isdaAssetClass");
490 if (it != u->additionalData().end()) {
491 std::string ac = boost::any_cast<std::string>(it->second);
492 if (assetClass == "")
493 assetClass = ac;
494 else if (ac != assetClass)
495 assetClassIsUnique = false;
496 }
497 }
498
503
504 if (assetClass == "") {
505 ALOG(
"ISDA asset class not found for TRS " <<
id() <<
", ISDA taxonomy undefined");
506 } else {
508 if (!assetClassIsUnique) {
509 WLOG(
"ISDA asset class not unique in TRS " <<
id() <<
" using first hit: " << assetClass);
510 }
512 if (assetClass == "Equity") {
514 additionalData_[
"isdaBaseProduct"] = string(
"Contract For Difference");
515 else
517 additionalData_[
"isdaSubProduct"] = string(
"Price Return Basic Performance");
518 } else if (assetClass == "Credit") {
521 } else {
522 WLOG(
"ISDA asset class " << assetClass <<
" not explicitly covered for TRS trade " <<
id()
523 << " using default BaseProduct 'Total Retuen Swap' and leaving sub-product blank");
524 }
525 }
526
527
528
529 QL_REQUIRE(missingFxIndexPairs.empty(), "TRS::build(): missing FXTerms for the following pairs: "
530 << boost::algorithm::join(missingFxIndexPairs, ", "));
531
532
533
534 QL_REQUIRE(!assetCurrency.empty(), "TRS::build(): no underlying given.");
535
536 std::string initialPriceCurrency =
538
540 for (auto const& ccy : assetCurrency) {
541 QL_REQUIRE(ccy == initialPriceCurrency, "TRS::build(): can not determine unique initial price currency "
542 "from asset currencies for initial price ("
544 << "), please add the initial price currency to the trade xml");
545 }
546 }
547
548
549
550 if (initialPrice != Null<Real>()) {
551 DLOG(
"initial price is given as " << initialPrice <<
" " << initialPriceCurrency);
553 DLOG(
"initial price after conversion to major ccy " << initialPrice);
554 } else {
555 DLOG(
"no initial price is given");
556 }
557
558 DLOG(
"fundingCurrency is " << fundingCurrency);
560 for (Size i = 0; i < assetCurrency.size(); ++i)
561 DLOG(
"assetCurrency #" << i <<
" is " << assetCurrency[i]);
562
563
564
565
566 QL_REQUIRE(
570 << " LegData nodes. These two must match. The NotionalType can also be omitted entirely.");
571
572 std::vector<Leg> fundingLegs;
573 std::vector<TRS::FundingData::NotionalType> fundingNotionalTypes;
575
577 QL_REQUIRE(ld.legType() == "Fixed" || ld.legType() == "Floating" || ld.legType() == "CMS" ||
578 ld.legType() == "CMB",
579 "TRS::build(): funding leg type: only fixed, floating, CMS, CMB are supported");
585 "TRS::build(): if notional is given in funding leg data, the notional type must be fixed, got "
586 << notionalType << " for funding leg #" << (i + 1));
587
588 struct TempNotionalSetter {
589 TempNotionalSetter(LegData& ld) : ld_(ld) {
590 if (ld.notionals().empty()) {
591 ld.notionals() = std::vector<Real>(1, 1.0);
592 ld.notionalDates().clear();
593 restoreEmptyLegDataNotionals_ = true;
594 } else {
595 restoreEmptyLegDataNotionals_ = false;
596 }
597 }
598 ~TempNotionalSetter() {
599 if (restoreEmptyLegDataNotionals_)
600 ld_.notionals().clear();
601 }
602 LegData& ld_;
603 bool restoreEmptyLegDataNotionals_;
604 };
605
606 TempNotionalSetter tmpSetter(ld);
607
608 auto legBuilder = engineFactory->legBuilder(ld.legType());
609 fundingLegs.push_back(legBuilder->buildLeg(ld, engineFactory,
requiredFixings_,
611 fundingNotionalTypes.push_back(notionalType);
612
613
614 if (ld.legType() == "CMB") {
615 auto cmbData = QuantLib::ext::dynamic_pointer_cast<ore::data::CMBLegData>(ld.concreteLegData());
616 QL_REQUIRE(cmbData, "TRS::build(): internal error, could to cast to CMBLegData.");
619 auto [source, target] =
623 }
624 }
625
626
627
628 DLOG(
"add required fixings for fundings legs with daily resets (if any)");
629
630 for (Size i = 0; i < fundingLegs.size(); ++i) {
632 for (auto const& c : fundingLegs[i]) {
633 if (auto cpn = QuantLib::ext::dynamic_pointer_cast<QuantLib::Coupon>(c)) {
634 QL_REQUIRE(QuantLib::ext::dynamic_pointer_cast<QuantLib::FixedRateCoupon>(c) ||
635 QuantLib::ext::dynamic_pointer_cast<QuantLib::IborCoupon>(c) ||
636 QuantLib::ext::dynamic_pointer_cast<QuantExt::OvernightIndexedCoupon>(c) ||
637 QuantLib::ext::dynamic_pointer_cast<QuantExt::AverageONIndexedCoupon>(c),
638 "daily reset funding legs support fixed rate, ibor and overnight indexed coupons only");
639 for (QuantLib::Date d = cpn->accrualStartDate(); d < cpn->accrualEndDate(); ++d) {
641 Date
fixingDate = underlyingIndex[j]->fixingCalendar().adjust(d, Preceding);
642 for (auto const& [n, _] : indexNamesAndQty)
644 for (auto const& n : fxIndices) {
646 n.first, cpn->date(), false, false);
647 }
648 }
649 }
650 }
651 }
653 for (auto const& c : fundingLegs[i]) {
654 if (auto cpn = QuantLib::ext::dynamic_pointer_cast<QuantLib::Coupon>(c)) {
656 Date fundingStartDate = cpn->accrualStartDate();
657 Size currentIdx = std::distance(valuationDates.begin(),
658 std::upper_bound(valuationDates.begin(),
659 valuationDates.end(),
661 if (currentIdx > 0)
662 --currentIdx;
664 for (auto const& [n, _] : indexNamesAndQty)
666 for (auto const& n : fxIndices) {
668 n.first, cpn->date(), false, false);
669 }
670 }
671 }
672 }
673 }
674 }
675
676
677
678 Date startDate = Date::maxDate();
679 for (auto const& l : fundingLegs) {
680 for (auto const& cf : l) {
681 QuantLib::ext::shared_ptr<Coupon> coupon = QuantLib::ext::dynamic_pointer_cast<Coupon>(cf);
682 if (coupon)
683 startDate = std::min(startDate, coupon->accrualStartDate());
684 }
685 }
686
688 for (
const auto& [
name, qty] : indexNamesAndQty) {
690 }
691
692
693
694 DLOG(
"build additional cashflow leg");
695
696 Leg additionalCashflowLeg;
697 bool additionalCashflowLegPayer = false;
698 std::string additionalCashflowLegCurrency = fundingCurrency;
701 "TRS::build(): additional cashflow data leg must have type 'Cashflow'");
707 }
708
709
710
711 std::vector<QuantLib::Currency> parsedAssetCurrencies;
712 for (auto const& c : assetCurrency) {
714 }
715
716
717
718 DLOG(
"build instrument and set trade member");
719
720 bool includeUnderlyingCashflowsInReturn;
723 } else {
724 includeUnderlyingCashflowsInReturn =
tradeType_ !=
"ContractForDifference";
725 }
726
727 auto wrapper = QuantLib::ext::make_shared<TRSWrapper>(
728 underlying_, underlyingIndex, underlyingMultiplier, includeUnderlyingCashflowsInReturn, initialPrice,
730 valuationDates, paymentDates, fundingLegs, fundingNotionalTypes,
parseCurrency(fundingCurrency),
732 additionalCashflowLegPayer,
parseCurrency(additionalCashflowLegCurrency), fxIndexAsset, fxIndexReturn,
733 fxIndexAdditionalCashflows, fxIndices);
734 wrapper->setPricingEngine(QuantLib::ext::make_shared<TRSWrapperAccrualEngine>());
735 instrument_ = QuantLib::ext::make_shared<VanillaInstrument>(wrapper);
736
737
738
739 if (Date today = Settings::instance().evaluationDate(); !valuationDates.empty() && valuationDates.front() > today) {
740 std::set<QuantLib::ext::shared_ptr<QuantExt::FxIndex>> tmp;
741 auto fxIndicesVal = fxIndices | boost::adaptors::map_values;
742 tmp.insert(fxIndicesVal.begin(), fxIndicesVal.end());
743 tmp.insert(fxIndexAsset.begin(), fxIndexAsset.end());
744 tmp.insert(fxIndexReturn);
745 tmp.insert(fxIndexAdditionalCashflows);
746 for (auto const& fx : tmp) {
747 if (fx != nullptr) {
749 IndexNameTranslator::instance().oreName(fx->name()));
750 }
751 }
752 }
753
754
755
757
759
760
762 maturity_ = std::max(valuationDates.back(), paymentDates.back());
763 for (auto const& l : fundingLegs) {
765 }
766 }
767}
void log() const
generate Boost log record to pass to corresponding sinks
const string & currency() const
const string & legType() const
QuantLib::ext::shared_ptr< LegAdditionalData > concreteLegData() const
void addData(const RequiredFixings &requiredFixings)
void addFixingDate(const QuantLib::Date &fixingDate, const std::string &indexName, const QuantLib::Date &payDate=Date::maxDate(), const bool alwaysAddIfPaysOnSettlement=false, const bool mandatoryFixing=true)
Utility class for Structured Trade errors, contains the Trade ID and Type.
const LegData & legData() const
const std::vector< NotionalType > & notionalType() const
const std::vector< LegData > & legData() const
QuantLib::Size fundingResetGracePeriod() const
const std::string & observationConvention() const
const std::vector< std::string > & paymentDates() const
const std::string & currency() const
const ScheduleData & scheduleData() const
const std::string & initialPriceCurrency() const
boost::optional< bool > payUnderlyingCashFlowsImmediately() const
const std::string & observationCalendar() const
const std::string & paymentCalendar() const
const std::string & observationLag() const
const std::string & paymentLag() const
const std::string & paymentConvention() const
Real initialPrice() const
std::map< std::string, SimmCreditQualifierMapping > creditQualifierMapping_
QuantLib::ext::shared_ptr< QuantExt::FxIndex > getFxIndex(const QuantLib::ext::shared_ptr< Market > market, const std::string &configuration, const std::string &domestic, const std::string &foreign, std::map< std::string, QuantLib::ext::shared_ptr< QuantExt::FxIndex > > &fxIndices, std::set< std::string > &missingFxIndexPairs) const
std::string creditRiskCurrency_
const std::string & sensitivityTemplate() const
string sensitivityTemplate_
const RequiredFixings & requiredFixings() const
void setSensitivityTemplate(const EngineBuilder &builder)
virtual const std::map< std::string, boost::any > & additionalData() const
returns all additional data returned by the trade once built
RequiredFixings requiredFixings_
QuantLib::ext::shared_ptr< InstrumentWrapper > instrument_
void reset()
Reset trade, clear all base class data. This does not reset accumulated timings for this trade.
const string & tradeType() const
std::map< std::string, boost::any > additionalData_
SafeStack< ValueType > value
Calendar parseCalendar(const string &s)
Convert text to QuantLib::Calendar.
Currency parseCurrencyWithMinors(const string &s)
Convert text to QuantLib::Currency.
Date parseDate(const string &s)
Convert std::string to QuantLib::Date.
QuantLib::Real convertMinorToMajorCurrency(const std::string &s, QuantLib::Real value)
Convert a value from a minor ccy to major.
Currency parseCurrency(const string &s)
Convert text to QuantLib::Currency.
BusinessDayConvention parseBusinessDayConvention(const string &s)
Convert text to QuantLib::BusinessDayConvention.
Period parsePeriod(const string &s)
Convert text to QuantLib::Period.
PaymentLag parsePaymentLag(const string &s)
Convert text to PaymentLag.
#define DLOG(text)
Logging Macro (Level = Debug)
#define ALOG(text)
Logging Macro (Level = Alert)
#define WLOG(text)
Logging Macro (Level = Warning)
QuantLib::Date fixingDate(const QuantLib::Date &d, const QuantLib::Period obsLag, const QuantLib::Frequency freq, bool interpolated)
std::string creditCurveNameFromSecuritySpecificCreditCurveName(const std::string &name)
std::pair< std::string, SimmCreditQualifierMapping > getCmbLegCreditQualifierMapping(const CMBLegData &ld, const QuantLib::ext::shared_ptr< ReferenceDataManager > &refData, const std::string &tradeId, const std::string &tradeType)
Size size(const ValueType &v)
std::string to_string(const LocationInfo &l)
void addTRSRequiredFixings(RequiredFixings &fixings, const std::vector< Leg > &returnLegs, const QuantLib::ext::shared_ptr< QuantExt::FxIndex > &ind=nullptr)
std::string getCmbLegCreditRiskCurrency(const CMBLegData &ld, const QuantLib::ext::shared_ptr< ReferenceDataManager > &refData)
boost::variant< QuantLib::Period, QuantLib::Natural > PaymentLag
Schedule makeSchedule(const ScheduleDates &data)