43#include <ql/cashflows/averagebmacoupon.hpp>
44#include <ql/cashflows/indexedcashflow.hpp>
45#include <ql/cashflows/inflationcoupon.hpp>
46#include <ql/errors.hpp>
47#include <ql/experimental/coupons/strippedcapflooredcoupon.hpp>
49#include <boost/accumulators/accumulators.hpp>
50#include <boost/accumulators/framework/accumulator_set.hpp>
51#include <boost/accumulators/statistics.hpp>
52#include <boost/accumulators/statistics/count.hpp>
53#include <boost/accumulators/statistics/covariance.hpp>
54#include <boost/accumulators/statistics/kurtosis.hpp>
55#include <boost/accumulators/statistics/max.hpp>
56#include <boost/accumulators/statistics/mean.hpp>
57#include <boost/accumulators/statistics/min.hpp>
58#include <boost/accumulators/statistics/skewness.hpp>
59#include <boost/accumulators/statistics/stats.hpp>
60#include <boost/accumulators/statistics/tail_quantile.hpp>
61#include <boost/accumulators/statistics/variates/covariate.hpp>
62#include <boost/lexical_cast.hpp>
63#include <boost/range/adaptor/indexed.hpp>
81 QuantLib::ext::shared_ptr<Market> market,
const std::string& configuration,
82 QuantLib::ext::shared_ptr<Portfolio> portfolio) {
83 LOG(
"portfolio valuation");
84 DayCounter dc = ActualActual(ActualActual::ISDA);
85 Date today = Settings::instance().evaluationDate();
99 for (
auto & [tradeId, trade] : portfolio->trades()) {
101 string npvCcy = trade->npvCurrency();
102 Real fx = 1.0, fxNotional = 1.0;
103 if (npvCcy != baseCurrency)
104 fx = market->fxRate(npvCcy + baseCurrency, configuration)->value();
105 if (trade->notionalCurrency() !=
"" && trade->notionalCurrency() != baseCurrency)
106 fxNotional = market->fxRate(trade->notionalCurrency() + baseCurrency, configuration)->value();
107 Real npv = trade->instrument()->NPV();
108 QL_REQUIRE(std::isfinite(npv),
"npv is not finite (" << npv <<
")");
112 .
add(trade->tradeType())
119 .
add(trade->notional())
120 .
add(trade->notionalCurrency() ==
"" ?
nullString_ : trade->notionalCurrency())
121 .
add(trade->notional() == Null<Real>() || trade->notionalCurrency() ==
""
123 : trade->notional() * fxNotional)
124 .
add(trade->envelope().nettingSetId())
125 .
add(trade->envelope().counterparty());
126 }
catch (std::exception& e) {
131 .
add(trade->tradeType())
146 LOG(
"NPV file written");
150 QuantLib::ext::shared_ptr<ore::data::Portfolio> portfolio,
151 QuantLib::ext::shared_ptr<ore::data::Market> market,
const std::string& configuration,
152 const bool includePastCashflows) {
154 Date
asof = Settings::instance().evaluationDate();
156 LOG(
"Writing cashflow report for " <<
asof);
167 .
addColumn(
"AccrualStartDate", Date(), 4)
173 .
addColumn(
"DiscountFactor",
double(), 10)
175 .
addColumn(
"FXRate(Local-Base)",
double(), 10)
176 .
addColumn(
"PresentValue(Base)",
double(), 10)
180 .
addColumn(
"FloorVolatility",
double(), 6)
182 .
addColumn(
"EffectiveFloorVolatility",
double(), 6)
183 .
addColumn(
"EffectiveCapVolatility",
double(), 6);
185 std::map<std::string, QuantLib::ext::shared_ptr<Trade>> trades = portfolio->trades();
187 for (
auto [tradeId, trade]: trades) {
193 if (!trade->hasCashflows()) {
194 WLOG(
"cashflow for " << trade->tradeType() <<
" " << trade->id() <<
" skipped");
200 auto addResults = trade->instrument()->additionalResults();
202 auto cashFlowResults = addResults.find(
"cashFlowResults");
205 auto lower = addResults.lower_bound(
"cashFlowResults");
206 auto upper = addResults.lower_bound(
"cashFlowResults_a");
208 std::map<Size, Size> cashflowNumber;
210 const Real multiplier = trade->instrument()->multiplier() * trade->instrument()->multiplier2();
212 if (lower != addResults.end()) {
214 for (
auto cashFlowResults = lower; cashFlowResults != upper; ++cashFlowResults) {
218 QL_REQUIRE(cashFlowResults->second.type() ==
typeid(std::vector<CashFlowResults>),
219 "internal error: cashflowResults type does not match CashFlowResults: '"
220 << cashFlowResults->second.type().name() <<
"'");
221 std::vector<CashFlowResults> cfResults =
222 boost::any_cast<std::vector<CashFlowResults>>(cashFlowResults->second);
224 for (
auto const& cf : cfResults) {
226 if (!cf.currency.empty()) {
228 }
else if (trade->legCurrencies().size() > cf.legNumber) {
229 ccy = trade->legCurrencies()[cf.legNumber];
231 ccy = trade->npvCurrency();
234 Real effectiveAmount = Null<Real>();
235 Real discountFactor = Null<Real>();
236 Real presentValue = Null<Real>();
237 Real presentValueBase = Null<Real>();
238 Real fxRateLocalBase = Null<Real>();
239 Real floorStrike = Null<Real>();
240 Real capStrike = Null<Real>();
241 Real floorVolatility = Null<Real>();
242 Real capVolatility = Null<Real>();
243 Real effectiveFloorVolatility = Null<Real>();
244 Real effectiveCapVolatility = Null<Real>();
246 if (cf.amount != Null<Real>())
247 effectiveAmount = cf.amount * multiplier;
248 if (cf.discountFactor != Null<Real>())
249 discountFactor = cf.discountFactor;
250 else if (!cf.currency.empty() && cf.payDate != Null<Date>() && market) {
251 discountFactor = cf.payDate <
asof
253 : market->discountCurve(cf.currency, configuration)->discount(cf.payDate);
255 if (cf.presentValue != Null<Real>()) {
256 presentValue = cf.presentValue * multiplier;
257 }
else if (effectiveAmount != Null<Real>() && discountFactor != Null<Real>()) {
258 presentValue = effectiveAmount * discountFactor;
260 if (cf.fxRateLocalBase != Null<Real>()) {
261 fxRateLocalBase = cf.fxRateLocalBase;
264 fxRateLocalBase = market->fxRate(ccy + baseCurrency)->value();
268 if (cf.presentValueBase != Null<Real>()) {
269 presentValueBase = cf.presentValueBase;
270 }
else if (presentValue != Null<Real>() && fxRateLocalBase != Null<Real>()) {
271 presentValueBase = presentValue * fxRateLocalBase;
273 if (cf.floorStrike != Null<Real>())
274 floorStrike = cf.floorStrike;
275 if (cf.capStrike != Null<Real>())
276 capStrike = cf.capStrike;
277 if (cf.floorVolatility != Null<Real>())
278 floorVolatility = cf.floorVolatility;
279 if (cf.capVolatility != Null<Real>())
280 capVolatility = cf.capVolatility;
281 if (cf.effectiveFloorVolatility != Null<Real>())
282 floorVolatility = cf.effectiveFloorVolatility;
283 if (cf.effectiveCapVolatility != Null<Real>())
284 capVolatility = cf.effectiveCapVolatility;
293 .
add(trade->tradeType())
294 .
add(++cashflowNumber[cf.legNumber])
298 .
add(effectiveAmount)
301 .
add(cf.accrualPeriod)
302 .
add(cf.accrualStartDate)
303 .
add(cf.accrualEndDate)
304 .
add(cf.accruedAmount * (cf.accruedAmount == Null<Real>() ? 1.0 : multiplier))
307 .
add(cf.notional * (cf.notional == Null<Real>() ? 1.0 : multiplier))
310 .
add(fxRateLocalBase)
311 .
add(presentValueBase)
315 .
add(floorVolatility)
317 .
add(effectiveFloorVolatility)
318 .
add(effectiveCapVolatility);
322 if (trade->legs().size() >= 1 && cashFlowResults == addResults.end()) {
325 auto maxLegNoIter = std::max_element(cashflowNumber.begin(), cashflowNumber.end());
326 Size addResultsLegs = 0;
327 if (maxLegNoIter != cashflowNumber.end())
328 addResultsLegs = maxLegNoIter->first + 1;
330 const vector<Leg>& legs = trade->legs();
331 for (
size_t i = 0; i < legs.size(); i++) {
332 const QuantLib::Leg& leg = legs[i];
333 bool payer = trade->legPayers()[i];
334 string ccy = trade->legCurrencies()[i];
335 Handle<YieldTermStructure> discountCurve;
337 discountCurve = market->discountCurve(ccy, configuration);
338 for (
size_t j = 0; j < leg.size(); j++) {
339 QuantLib::ext::shared_ptr<QuantLib::CashFlow> ptrFlow = leg[j];
340 Date payDate = ptrFlow->date();
341 if (!ptrFlow->hasOccurred(
asof) || includePastCashflows) {
342 Real amount = ptrFlow->amount();
343 string flowType =
"";
346 std::string ccy = trade->legCurrencies()[i];
348 QuantLib::ext::shared_ptr<QuantLib::Coupon> ptrCoupon =
349 QuantLib::ext::dynamic_pointer_cast<QuantLib::Coupon>(ptrFlow);
350 QuantLib::ext::shared_ptr<QuantExt::CommodityCashFlow> ptrCommCf =
351 QuantLib::ext::dynamic_pointer_cast<QuantExt::CommodityCashFlow>(ptrFlow);
356 Date accrualStartDate, accrualEndDate;
360 coupon = ptrCoupon->rate();
361 accrual = ptrCoupon->accrualPeriod();
362 notional = ptrCoupon->nominal();
363 accrualStartDate = ptrCoupon->accrualStartDate();
364 accrualEndDate = ptrCoupon->accrualEndDate();
365 accruedAmount = ptrCoupon->accruedAmount(
asof);
367 accruedAmount *= -1.0;
368 flowType =
"Interest";
369 }
else if (ptrCommCf) {
370 coupon = Null<Real>();
371 accrual = Null<Real>();
373 ptrCommCf->periodQuantity();
374 accrualStartDate = accrualEndDate = Null<Date>();
375 accruedAmount = Null<Real>();
376 flowType =
"Notional (units)";
378 coupon = Null<Real>();
379 accrual = Null<Real>();
380 notional = Null<Real>();
381 accrualStartDate = accrualEndDate = Null<Date>();
382 accruedAmount = Null<Real>();
383 flowType =
"Notional";
386 if (
auto cpn = QuantLib::ext::dynamic_pointer_cast<QuantLib::Coupon>(ptrFlow)) {
390 QuantLib::ext::shared_ptr<QuantLib::FloatingRateCoupon> ptrFloat =
391 QuantLib::ext::dynamic_pointer_cast<QuantLib::FloatingRateCoupon>(ptrFlow);
392 QuantLib::ext::shared_ptr<QuantLib::InflationCoupon> ptrInfl =
393 QuantLib::ext::dynamic_pointer_cast<QuantLib::InflationCoupon>(ptrFlow);
394 QuantLib::ext::shared_ptr<QuantLib::IndexedCashFlow> ptrIndCf =
395 QuantLib::ext::dynamic_pointer_cast<QuantLib::IndexedCashFlow>(ptrFlow);
396 QuantLib::ext::shared_ptr<QuantExt::FXLinkedCashFlow> ptrFxlCf =
397 QuantLib::ext::dynamic_pointer_cast<QuantExt::FXLinkedCashFlow>(ptrFlow);
398 QuantLib::ext::shared_ptr<QuantExt::EquityCoupon> ptrEqCp =
399 QuantLib::ext::dynamic_pointer_cast<QuantExt::EquityCoupon>(ptrFlow);
402 Real fixingValue = Null<Real>();
406 flowType =
"InterestProjected";
409 fixingValue = ptrFloat->index()->fixing(
fixingDate);
413 if (
auto c = QuantLib::ext::dynamic_pointer_cast<QuantLib::IborCoupon>(ptrFloat)) {
414 fixingValue = (c->rate() - c->spread()) / c->gearing();
417 if (
auto c = QuantLib::ext::dynamic_pointer_cast<QuantLib::CappedFlooredIborCoupon>(
419 fixingValue = (c->underlying()->rate() - c->underlying()->spread()) /
420 c->underlying()->gearing();
424 QuantLib::ext::dynamic_pointer_cast<QuantLib::StrippedCappedFlooredCoupon>(
426 if (
auto c = QuantLib::ext::dynamic_pointer_cast<QuantLib::CappedFlooredIborCoupon>(
428 fixingValue = (c->underlying()->rate() - c->underlying()->spread()) /
429 c->underlying()->gearing();
436 if (
auto on = QuantLib::ext::dynamic_pointer_cast<QuantExt::AverageONIndexedCoupon>(
438 fixingValue = (on->rate() - on->spread()) / on->gearing();
440 QuantLib::ext::dynamic_pointer_cast<QuantExt::OvernightIndexedCoupon>(
442 fixingValue = (on->rate() - on->effectiveSpread()) / on->gearing();
443 }
else if (
auto c = QuantLib::ext::dynamic_pointer_cast<QuantLib::AverageBMACoupon>(
445 fixingValue = (c->rate() - c->spread()) / c->gearing();
446 }
else if (
auto c = QuantLib::ext::dynamic_pointer_cast<
448 fixingValue = (c->underlying()->rate() - c->underlying()->spread()) /
449 c->underlying()->gearing();
450 }
else if (
auto c = QuantLib::ext::dynamic_pointer_cast<
452 fixingValue = (c->underlying()->rate() - c->underlying()->effectiveSpread()) /
453 c->underlying()->gearing();
454 }
else if (
auto c = QuantLib::ext::dynamic_pointer_cast<
456 fixingValue = (c->underlying()->rate() - c->underlying()->spread()) /
457 c->underlying()->gearing();
458 }
else if (
auto sp = QuantLib::ext::dynamic_pointer_cast<QuantExt::SubPeriodsCoupon1>(
460 fixingValue = (sp->rate() - sp->spread()) / sp->gearing();
462 }
else if (ptrInfl) {
464 fixingValue = ptrInfl->indexFixing();
465 flowType =
"Inflation";
466 }
else if (ptrIndCf) {
468 fixingValue = ptrIndCf->indexFixing();
470 }
else if (ptrFxlCf) {
472 fixingValue = ptrFxlCf->fxRate();
473 }
else if (ptrEqCp) {
475 fixingValue = ptrEqCp->equityCurve()->fixing(
fixingDate);
476 }
else if (ptrCommCf) {
478 fixingValue = ptrCommCf->fixing();
481 fixingValue = Null<Real>();
484 Real effectiveAmount = Null<Real>();
485 Real discountFactor = Null<Real>();
486 Real presentValue = Null<Real>();
487 Real presentValueBase = Null<Real>();
488 Real fxRateLocalBase = Null<Real>();
489 Real floorStrike = Null<Real>();
490 Real capStrike = Null<Real>();
491 Real floorVolatility = Null<Real>();
492 Real capVolatility = Null<Real>();
493 Real effectiveFloorVolatility = Null<Real>();
494 Real effectiveCapVolatility = Null<Real>();
496 if (amount != Null<Real>())
497 effectiveAmount = amount * multiplier;
500 discountFactor = ptrFlow->hasOccurred(
asof) ? 0.0 : discountCurve->discount(payDate);
501 if (effectiveAmount != Null<Real>())
502 presentValue = discountFactor * effectiveAmount;
504 fxRateLocalBase = market->fxRate(ccy + baseCurrency)->value();
505 presentValueBase = presentValue * fxRateLocalBase;
513 QuantLib::ext::shared_ptr<CashFlow> c = ptrFlow;
515 QuantLib::ext::dynamic_pointer_cast<StrippedCappedFlooredCoupon>(ptrFlow)) {
516 c = tmp->underlying();
519 std::string qlIndexName;
520 bool usesCapVol =
false, usesSwaptionVol =
false;
521 Period swaptionTenor;
522 if (
auto tmp = QuantLib::ext::dynamic_pointer_cast<CappedFlooredCoupon>(c)) {
523 floorStrike = tmp->effectiveFloor();
524 capStrike = tmp->effectiveCap();
525 volFixingDate = tmp->fixingDate();
526 qlIndexName = tmp->index()->name();
527 if (
auto cms = QuantLib::ext::dynamic_pointer_cast<CmsCoupon>(tmp->underlying())) {
528 swaptionTenor = cms->swapIndex()->tenor();
529 qlIndexName = cms->swapIndex()->iborIndex()->name();
530 usesSwaptionVol =
true;
531 }
else if(
auto cms = boost::dynamic_pointer_cast<DurationAdjustedCmsCoupon>(tmp->underlying())) {
532 swaptionTenor = cms->swapIndex()->tenor();
533 qlIndexName = cms->swapIndex()->iborIndex()->name();
534 usesSwaptionVol =
true;
535 }
else if (
auto ibor = QuantLib::ext::dynamic_pointer_cast<IborCoupon>(tmp->underlying())) {
536 qlIndexName = ibor->index()->name();
539 }
else if (
auto tmp =
540 QuantLib::ext::dynamic_pointer_cast<CappedFlooredOvernightIndexedCoupon>(
542 floorStrike = tmp->effectiveFloor();
543 capStrike = tmp->effectiveCap();
544 volFixingDate = tmp->underlying()->fixingDates().front();
545 qlIndexName = tmp->index()->name();
547 if (floorStrike != Null<Real>())
548 effectiveFloorVolatility = tmp->effectiveFloorletVolatility();
549 if (capStrike != Null<Real>())
550 effectiveCapVolatility = tmp->effectiveCapletVolatility();
551 }
else if (
auto tmp =
552 QuantLib::ext::dynamic_pointer_cast<CappedFlooredAverageONIndexedCoupon>(
554 floorStrike = tmp->effectiveFloor();
555 capStrike = tmp->effectiveCap();
556 volFixingDate = tmp->underlying()->fixingDates().front();
557 qlIndexName = tmp->index()->name();
559 if (floorStrike != Null<Real>())
560 effectiveFloorVolatility = tmp->effectiveFloorletVolatility();
561 if (capStrike != Null<Real>())
562 effectiveCapVolatility = tmp->effectiveCapletVolatility();
563 }
else if (
auto tmp =
564 QuantLib::ext::dynamic_pointer_cast<CappedFlooredAverageBMACoupon>(c)) {
565 floorStrike = tmp->effectiveFloor();
566 capStrike = tmp->effectiveCap();
567 volFixingDate = tmp->underlying()->fixingDates().front();
568 qlIndexName = tmp->index()->name();
570 if (floorStrike != Null<Real>())
571 effectiveFloorVolatility = tmp->effectiveFloorletVolatility();
572 if (capStrike != Null<Real>())
573 effectiveCapVolatility = tmp->effectiveCapletVolatility();
578 if (volFixingDate != Date() &&
fixingDate > market->asofDate()) {
579 volFixingDate = std::max(volFixingDate, market->asofDate() + 1);
580 if (floorStrike != Null<Real>()) {
581 if (usesSwaptionVol) {
584 ->swaptionVol(IndexNameTranslator::instance().oreName(qlIndexName),
586 ->volatility(volFixingDate, swaptionTenor, floorStrike);
587 }
else if (usesCapVol && floorVolatility == Null<Real>()) {
590 ->capFloorVol(IndexNameTranslator::instance().oreName(qlIndexName),
592 ->volatility(volFixingDate, floorStrike);
595 if (capStrike != Null<Real>()) {
596 if (usesSwaptionVol) {
599 ->swaptionVol(IndexNameTranslator::instance().oreName(qlIndexName),
601 ->volatility(volFixingDate, swaptionTenor, capStrike);
602 }
else if (usesCapVol && capVolatility == Null<Real>()) {
605 ->capFloorVol(IndexNameTranslator::instance().oreName(qlIndexName),
607 ->volatility(volFixingDate, capStrike);
615 .
add(trade->tradeType())
617 .
add(i + addResultsLegs)
620 .
add(effectiveAmount)
624 .
add(accrualStartDate)
626 .
add(accruedAmount * (accruedAmount == Null<Real>() ? 1.0 : multiplier))
629 .
add(notional * (notional == Null<Real>() ? 1.0 : multiplier))
632 .
add(fxRateLocalBase)
633 .
add(presentValueBase)
637 .
add(floorVolatility)
639 .
add(effectiveFloorVolatility)
640 .
add(effectiveCapVolatility);
646 }
catch (std::exception& e) {
653 LOG(
"Cashflow report written");
657 QuantLib::ext::shared_ptr<ore::data::Market> market,
const std::string& configuration,
658 const std::string& baseCcy,
const Date& horizon) {
667 Size tradeIdColumn = 0;
668 Size tradeTypeColumn = 1;
669 Size payDateColumn = 4;
672 QL_REQUIRE(cashflowReport.
header(tradeIdColumn) ==
"TradeId",
"incorrect trade id column " << tradeIdColumn);
673 QL_REQUIRE(cashflowReport.
header(tradeTypeColumn) ==
"Type",
"incorrect trade type column " << tradeTypeColumn);
674 QL_REQUIRE(cashflowReport.
header(payDateColumn) ==
"PayDate",
"incorrect payment date column " << payDateColumn);
675 QL_REQUIRE(cashflowReport.
header(ccyColumn) ==
"Currency",
"incorrect currency column " << ccyColumn);
676 QL_REQUIRE(cashflowReport.
header(pvColumn) ==
"PresentValue",
"incorrect pv column " << pvColumn);
678 map<string, Real> npvMap;
679 Date
asof = Settings::instance().evaluationDate();
680 for (Size i = 0; i < cashflowReport.
rows(); ++i) {
681 string tradeId = QuantLib::ext::get<string>(cashflowReport.
data(tradeIdColumn).at(i));
682 string tradeType = QuantLib::ext::get<string>(cashflowReport.
data(tradeTypeColumn).at(i));
683 Date payDate = QuantLib::ext::get<Date>(cashflowReport.
data(payDateColumn).at(i));
684 string ccy = QuantLib::ext::get<string>(cashflowReport.
data(ccyColumn).at(i));
685 Real pv = QuantLib::ext::get<Real>(cashflowReport.
data(pvColumn).at(i));
690 "Cashflow in row " + std::to_string(i) +
691 " has no ccy. Assuming ccy = baseCcy = " + baseCcy +
".")
694 if (!ccy.empty() && ccy != baseCcy)
695 fx = market->fxRate(ccy + baseCcy, configuration)->value();
696 if (npvMap.find(tradeId) == npvMap.end())
697 npvMap[tradeId] = 0.0;
698 if (payDate >
asof && payDate <= horizon) {
699 npvMap[tradeId] += pv * fx;
700 DLOG(
"Cashflow NPV for trade " << tradeId <<
": pv " << pv <<
" fx " << fx <<
" sum " << npvMap[tradeId]);
704 LOG(
"Writing cashflow NPV report for " <<
asof);
714 LOG(
"Cashflow NPV report written");
719 const bool continueOnError) {
720 LOG(
"Write curves... ");
722 QL_REQUIRE(marketConfig.
hasConfiguration(configID),
"curve configuration " << configID <<
" not found");
724 map<string, string> discountCurves = marketConfig.
mapping(MarketObject::DiscountCurve, configID);
725 map<string, string> YieldCurves = marketConfig.
mapping(MarketObject::YieldCurve, configID);
726 map<string, string> indexCurves = marketConfig.
mapping(MarketObject::IndexCurve, configID);
727 map<string, string> zeroInflationIndices, defaultCurves;
729 zeroInflationIndices = marketConfig.
mapping(MarketObject::ZeroInflationCurve, configID);
731 defaultCurves = marketConfig.
mapping(MarketObject::DefaultCurve, configID);
733 vector<Handle<YieldTermStructure>> yieldCurves;
734 vector<Handle<ZeroInflationIndex>> zeroInflationFixings;
735 vector<Handle<DefaultProbabilityTermStructure>> probabilityCurves;
739 for (
auto it : discountCurves) {
740 DLOG(
"discount curve - " << it.first);
742 yieldCurves.push_back(market->discountCurve(it.first, configID));
743 report.
addColumn(it.first,
double(), 15);
744 }
catch (
const std::exception& e) {
745 if (continueOnError) {
746 WLOG(
"skip this curve: " << e.what());
752 for (
auto it : YieldCurves) {
753 DLOG(
"yield curve - " << it.first);
755 yieldCurves.push_back(market->yieldCurve(it.first, configID));
756 report.
addColumn(it.first,
double(), 15);
757 }
catch (
const std::exception& e) {
758 if (continueOnError) {
759 WLOG(
"skip this curve: " << e.what());
765 for (
auto it : indexCurves) {
766 DLOG(
"index curve - " << it.first);
768 yieldCurves.push_back(market->iborIndex(it.first, configID)->forwardingTermStructure());
769 report.
addColumn(it.first,
double(), 15);
770 }
catch (
const std::exception& e) {
771 if (continueOnError) {
772 WLOG(
"skip this curve: " << e.what());
778 for (
auto it : zeroInflationIndices) {
779 DLOG(
"inflation curve - " << it.first);
781 zeroInflationFixings.push_back(market->zeroInflationIndex(it.first, configID));
782 report.
addColumn(it.first,
double(), 15);
783 }
catch (
const std::exception& e) {
784 if (continueOnError) {
785 WLOG(
"skip this curve: " << e.what());
791 for (
auto it : defaultCurves) {
792 DLOG(
"default curve - " << it.first);
794 probabilityCurves.push_back(market->defaultCurve(it.first, configID)->curve());
795 report.
addColumn(it.first,
double(), 15);
796 }
catch (
const std::exception& e) {
797 if (continueOnError) {
798 WLOG(
"skip this curve: " << e.what());
805 for (Size j = 0; j < grid.
size(); ++j) {
808 for (Size i = 0; i < yieldCurves.size(); ++i)
809 report.
add(yieldCurves[i]->discount(date));
810 for (Size i = 0; i < zeroInflationFixings.size(); ++i)
811 report.
add(zeroInflationFixings[i]->fixing(date));
812 for (Size i = 0; i < probabilityCurves.size(); ++i)
813 report.
add(probabilityCurves[i]->survivalProbability(date));
819 const string& tradeId) {
820 const vector<Date> dates = postProcess->cube()->dates();
821 Date today = Settings::instance().evaluationDate();
822 DayCounter dc = ActualActual(ActualActual::ISDA);
823 const vector<Real>& epe = postProcess->tradeEPE(tradeId);
824 const vector<Real>& ene = postProcess->tradeENE(tradeId);
825 const vector<Real>& ee_b = postProcess->tradeEE_B(tradeId);
826 const vector<Real>& eee_b = postProcess->tradeEEE_B(tradeId);
827 const vector<Real>& pfe = postProcess->tradePFE(tradeId);
828 const vector<Real>& aepe = postProcess->allocatedTradeEPE(tradeId);
829 const vector<Real>& aene = postProcess->allocatedTradeENE(tradeId);
851 for (Size j = 0; j < dates.size(); ++j) {
853 Time time = dc.yearFraction(today, dates[j]);
870 const string& nettingSetId) {
871 const vector<Date> dates = postProcess->cube()->dates();
872 Date today = Settings::instance().evaluationDate();
873 DayCounter dc = ActualActual(ActualActual::ISDA);
874 const vector<Real>& epe = postProcess->netEPE(nettingSetId);
875 const vector<Real>& ene = postProcess->netENE(nettingSetId);
876 const vector<Real>& ee_b = postProcess->netEE_B(nettingSetId);
877 const vector<Real>& eee_b = postProcess->netEEE_B(nettingSetId);
878 const vector<Real>& pfe = postProcess->netPFE(nettingSetId);
879 const vector<Real>& ecb = postProcess->expectedCollateral(nettingSetId);
891 for (Size j = 0; j < dates.size(); ++j) {
892 Real time = dc.yearFraction(today, dates[j]);
907 const string& nettingSetId) {
914 .
addColumn(
"ExpectedCollateral",
double(), 2)
928 .
addColumn(
"ExpectedCollateral",
double(), 2)
932 for (
const auto& [n,_] : postProcess->nettingSetIds()) {
939 QuantLib::ext::shared_ptr<PostProcess> postProcess,
940 const string& nettingSetId) {
941 const vector<Real> grid = postProcess->spreadSensitivityTimes();
942 const vector<Real>& sensiHazardRate = postProcess->netCvaHazardRateSensitivity(nettingSetId);
943 const vector<Real>& sensiCdsSpread = postProcess->netCvaSpreadSensitivity(nettingSetId);
946 .
addColumn(
"CvaHazardRateSensitivity",
double(), 6)
947 .
addColumn(
"CvaSpreadSensitivity",
double(), 6);
949 if (sensiHazardRate.size() == 0 || sensiCdsSpread.size() == 0)
952 for (Size j = 0; j < grid.size(); ++j) {
953 report.
next().
add(nettingSetId).
add(grid[j]).
add(sensiHazardRate[j]).
add(sensiCdsSpread[j]);
959 QuantLib::ext::shared_ptr<Portfolio> portfolio, QuantLib::ext::shared_ptr<PostProcess> postProcess) {
960 const vector<Date> dates = postProcess->cube()->dates();
961 DayCounter dc = ActualActual(ActualActual::ISDA);
969 .
addColumn(
"FBAexOwnSP",
double(), precision)
970 .
addColumn(
"FCAexOwnSP",
double(), precision)
971 .
addColumn(
"FBAexAllSP",
double(), precision)
972 .
addColumn(
"FCAexAllSP",
double(), precision)
975 .
addColumn(
"OurKVACCR",
double(), precision)
976 .
addColumn(
"TheirKVACCR",
double(), precision)
977 .
addColumn(
"OurKVACVA",
double(), precision)
978 .
addColumn(
"TheirKVACVA",
double(), precision)
979 .
addColumn(
"CollateralFloor",
double(), precision)
980 .
addColumn(
"AllocatedCVA",
double(), precision)
981 .
addColumn(
"AllocatedDVA",
double(), precision)
983 .
addColumn(
"BaselEPE",
double(), precision)
984 .
addColumn(
"BaselEEPE",
double(), precision);
986 for (
const auto& [n, _] : postProcess->nettingSetIds()) {
988 postProcess->nettingSetCVA(n);
992 .
add(postProcess->nettingSetCVA(n))
993 .
add(postProcess->nettingSetDVA(n))
994 .
add(postProcess->nettingSetFBA(n))
995 .
add(postProcess->nettingSetFCA(n))
996 .
add(postProcess->nettingSetFBA_exOwnSP(n))
997 .
add(postProcess->nettingSetFCA_exOwnSP(n))
998 .
add(postProcess->nettingSetFBA_exAllSP(n))
999 .
add(postProcess->nettingSetFCA_exAllSP(n))
1000 .
add(postProcess->nettingSetCOLVA(n))
1001 .
add(postProcess->nettingSetMVA(n))
1002 .
add(postProcess->nettingSetOurKVACCR(n))
1003 .
add(postProcess->nettingSetTheirKVACCR(n))
1004 .
add(postProcess->nettingSetOurKVACVA(n))
1005 .
add(postProcess->nettingSetTheirKVACVA(n))
1006 .
add(postProcess->nettingSetCollateralFloor(n))
1007 .
add(postProcess->nettingSetCVA(n))
1008 .
add(postProcess->nettingSetDVA(n))
1009 .
add(allocationMethod)
1010 .
add(postProcess->netEPE_B(n))
1011 .
add(postProcess->netEEPE_B(n));
1012 }
catch (
const std::exception& e) {
1014 {{
"nettingSetId", n}})
1018 for (
auto& [tid, trade] : portfolio->trades()) {
1020 string nid = trade->envelope().nettingSetId();
1024 postProcess->tradeCVA(tid);
1028 .
add(postProcess->tradeCVA(tid))
1029 .
add(postProcess->tradeDVA(tid))
1030 .
add(postProcess->tradeFBA(tid))
1031 .
add(postProcess->tradeFCA(tid))
1032 .
add(postProcess->tradeFBA_exOwnSP(tid))
1033 .
add(postProcess->tradeFCA_exOwnSP(tid))
1034 .
add(postProcess->tradeFBA_exAllSP(tid))
1035 .
add(postProcess->tradeFCA_exAllSP(tid))
1043 .
add(postProcess->allocatedTradeCVA(tid))
1044 .
add(postProcess->allocatedTradeDVA(tid))
1045 .
add(allocationMethod)
1046 .
add(postProcess->tradeEPE_B(tid))
1047 .
add(postProcess->tradeEEPE_B(tid));
1048 }
catch (
const std::exception& e) {
1058void ReportWriter::writeNettingSetColva(
ore::data::Report& report, QuantLib::ext::shared_ptr<PostProcess> postProcess,
1059 const string& nettingSetId) {
1060 const vector<Date> dates = postProcess->cube()->dates();
1061 Date today = Settings::instance().evaluationDate();
1062 DayCounter dc = ActualActual(ActualActual::ISDA);
1063 const vector<Real>& collateral = postProcess->expectedCollateral(nettingSetId);
1064 const vector<Real>& colvaInc = postProcess->colvaIncrements(nettingSetId);
1065 const vector<Real>& floorInc = postProcess->collateralFloorIncrements(nettingSetId);
1066 Real colva = postProcess->nettingSetCOLVA(nettingSetId);
1067 Real floorValue = postProcess->nettingSetCollateralFloor(nettingSetId);
1069 report.
addColumn(
"NettingSet",
string())
1072 .
addColumn(
"CollateralBalance",
double(), 4)
1073 .
addColumn(
"COLVA Increment",
double(), 4)
1075 .
addColumn(
"CollateralFloor Increment",
double(), 4)
1076 .
addColumn(
"CollateralFloor",
double(), 4);
1087 Real colvaSum = 0.0;
1088 Real floorSum = 0.0;
1089 for (Size j = 0; j < dates.size(); ++j) {
1090 Real time = dc.yearFraction(today, dates[j]);
1091 colvaSum += colvaInc[j + 1];
1092 floorSum += floorInc[j + 1];
1097 .
add(collateral[j + 1])
1098 .
add(colvaInc[j + 1])
1100 .
add(floorInc[j + 1])
1108 for (
auto const& k :
data.keys()) {
1110 report.
addColumn(tmp.c_str(),
double(), 8);
1112 for (Size d = 0; d <
data.dimDates(); ++d) {
1113 for (Size s = 0; s <
data.dimSamples(); ++s) {
1116 for (
auto const& k :
data.keys()) {
1117 report.
add(
data.get(d, s, k.first, k.second));
1125 const std::vector<QuantLib::ext::shared_ptr<SensitivityCube>>& sensitivityCubes,
1126 Real outputThreshold) {
1128 LOG(
"Writing Scenario report");
1133 report.
addColumn(
"Base NPV",
double(), 2);
1134 report.
addColumn(
"ShiftSize_1",
double(), 6);
1135 report.
addColumn(
"ShiftSize_2",
double(), 6);
1136 report.
addColumn(
"Scenario NPV",
double(), 2);
1137 report.
addColumn(
"Difference",
double(), 2);
1139 for (
auto const& sensitivityCube : sensitivityCubes) {
1141 auto scenarioDescriptions = sensitivityCube->scenarioDescriptions();
1142 auto tradeIds = sensitivityCube->tradeIdx();
1143 auto npvCube = sensitivityCube->npvCube();
1145 for (
const auto& [tradeId, i] : tradeIds) {
1147 Real baseNpv = npvCube->getT0(i);
1148 for (
const auto& [j, scenarioNpv] : npvCube->getTradeNPVs(i)) {
1149 auto scenarioDescription = scenarioDescriptions[j];
1150 Real difference = scenarioNpv - baseNpv;
1151 Real shift1 = scenarioDescription.key1().keytype == RiskFactorKey::KeyType::None
1153 : sensitivityCube->actualShiftSize(scenarioDescription.key1());
1154 Real shift2 = scenarioDescription.key2().keytype == RiskFactorKey::KeyType::None
1156 : sensitivityCube->actualShiftSize(scenarioDescription.key2());
1157 if (fabs(difference) > outputThreshold) {
1159 report.
add(tradeId);
1161 report.
add(scenarioDescription.typeString());
1162 report.
add(baseNpv);
1165 report.
add(scenarioNpv);
1166 report.
add(difference);
1167 }
else if (!std::isfinite(difference)) {
1169 ALOG(
"sensitivity scenario for trade " << tradeId <<
", factor " << scenarioDescription.factors()
1170 <<
" is not finite (" << difference <<
")");
1177 LOG(
"Scenario report finished");
1180void ReportWriter::writeSensitivityReport(
Report& report,
const QuantLib::ext::shared_ptr<SensitivityStream>& ss,
1181 Real outputThreshold, Size outputPrecision) {
1183 LOG(
"Writing Sensitivity report");
1185 Size shiftSizePrecision = outputPrecision < 6 ? 6 : outputPrecision;
1186 Size amountPrecision = outputPrecision < 2 ? 2 : outputPrecision;
1191 report.
addColumn(
"ShiftSize_1",
double(), shiftSizePrecision);
1193 report.
addColumn(
"ShiftSize_2",
double(), shiftSizePrecision);
1195 report.
addColumn(
"Base NPV",
double(), amountPrecision);
1196 report.
addColumn(
"Delta",
double(), amountPrecision);
1197 report.
addColumn(
"Gamma",
double(), amountPrecision);
1202 if ((outputThreshold == Null<Real>()) || (fabs(sr.delta) > outputThreshold ||
1203 (sr.gamma != Null<Real>() && fabs(sr.gamma) > outputThreshold))) {
1205 report.
add(sr.tradeId);
1208 report.
add(sr.shift_1);
1210 report.
add(sr.shift_2);
1211 report.
add(sr.currency);
1212 report.
add(sr.baseNpv);
1213 report.
add(sr.delta);
1214 report.
add(sr.gamma);
1215 }
else if (!std::isfinite(sr.delta) || !std::isfinite(sr.gamma)) {
1217 ALOG(
"sensitivity record has infinite values: " << sr);
1222 LOG(
"Sensitivity report finished");
1226 const std::map<RiskFactorKey, QuantLib::Real>& shiftSizes,
1227 const std::map<RiskFactorKey, QuantLib::Real>& baseValues,
1228 const std::map<RiskFactorKey, std::string>& keyToFactor) {
1229 LOG(
"Writing Sensitivity Config report");
1236 for (
auto const& [key, shift] : shiftSizes) {
1238 std::string keyStr =
"na", factorStr =
"na";
1239 Real baseValue = Null<Real>();
1241 if (
auto it = keyToFactor.find(key); it != keyToFactor.end())
1242 factorStr = it->second;
1243 if (
auto it = baseValues.find(key); it != baseValues.end())
1244 baseValue = it->second;
1245 report.
add(keyStr).
add(factorStr).
add(baseValue).
add(shift);
1249 LOG(
"Sensitivity Config report finished.");
1253void addMapResults(boost::any resultMap,
const std::string& tradeId,
const std::string& resultName,
Report& report) {
1254 T map = boost::any_cast<T>(resultMap);
1255 for (
auto it : map) {
1256 std::string
name = resultName +
"_" + it.first.code();
1257 boost::any tmp = it.second;
1263void ReportWriter::writeAdditionalResultsReport(
Report& report, QuantLib::ext::shared_ptr<Portfolio> portfolio,
1264 QuantLib::ext::shared_ptr<Market> market,
const std::string& baseCurrency,
1265 const std::size_t precision) {
1267 LOG(
"Writing AdditionalResults report");
1274 for (
auto & [tId, trade] : portfolio->trades()) {
1277 string tradeId = tId;
1278 string tradeType = trade->tradeType();
1279 Real notional2 = Null<Real>();
1280 string notional2Ccy =
"";
1282 auto additionalData = trade->additionalData();
1283 for (
const auto& kv : additionalData) {
1285 if (boost::starts_with(p.first,
"vector")) {
1286 vector<std::string> tokens;
1287 string vect = p.second;
1288 vect.erase(remove(vect.begin(), vect.end(),
'\"'), vect.end());
1289 boost::split(tokens, vect, boost::is_any_of(
","));
1290 for (Size i = 0; i < tokens.size(); i++) {
1291 boost::trim(tokens[i]);
1294 .
add(kv.first +
"[" + std::to_string(i) +
"]")
1295 .
add(p.first.substr(7))
1302 if (additionalData.count(
"notional[2]") != 0 && additionalData.count(
"notionalCurrency[2]") != 0) {
1303 notional2 = trade->additionalDatum<Real>(
"notional[2]");
1304 notional2Ccy = trade->additionalDatum<
string>(
"notionalCurrency[2]");
1307 auto additionalResults = trade->instrument()->additionalResults();
1308 if (additionalResults.count(
"notional[2]") != 0 && additionalResults.count(
"notionalCurrency[2]") != 0) {
1309 notional2 = trade->instrument()->qlInstrument()->result<Real>(
"notional[2]");
1310 notional2Ccy = trade->instrument()->qlInstrument()->result<
string>(
"notionalCurrency[2]");
1313 if (notional2 != Null<Real>() && notional2Ccy !=
"") {
1315 if (notional2Ccy != baseCurrency)
1316 fx = market->fxRate(notional2Ccy + baseCurrency)->value();
1317 std::ostringstream oss;
1318 oss << std::fixed << std::setprecision(8) << notional2 * fx;
1326 auto instruments = trade->instrument()->additionalInstruments();
1327 auto multipliers = trade->instrument()->additionalMultipliers();
1328 QL_REQUIRE(instruments.size() == multipliers.size(),
1329 "Expected the number of "
1330 <<
"additional instruments (" << instruments.size() <<
") to equal the number of "
1331 <<
"additional multipliers (" << multipliers.size() <<
").");
1333 for (Size i = 0; i <= instruments.size(); ++i) {
1335 if (i > 0 && instruments[i - 1] ==
nullptr)
1338 std::map<std::string, boost::any> thisAddResults =
1339 i == 0 ? additionalResults : instruments[i - 1]->additionalResults();
1343 tradeId = i == 0 ? trade->id() : (
"_" + trade->id() +
"_" + std::to_string(i));
1347 if (!thisAddResults.empty() && thisAddResults.count(
"instMultiplier") == 0) {
1348 thisAddResults[
"instMultiplier"] = i == 0 ? trade->instrument()->multiplier() : multipliers[i - 1];
1352 for (
const auto& kv : thisAddResults) {
1355 addMapResults<result_type_matrix>(kv.second, tradeId, kv.first, report);
1357 addMapResults<result_type_vector>(kv.second, tradeId, kv.first, report);
1359 addMapResults<result_type_scalar>(kv.second, tradeId, kv.first, report);
1362 if (boost::starts_with(p.first,
"vector")) {
1363 vector<std::string> tokens;
1364 string vect = p.second;
1365 vect.erase(remove(vect.begin(), vect.end(),
'\"'), vect.end());
1366 boost::split(tokens, vect, boost::is_any_of(
","));
1367 for (Size i = 0; i < tokens.size(); i++) {
1368 boost::trim(tokens[i]);
1371 .
add(kv.first +
"[" + std::to_string(i) +
"]")
1372 .
add(p.first.substr(7))
1380 }
catch (
const std::exception& e) {
1382 "Error during trade pricing (additional results)", e.what())
1389 LOG(
"AdditionalResults report written");
1393 const Date& d = actualDate == Null<Date>() ? md.
asofDate() : actualDate;
1397void ReportWriter::writeMarketData(
Report& report,
const QuantLib::ext::shared_ptr<Loader>& loader,
const Date&
asof,
1398 const set<string>& quoteNames,
bool returnAll) {
1400 LOG(
"Writing MarketData report");
1405 for (
const auto& md : loader->loadQuotes(
asof)) {
1406 addMarketDatum(report, *md, loader->actualDate());
1412 set<string> regexStrs;
1415 vector<std::regex> regexes;
1416 regexes.reserve(regexStrs.size());
1417 for (
auto regexStr : regexStrs) {
1418 regexes.push_back(regex(regexStr));
1421 for (
const auto& md : loader->loadQuotes(
asof)) {
1422 const auto& mdName = md->name();
1424 if (names.find(mdName) != names.end()) {
1425 addMarketDatum(report, *md, loader->actualDate());
1430 for (
const auto& regex : regexes) {
1431 if (regex_match(mdName, regex)) {
1432 addMarketDatum(report, *md, loader->actualDate());
1439 LOG(
"MarketData report written");
1442void ReportWriter::writeFixings(
Report& report,
const QuantLib::ext::shared_ptr<Loader>& loader) {
1444 LOG(
"Writing Fixings report");
1448 for (
const auto& f : loader->loadFixings()) {
1453 LOG(
"Fixings report written");
1456void ReportWriter::writeDividends(
Report& report,
const QuantLib::ext::shared_ptr<Loader>& loader) {
1458 LOG(
"Writing Dividends report");
1460 report.
addColumn(
"dividendExDate", Date())
1462 .
addColumn(
"dividendRate",
double(), 10)
1463 .
addColumn(
"dividendPaymentDate", Date());
1465 for (
const auto& f : loader->loadDividends()) {
1470 LOG(
"Dividends report written");
1473void ReportWriter::writePricingStats(
ore::data::Report& report,
const QuantLib::ext::shared_ptr<Portfolio>& portfolio) {
1475 LOG(
"Writing Pricing stats report");
1483 for (
auto const& [tid, trade] : portfolio->trades()) {
1484 std::size_t num = trade->getNumberOfPricings();
1485 Size cumulative = trade->getCumulativePricingTime() / 1000;
1486 Size average = num > 0 ? cumulative / num : 0;
1491 LOG(
"Pricing stats report written");
1494void ReportWriter::writeCube(
ore::data::Report& report,
const QuantLib::ext::shared_ptr<NPVCube>& cube,
1495 const std::map<std::string, std::string>& nettingSetMap) {
1496 LOG(
"Writing cube report");
1506 const map<string, Size>& idsAndPos = cube->idsAndIndexes();
1507 vector<string> dateStrings(cube->numDates());
1508 for (Size i = 0; i < cube->numDates(); ++i) {
1509 std::ostringstream oss;
1510 oss << QuantLib::io::iso_date(cube->dates()[i]);
1511 dateStrings[i] = oss.str();
1514 std::ostringstream oss;
1515 oss << QuantLib::io::iso_date(cube->asof());
1516 string asofString = oss.str();
1518 vector<string> ids(idsAndPos.size());
1519 vector<string> nettingSetIds(idsAndPos.size());
1520 for (
const auto& [
id, idCubePos] : idsAndPos) {
1521 ids[idCubePos] = id;
1522 auto it = nettingSetMap.find(
id);
1523 if (it != nettingSetMap.end())
1524 nettingSetIds[idCubePos] = it->second;
1526 nettingSetIds[idCubePos] =
"";
1530 for (Size i = 0; i < ids.size(); ++i) {
1533 .
add(nettingSetIds[i])
1534 .
add(
static_cast<Size
>(0))
1536 .
add(
static_cast<Size
>(0))
1537 .
add(
static_cast<Size
>(0))
1538 .
add(cube->getT0(i));
1542 for (Size i = 0; i < ids.size(); i++) {
1543 for (Size j = 0; j < cube->numDates(); j++) {
1544 for (Size k = 0; k < cube->samples(); k++) {
1545 for (Size l = 0; l < cube->depth(); l++) {
1548 .
add(nettingSetIds[i])
1550 .
add(dateStrings[j])
1553 .
add(cube->get(i, j, k, l));
1561 LOG(
"Cube report written");
1570void ReportWriter::writeSIMMReport(
1572 const QuantLib::ext::shared_ptr<Report> report,
const bool hasNettingSetDetails,
const string& simmResultCcy,
1573 const string& simmCalcCcyCall,
const string& simmCalcCcyPost,
const string& reportCcy, Real fxSpot,
1574 Real outputThreshold) {
1578 map<SimmSide, map<NettingSetDetails, map<string, SimmResults>>> finalSimmResults;
1579 for (
const auto& sv : finalSimmResultsMap) {
1581 for (
const auto& nv : sv.second) {
1583 const string& regulation = nv.second.first;
1584 const SimmResults& simmResults = nv.second.second;
1586 finalSimmResults[side][nettingSetDetails][regulation] = simmResults;
1590 writeSIMMReport(finalSimmResults, report, hasNettingSetDetails, simmResultCcy, simmCalcCcyCall, simmCalcCcyPost,
1591 reportCcy,
true, fxSpot, outputThreshold);
1594void ReportWriter::writeSIMMReport(
1595 const map<SimmSide, map<
NettingSetDetails, map<string, SimmResults>>>& simmResultsMap,
1596 const QuantLib::ext::shared_ptr<Report> report,
const bool hasNettingSetDetails,
const string& simmResultCcy,
1597 const string& simmCalcCcyCall,
const string& simmCalcCcyPost,
const string& reportCcy,
const bool isFinalSimm, Real fxSpot,
1598 Real outputThreshold) {
1601 LOG(
"Writing SIMM results report.");
1603 LOG(
"Writing full SIMM results report.");
1607 report->addColumn(
"Portfolio",
string());
1608 if (hasNettingSetDetails) {
1609 for (
const string& field : NettingSetDetails::optionalFieldNames())
1610 report->addColumn(field,
string());
1613 report->addColumn(
"ProductClass",
string())
1614 .addColumn(
"RiskClass",
string())
1615 .addColumn(
"MarginType",
string())
1616 .addColumn(
"Bucket",
string())
1617 .addColumn(
"SimmSide",
string())
1618 .addColumn(
"Regulation",
string())
1619 .addColumn(
"InitialMargin",
double(), 2)
1620 .addColumn(
"Currency",
string())
1621 .addColumn(
"CalculationCurrency",
string());
1622 if (!reportCcy.empty()) {
1623 report->addColumn(
"InitialMargin(Report)",
double(), 2).addColumn(
"ReportCurrency",
string());
1627 if (reportCcy.empty() && fxSpot != 1.0)
1630 const vector<SimmSide> sides({SimmSide::Call, SimmSide::Post});
1631 for (
const SimmSide side : sides) {
1635 Real sumSidePortfolios = 0.0;
1636 Real sumSidePortfoliosReporting = 0.0;
1638 set<string> winningRegs;
1639 if (simmResultsMap.find(side) != simmResultsMap.end()) {
1640 for (
const auto& nv : simmResultsMap.at(side)) {
1641 const NettingSetDetails& portfolioId = nv.first;
1642 const auto& simmResultsMap = nv.second;
1645 QL_REQUIRE(simmResultsMap.size() <= 1,
1646 "Final SIMM results should only have one (winning) regulation per netting set.");
1648 for (
const auto& rv : simmResultsMap) {
1649 const string& regulation = rv.first;
1650 const SimmResults& results = rv.second;
1653 winningRegs.insert(regulation);
1655 QL_REQUIRE(results.resultCurrency() == simmResultCcy,
1656 "writeSIMMReport(): SIMM results ("
1657 << results.resultCurrency() <<
") should be denominated in the SIMM result currency ("
1658 << simmResultCcy <<
").");
1661 for (
const auto& result : results.data()) {
1663 const auto& key = result.first;
1667 string b = get<3>(key);
1668 Real im = result.second;
1669 Real simmReporting = 0.0;
1672 if (fabs(im) >= outputThreshold ||
1673 (pc == ProductClass::All && rc == RiskClass::All && mt == MarginType::All)) {
1675 map<string, string> nettingSetMap = portfolioId.mapRepresentation();
1676 for (
const string& field : NettingSetDetails::fieldNames(hasNettingSetDetails)) {
1677 report->add(nettingSetMap[field]);
1686 .add(results.resultCurrency())
1687 .add(results.calculationCurrency());
1688 if (!reportCcy.empty()) {
1689 simmReporting = result.second * fxSpot;
1690 report->add(simmReporting).add(reportCcy);
1695 (pc == ProductClass::All && rc == RiskClass::All && mt == MarginType::All)) {
1696 sumSidePortfolios += result.second;
1697 sumSidePortfoliosReporting += simmReporting;
1708 string finalWinningReg = winningRegs.size() == 1 ? *(winningRegs.begin()) :
"";
1712 Size numNettingSetFields = NettingSetDetails::fieldNames(hasNettingSetDetails).size();
1713 for (Size t = 0; t < numNettingSetFields; t++)
1720 .add(finalWinningReg)
1721 .add(sumSidePortfolios)
1723 .add(side == SimmSide::Call ? simmCalcCcyCall : simmCalcCcyPost);
1726 if (!reportCcy.empty())
1727 report->add(sumSidePortfoliosReporting).add(reportCcy);
1732 LOG(
"SIMM results report written.");
1736void ReportWriter::writeSIMMData(
const ore::analytics::Crif& simmData,
const QuantLib::ext::shared_ptr<Report>& dataReport,
1737 const bool hasNettingSetDetails) {
1739 LOG(
"Writing SIMM data report.");
1743 bool hasRegulations =
false;
1744 for (
const auto& cr : simmData) {
1745 if (!cr.collectRegulations.empty() || !cr.postRegulations.empty()) {
1746 hasRegulations =
true;
1752 dataReport->addColumn(
"Portfolio",
string());
1753 if (hasNettingSetDetails) {
1754 for (
const string& field : NettingSetDetails::optionalFieldNames())
1755 dataReport->addColumn(field,
string());
1758 dataReport->addColumn(
"RiskType",
string())
1759 .addColumn(
"ProductClass",
string())
1760 .addColumn(
"Bucket",
string())
1761 .addColumn(
"Qualifier",
string())
1762 .addColumn(
"Label1",
string())
1763 .addColumn(
"Label2",
string())
1764 .addColumn(
"Amount",
double())
1765 .addColumn(
"IMModel",
string());
1768 dataReport->addColumn(
"collect_regulations",
string())
1769 .addColumn(
"post_regulations",
string());
1772 for (
const auto& cr : simmData) {
1778 if (cr.imModel ==
"Schedule")
1783 if (cr.imModel.empty() &&
1784 (cr.riskType == CrifRecord::RiskType::Notional || cr.riskType == CrifRecord::RiskType::PV))
1789 map<string, string> nettingSetMap = cr.nettingSetDetails.mapRepresentation();
1790 for (
const string& field : NettingSetDetails::fieldNames(hasNettingSetDetails))
1791 dataReport->add(nettingSetMap[field]);
1801 if (hasRegulations) {
1802 string collectRegString = cr.collectRegulations.find(
',') == string::npos
1803 ? cr.collectRegulations
1804 :
'\"' + cr.collectRegulations +
'\"';
1805 string postRegString = cr.postRegulations.find(
',') == string::npos
1806 ? cr.postRegulations
1807 :
'\"' + cr.postRegulations +
'\"';
1809 dataReport->add(collectRegString)
1810 .add(postRegString);
1816 LOG(
"SIMM data report written.");
1819void ReportWriter::writeCrifReport(
const QuantLib::ext::shared_ptr<Report>& report,
const Crif& crif) {
1824 bool hasNettingSetDetails =
false;
1825 for (
const auto& cr : crif) {
1826 if (!cr.nettingSetDetails.emptyOptionalFields())
1827 hasNettingSetDetails =
true;
1830 std::vector<string> addFields;
1831 bool hasCollectRegulations =
false;
1832 bool hasPostRegulations =
false;
1833 bool hasScheduleTrades =
false;
1834 for (
const auto& cr : crif) {
1836 for (
const auto& af : cr.additionalFields) {
1837 if (std::find(addFields.begin(), addFields.end(), af.first) == addFields.end()) {
1838 addFields.push_back(af.first);
1843 if (!hasCollectRegulations)
1844 hasCollectRegulations = !cr.collectRegulations.empty();
1845 if (!hasPostRegulations)
1846 hasPostRegulations = !cr.postRegulations.empty();
1849 if (!hasScheduleTrades) {
1851 hasScheduleTrades =
parseIMModel(cr.imModel) == SimmConfiguration::IMModel::Schedule;
1852 }
catch (std::exception&) {
1859 report->addColumn(
"TradeID",
string()).addColumn(
"PortfolioID",
string());
1862 if (hasNettingSetDetails) {
1863 for (
const string& optionalField : NettingSetDetails::optionalFieldNames())
1864 report->addColumn(optionalField,
string());
1867 report->addColumn(
"ProductClass",
string())
1868 .addColumn(
"RiskType",
string())
1869 .addColumn(
"Qualifier",
string())
1870 .addColumn(
"Bucket",
string())
1871 .addColumn(
"Label1",
string())
1872 .addColumn(
"Label2",
string())
1873 .addColumn(
"AmountCurrency",
string())
1874 .addColumn(
"Amount",
double(), 2)
1875 .addColumn(
"AmountUSD",
double(), 2)
1876 .addColumn(
"IMModel",
string())
1877 .addColumn(
"TradeType",
string());
1879 if (hasScheduleTrades || crif.
type() == Crif::CrifType::Frtb)
1880 report->addColumn(
"end_date",
string());
1882 if (crif.
type() == Crif::CrifType::Frtb) {
1883 report->addColumn(
"Label3",
string())
1884 .addColumn(
"CreditQuality",
string())
1885 .addColumn(
"LongShortInd",
string())
1886 .addColumn(
"CoveredBondInd",
string())
1887 .addColumn(
"TrancheThickness",
string())
1888 .addColumn(
"BB_RW",
string());
1891 if (hasCollectRegulations)
1892 report->addColumn(
"collect_regulations",
string());
1894 if (hasPostRegulations)
1895 report->addColumn(
"post_regulations",
string());
1898 for (
const string& f : addFields) {
1899 report->addColumn(f,
string());
1903 for (
const auto& cr : crif) {
1905 report->next().add(cr.tradeId).add(cr.portfolioId);
1907 if (hasNettingSetDetails) {
1909 for (
const string& optionalField : NettingSetDetails::optionalFieldNames())
1910 report->add(crNettingSetDetailsMap[optionalField]);
1919 .add(cr.amountCurrency)
1925 if (hasScheduleTrades || crif.
type() == Crif::CrifType::Frtb)
1926 report->add(cr.endDate);
1928 if (crif.
type() == Crif::CrifType::Frtb) {
1929 report->add(cr.label3)
1930 .add(cr.creditQuality)
1931 .add(cr.longShortInd)
1932 .add(cr.coveredBondInd)
1933 .add(cr.trancheThickness)
1937 if (hasCollectRegulations) {
1939 report->add(regString);
1942 if (hasPostRegulations) {
1944 report->add(regString);
1947 for (
const string& af : addFields) {
1948 if (cr.additionalFields.find(af) == cr.additionalFields.end())
1951 report->add(cr.getAdditionalFieldAsStr(af));
1958void ReportWriter::writeScenarioStatistics(
const QuantLib::ext::shared_ptr<ScenarioGenerator>& generator,
1959 const std::vector<RiskFactorKey>& keys,
const Size numPaths,
1970 std::vector<boost::accumulators::accumulator_set<
1971 double, boost::accumulators::stats<boost::accumulators::tag::min, boost::accumulators::tag::max,
1972 boost::accumulators::tag::mean, boost::accumulators::tag::variance,
1973 boost::accumulators::tag::skewness, boost::accumulators::tag::kurtosis>>>
1974 acc(keys.size() * dates.size());
1976 for (Size i = 0; i < numPaths; ++i) {
1977 for (Size d = 0; d < dates.size(); ++d) {
1978 QuantLib::ext::shared_ptr<Scenario> currentScenario =
generator->next(dates[d]);
1979 for (Size k = 0; k < keys.size(); ++k) {
1980 acc[d * keys.size() + k](currentScenario->get(keys[k]));
1984 for (Size d = 0; d < dates.size(); ++d) {
1985 for (Size k = 0; k < keys.size(); ++k) {
1986 Size idx = d * keys.size() + k;
1990 .add(boost::accumulators::min(acc[idx]))
1991 .add(boost::accumulators::mean(acc[idx]))
1992 .add(boost::accumulators::max(acc[idx]))
1993 .add(std::sqrt(boost::accumulators::variance(acc[idx])));
1994 if (!
close_enough(boost::accumulators::variance(acc[idx]), 0.0)) {
1995 report.
add(boost::accumulators::skewness(acc[idx])).add(boost::accumulators::kurtosis(acc[idx]));
1999 report.
add(0.0).
add(0.0);
2008void distributionCount(I begin, I end,
const Size steps, std::vector<Real>& bounds, std::vector<Size>& counts) {
2009 Real xmin = *std::min_element(begin, end);
2010 Real xmax = *std::max_element(begin, end);
2011 std::vector<Real> v(begin, end);
2012 std::sort(v.begin(), v.end());
2013 Real h = (xmax - xmin) /
static_cast<Real
>(steps);
2015 counts.resize(steps);
2016 bounds.resize(steps);
2017 for (Size i = 0; i <
steps; ++i) {
2018 Real v1 = xmin +
static_cast<Real
>(i + 1) * h;
2021 Size idx1 = i ==
steps - 1 ? v.size() : std::upper_bound(v.begin(), v.end(), v1) - v.begin();
2022 counts[i] = idx1 - idx0;
2029void ReportWriter::writeScenarioDistributions(
const QuantLib::ext::shared_ptr<ScenarioGenerator>& generator,
2030 const std::vector<RiskFactorKey>& keys,
const Size numPaths,
2031 const std::vector<Date>& dates,
const Size distSteps,
2038 std::vector<std::vector<std::vector<Real>>> values(
2039 dates.size(), std::vector<std::vector<Real>>(keys.size(), std::vector<Real>(numPaths, 0.0)));
2041 for (Size i = 0; i < numPaths; ++i) {
2042 for (Size d = 0; d < dates.size(); ++d) {
2043 QuantLib::ext::shared_ptr<Scenario> currentScenario =
generator->next(dates[d]);
2044 for (Size k = 0; k < keys.size(); ++k) {
2045 values[d][k][i] = currentScenario->get(keys[k]);
2050 std::vector<Real> bounds;
2051 std::vector<Size> counts;
2052 for (Size d = 0; d < dates.size(); ++d) {
2053 for (Size k = 0; k < keys.size(); ++k) {
2054 distributionCount(values[d][k].begin(), values[d][k].end(), distSteps, bounds, counts);
2055 for (Size i = 0; i < distSteps; ++i) {
2063void ReportWriter::writeHistoricalScenarioDetails(
2064 const QuantLib::ext::shared_ptr<ore::analytics::HistoricalScenarioGenerator>& generator,
ore::data::Report& report) {
2070 .
addColumn(
"AdjustmentFactor1",
double(), 8)
2071 .
addColumn(
"AdjustmentFactor2",
double(), 8)
2072 .
addColumn(
"ScenarioValue1",
double(), 8)
2073 .
addColumn(
"ScenarioValue2",
double(), 8)
2076 .
addColumn(
"ScenarioValue",
double(), 8);
2081 for (
auto const& d :
generator->lastHistoricalScenarioCalculationDetails()) {
2083 .
add(d.scenarioDate1)
2084 .
add(d.scenarioDate2)
2087 .
add(d.adjustmentFactor1)
2088 .
add(d.adjustmentFactor2)
2089 .
add(d.scenarioValue1)
2090 .
add(d.scenarioValue2)
2093 .
add(d.scenarioValue);
2099void ReportWriter::writeStockSplitReport(
const QuantLib::ext::shared_ptr<Scenario>& baseScenario,
2100 const QuantLib::ext::shared_ptr<ore::analytics::HistoricalScenarioLoader>& hsloader,
2101 const QuantLib::ext::shared_ptr<ore::data::AdjustmentFactors>& adjFactors,
2102 const QuantLib::ext::shared_ptr<ore::data::Report>& report) {
2104 report->addColumn(
"EquityId",
string())
2105 .addColumn(
"Date", Date())
2106 .addColumn(
"Price",
double(), 8)
2107 .addColumn(
"Factor",
double(), 8)
2108 .addColumn(
"CumulatedFactor",
double(), 8)
2109 .addColumn(
"AdjustedPrice",
double(), 8);
2112 std::set<std::string> names;
2113 for (
auto const& k : baseScenario->keys()) {
2114 if (k.keytype == RiskFactorKey::KeyType::EquitySpot) {
2115 names.insert(k.name);
2119 std::vector<QuantLib::Date> hsdates = hsloader->dates();
2121 for (
auto const&
name : names) {
2123 std::set<QuantLib::Date> dates = adjFactors->dates(
name);
2124 dates.insert(hsdates.begin(), hsdates.end());
2126 for (
auto const& d : dates) {
2128 Real price = Null<Real>();
2129 if (std::find(hsdates.begin(), hsdates.end(), d) != hsdates.end()) {
2130 auto scen = hsloader->getHistoricalScenario(d);
2133 price = scen->get(rf);
2135 Real factor = adjFactors->getFactorContribution(
name, d);
2136 Real cumFactor = adjFactors->getFactor(
name, d);
2137 Real adjPrice = price == Null<Real>() ? Null<Real>() : price * cumFactor;
2139 report->next().add(
name).add(d).add(price).add(factor).add(cumFactor).add(adjPrice);
2146void ReportWriter::writeHistoricalScenarioDistributions(
2147 QuantLib::ext::shared_ptr<HistoricalScenarioGenerator>& hsgen,
2148 const QuantLib::ext::shared_ptr<ore::analytics::ScenarioSimMarket>& simMarket,
2149 const QuantLib::ext::shared_ptr<ore::analytics::ScenarioSimMarketParameters>& simMarketParams,
2150 QuantLib::ext::shared_ptr<ore::data::Report> histScenDetailsReport, QuantLib::ext::shared_ptr<ore::data::Report> statReport,
2151 QuantLib::ext::shared_ptr<ore::data::Report> distReport, Size distSteps) {
2154 simMarket->scenarioGenerator() = hsgen;
2155 hsgen->baseScenario() = simMarket->baseScenario();
2158 if (!statReport && !distReport)
2162 auto hsgent = QuantLib::ext::make_shared<HistoricalScenarioGeneratorTransform>(hsgen, simMarket, simMarketParams);
2164 const vector<RiskFactorKey>& keys = hsgen->baseScenario()->keys();
2165 Size numScen = hsgen->numScenarios();
2166 Date
asof = hsgen->baseScenario()->asof();
2171 writeScenarioStatistics(hsgent, keys, numScen, {
asof}, *statReport);
2176 QL_REQUIRE(distSteps != Null<Size>(),
2177 "When creating a distribution report, a valid distribution step size is required");
2179 writeScenarioDistributions(hsgent, keys, numScen, {
asof}, distSteps, *distReport);
2183 if (histScenDetailsReport) {
2185 writeHistoricalScenarioDetails(hsgent, *histScenDetailsReport);
2189void ReportWriter::writeHistoricalScenarios(
const QuantLib::ext::shared_ptr<HistoricalScenarioLoader>& hsloader,
2190 const QuantLib::ext::shared_ptr<ore::data::Report>& report) {
2193 std::set<RiskFactorKey> allKeys;
2194 for (
const auto& s : hsloader->historicalScenarios())
2195 allKeys.insert(s->keys().begin(), s->keys().end());
2196 ScenarioWriter sw(
nullptr, report, std::vector<RiskFactorKey>(allKeys.begin(), allKeys.end()));
2197 bool writeHeader =
true;
2198 for (
const auto& s : hsloader->historicalScenarios()) {
2200 writeHeader =
false;
2211void ReportWriter::writeIMScheduleSummaryReport(
2213 const QuantLib::ext::shared_ptr<Report> report,
const bool hasNettingSetDetails,
const string& simmResultCcy,
2214 const string& reportCcy, Real fxSpot, Real outputThreshold) {
2216 LOG(
"Writing IM Schedule results summary report.");
2219 report->addColumn(
"Portfolio",
string());
2220 if (hasNettingSetDetails) {
2221 for (
const string& field : NettingSetDetails::optionalFieldNames())
2222 report->addColumn(field,
string());
2225 report->addColumn(
"ProductClass",
string())
2226 .addColumn(
"GrossIM",
double(), 2)
2227 .addColumn(
"GrossCurrentRC",
double(), 2)
2228 .addColumn(
"NetCurrentRC",
double(), 2)
2229 .addColumn(
"NetToGrossRatio",
double(), 6)
2230 .addColumn(
"Side",
string())
2231 .addColumn(
"Regulation",
string())
2232 .addColumn(
"ScheduleIM",
double(), 2)
2233 .addColumn(
"Currency",
string());
2234 if (!reportCcy.empty()) {
2235 report->addColumn(
"ScheduleIM(Report)",
double(), 2).addColumn(
"ReportCurrency",
string());
2238 const vector<SimmSide> sides({SimmSide::Call, SimmSide::Post});
2239 for (
const SimmSide side : sides) {
2240 const string& sideString =
to_string(side);
2243 Real sumSideScheduleIM = 0.0;
2244 Real sumSideScheduleIMReporting = 0.0;
2246 std::set<std::string> winningRegs;
2247 if (finalResultsMap.find(side) != finalResultsMap.end()) {
2248 for (
const auto& nv : finalResultsMap.at(side)) {
2250 const string& regulation = nv.second.first;
2253 winningRegs.insert(regulation);
2255 QL_REQUIRE(results.
currency() == simmResultCcy,
2256 "writeIMScheduleSummaryReport(): IMSchedule results ("
2257 << results.
currency() <<
") should be denominated in the SIMM result currency ("
2258 << simmResultCcy <<
").");
2261 for (
const auto& imScheduleResult : results.
data()) {
2269 for (
const string& field : NettingSetDetails::fieldNames(hasNettingSetDetails)) {
2270 report->add(nettingSetMap.at(field));
2282 if (!reportCcy.empty()) {
2283 Real scheduleIMReporting = im * fxSpot;
2284 report->add(scheduleIMReporting).add(reportCcy);
2286 if (pc == ProductClass::All)
2287 sumSideScheduleIMReporting += scheduleIMReporting;
2290 if (pc == ProductClass::All)
2298 string finalWinningReg = winningRegs.size() == 1 ? *(winningRegs.begin()) :
"";
2302 Size numNettingSetFields = NettingSetDetails::fieldNames(hasNettingSetDetails).size();
2303 for (Size t = 0; t < numNettingSetFields; t++)
2305 report->add(
to_string(ProductClass::All))
2311 .add(finalWinningReg)
2312 .add(sumSideScheduleIM)
2313 .add(simmResultCcy);
2316 if (!reportCcy.empty())
2317 report->add(sumSideScheduleIMReporting).add(reportCcy);
2322 LOG(
"IM Schedule results summary report written.");
2325void ReportWriter::writeIMScheduleTradeReport(
const map<
string, vector<IMScheduleTradeData>>& tradeResults,
2326 const QuantLib::ext::shared_ptr<ore::data::Report> report,
2327 const bool hasNettingSetDetails) {
2329 LOG(
"Writing IM Schedule trade results report.");
2331 report->addColumn(
"TradeId",
string());
2334 report->addColumn(
"Portfolio",
string());
2335 if (hasNettingSetDetails) {
2336 for (
const string& field : NettingSetDetails::optionalFieldNames())
2337 report->addColumn(field,
string());
2340 report->addColumn(
"ProductClass",
string())
2341 .addColumn(
"EndDate",
string())
2342 .addColumn(
"Maturity",
double(), 5)
2343 .addColumn(
"Label",
string())
2344 .addColumn(
"Multiplier",
double(), 2)
2345 .addColumn(
"Notional",
double(), 2)
2346 .addColumn(
"NotionalCurrency",
string())
2347 .addColumn(
"PV",
double(), 2)
2348 .addColumn(
"PVCurrency",
string())
2349 .addColumn(
"Notional(Base)",
double(), 2)
2350 .addColumn(
"PV(Base)",
double(), 2)
2351 .addColumn(
"BaseCurrency",
string())
2352 .addColumn(
"GrossIM(Base)",
double(), 2)
2353 .addColumn(
"CollectRegulations",
string())
2354 .addColumn(
"PostRegulations",
string());
2357 for (
const auto& kv : tradeResults) {
2358 const string& tradeId = kv.first;
2365 report->add(tradeId);
2368 for (
const string& field : NettingSetDetails::fieldNames(hasNettingSetDetails)) {
2369 report->add(nettingSetMap.at(field));
2371 const string collectRegsString = tradeData.
collectRegulations.find(
',') == string::npos
2374 const string postRegsString = tradeData.
postRegulations.find(
',') == string::npos
2390 .add(collectRegsString)
2391 .add(postRegsString);
2397 LOG(
"IM Schedule trade results report written.");
2401 const ext::shared_ptr<InMemoryReport>& cashFlowReport,
2402 const ext::shared_ptr<ore::data::Market>& market,
2403 const std::string& baseCurrency) {
2404 Size tradeIdColumn = 0;
2405 Size dateColumn = 4;
2406 Size amountColumn = 6;
2408 QL_REQUIRE(cashFlowReport->header(tradeIdColumn) ==
"TradeId",
"incorrect trade id column " << tradeIdColumn);
2409 QL_REQUIRE(cashFlowReport->header(amountColumn) ==
"Amount",
"incorrect trade id column " << amountColumn);
2410 QL_REQUIRE(cashFlowReport->header(ccyColumn) ==
"Currency",
"incorrect trade id column " << ccyColumn);
2411 QL_REQUIRE(cashFlowReport->header(dateColumn) ==
"PayDate",
"incorrect trade id column " << dateColumn);
2414 for (Size i = 0; i < cashFlowReport->rows(); ++i) {
2415 string id = boost::get<string>(cashFlowReport->data(tradeIdColumn).at(i));
2418 Date date = boost::get<Date>(cashFlowReport->data(dateColumn).at(i));
2419 if (date <= d0 || date > d1)
2421 string ccy = boost::get<string>(cashFlowReport->data(ccyColumn).at(i));
2422 Real amount = boost::get<Real>(cashFlowReport->data(amountColumn).at(i));
2424 if (ccy != baseCurrency)
2425 fx = market->fxRate(ccy + baseCurrency)->value();
2426 flow += fx * amount;
2433 const ext::shared_ptr<InMemoryReport>& t0NpvReport,
2434 const ext::shared_ptr<InMemoryReport>& t0NpvLaggedReport,
2435 const ext::shared_ptr<InMemoryReport>& t1NpvLaggedReport,
2436 const ext::shared_ptr<InMemoryReport>& t1NpvReport,
2437 const ext::shared_ptr<InMemoryReport>& t0CashFlowReport,
2438 const Date& startDate,
const Date& endDate,
2439 const std::string& baseCurrency,
2440 const ext::shared_ptr<ore::data::Market>& market,
2441 const std::string& configuration,
2442 const ext::shared_ptr<Portfolio>& portfolio) {
2453 .
addColumn(
"NPV(asof=t0;mkt=t1)",
double(), 6)
2454 .
addColumn(
"NPV(asof=t1;mkt=t0)",
double(), 6)
2456 .
addColumn(
"PeriodCashFlow",
double(), 6)
2458 .
addColumn(
"HypotheticalCleanPnL",
double(), 6)
2463 Size tradeIdColumn = 0;
2464 Size tradeTypeColumn = 1;
2465 Size maturityDateColumn = 2;
2466 Size maturityTimeColumn = 3;
2467 Size npvBaseColumn = 6;
2468 Size baseCcyColumn = 7;
2470 QL_REQUIRE(t0NpvReport->rows() == t0NpvLaggedReport->rows(),
"different number of rows in npv reports");
2471 QL_REQUIRE(t0NpvReport->rows() == t1NpvLaggedReport->rows(),
"different number of rows in npv reports");
2472 QL_REQUIRE(t0NpvReport->rows() == t1NpvReport->rows(),
"different number of rows in npv reports");
2474 QL_REQUIRE(t0NpvReport->header(tradeIdColumn) ==
"TradeId",
"incorrect trade id column " << tradeIdColumn);
2475 QL_REQUIRE(t0NpvReport->header(tradeTypeColumn) ==
"TradeType",
"incorrect trade type column " << tradeTypeColumn);
2476 QL_REQUIRE(t0NpvReport->header(maturityDateColumn) ==
"Maturity",
"incorrect maturity date column " << maturityDateColumn);
2477 QL_REQUIRE(t0NpvReport->header(maturityTimeColumn) ==
"MaturityTime",
"incorrect maturity time column " << maturityTimeColumn);
2478 QL_REQUIRE(t0NpvReport->header(npvBaseColumn) ==
"NPV(Base)",
"incorrect npv base column " << npvBaseColumn);
2479 QL_REQUIRE(t0NpvReport->header(baseCcyColumn) ==
"BaseCurrency",
"incorrect base currency column " << baseCcyColumn);
2481 QL_REQUIRE(t0NpvLaggedReport->header(tradeIdColumn) ==
"TradeId",
"incorrect trade id column " << tradeIdColumn);
2482 QL_REQUIRE(t0NpvLaggedReport->header(tradeTypeColumn) ==
"TradeType",
"incorrect trade type column " << tradeTypeColumn);
2483 QL_REQUIRE(t0NpvLaggedReport->header(maturityDateColumn) ==
"Maturity",
"incorrect maturity date column " << maturityDateColumn);
2484 QL_REQUIRE(t0NpvLaggedReport->header(maturityTimeColumn) ==
"MaturityTime",
"incorrect maturity time column " << maturityTimeColumn);
2485 QL_REQUIRE(t0NpvLaggedReport->header(npvBaseColumn) ==
"NPV(Base)",
"incorrect npv base column " << npvBaseColumn);
2486 QL_REQUIRE(t0NpvLaggedReport->header(baseCcyColumn) ==
"BaseCurrency",
"incorrect base currency column " << baseCcyColumn);
2488 QL_REQUIRE(t1NpvLaggedReport->header(tradeIdColumn) ==
"TradeId",
"incorrect trade id column " << tradeIdColumn);
2489 QL_REQUIRE(t1NpvLaggedReport->header(tradeTypeColumn) ==
"TradeType",
"incorrect trade type column " << tradeTypeColumn);
2490 QL_REQUIRE(t1NpvLaggedReport->header(maturityDateColumn) ==
"Maturity",
"incorrect maturity date column " << maturityDateColumn);
2491 QL_REQUIRE(t1NpvLaggedReport->header(maturityTimeColumn) ==
"MaturityTime",
"incorrect maturity time column " << maturityTimeColumn);
2492 QL_REQUIRE(t1NpvLaggedReport->header(npvBaseColumn) ==
"NPV(Base)",
"incorrect npv base column " << npvBaseColumn);
2493 QL_REQUIRE(t1NpvLaggedReport->header(baseCcyColumn) ==
"BaseCurrency",
"incorrect base currency column " << baseCcyColumn);
2495 QL_REQUIRE(t1NpvReport->header(tradeIdColumn) ==
"TradeId",
"incorrect trade id column " << tradeIdColumn);
2496 QL_REQUIRE(t1NpvReport->header(tradeTypeColumn) ==
"TradeType",
"incorrect trade type column " << tradeTypeColumn);
2497 QL_REQUIRE(t1NpvReport->header(maturityDateColumn) ==
"Maturity",
"incorrect maturity date column " << maturityDateColumn);
2498 QL_REQUIRE(t1NpvReport->header(maturityTimeColumn) ==
"MaturityTime",
"incorrect maturity time column " << maturityTimeColumn);
2499 QL_REQUIRE(t1NpvReport->header(npvBaseColumn) ==
"NPV(Base)",
"incorrect npv base column " << npvBaseColumn);
2500 QL_REQUIRE(t1NpvReport->header(baseCcyColumn) ==
"BaseCurrency",
"incorrect base currency column " << baseCcyColumn);
2502 for (Size i = 0; i < t0NpvReport->rows(); ++i) {
2504 string tradeId = boost::get<string>(t0NpvReport->data(tradeIdColumn).at(i));
2505 string tradeId2 = boost::get<string>(t0NpvLaggedReport->data(tradeIdColumn).at(i));
2506 string tradeId3 = boost::get<string>(t1NpvLaggedReport->data(tradeIdColumn).at(i));
2507 string tradeId4 = boost::get<string>(t1NpvReport->data(tradeIdColumn).at(i));
2508 QL_REQUIRE(tradeId == tradeId2 && tradeId == tradeId3 && tradeId == tradeId4,
"inconsistent ordering of NPV reports");
2509 string tradeType = boost::get<string>(t0NpvReport->data(tradeTypeColumn).at(i));
2510 Date maturityDate = boost::get<Date>(t0NpvReport->data(maturityDateColumn).at(i));
2511 Real maturityTime = boost::get<Real>(t0NpvReport->data(maturityTimeColumn).at(i));
2512 string ccy = boost::get<string>(t0NpvReport->data(baseCcyColumn).at(i));
2513 QL_REQUIRE(ccy == baseCurrency,
"inconsistent NPV and base currencies");
2514 Real t0Npv = boost::get<Real>(t0NpvReport->data(npvBaseColumn).at(i));
2515 Real t0NpvLagged = boost::get<Real>(t0NpvLaggedReport->data(npvBaseColumn).at(i));
2516 Real t1NpvLagged = boost::get<Real>(t1NpvLaggedReport->data(npvBaseColumn).at(i));
2517 Real t1Npv = boost::get<Real>(t1NpvReport->data(npvBaseColumn).at(i));
2519 Real hypotheticalCleanPnl = t0NpvLagged - t0Npv;
2520 Real periodFlow =
aggregateTradeFlow(tradeId, startDate, endDate, t0CashFlowReport, market, baseCurrency);
2521 Real theta = t1NpvLagged - t0Npv + periodFlow;
2522 Real dirtyPnl = t1Npv - t0Npv;
2523 Real cleanPnl = dirtyPnl + periodFlow;
2524 LOG(
"PnL report, writing line " << i <<
" tradeId " << tradeId);
2539 .
add(hypotheticalCleanPnl)
2544 }
catch (std::exception& e) {
2545 ALOG(
"Error writing PnL report line: " << e.what());
2551 LOG(
"PnL report written.");
Container for storing simulated market data.
const std::string & currency() const
const std::map< CrifRecord::ProductClass, IMScheduleResult > & data() const
Return the map containing the results.
virtual void writeNettingSetCvaSensitivities(ore::data::Report &report, QuantLib::ext::shared_ptr< PostProcess > postProcess, const std::string &nettingSetId)
virtual void writeCashflowNpv(ore::data::Report &report, const ore::data::InMemoryReport &cashflowReport, QuantLib::ext::shared_ptr< ore::data::Market > market, const std::string &configuration, const std::string &baseCcy, const Date &horizon=Date::maxDate())
virtual void writeNettingSetExposures(ore::data::Report &report, QuantLib::ext::shared_ptr< PostProcess > postProcess, const std::string &nettingSetId)
virtual void writeCurves(ore::data::Report &report, const std::string &configID, const DateGrid &grid, const TodaysMarketParameters &marketConfig, const QuantLib::ext::shared_ptr< Market > &market, const bool continueOnError=false)
virtual void writeTradeExposures(ore::data::Report &report, QuantLib::ext::shared_ptr< PostProcess > postProcess, const std::string &tradeId)
virtual void writeCashflow(ore::data::Report &report, const std::string &baseCurrency, QuantLib::ext::shared_ptr< ore::data::Portfolio > portfolio, QuantLib::ext::shared_ptr< ore::data::Market > market=QuantLib::ext::shared_ptr< ore::data::Market >(), const std::string &configuration=ore::data::Market::defaultConfiguration, const bool includePastCashflows=false)
virtual void writeXVA(ore::data::Report &report, const string &allocationMethod, QuantLib::ext::shared_ptr< Portfolio > portfolio, QuantLib::ext::shared_ptr< PostProcess > postProcess)
virtual void writeNpv(ore::data::Report &report, const std::string &baseCurrency, QuantLib::ext::shared_ptr< ore::data::Market > market, const std::string &configuration, QuantLib::ext::shared_ptr< Portfolio > portfolio)
Data types stored in the scenario class.
Class for writing scenarios to file.
void writeScenario(const QuantLib::ext::shared_ptr< Scenario > &s, const bool writeHeader)
Write a single scenario.
SimmSide
Enum indicating the relevant side of the SIMM calculation.
QuantLib::Size size() const
const std::vector< QuantLib::Period > & tenors() const
const string & header(Size i) const
const vector< ReportType > & data(Size i) const
const Handle< Quote > & quote() const
const string & name() const
const map< string, string > mapRepresentation() const
virtual Report & add(const ReportType &rt)=0
virtual Report & next()=0
virtual Report & addColumn(const string &name, const ReportType &, Size precision=0)=0
bool hasConfiguration(const string &configuration) const
const map< string, string > & mapping(const MarketObject o, const string &configuration) const
bool hasMarketObject(const MarketObject &o) const
pair< string, string > parseBoostAny(const boost::any &anyType, Size precision)
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)
RandomVariable log(RandomVariable x)
boost::shared_ptr< Coupon > unpackIndexedCoupon(const boost::shared_ptr< Coupon > &c)
Matrix generator(const Matrix &t, const Real horizon)
void addMapResults(boost::any resultMap, const std::string &tradeId, const std::string &resultName, Report &report)
SimmConfiguration::RiskClass RiskClass
SimmConfiguration::MarginType MarginType
CrifRecord::ProductClass ProductClass
SimmConfiguration::IMModel parseIMModel(const string &model)
string reconstructFactor(const RiskFactorKey &key, const string &desc)
Reconstruct the string description from a risk factor key and its index description desc.
std::map< Currency, std::vector< Real >, CurrencyComparator > result_type_vector
std::string escapeCommaSeparatedList(const std::string &str, const char &csvQuoteChar)
std::map< Currency, Real, CurrencyComparator > result_type_scalar
void addNettingSetExposure(ore::data::Report &report, QuantLib::ext::shared_ptr< PostProcess > postProcess, const string &nettingSetId)
Real aggregateTradeFlow(const std::string &tradeId, const Date &d0, const Date &d1, const ext::shared_ptr< InMemoryReport > &cashFlowReport, const ext::shared_ptr< ore::data::Market > &market, const std::string &baseCurrency)
std::map< Currency, Matrix, CurrencyComparator > result_type_matrix
std::string prettyPrintInternalCurveName(std::string name)
Size size(const ValueType &v)
std::string to_string(const LocationInfo &l)
void partitionQuotes(const set< string > "eNames, set< string > &names, set< string > ®exes)
A Class to write ORE outputs to reports.
std::string presentValueCcy
QuantLib::Real presentValue
QuantLib::Real multiplier
std::string collectRegulations
QuantLib::Real grossMarginCalc
std::string calculationCcy
ore::data::NettingSetDetails nettingSetDetails
CrifRecord::ProductClass productClass
std::string postRegulations
QuantLib::Real notionalCalc
QuantLib::Real presentValueCalc
QuantLib::Real scheduleIM
Structured analytics error.
std::vector< Size > steps