26#include <ql/cashflows/cashflows.hpp>
27#include <ql/cashflows/fixedratecoupon.hpp>
28#include <ql/currencies/exchangeratemanager.hpp>
38 const std::vector<QuantLib::ext::shared_ptr<ore::data::Trade>>& underlying,
39 const std::vector<QuantLib::ext::shared_ptr<Index>>& underlyingIndex,
const std::vector<Real> underlyingMultiplier,
40 const bool includeUnderlyingCashflowsInReturn,
const Real initialPrice,
const Currency& initialPriceCurrency,
41 const std::vector<Currency>& assetCurrency,
const Currency& returnCurrency,
42 const std::vector<Date>& valuationSchedule,
const std::vector<Date>& paymentSchedule,
43 const std::vector<Leg>& fundingLegs,
const std::vector<TRS::FundingData::NotionalType>& fundingNotionalTypes,
44 const Currency& fundingCurrency,
const Size fundingResetGracePeriod,
const bool paysAsset,
const bool paysFunding,
45 const Leg& additionalCashflowLeg,
const bool additionalCashflowLegPayer,
const Currency& additionalCashflowCurrency,
46 const std::vector<QuantLib::ext::shared_ptr<FxIndex>>& fxIndexAsset,
const QuantLib::ext::shared_ptr<FxIndex>& fxIndexReturn,
47 const QuantLib::ext::shared_ptr<FxIndex>& fxIndexAdditionalCashflows,
48 const std::map<std::string, QuantLib::ext::shared_ptr<QuantExt::FxIndex>>& addFxIndices)
49 : underlying_(underlying), underlyingIndex_(underlyingIndex), underlyingMultiplier_(underlyingMultiplier),
50 includeUnderlyingCashflowsInReturn_(includeUnderlyingCashflowsInReturn), initialPrice_(initialPrice),
51 initialPriceCurrency_(initialPriceCurrency), assetCurrency_(assetCurrency), returnCurrency_(returnCurrency),
52 valuationSchedule_(valuationSchedule), paymentSchedule_(paymentSchedule), fundingLegs_(fundingLegs),
53 fundingNotionalTypes_(fundingNotionalTypes), fundingCurrency_(fundingCurrency),
54 fundingResetGracePeriod_(fundingResetGracePeriod), paysAsset_(paysAsset), paysFunding_(paysFunding),
55 additionalCashflowLeg_(additionalCashflowLeg), additionalCashflowLegPayer_(additionalCashflowLegPayer),
56 additionalCashflowCurrency_(additionalCashflowCurrency), fxIndexAsset_(fxIndexAsset),
57 fxIndexReturn_(fxIndexReturn), fxIndexAdditionalCashflows_(fxIndexAdditionalCashflows),
58 addFxIndices_(addFxIndices) {
60 QL_REQUIRE(!
paymentSchedule_.empty(),
"TRSWrapper::TRSWrapper(): payment schedule must not be empty()");
63 "TRSWrapper::TRSWrapper(): valuation schedule size ("
69 "TRSWrapper::TRSWrapper(): valuation schedule dates must be monotonic, at "
75 "TRSWrapper::TRSWrapper(): payment schedule dates must be monotonic, at "
82 <<
") must be >= valuation date ("
88 <<
") must match funding notitional types ("
91 QL_REQUIRE(!
underlying_.empty(),
"TRSWrapper::TRSWrapper(): no underlying given, at least one is required");
92 QL_REQUIRE(underlying.size() == underlyingIndex.size(),
93 "TRSWrapper::TRSWrapper(): number of underlyings ("
94 << underlying.size() <<
") does not match underlying index size (" << underlyingIndex.size() <<
")");
95 QL_REQUIRE(underlying.size() == underlyingMultiplier.size(),
"TRSWrapper::TRSWrapper(): number of underlyings ("
97 <<
") does not match underlying index size ("
98 << underlyingMultiplier.size() <<
")");
99 QL_REQUIRE(underlying.size() == assetCurrency.size(),
100 "TRSWrapper::TRSWrapper(): number of underlyings ("
101 << underlying.size() <<
") does not match asset currency size (" << assetCurrency.size() <<
")");
102 QL_REQUIRE(underlying.size() == fxIndexAsset.size(),
103 "TRSWrapper::TRSWrapper(): number of underlyings ("
104 << underlying.size() <<
") does not match fx index asset size (" << fxIndexAsset.size() <<
")");
107 registerWith(
underlying_[i]->instrument()->qlInstrument());
117 for (
auto const& f : fxIndexAsset)
119 registerWith(fxIndexReturn);
120 registerWith(fxIndexAdditionalCashflows);
128 for (
auto const& c : l)
138 QL_REQUIRE(a !=
nullptr,
"wrong argument type in TRSWrapper");
167 QL_REQUIRE(!a.empty(),
"empty asset currency");
175 std::vector<Real>& fxConversionFactor, QuantLib::Date& startDate,
176 QuantLib::Date& endDate,
bool& usingInitialPrice,
177 const Size nth)
const {
178 Date today = Settings::instance().evaluationDate();
180 std::distance(
arguments_.paymentSchedule_.begin(),
181 std::upper_bound(
arguments_.paymentSchedule_.begin(),
arguments_.paymentSchedule_.end(), today)) +
183 Date v0 = payIdx <
arguments_.valuationSchedule_.size() ?
arguments_.valuationSchedule_[payIdx] : Date::maxDate();
185 payIdx <
arguments_.valuationSchedule_.size() - 1 ?
arguments_.valuationSchedule_[payIdx + 1] : Date::maxDate();
188 if (nth > 0 && (payIdx >=
arguments_.paymentSchedule_.size() || v0 > today))
191 std::fill(underlyingStartValue.begin(), underlyingStartValue.end(), 0.0);
192 std::fill(fxConversionFactor.begin(), fxConversionFactor.end(), 1.0);
193 startDate = Null<Date>();
194 endDate = Null<Date>();
195 usingInitialPrice =
false;
197 for (Size i = 0; i <
arguments_.underlying_.size(); ++i) {
198 if (payIdx <
arguments_.paymentSchedule_.size()) {
204 QL_REQUIRE(payIdx == 0,
"TRSWrapper: internal error, expected valuation date "
205 << v0 <<
" for pay date = " <<
arguments_.paymentSchedule_[payIdx]
206 <<
" to be the first valuation date, since it is > today (" << today
208 if (nth == 0 &&
arguments_.initialPrice_ != Null<Real>()) {
213 Real fx0 = getFxConversionRate(today,
arguments_.initialPriceCurrency_,
215 DLOG(
"start value (underlying "
216 << std::to_string(i + 1) <<
"): s0=" << s0 <<
" (from fixed initial price), fx0=" << fx0
217 <<
" => " << fx0 * s0 <<
" on today (valuation start date is " << v0 <<
")");
218 underlyingStartValue[i] = s0;
219 fxConversionFactor[i] = fx0;
223 usingInitialPrice =
true;
225 underlyingStartValue[i] = 0.0;
226 fxConversionFactor[i] = 1.0;
229 DLOG(
"start value (underlying " << std::to_string(i + 1) <<
") is null, because eval date ("
230 << today <<
") is <= start valuation date (" << v0
231 <<
") for nth current period " << nth
232 <<
" and no intiial price is given");
233 underlyingStartValue[i] = Null<Real>();
234 fxConversionFactor[i] = Null<Real>();
235 startDate = Null<Date>();
240 Real s0 = 0.0, fx0 = 1.0;
241 if (nth == 0 &&
arguments_.initialPrice_ != Null<Real>() &&
242 v0 ==
arguments_.valuationSchedule_.front()) {
244 DLOG(
"initial price is given as " <<
arguments_.initialPrice_ <<
" "
250 usingInitialPrice =
true;
253 s0 = getUnderlyingFixing(i, v0,
false) *
arguments_.underlyingMultiplier_[i];
256 DLOG(
"start value (underlying " << std::to_string(i + 1) <<
"): s0=" << s0 <<
" fx0=" << fx0 <<
" => "
257 << fx0 * s0 <<
" on " << v0 <<
" in nth current period " << nth);
258 underlyingStartValue[i] = s0;
259 fxConversionFactor[i] = fx0;
266 DLOG(
"start value (underlying " << std::to_string(i + 1) <<
") is null, because eval date (" << today
267 <<
") is >= last date in payment schedule ("
268 <<
arguments_.paymentSchedule_.back() <<
") in nth current period " << nth);
269 underlyingStartValue[i] = Null<Real>();
270 fxConversionFactor[i] = Null<Real>();
271 startDate = Null<Date>();
279Real getFxIndexFixing(
const QuantLib::ext::shared_ptr<FxIndex>& fx,
const Currency& source,
const Date& d,
280 const bool enforceProjection) {
281 bool invert = fx->targetCurrency() == source;
283 if (enforceProjection) {
284 res = fx->forecastFixing(0.0);
286 Date adjustedDate = fx->fixingCalendar().adjust(d, Preceding);
287 res = fx->fixing(adjustedDate,
false);
289 return invert ? 1.0 / res : res;
294 const bool enforceProjection)
const {
296 if (source == target)
302 for (Size i = 0; i <
arguments_.fxIndexAsset_.size(); ++i) {
305 if (source ==
arguments_.fxIndexAsset_[i]->sourceCurrency() ||
306 source ==
arguments_.fxIndexAsset_[i]->targetCurrency()) {
307 result1 = getFxIndexFixing(
arguments_.fxIndexAsset_[i], source, date, enforceProjection);
312 if (
arguments_.fxIndexReturn_ !=
nullptr && (source ==
arguments_.fxIndexReturn_->sourceCurrency() ||
313 source ==
arguments_.fxIndexReturn_->targetCurrency())) {
314 result1 = getFxIndexFixing(
arguments_.fxIndexReturn_, source, date, enforceProjection);
315 }
else if (
arguments_.fxIndexAdditionalCashflows_ !=
nullptr &&
316 (source ==
arguments_.fxIndexAdditionalCashflows_->sourceCurrency() ||
317 source ==
arguments_.fxIndexAdditionalCashflows_->targetCurrency())) {
318 result1 = getFxIndexFixing(
arguments_.fxIndexAdditionalCashflows_, source, date, enforceProjection);
320 QL_FAIL(
"TRSWrapperAccrualEngine: could not convert " << source.code() <<
" to funding currency "
322 <<
", are all required FXTerms set up?");
330 for (Size i = 0; i <
arguments_.fxIndexAsset_.size(); ++i) {
333 if (target ==
arguments_.fxIndexAsset_[i]->sourceCurrency() ||
334 target ==
arguments_.fxIndexAsset_[i]->targetCurrency()) {
335 result2 = getFxIndexFixing(
arguments_.fxIndexAsset_[i], target, date, enforceProjection);
340 if (
arguments_.fxIndexReturn_ !=
nullptr && (target ==
arguments_.fxIndexReturn_->sourceCurrency() ||
341 target ==
arguments_.fxIndexReturn_->targetCurrency())) {
342 result2 = getFxIndexFixing(
arguments_.fxIndexReturn_, target, date, enforceProjection);
343 }
else if (
arguments_.fxIndexAdditionalCashflows_ !=
nullptr &&
344 (target ==
arguments_.fxIndexAdditionalCashflows_->sourceCurrency() ||
345 target ==
arguments_.fxIndexAdditionalCashflows_->targetCurrency())) {
346 result2 = getFxIndexFixing(
arguments_.fxIndexAdditionalCashflows_, target, date, enforceProjection);
348 QL_FAIL(
"TRSWrapperAccrualEngine: could not convert " << source.code() <<
" to funding currency "
350 <<
", are all required FXTerms set up?");
355 return result1 / result2;
359 Date today = Settings::instance().evaluationDate();
360 QL_REQUIRE(date <= today,
"TRSWrapperAccrualEngine: internal error, getUnderlyingFixing("
361 << date <<
") for future date requested (today=" << today <<
")");
362 if (enforceProjection) {
365 Date adjustedDate =
arguments_.underlyingIndex_[i]->fixingCalendar().adjust(date, Preceding);
367 auto tmp =
arguments_.underlyingIndex_[i]->fixing(adjustedDate);
369 }
catch (
const std::exception&) {
370 if (adjustedDate == today)
379 Date today = Settings::instance().evaluationDate();
381 DLOG(
"TRSWrapperAccrualEngine: today = " << today <<
", paysAsset = " << std::boolalpha <<
arguments_.paysAsset_
382 <<
", paysFunding = " << std::boolalpha <<
arguments_.paysFunding_);
384 Real assetMultiplier = (
arguments_.paysAsset_ ? -1.0 : 1.0);
385 Real fundingMultiplier = (
arguments_.paysFunding_ ? -1.0 : 1.0);
390 results_.additionalResults[
"returnLegInitialPriceCurrency"] =
arguments_.initialPriceCurrency_.code();
394 Real assetLegNpv = 0.0;
395 Size nthCurrentPeriod = 0;
396 std::vector<CashFlowResults> cfResults;
398 std::vector<Real> underlyingStartValue(
arguments_.underlying_.size(), 0.0);
399 std::vector<Real> fxConversionFactor(
arguments_.underlying_.size(), 1.0);
400 Date startDate = Null<Date>();
401 Date endDate = Null<Date>();
402 bool usingInitialPrice;
404 while (computeStartValue(underlyingStartValue, fxConversionFactor, startDate, endDate, usingInitialPrice,
409 for (Size i = 0; i <
arguments_.underlying_.size(); ++i) {
411 std::string resultSuffix =
arguments_.underlying_.size() > 1 ?
"_" + std::to_string(i + 1) :
"";
412 if (nthCurrentPeriod > 0)
413 resultSuffix +=
"_nth(" + std::to_string(nthCurrentPeriod) +
")";
415 results_.additionalResults[
"underlyingCurrency" + resultSuffix] =
arguments_.assetCurrency_[i].code();
417 if (underlyingStartValue[i] != Null<Real>()) {
419 if (endDate == Null<Date>()) {
420 s1 =
arguments_.underlying_[i]->instrument()->NPV();
421 fx1 = getFxConversionRate(today,
arguments_.assetCurrency_[i],
arguments_.returnCurrency_,
true);
423 s1 = getUnderlyingFixing(i, endDate,
false) *
arguments_.underlyingMultiplier_[i];
424 fx1 = getFxConversionRate(endDate,
arguments_.assetCurrency_[i],
arguments_.returnCurrency_,
false);
426 assetLegNpv += fx1 * s1 - underlyingStartValue[i] * fxConversionFactor[i];
427 DLOG(
"end value (underlying " << std::to_string(i + 1) <<
"): s1=" << s1 <<
" fx1=" << fx1 <<
" => "
428 << fx1 * s1 <<
" on "
429 << io::iso_date(endDate == Null<Date>() ? today : endDate));
432 results_.additionalResults[
"s0" + resultSuffix] = underlyingStartValue[i];
433 results_.additionalResults[
"fx0" + resultSuffix] = fxConversionFactor[i];
434 results_.additionalResults[
"s1" + resultSuffix] = s1;
435 results_.additionalResults[
"fx1" + resultSuffix] = fx1;
436 results_.additionalResults[
"underlyingMultiplier" + resultSuffix] =
arguments_.underlyingMultiplier_[i];
439 cfResults.emplace_back();
440 cfResults.back().amount = fx1 * s1;
441 if (
arguments_.underlying_.size() == 1 || !usingInitialPrice) {
442 cfResults.back().amount -= underlyingStartValue[i] * fxConversionFactor[i];
444 cfResults.back().amount *= assetMultiplier;
445 cfResults.back().payDate = today;
446 cfResults.back().currency =
arguments_.returnCurrency_.code();
447 cfResults.back().legNumber = 0;
448 cfResults.back().type =
"AccruedReturn" + resultSuffix;
449 cfResults.back().accrualStartDate = startDate;
450 cfResults.back().accrualEndDate = endDate == Null<Date>() ? today : endDate;
451 cfResults.back().fixingValue = s1 /
arguments_.underlyingMultiplier_[i];
452 cfResults.back().notional = underlyingStartValue[i] * fxConversionFactor[i];
455 if (
arguments_.underlying_.size() > 1 && usingInitialPrice && i == 0) {
456 cfResults.emplace_back();
457 cfResults.back().amount = -underlyingStartValue[i] * fxConversionFactor[i];
458 cfResults.back().payDate = today;
459 cfResults.back().currency =
arguments_.returnCurrency_.code();
460 cfResults.back().legNumber = 0;
461 cfResults.back().type =
"AccruedReturn" + resultSuffix;
462 cfResults.back().accrualStartDate = startDate;
463 cfResults.back().accrualEndDate = endDate == Null<Date>() ? today : endDate;
464 cfResults.back().notional = underlyingStartValue[i] * fxConversionFactor[i];
468 if (
arguments_.includeUnderlyingCashflowsInReturn_ && startDate != Null<Date>() && startDate < today) {
471 for (
auto const& l :
arguments_.underlying_[i]->legs()) {
472 for (
auto const& c : l) {
473 if (!c->hasOccurred(startDate) && c->hasOccurred(today)) {
474 Real tmp = c->amount() *
arguments_.underlyingMultiplier_[i];
477 cfResults.emplace_back();
478 cfResults.back().amount = assetMultiplier * (tmp * fx1);
479 cfResults.back().payDate = c->date();
480 cfResults.back().currency =
arguments_.returnCurrency_.code();
481 cfResults.back().legNumber = 1;
482 cfResults.back().type =
"UnderlyingCashFlow" + resultSuffix;
483 cfResults.back().notional = underlyingStartValue[i] * fxConversionFactor[i];
488 Real dividends = 0.0;
489 if (
auto e = QuantLib::ext::dynamic_pointer_cast<EquityIndex2>(
arguments_.underlyingIndex_[i])) {
491 e->dividendsBetweenDates(startDate + 1, today) *
arguments_.underlyingMultiplier_[i];
492 }
else if (
auto e = QuantLib::ext::dynamic_pointer_cast<CompositeIndex>(
arguments_.underlyingIndex_[i])) {
494 e->dividendsBetweenDates(startDate + 1, today) *
arguments_.underlyingMultiplier_[i];
499 cfResults.emplace_back();
500 cfResults.back().amount = assetMultiplier * (dividends * fx1);
501 cfResults.back().payDate = today;
502 cfResults.back().currency =
arguments_.returnCurrency_.code();
503 cfResults.back().legNumber = 2;
504 cfResults.back().type =
"UnderlyingDividends" + resultSuffix;
505 cfResults.back().notional = underlyingStartValue[i] * fxConversionFactor[i];
508 DLOG(
"add cashflows in return period (" << io::iso_date(startDate) <<
", " << io::iso_date(today)
509 <<
"]: amount in asset ccy = " << cf <<
", fx conversion "
510 << fx1 <<
" => " << cf * fx1);
512 results_.additionalResults[
"underlyingCashflows" + resultSuffix] = cf;
514 assetLegNpv += cf * fx1;
522 results_.additionalResults[
"assetLegNpv"] = assetMultiplier * assetLegNpv;
524 DLOG(
"asset leg npv = " << assetMultiplier * assetLegNpv <<
" " <<
arguments_.returnCurrency_.code());
528 Real fundingLegNpv = 0.0;
530 for (Size i = 0; i <
arguments_.fundingLegs_.size(); ++i) {
533 for (Size cpnNo = 0; cpnNo <
arguments_.fundingLegs_[i].size(); ++cpnNo) {
535 Real localFundingLegNpv = 0.0;
537 auto cpn = QuantLib::ext::dynamic_pointer_cast<QuantLib::Coupon>(
arguments_.fundingLegs_[i][cpnNo]);
538 if (cpn ==
nullptr || cpn->date() <= today || cpn->accrualStartDate() >= today)
543 Date fundingStartDate = cpn->accrualStartDate();
544 Real fundingCouponNotional = cpn->nominal();
545 Size currentIdx = std::distance(
arguments_.valuationSchedule_.begin(),
546 std::upper_bound(
arguments_.valuationSchedule_.begin(),
548 fundingStartDate +
arguments_.fundingResetGracePeriod_));
552 if (
arguments_.valuationSchedule_[currentIdx] > today) {
553 DLOG(
"fundingLegNpv = 0 for funding leg #" << (i + 1) <<
", because last relevant valuation date ("
555 <<
") is >= eval date (" << today <<
")");
559 localFundingLegNpv = cpn->accruedAmount(today);
560 Real fundingLegNotionalFactor = 0.0;
561 std::string resultSuffix =
arguments_.fundingLegs_.size() > 1 ?
"_" + std::to_string(i + 1) :
"";
564 results_.additionalResults[
"fundingCouponRate" + resultSuffix] = cpn->rate();
568 bool isOvernightCoupon = QuantLib::ext::dynamic_pointer_cast<QuantExt::OvernightIndexedCoupon>(cpn) !=
nullptr;
570 for (Size j = 0; j <
arguments_.underlying_.size(); ++j) {
572 std::string resultSuffix2 =
arguments_.underlying_.size() > 1 ?
"_" + std::to_string(j + 1) :
"";
575 resultSuffix2 +=
"_nth(" + std::to_string(nthCpn) +
")";
579 fundingLegNotionalFactor = 1.0;
583 Real localNotionalFactor = 0.0, localFxFactor = 1.0;
584 if (currentIdx == 0 &&
arguments_.initialPrice_ != Null<Real>()) {
586 localNotionalFactor =
589 localFxFactor = getFxConversionRate(
arguments_.valuationSchedule_[currentIdx],
594 localNotionalFactor =
arguments_.underlyingMultiplier_[j] *
595 getUnderlyingFixing(j,
arguments_.valuationSchedule_[currentIdx],
false);
601 fundingLegNotionalFactor += localNotionalFactor * localFxFactor;
603 results_.additionalResults[
"fundingLegNotional" + resultSuffix + resultSuffix2] =
605 results_.additionalResults[
"fundingLegFxRate" + resultSuffix + resultSuffix2] = localFxFactor;
608 (QuantLib::ext::dynamic_pointer_cast<FixedRateCoupon>(cpn) ||
609 QuantLib::ext::dynamic_pointer_cast<IborCoupon>(cpn))) {
612 cpn->dayCounter().yearFraction(cpn->accrualStartDate(), std::min(cpn->accrualEndDate(), today));
613 for (QuantLib::Date d = cpn->accrualStartDate(); d < std::min(cpn->accrualEndDate(), today); ++d) {
614 Real dcfLocal = cpn->dayCounter().yearFraction(d, d + 1);
616 Real localNotionalFactor = getUnderlyingFixing(j,
fixingDate,
false) *
617 arguments_.underlyingMultiplier_[j] * dcfLocal / dcfTotal;
620 fundingLegNotionalFactor += localNotionalFactor * localFxFactor;
622 results_.additionalResults[
"fundingLegNotional" + resultSuffix + resultSuffix2 +
"_" +
624 results_.additionalResults[
"fundingLegFxRate" + resultSuffix + resultSuffix2 +
"_" +
629 auto overnightCpn = QuantLib::ext::dynamic_pointer_cast<QuantExt::OvernightIndexedCoupon>(cpn);
630 auto valueDates = overnightCpn->valueDates();
631 auto fixingValues = overnightCpn->indexFixings();
632 auto dts = overnightCpn->dt();
633 double accruedInterest = 0;
634 double accruedSpreadInterest = 0;
635 double gearing = overnightCpn->gearing();
636 double spread = overnightCpn->spread();
637 for (
size_t i = 0; i < valueDates.size() - 1; ++i) {
638 const Date& valueDate = valueDates[i];
640 double irFixing = fixingValues[i];
641 if (overnightCpn->includeSpread())
642 irFixing += overnightCpn->spread();
643 if (valueDate < today) {
645 arguments_.underlyingIndex_[j]->fixingCalendar().adjust(valueDate, Preceding);
650 results_.additionalResults[
"fundingLegNotional" + resultSuffix + resultSuffix2 +
"_" +
652 results_.additionalResults[
"fundingLegFxRate" + resultSuffix + resultSuffix2 +
"_" +
654 results_.additionalResults[
"fundingLegOISRate" + resultSuffix + resultSuffix2 +
"_" +
656 results_.additionalResults[
"fundingLegDCF" + resultSuffix + resultSuffix2 +
"_" +
658 localNotional *= localFxFactor;
659 accruedInterest = localNotional * irFixing * dt + accruedInterest * (1 + irFixing * dt);
660 if (!overnightCpn->includeSpread()) {
661 accruedSpreadInterest += localNotional * spread * dt;
663 results_.additionalResults[
"fundingLegAccruedInterest" + resultSuffix + resultSuffix2 +
665 accruedInterest + accruedSpreadInterest;
668 fundingLegNotionalFactor = (gearing * accruedInterest + accruedSpreadInterest) / localFundingLegNpv;
670 QuantLib::ext::dynamic_pointer_cast<AverageONIndexedCoupon>(cpn) !=
nullptr) {
671 auto overnightCpn = QuantLib::ext::dynamic_pointer_cast<QuantExt::AverageONIndexedCoupon>(cpn);
672 auto valueDates = overnightCpn->valueDates();
673 auto fixingValues = overnightCpn->indexFixings();
674 auto dts = overnightCpn->dt();
675 double accruedInterest = 0;
676 double spread = overnightCpn->spread();
677 double gearing = overnightCpn->gearing();
678 for (
size_t i = 0; i < valueDates.size() - 1; ++i) {
679 const Date& valueDate = valueDates[i];
681 double irFixing = fixingValues[i];
683 if (valueDate < today) {
685 arguments_.underlyingIndex_[j]->fixingCalendar().adjust(valueDate, Preceding);
690 results_.additionalResults[
"fundingLegNotional" + resultSuffix + resultSuffix2 +
"_" +
692 results_.additionalResults[
"fundingLegFxRate" + resultSuffix + resultSuffix2 +
"_" +
694 results_.additionalResults[
"fundingLegOISRate" + resultSuffix + resultSuffix2 +
"_" +
696 results_.additionalResults[
"fundingLegDCF" + resultSuffix + resultSuffix2 +
"_" +
698 localNotional *= localFxFactor;
699 accruedInterest += localNotional * (gearing * irFixing + spread) * dt;
700 results_.additionalResults[
"fundingLegAccruedInterest" + resultSuffix + resultSuffix2 +
704 fundingLegNotionalFactor = accruedInterest / localFundingLegNpv;
706 QL_FAIL(
"daily reset funding legs support fixed rate, ibor and overnight indexed coupons only");
708 QL_FAIL(
"internal error: unknown notional type, contact dev");
712 DLOG(
"fundingLegNpv for funding leg #"
713 << (i + 1) <<
" is " << fundingMultiplier * localFundingLegNpv <<
" * " << fundingLegNotionalFactor
714 <<
" = " << fundingMultiplier * localFundingLegNpv * fundingLegNotionalFactor <<
" "
715 <<
arguments_.fundingCurrency_ <<
" (notional type of leg is '" <<
arguments_.fundingNotionalTypes_[i]
718 localFundingLegNpv *= fundingLegNotionalFactor;
720 results_.additionalResults[
"fundingLegNpv" + resultSuffix] = fundingMultiplier * localFundingLegNpv;
723 cfResults.emplace_back();
724 cfResults.back().amount = fundingMultiplier * localFundingLegNpv;
725 cfResults.back().payDate = today;
726 cfResults.back().currency =
arguments_.fundingCurrency_.code();
727 cfResults.back().legNumber = 3 + i;
728 cfResults.back().type =
"AccruedFunding" + (nthCpn > 0 ?
"_nth(" + std::to_string(nthCpn) +
")" :
"");
729 cfResults.back().accrualStartDate = std::min(fundingStartDate, today);
730 cfResults.back().accrualEndDate = today;
732 ? fundingCouponNotional
733 : fundingLegNotionalFactor;
735 results_.additionalResults[
"fundingLegNotional" + resultSuffix] = cfResults.back().notional;
737 fundingLegNpv += localFundingLegNpv;
742 DLOG(
"total funding leg(s) npv is " << fundingMultiplier * fundingLegNpv);
744 results_.additionalResults[
"fundingLegNpv"] = fundingMultiplier * fundingLegNpv;
745 results_.additionalResults[
"fundingLegNpvCurrency"] =
arguments_.fundingCurrency_.code();
749 Real additionalCashflowLegNpv = 0.0;
750 for (
auto const& cf :
arguments_.additionalCashflowLeg_) {
751 if (cf->date() > today) {
752 Real tmp = cf->amount() * (
arguments_.additionalCashflowLegPayer_ ? -1.0 : 1.0);
753 additionalCashflowLegNpv += tmp;
755 cfResults.emplace_back();
756 cfResults.back().amount = tmp;
757 cfResults.back().payDate = cf->date();
758 cfResults.back().currency =
arguments_.additionalCashflowCurrency_.code();
759 cfResults.back().legNumber = 0;
760 cfResults.back().type =
"AdditionalCashFlow";
763 DLOG(
"additionalCashflowLegNpv = " << additionalCashflowLegNpv <<
" " <<
arguments_.additionalCashflowCurrency_);
764 results_.additionalResults[
"additionalCashflowLegNpv"] = additionalCashflowLegNpv;
765 results_.additionalResults[
"additionalCashflowLegNpvCurrency"] =
arguments_.additionalCashflowCurrency_.code();
769 Real fxAssetToPnlCcy = getFxConversionRate(today,
arguments_.returnCurrency_,
arguments_.fundingCurrency_,
true);
770 Real fxAdditionalCashflowLegToPnlCcy =
771 getFxConversionRate(today,
arguments_.additionalCashflowCurrency_,
arguments_.fundingCurrency_,
true);
773 results_.additionalResults[
"fxConversionAssetLegNpvToPnlCurrency"] = fxAssetToPnlCcy;
774 results_.additionalResults[
"fxConversionAdditionalCashflowLegNpvToPnlCurrency"] = fxAdditionalCashflowLegToPnlCcy;
777 results_.value = assetMultiplier * assetLegNpv * fxAssetToPnlCcy + fundingMultiplier * fundingLegNpv +
778 additionalCashflowLegNpv * fxAdditionalCashflowLegToPnlCcy;
781 for (Size j = 0; j <
arguments_.underlying_.size(); ++j) {
784 if (underlyingStartValue[j] == Null<Real>()) {
786 arguments_.underlyingMultiplier_[j] * getUnderlyingFixing(j, today,
true) *
793 for (Size j = 0; j <
arguments_.underlying_.size(); ++j) {
795 std::string resultSuffix =
arguments_.underlying_.size() == 1 ?
"" :
"_" + std::to_string(j);
796 Real startFixing = Null<Real>(), todaysFixing = Null<Real>();
798 startFixing = getUnderlyingFixing(j, startDate,
false);
802 todaysFixing = getUnderlyingFixing(j, today,
true);
805 results_.additionalResults[
"startFixing" + resultSuffix] = startFixing;
806 results_.additionalResults[
"todaysFixing" + resultSuffix] = todaysFixing;
809 for (
auto const& d :
arguments_.addFxIndices_) {
810 Real startFixing = Null<Real>(), todaysFixing = Null<Real>();
812 startFixing = d.second->fixing(d.second->fixingCalendar().adjust(startDate, Preceding));
816 todaysFixing = d.second->fixing(d.second->fixingCalendar().adjust(today, Preceding),
true);
819 results_.additionalResults[
"startFxFixing(" + d.first +
")"] = startFixing;
820 results_.additionalResults[
"todaysFxFixing(" + d.first +
")"] = todaysFixing;
824 results_.additionalResults[
"cashFlowResults"] = cfResults;
828 for (Size i = 0; i <
arguments_.underlying_.size(); ++i) {
829 for (
auto const& [key,
value] :
arguments_.underlying_[i]->instrument()->additionalResults()) {
830 results_.additionalResults[
"und_ar_" + std::to_string(i + 1) +
"_" + key] =
value;
834 DLOG(
"TRSWrapperAccrualEngine: all done, total npv = " <<
results_.value <<
" "
const Instrument::results * results_
std::vector< TRS::FundingData::NotionalType > fundingNotionalTypes_
std::vector< QuantLib::Date > paymentSchedule_
std::vector< QuantLib::ext::shared_ptr< QuantLib::Index > > underlyingIndex_
std::map< std::string, QuantLib::ext::shared_ptr< QuantExt::FxIndex > > addFxIndices_
std::vector< QuantLib::Date > valuationSchedule_
QuantLib::Real initialPrice_
QuantLib::Currency returnCurrency_
std::vector< QuantLib::Real > underlyingMultiplier_
std::vector< QuantLib::Leg > fundingLegs_
bool additionalCashflowLegPayer_
std::vector< QuantLib::ext::shared_ptr< ore::data::Trade > > underlying_
QuantLib::Currency fundingCurrency_
std::vector< QuantLib::Currency > assetCurrency_
QuantLib::Size fundingResetGracePeriod_
QuantLib::Currency initialPriceCurrency_
QuantLib::ext::shared_ptr< QuantExt::FxIndex > fxIndexReturn_
QuantLib::ext::shared_ptr< QuantExt::FxIndex > fxIndexAdditionalCashflows_
QuantLib::Leg additionalCashflowLeg_
bool includeUnderlyingCashflowsInReturn_
QuantLib::Currency additionalCashflowCurrency_
void validate() const override
std::vector< QuantLib::ext::shared_ptr< QuantExt::FxIndex > > fxIndexAsset_
QuantLib::Real getFxConversionRate(const QuantLib::Date &date, const QuantLib::Currency &source, const QuantLib::Currency &target, const bool enforceProjection) const
void calculate() const override
bool computeStartValue(std::vector< QuantLib::Real > &underlyingStartValue, std::vector< QuantLib::Real > &fxConversionFactor, QuantLib::Date &startDate, QuantLib::Date &endDate, bool &usingInitialPrice, const Size nth) const
Real getUnderlyingFixing(const Size i, const QuantLib::Date &date, const bool enforceProjection) const
const QuantLib::Currency additionalCashflowCurrency_
void fetchResults(const QuantLib::PricingEngine::results *) const override
std::vector< TRS::FundingData::NotionalType > fundingNotionalTypes_
std::vector< QuantLib::Date > paymentSchedule_
std::vector< QuantLib::ext::shared_ptr< QuantLib::Index > > underlyingIndex_
const QuantLib::Currency fundingCurrency_
const QuantLib::Currency returnCurrency_
std::map< std::string, QuantLib::ext::shared_ptr< QuantExt::FxIndex > > addFxIndices_
std::vector< QuantLib::Date > valuationSchedule_
bool isExpired() const override
const QuantLib::Currency initialPriceCurrency_
QuantLib::Real initialPrice_
std::vector< QuantLib::Real > underlyingMultiplier_
std::vector< QuantLib::Leg > fundingLegs_
bool additionalCashflowLegPayer_
TRSWrapper(const std::vector< QuantLib::ext::shared_ptr< ore::data::Trade > > &underlying, const std::vector< QuantLib::ext::shared_ptr< QuantLib::Index > > &underlyingIndex, const std::vector< QuantLib::Real > underlyingMultiplier, const bool includeUnderlyingCashflowsInReturn, const QuantLib::Real initialPrice, const QuantLib::Currency &initialPriceCurrency, const std::vector< QuantLib::Currency > &assetCurrency, const QuantLib::Currency &returnCurrency, const std::vector< QuantLib::Date > &valuationSchedule, const std::vector< QuantLib::Date > &paymentSchedule, const std::vector< QuantLib::Leg > &fundingLegs, const std::vector< TRS::FundingData::NotionalType > &fundingNotionalTypes, const QuantLib::Currency &fundingCurrency, const QuantLib::Size fundingResetGracePeriod, const bool paysAsset, const bool paysFunding, const QuantLib::Leg &additionalCashflowLeg, const bool additionalCashflowLegPayer, const QuantLib::Currency &additionalCashflowCurrency, const std::vector< QuantLib::ext::shared_ptr< QuantExt::FxIndex > > &fxIndexAsset, const QuantLib::ext::shared_ptr< QuantExt::FxIndex > &fxIndexReturn, const QuantLib::ext::shared_ptr< QuantExt::FxIndex > &fxIndexAdditionalCashflows, const std::map< std::string, QuantLib::ext::shared_ptr< QuantExt::FxIndex > > &addFxindices)
std::vector< QuantLib::ext::shared_ptr< ore::data::Trade > > underlying_
const QuantLib::Size fundingResetGracePeriod_
QuantLib::ext::shared_ptr< QuantExt::FxIndex > fxIndexReturn_
QuantLib::ext::shared_ptr< QuantExt::FxIndex > fxIndexAdditionalCashflows_
QuantLib::Leg additionalCashflowLeg_
bool includeUnderlyingCashflowsInReturn_
void setupArguments(QuantLib::PricingEngine::arguments *) const override
std::vector< QuantLib::ext::shared_ptr< QuantExt::FxIndex > > fxIndexAsset_
const std::vector< QuantLib::Currency > assetCurrency_
SafeStack< ValueType > value
#define DLOG(text)
Logging Macro (Level = Debug)
QuantLib::Date fixingDate(const QuantLib::Date &d, const QuantLib::Period obsLag, const QuantLib::Frequency freq, bool interpolated)
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
Real currentNotional(const Leg &leg)
std::string to_string(const LocationInfo &l)
Serializable Credit Default Swap.
Swap::arguments * arguments_
string conversion utilities
generic wrapper for trs (bond, convertible bond, equity, ...)