Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
trs.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2020 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
25
30
31#include <ql/cashflows/cashflows.hpp>
32#include <ql/cashflows/fixedratecoupon.hpp>
33
34#include <boost/assign/list_of.hpp>
35#include <boost/bimap.hpp>
36#include <boost/optional.hpp>
37#include <boost/range/adaptor/map.hpp>
38
39namespace ore {
40namespace data {
41
42void addTRSRequiredFixings(RequiredFixings& fixings, const std::vector<Leg>& returnLegs,
43 const QuantLib::ext::shared_ptr<QuantExt::FxIndex>& ind = nullptr) {
44 QL_REQUIRE(returnLegs.size() > 0, "TrsUnderlyingBuilder: No returnLeg built");
45 auto fdg = QuantLib::ext::make_shared<FixingDateGetter>(fixings);
46 fdg->setAdditionalFxIndex(ind);
47 for (const auto& rl : returnLegs)
48 addToRequiredFixings(rl, fdg);
49}
50
52 XMLUtils::checkNode(node, "ReturnData");
53 payer_ = XMLUtils::getChildValueAsBool(node, "Payer", true);
54 currency_ = XMLUtils::getChildValue(node, "Currency", true);
55 scheduleData_.fromXML(XMLUtils::getChildNode(node, "ScheduleData"));
56 observationLag_ = XMLUtils::getChildValue(node, "ObservationLag", false);
57 observationConvention_ = XMLUtils::getChildValue(node, "ObservationConvention", false);
58 observationCalendar_ = XMLUtils::getChildValue(node, "ObservationCalendar", false);
59 paymentLag_ = XMLUtils::getChildValue(node, "PaymentLag", false);
60 paymentConvention_ = XMLUtils::getChildValue(node, "PaymentConvention", false);
61 paymentCalendar_ = XMLUtils::getChildValue(node, "PaymentCalendar", false);
62 paymentDates_ = XMLUtils::getChildrenValues(node, "PaymentDates", "PaymentDate", false);
63 initialPrice_ = Null<Real>();
64 if (auto n = XMLUtils::getChildNode(node, "InitialPrice")) {
66 }
67 initialPriceCurrency_ = XMLUtils::getChildValue(node, "InitialPriceCurrency");
69 if (auto n = XMLUtils::getChildNode(node, "PayUnderlyingCashFlowsImmediately")) {
71 }
72 fxTerms_ = XMLUtils::getChildrenValues(node, "FXTerms", "FXIndex", false);
73}
74
76 XMLNode* n = doc.allocNode("ReturnData");
77 XMLUtils::addChild(doc, n, "Payer", payer_);
78 XMLUtils::addChild(doc, n, "Currency", currency_);
79 XMLUtils::appendNode(n, scheduleData_.toXML(doc));
80 if (!observationLag_.empty())
81 XMLUtils::addChild(doc, n, "ObservationLag", observationLag_);
82 if (!observationConvention_.empty())
83 XMLUtils::addChild(doc, n, "ObservationConvention", observationConvention_);
84 if (!observationCalendar_.empty())
85 XMLUtils::addChild(doc, n, "ObservationCalendar", observationCalendar_);
86 if (!paymentLag_.empty())
87 XMLUtils::addChild(doc, n, "PaymentLag", paymentLag_);
88 if (!paymentConvention_.empty())
89 XMLUtils::addChild(doc, n, "PaymentConvention", paymentConvention_);
90 if (!paymentCalendar_.empty())
91 XMLUtils::addChild(doc, n, "PaymentCalendar", paymentCalendar_);
92 if (!paymentDates_.empty())
93 XMLUtils::addChildren(doc, n, "PaymentDates", "PaymentDate", paymentDates_);
94 if (initialPrice_ != Null<Real>())
95 XMLUtils::addChild(doc, n, "InitialPrice", initialPrice_);
96 if (!initialPriceCurrency_.empty())
97 XMLUtils::addChild(doc, n, "InitialPriceCurrency", initialPriceCurrency_);
98 if (payUnderlyingCashFlowsImmediately_)
99 XMLUtils::addChild(doc, n, "PayUnderlyingCashFlowsImmediately", *payUnderlyingCashFlowsImmediately_);
100 if (!fxTerms_.empty())
101 XMLUtils::addChildren(doc, n, "FXTerms", "FXIndex", fxTerms_);
102 return n;
103}
104
106 XMLUtils::checkNode(node, "FundingData");
107 vector<XMLNode*> nodes = XMLUtils::getChildrenNodes(node, "LegData");
108 for (auto const n : nodes) {
109 LegData ld;
110 ld.fromXML(n);
111 legData_.push_back(ld);
112 }
113 vector<XMLNode*> nodes2 = XMLUtils::getChildrenNodes(node, "NotionalType");
114 for (auto const n : nodes2) {
115 notionalType_.push_back(parseTrsFundingNotionalType(XMLUtils::getNodeValue(n)));
116 }
117 fundingResetGracePeriod_ = XMLUtils::getChildValueAsInt(node, "FundingResetGracePeriod", false, 0);
118}
119
121 XMLNode* n = doc.allocNode("FundingData");
122 for (auto& l : legData_) {
123 XMLUtils::appendNode(n, l.toXML(doc));
124 }
125 for (auto const t : notionalType_) {
126 XMLUtils::addChild(doc, n, "NotionalType", ore::data::to_string(t));
127 }
128 if(fundingResetGracePeriod_ > 0) {
129 XMLUtils::addChild(doc, n, "FundingResetGracePeriod", std::to_string(fundingResetGracePeriod_));
130 }
131 return n;
132}
133
135 XMLUtils::checkNode(node, "AdditionalCashflowData");
136 XMLNode* tmp = XMLUtils::getChildNode(node, "LegData");
137 if (tmp)
138 legData_.fromXML(tmp);
139 else
140 legData_ = LegData();
141}
142
144 XMLNode* n = doc.allocNode("AdditionalCashflowData");
145 if (legData_.concreteLegData())
146 XMLUtils::appendNode(n, legData_.toXML(doc));
147 return n;
148}
149
150std::map<AssetClass, std::set<std::string>>
151TRS::underlyingIndices(const QuantLib::ext::shared_ptr<ReferenceDataManager>& referenceDataManager) const {
152 std::map<AssetClass, std::set<std::string>> result;
153 for (Size i = 0; i < underlying_.size(); ++i) {
154 QL_REQUIRE(underlying_[i], "TRS::underlyingIndices(): underlying trade is null");
155 // a builder might update the underlying (e.g. promote it from bond to convertible bond)
156 if (underlyingDerivativeId_[i].empty()) {
157 for (auto const& b : TrsUnderlyingBuilderFactory::instance().getBuilders()) {
158 b.second->updateUnderlying(referenceDataManager, underlying_[i], id());
159 }
160 }
161 for (auto const& tmp : underlying_[i]->underlyingIndices(referenceDataManager))
162 result[tmp.first].insert(tmp.second.begin(), tmp.second.end());
163 }
164 return result;
165}
166
168 Trade::fromXML(node);
169
170 // set id early since we use it below to set the underlying trade's id
171 this->id() = XMLUtils::getAttribute(node, "id");
172
173 // trs data node
174 XMLNode* dataNode = XMLUtils::getChildNode(node, tradeType_ + "Data");
175 QL_REQUIRE(dataNode, tradeType_ + "Data node required");
176
177 // read underlying data
178 XMLNode* underlyingDataNode = XMLUtils::getChildNode(dataNode, "UnderlyingData");
179 QL_REQUIRE(underlyingDataNode, "UnderlyingData node required");
180 std::vector<XMLNode*> underlyingTradeNodes = XMLUtils::getChildrenNodes(underlyingDataNode, "Trade");
181 std::vector<XMLNode*> underlyingTradeNodes2 = XMLUtils::getChildrenNodes(underlyingDataNode, "Derivative");
182 QL_REQUIRE(!underlyingTradeNodes.empty() || !underlyingTradeNodes2.empty(),
183 "at least one 'Trade' or 'Derivative' node required");
184 Size underlyingCounter = 0;
185 underlying_.clear();
187 for (auto const n : underlyingTradeNodes) {
188 std::string tradeType = XMLUtils::getChildValue(n, "TradeType", true);
189 QuantLib::ext::shared_ptr<Trade> u;
190 try {
191 u = TradeFactory::instance().build(tradeType);
192 } catch (const std::exception& e) {
193 QL_FAIL("Failed for build TRS underlying trade # " << underlyingCounter + 1 << ": " << e.what());
194 }
195 u->id() = this->id() + "_underlying" +
196 (underlyingTradeNodes.size() > 1 ? "_" + std::to_string(underlyingCounter++) : "");
197 u->fromXML(n);
198 underlyingDerivativeId_.push_back(std::string());
199 underlying_.push_back(u);
200 }
201 for (auto const n : underlyingTradeNodes2) {
202 underlyingDerivativeId_.push_back(XMLUtils::getChildValue(n, "Id", true));
203 auto t = XMLUtils::getChildNode(n, "Trade");
204 QL_REQUIRE(t != nullptr, "expected 'Trade' node under 'Derivative' node");
205 std::string tradeType = XMLUtils::getChildValue(t, "TradeType", true);
206 auto u = TradeFactory::instance().build(tradeType);
207 QL_REQUIRE(u, "No trade builder found for TRS derivative trade type '"
208 << tradeType << "' when processing underlying trade #" << (underlyingCounter + 1));
209 u->id() = this->id() + "_underlying" +
210 (underlyingTradeNodes.size() > 1 ? "_" + std::to_string(underlyingCounter++) : "");
211 u->fromXML(t);
212 underlying_.push_back(u);
213 }
214
215 // read return data
216 XMLNode* returnDataNode = XMLUtils::getChildNode(dataNode, "ReturnData");
217 returnData_.fromXML(returnDataNode);
218
219 // read funding data
220 XMLNode* fundingDataNode = XMLUtils::getChildNode(dataNode, "FundingData");
221 if (fundingDataNode)
222 fundingData_.fromXML(fundingDataNode);
223 else
225
226 // read additional cashflow data
227 XMLNode* additionalCashflowDataNode = XMLUtils::getChildNode(dataNode, "AdditionalCashflowData");
228 if (additionalCashflowDataNode)
229 additionalCashflowData_.fromXML(additionalCashflowDataNode);
230 else
232}
233
235 XMLNode* node = Trade::toXML(doc);
236 XMLNode* dataNode = doc.allocNode(tradeType_ + "Data");
237 XMLUtils::appendNode(node, dataNode);
238
239 XMLNode* underlyingDataNode = doc.allocNode("UnderlyingData");
240 XMLUtils::appendNode(dataNode, underlyingDataNode);
241
242 for (Size i = 0; i < underlying_.size(); ++i) {
243 if (underlyingDerivativeId_[i].empty()) {
244 XMLUtils::appendNode(underlyingDataNode, underlying_[i]->toXML(doc));
245 } else {
246 auto d = XMLUtils::addChild(doc, underlyingDataNode, "Derivative");
249 }
250 }
251
252 XMLUtils::appendNode(dataNode, returnData_.toXML(doc));
253 if (!fundingData_.legData().empty())
257
258 return node;
259}
260
261QuantLib::ext::shared_ptr<QuantExt::FxIndex>
262TRS::getFxIndex(const QuantLib::ext::shared_ptr<Market> market, const std::string& configuration, const std::string& domestic,
263 const std::string& foreign, std::map<std::string, QuantLib::ext::shared_ptr<QuantExt::FxIndex>>& fxIndices,
264 std::set<std::string>& missingFxIndexPairs) const {
265 if (domestic == foreign)
266 return nullptr;
267 std::set<std::string> requiredCcys = {domestic, foreign};
268 for (auto const& f : returnData_.fxTerms()) {
269 auto fx = parseFxIndex(f);
270 std::set<std::string> indexCcys = {fx->sourceCurrency().code(), fx->targetCurrency().code()};
271 if (requiredCcys == indexCcys) {
272 auto h = fxIndices.find(f);
273 if (h != fxIndices.end())
274 return h->second;
275 DLOG("setting up fx index for domestic=" << domestic << " foreign=" << foreign);
276 auto fx = buildFxIndex(f, domestic, foreign, market, configuration, false);
277 fxIndices[f] = fx;
278 return fx;
279 }
280 }
281
282 // build a fx index, so that the processing can continue, but add to the error messages,
283 // which - if not empty - will fail the trade build eventually
284
285 std::string f("FX-GENERIC-" + domestic + "-" + foreign);
286 auto fx = buildFxIndex(f, domestic, foreign, market, configuration, false);
287 fxIndices[f] = fx;
288 missingFxIndexPairs.insert(domestic + foreign);
289 return fx;
290}
291
292void TRS::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
293
294 DLOG("TRS::build() called for id = " << id());
295
296 // clear trade members
297
298 reset();
299
300 creditRiskCurrency_.clear();
303
304 // checks
305
306 std::set<bool> fundingLegPayers;
307 std::set<std::string> fundingCurrencies;
308
309 bool fundingLegPayer = !(returnData_.payer());
310 std::string fundingCurrency = returnData_.currency();
311
312 for (auto const& l : fundingData_.legData()) {
313 fundingLegPayer = l.isPayer();
314 fundingCurrency = l.currency();
315 fundingLegPayers.insert(fundingLegPayer);
316 fundingCurrencies.insert(fundingCurrency);
317 }
318
319 QL_REQUIRE(fundingLegPayers.size() <= 1, "funding leg payer flags must match");
320 QL_REQUIRE(fundingCurrencies.size() <= 1, "funding leg currencies must match");
321
322 // a builder might update the underlying (e.g. promote it from bond to convertible bond)
323
324 for (Size i = 0; i < underlying_.size(); ++i) {
325 if (underlyingDerivativeId_[i].empty()) {
326 for (auto const& b : TrsUnderlyingBuilderFactory::instance().getBuilders()) {
327 b.second->updateUnderlying(engineFactory->referenceData(), underlying_[i], id());
328 }
329 }
330 }
331
332 // build underlying trade, add required fixing from there to this trade
333
334 for (Size i = 0; i < underlying_.size(); ++i) {
335 DLOG("build underlying trade #" << (i + 1) << " of type '" << underlying_[i]->tradeType() << "'");
336 underlying_[i]->reset();
337 underlying_[i]->build(engineFactory);
339 // populate sensi template from first underlying, we have to make _some_ assumption here!
340 if (sensitivityTemplate_.empty()) {
342 }
343 }
344
345 // propagate additional data from underlyings to trs trade
346 for (Size i = 0; i < underlying_.size(); ++i) {
347 for (auto const& [key, value] : underlying_[i]->additionalData()) {
348 additionalData_["und_ad_" + std::to_string(i + 1) + "_" + key] = value;
349 }
350 }
351
352 // we use dirty prices, so we need accrued amounts in the past
354
355 // build return leg valuation and payment date vectors
356
357 DLOG("build valuation and payment dates vectors");
358
359 std::vector<Date> valuationDates, paymentDates;
360
361 QuantLib::Schedule schedule = makeSchedule(returnData_.scheduleData());
362 QL_REQUIRE(schedule.dates().size() >= 2, "at least two dates required in return schedule");
363
364 Calendar observationCalendar = parseCalendar(returnData_.observationCalendar());
365 BusinessDayConvention observationConvention = returnData_.observationConvention().empty()
366 ? Unadjusted
368 Period observationLag = returnData_.observationLag().empty() ? 0 * Days : parsePeriod(returnData_.observationLag());
369
370 Calendar paymentCalendar = parseCalendar(returnData_.paymentCalendar());
371 BusinessDayConvention paymentConvention = returnData_.paymentConvention().empty()
372 ? Unadjusted
375 Period plPeriod = boost::apply_visitor(PaymentLagPeriod(), paymentLag);
376
377 for (auto const& d : schedule.dates()) {
378 valuationDates.push_back(observationCalendar.advance(d, -observationLag, observationConvention));
379 if (d != schedule.dates().front())
380 paymentDates.push_back(paymentCalendar.advance(d, plPeriod, paymentConvention));
381 }
382
383 if (!returnData_.paymentDates().empty()) {
384 paymentDates.clear();
385 QL_REQUIRE(returnData_.paymentDates().size() + 1 == valuationDates.size(),
386 "paymentDates size (" << returnData_.paymentDates().size() << ") does no match valuatioDates size ("
387 << valuationDates.size() << ") minus 1");
388 for (auto const& s : returnData_.paymentDates())
389 paymentDates.push_back(parseDate(s));
390 }
391
392 DLOG("valuation schedule:");
393 for (auto const& d : valuationDates)
395
396 DLOG("payment schedule:");
397 for (auto const& d : paymentDates)
399
400 // build indices corresponding to underlying trades and populate necessary data
401
402 std::map<std::string, double> indexNamesAndQty;
403 std::map<std::string, QuantLib::ext::shared_ptr<QuantExt::FxIndex>> initialFxIndices, fxIndices, fxIndicesDummy;
404
405 // get fx indices for conversion return and add cf ccy to funding ccy
406
407 std::set<std::string> missingFxIndexPairs;
408
409 auto fxIndexReturn = getFxIndex(engineFactory->market(), engineFactory->configuration(MarketContext::pricing),
410 returnData_.currency(), fundingCurrency, initialFxIndices, missingFxIndexPairs);
411 auto fxIndexAdditionalCashflows =
413 ? fxIndexReturn
414 : getFxIndex(engineFactory->market(), engineFactory->configuration(MarketContext::pricing),
415 additionalCashflowData_.legData().currency(), fundingCurrency, fxIndicesDummy,
416 missingFxIndexPairs);
417
418 Real initialPrice = returnData_.initialPrice();
419
420 std::vector<QuantLib::ext::shared_ptr<QuantLib::Index>> underlyingIndex(underlying_.size(), nullptr);
421 std::vector<Real> underlyingMultiplier(underlying_.size(), Null<Real>());
422 std::vector<std::string> assetCurrency(underlying_.size(), fundingCurrency);
423 std::vector<QuantLib::ext::shared_ptr<QuantExt::FxIndex>> fxIndexAsset(underlying_.size(), nullptr);
424
425 maturity_ = Date::minDate();
426
427 for (Size i = 0; i < underlying_.size(); ++i) {
428
429 DLOG("build underlying index for underlying #" << (i + 1));
430
431 std::string localCreditRiskCurrency;
432 Date localMaturity;
433 std::map<std::string, double> localIndexNamesAndQuantities;
434 std::map<std::string, QuantLib::ext::shared_ptr<QuantExt::FxIndex>> localFxIndices = initialFxIndices;
435 Real dummyInitialPrice = 1.0; // initial price is only updated if we have one underlying
436
437 std::vector<Leg> returnLegs;
438 auto builder = TrsUnderlyingBuilderFactory::instance().getBuilder(
439 underlyingDerivativeId_[i].empty() ? underlying_[i]->tradeType() : "Derivative");
440 builder->build(id(), underlying_[i], valuationDates, paymentDates, fundingCurrency, engineFactory,
441 underlyingIndex[i], underlyingMultiplier[i], localIndexNamesAndQuantities, localFxIndices,
442 underlying_.size() == 1 ? initialPrice : dummyInitialPrice, assetCurrency[i],
443 localCreditRiskCurrency, creditQualifierMapping_, localMaturity,
444 std::bind(&TRS::getFxIndex, this, std::placeholders::_1, std::placeholders::_2,
445 std::placeholders::_3, std::placeholders::_4, std::placeholders::_5,
446 std::ref(missingFxIndexPairs)),
448
449 addTRSRequiredFixings(requiredFixings_, returnLegs, fxIndexReturn);
450
451 // update global credit risk currency
452
453 if (creditRiskCurrency_.empty()) {
454 creditRiskCurrency_ = localCreditRiskCurrency;
455 } else if (!localCreditRiskCurrency.empty() && creditRiskCurrency_ != localCreditRiskCurrency) {
456 ore::data::StructuredTradeErrorMessage(id(), tradeType(), "Ambiguous SIMM CreditQ currencies for TRS",
457 "Will use '" + creditRiskCurrency_ + "', found '" +
458 localCreditRiskCurrency + "' in addition.")
459 .log();
460 }
461
462 // update global maturity date
463
464 maturity_ = std::max(maturity_, localMaturity);
465
466 // get fx indices for conversion of asset to funding ccy
467
468 DLOG("underlying #" << (i + 1) << " has asset ccy " << assetCurrency[i] << ", funding ccy is "
469 << fundingCurrency << ", return ccy is " << returnData_.currency());
470
471 fxIndexAsset[i] = getFxIndex(engineFactory->market(), engineFactory->configuration(MarketContext::pricing),
472 assetCurrency[i], fundingCurrency, localFxIndices, missingFxIndexPairs);
473 DLOG("underlying #" << (i + 1) << " index (" << underlyingIndex[i]->name() << ") built.");
474 DLOG("underlying #" << (i + 1) << " multiplier is " << underlyingMultiplier[i]);
475
476 // update global indexNames and fxIndices
477
478 for (const auto& [indexName, qty] : localIndexNamesAndQuantities) {
479 indexNamesAndQty[indexName] += (returnData_.payer() ? -1.0 : 1.0) * qty;
480 }
481
482 fxIndices.insert(localFxIndices.begin(), localFxIndices.end());
483 }
484
485 // ISDA taxonomy
486 bool assetClassIsUnique = true;
487 std::string assetClass;
488 for (auto u : underlying_) {
489 auto it = u->additionalData().find("isdaAssetClass");
490 if (it != u->additionalData().end()) {
491 std::string ac = boost::any_cast<std::string>(it->second);
492 if (assetClass == "")
493 assetClass = ac;
494 else if (ac != assetClass)
495 assetClassIsUnique = false;
496 }
497 }
498
499 additionalData_["isdaAssetClass"] = string("");
500 additionalData_["isdaBaseProduct"] = string("");
501 additionalData_["isdaSubProduct"] = string("");
502 additionalData_["isdaTransaction"] = string("");
503
504 if (assetClass == "") {
505 ALOG("ISDA asset class not found for TRS " << id() << ", ISDA taxonomy undefined");
506 } else {
507 additionalData_["isdaAssetClass"] = assetClass;
508 if (!assetClassIsUnique) {
509 WLOG("ISDA asset class not unique in TRS " << id() << " using first hit: " << assetClass);
510 }
511 additionalData_["isdaBaseProduct"] = string("Total Return Swap");
512 if (assetClass == "Equity") {
513 if (tradeType_ == "ContractForDifference")
514 additionalData_["isdaBaseProduct"] = string("Contract For Difference");
515 else
516 additionalData_["isdaBaseProduct"] = string("Swap");
517 additionalData_["isdaSubProduct"] = string("Price Return Basic Performance");
518 } else if (assetClass == "Credit") {
519 additionalData_["isdaBaseProduct"] = string("Total Return Swap");
520 additionalData_["isdaSubProduct"] = string("");
521 } else {
522 WLOG("ISDA asset class " << assetClass << " not explicitly covered for TRS trade " << id()
523 << " using default BaseProduct 'Total Retuen Swap' and leaving sub-product blank");
524 }
525 }
526
527 // check that we have all fx terms that we needed to build the fx indices
528
529 QL_REQUIRE(missingFxIndexPairs.empty(), "TRS::build(): missing FXTerms for the following pairs: "
530 << boost::algorithm::join(missingFxIndexPairs, ", "));
531
532 // set initial price currency
533
534 QL_REQUIRE(!assetCurrency.empty(), "TRS::build(): no underlying given.");
535
536 std::string initialPriceCurrency =
537 returnData_.initialPriceCurrency().empty() ? assetCurrency.front() : returnData_.initialPriceCurrency();
538
539 if (initialPrice != Null<Real>() && returnData_.initialPriceCurrency().empty()) {
540 for (auto const& ccy : assetCurrency) {
541 QL_REQUIRE(ccy == initialPriceCurrency, "TRS::build(): can not determine unique initial price currency "
542 "from asset currencies for initial price ("
544 << "), please add the initial price currency to the trade xml");
545 }
546 }
547
548 // log some results from the build, convert initial price to major ccy if necessary
549
550 if (initialPrice != Null<Real>()) {
551 DLOG("initial price is given as " << initialPrice << " " << initialPriceCurrency);
552 initialPrice = convertMinorToMajorCurrency(initialPriceCurrency, initialPrice);
553 DLOG("initial price after conversion to major ccy " << initialPrice);
554 } else {
555 DLOG("no initial price is given");
556 }
557
558 DLOG("fundingCurrency is " << fundingCurrency);
559 DLOG("creditRiskCurrency is " << creditRiskCurrency_);
560 for (Size i = 0; i < assetCurrency.size(); ++i)
561 DLOG("assetCurrency #" << i << " is " << assetCurrency[i]);
562
563 // build funding legs, so far the supported types are
564 // Fixed, Floating
565
566 QL_REQUIRE(
567 fundingData_.notionalType().empty() || fundingData_.notionalType().size() == fundingData_.legData().size(),
568 "TRS::build(): got " << fundingData_.notionalType().size() << " NotionalType tags in FundingData, but "
569 << fundingData_.legData().size()
570 << " LegData nodes. These two must match. The NotionalType can also be omitted entirely.");
571
572 std::vector<Leg> fundingLegs;
573 std::vector<TRS::FundingData::NotionalType> fundingNotionalTypes;
574 for (Size i = 0; i < fundingData_.legData().size(); ++i) {
575
576 auto& ld = fundingData_.legData()[i];
577 QL_REQUIRE(ld.legType() == "Fixed" || ld.legType() == "Floating" || ld.legType() == "CMS" ||
578 ld.legType() == "CMB",
579 "TRS::build(): funding leg type: only fixed, floating, CMS, CMB are supported");
580 TRS::FundingData::NotionalType notionalType =
581 fundingData_.notionalType().empty() ? (ld.notionals().empty() ? TRS::FundingData::NotionalType::PeriodReset
584 QL_REQUIRE(ld.notionals().empty() || notionalType == TRS::FundingData::NotionalType::Fixed,
585 "TRS::build(): if notional is given in funding leg data, the notional type must be fixed, got "
586 << notionalType << " for funding leg #" << (i + 1));
587
588 struct TempNotionalSetter {
589 TempNotionalSetter(LegData& ld) : ld_(ld) {
590 if (ld.notionals().empty()) {
591 ld.notionals() = std::vector<Real>(1, 1.0);
592 ld.notionalDates().clear();
593 restoreEmptyLegDataNotionals_ = true;
594 } else {
595 restoreEmptyLegDataNotionals_ = false;
596 }
597 }
598 ~TempNotionalSetter() {
599 if (restoreEmptyLegDataNotionals_)
600 ld_.notionals().clear();
601 }
602 LegData& ld_;
603 bool restoreEmptyLegDataNotionals_;
604 };
605
606 TempNotionalSetter tmpSetter(ld);
607
608 auto legBuilder = engineFactory->legBuilder(ld.legType());
609 fundingLegs.push_back(legBuilder->buildLeg(ld, engineFactory, requiredFixings_,
610 engineFactory->configuration(MarketContext::pricing)));
611 fundingNotionalTypes.push_back(notionalType);
612
613 // update credit risk currency and credit qualifier mapping for CMB leg
614 if (ld.legType() == "CMB") {
615 auto cmbData = QuantLib::ext::dynamic_pointer_cast<ore::data::CMBLegData>(ld.concreteLegData());
616 QL_REQUIRE(cmbData, "TRS::build(): internal error, could to cast to CMBLegData.");
617 if(creditRiskCurrency_.empty())
618 creditRiskCurrency_ = getCmbLegCreditRiskCurrency(*cmbData, engineFactory->referenceData());
619 auto [source, target] =
620 getCmbLegCreditQualifierMapping(*cmbData, engineFactory->referenceData(), id(), tradeType());
621 creditQualifierMapping_[source] = target;
623 }
624 }
625
626 // add required fixings for funding legs with daily resets
627
628 DLOG("add required fixings for fundings legs with daily resets (if any)");
629
630 for (Size i = 0; i < fundingLegs.size(); ++i) {
631 if (fundingNotionalTypes[i] == TRS::FundingData::NotionalType::DailyReset) {
632 for (auto const& c : fundingLegs[i]) {
633 if (auto cpn = QuantLib::ext::dynamic_pointer_cast<QuantLib::Coupon>(c)) {
634 QL_REQUIRE(QuantLib::ext::dynamic_pointer_cast<QuantLib::FixedRateCoupon>(c) ||
635 QuantLib::ext::dynamic_pointer_cast<QuantLib::IborCoupon>(c) ||
636 QuantLib::ext::dynamic_pointer_cast<QuantExt::OvernightIndexedCoupon>(c) ||
637 QuantLib::ext::dynamic_pointer_cast<QuantExt::AverageONIndexedCoupon>(c),
638 "daily reset funding legs support fixed rate, ibor and overnight indexed coupons only");
639 for (QuantLib::Date d = cpn->accrualStartDate(); d < cpn->accrualEndDate(); ++d) {
640 for (Size j = 0; j < underlying_.size(); ++j) {
641 Date fixingDate = underlyingIndex[j]->fixingCalendar().adjust(d, Preceding);
642 for (auto const& [n, _] : indexNamesAndQty)
643 requiredFixings_.addFixingDate(fixingDate, n, cpn->date(), false, false);
644 for (auto const& n : fxIndices) {
645 requiredFixings_.addFixingDate(n.second->fixingCalendar().adjust(fixingDate, Preceding),
646 n.first, cpn->date(), false, false);
647 }
648 }
649 }
650 }
651 }
652 } else if (fundingNotionalTypes[i] == TRS::FundingData::NotionalType::PeriodReset) {
653 for (auto const& c : fundingLegs[i]) {
654 if (auto cpn = QuantLib::ext::dynamic_pointer_cast<QuantLib::Coupon>(c)) {
655 for (Size j = 0; j < underlying_.size(); ++j) {
656 Date fundingStartDate = cpn->accrualStartDate();
657 Size currentIdx = std::distance(valuationDates.begin(),
658 std::upper_bound(valuationDates.begin(),
659 valuationDates.end(),
660 fundingStartDate + fundingData_.fundingResetGracePeriod()));
661 if (currentIdx > 0)
662 --currentIdx;
663 Date fixingDate = valuationDates[currentIdx];
664 for (auto const& [n, _] : indexNamesAndQty)
665 requiredFixings_.addFixingDate(fixingDate, n, cpn->date(), false, false);
666 for (auto const& n : fxIndices) {
667 requiredFixings_.addFixingDate(n.second->fixingCalendar().adjust(fixingDate, Preceding),
668 n.first, cpn->date(), false, false);
669 }
670 }
671 }
672 }
673 }
674 }
675
676 // set start date
677
678 Date startDate = Date::maxDate();
679 for (auto const& l : fundingLegs) {
680 for (auto const& cf : l) {
681 QuantLib::ext::shared_ptr<Coupon> coupon = QuantLib::ext::dynamic_pointer_cast<Coupon>(cf);
682 if (coupon)
683 startDate = std::min(startDate, coupon->accrualStartDate());
684 }
685 }
686
687 additionalData_["startDate"] = to_string(startDate);
688 for (const auto& [name, qty] : indexNamesAndQty) {
689 additionalData_["underlying_quantity_" + name] = qty;
690 }
691
692 // build additional cashflow leg (if given)
693
694 DLOG("build additional cashflow leg");
695
696 Leg additionalCashflowLeg;
697 bool additionalCashflowLegPayer = false;
698 std::string additionalCashflowLegCurrency = fundingCurrency;
700 QL_REQUIRE(additionalCashflowData_.legData().legType() == "Cashflow",
701 "TRS::build(): additional cashflow data leg must have type 'Cashflow'");
702 additionalCashflowLeg = engineFactory->legBuilder(additionalCashflowData_.legData().legType())
703 ->buildLeg(additionalCashflowData_.legData(), engineFactory, requiredFixings_,
704 engineFactory->configuration(MarketContext::pricing));
705 additionalCashflowLegPayer = additionalCashflowData_.legData().isPayer();
706 additionalCashflowLegCurrency = additionalCashflowData_.legData().currency();
707 }
708
709 // parse asset currencies
710
711 std::vector<QuantLib::Currency> parsedAssetCurrencies;
712 for (auto const& c : assetCurrency) {
713 parsedAssetCurrencies.push_back(parseCurrency(c));
714 }
715
716 // build instrument
717
718 DLOG("build instrument and set trade member");
719
720 bool includeUnderlyingCashflowsInReturn;
722 includeUnderlyingCashflowsInReturn = !(*returnData_.payUnderlyingCashFlowsImmediately());
723 } else {
724 includeUnderlyingCashflowsInReturn = tradeType_ != "ContractForDifference";
725 }
726
727 auto wrapper = QuantLib::ext::make_shared<TRSWrapper>(
728 underlying_, underlyingIndex, underlyingMultiplier, includeUnderlyingCashflowsInReturn, initialPrice,
729 parseCurrencyWithMinors(initialPriceCurrency), parsedAssetCurrencies, parseCurrency(returnData_.currency()),
730 valuationDates, paymentDates, fundingLegs, fundingNotionalTypes, parseCurrency(fundingCurrency),
731 fundingData_.fundingResetGracePeriod(), returnData_.payer(), fundingLegPayer, additionalCashflowLeg,
732 additionalCashflowLegPayer, parseCurrency(additionalCashflowLegCurrency), fxIndexAsset, fxIndexReturn,
733 fxIndexAdditionalCashflows, fxIndices);
734 wrapper->setPricingEngine(QuantLib::ext::make_shared<TRSWrapperAccrualEngine>());
735 instrument_ = QuantLib::ext::make_shared<VanillaInstrument>(wrapper);
736
737 // if the first valuation date is > today, we potentially need fixings for fx conversion as of "today"
738
739 if (Date today = Settings::instance().evaluationDate(); !valuationDates.empty() && valuationDates.front() > today) {
740 std::set<QuantLib::ext::shared_ptr<QuantExt::FxIndex>> tmp;
741 auto fxIndicesVal = fxIndices | boost::adaptors::map_values;
742 tmp.insert(fxIndicesVal.begin(), fxIndicesVal.end());
743 tmp.insert(fxIndexAsset.begin(), fxIndexAsset.end());
744 tmp.insert(fxIndexReturn);
745 tmp.insert(fxIndexAdditionalCashflows);
746 for (auto const& fx : tmp) {
747 if (fx != nullptr) {
748 requiredFixings_.addFixingDate(fx->fixingCalendar().adjust(today, Preceding),
749 IndexNameTranslator::instance().oreName(fx->name()));
750 }
751 }
752 }
753
754 // set trade member variables (leave legs empty for the time being, we just have the funding leg really)
755
756 npvCurrency_ = fundingCurrency;
757
758 notional_ = 0.0; // we have overridden notional() to return this
759
760 // if the maturity date was not set by the trs underlying builder, set it here
761 if (maturity_ == Date::minDate()) {
762 maturity_ = std::max(valuationDates.back(), paymentDates.back());
763 for (auto const& l : fundingLegs) {
764 maturity_ = std::max(maturity_, CashFlows::maturityDate(l));
765 }
766 }
767}
768
769QuantLib::Real TRS::notional() const {
770 // try to get the notional from the additional results of the instrument
771 try {
772 return instrument_->qlInstrument()->result<Real>("currentNotional");
773 } catch (const std::exception& e) {
774 if (strcmp(e.what(), "currentNotional not provided"))
775 ALOG("error when retrieving notional: " << e.what());
776 }
777 // if not provided, return null
778 return Null<Real>();
779}
780
781// clang-format off
782boost::bimap<std::string, TRS::FundingData::NotionalType>
783 types = boost::assign::list_of<boost::bimap<std::string, TRS::FundingData::NotionalType>::relation>
787// clang-format on
788
790 auto it = types.left.find(s);
791 QL_REQUIRE(it != types.left.end(),
792 "parseTrsFundingNotionalType '" << s << "' failed, expected PeriodReset, DailyReset, Fixed");
793 return it->second;
794}
795
796std::ostream& operator<<(std::ostream& os, const TRS::FundingData::NotionalType t) {
797 auto it = types.right.find(t);
798 QL_REQUIRE(it != types.right.end(), "operator<<(" << static_cast<int>(t) << ") failed, this is an internal error.");
799 os << it->second;
800 return os;
801}
802
803} // namespace data
804} // namespace ore
void log() const
generate Boost log record to pass to corresponding sinks
Definition: log.cpp:491
Serializable object holding leg data.
Definition: legdata.hpp:844
const string & currency() const
Definition: legdata.hpp:873
bool isPayer() const
Definition: legdata.hpp:872
virtual void fromXML(XMLNode *node) override
Definition: legdata.cpp:759
const string & legType() const
Definition: legdata.hpp:890
const vector< string > & notionalDates() const
Definition: legdata.hpp:876
QuantLib::ext::shared_ptr< LegAdditionalData > concreteLegData() const
Definition: legdata.hpp:891
const vector< double > & notionals() const
Definition: legdata.hpp:875
void addData(const RequiredFixings &requiredFixings)
void addFixingDate(const QuantLib::Date &fixingDate, const std::string &indexName, const QuantLib::Date &payDate=Date::maxDate(), const bool alwaysAddIfPaysOnSettlement=false, const bool mandatoryFixing=true)
virtual void fromXML(XMLNode *node) override
Definition: schedule.cpp:179
Utility class for Structured Trade errors, contains the Trade ID and Type.
void fromXML(XMLNode *node) override
Definition: trs.cpp:134
XMLNode * toXML(XMLDocument &doc) const override
Definition: trs.cpp:143
const LegData & legData() const
Definition: trs.hpp:114
const std::vector< NotionalType > & notionalType() const
Definition: trs.hpp:95
void fromXML(XMLNode *node) override
Definition: trs.cpp:105
XMLNode * toXML(XMLDocument &doc) const override
Definition: trs.cpp:120
const std::vector< LegData > & legData() const
Definition: trs.hpp:93
QuantLib::Size fundingResetGracePeriod() const
Definition: trs.hpp:97
boost::optional< bool > payUnderlyingCashFlowsImmediately_
Definition: trs.hpp:82
std::string observationConvention_
Definition: trs.hpp:76
const std::string & observationConvention() const
Definition: trs.hpp:58
std::vector< std::string > fxTerms_
Definition: trs.hpp:81
std::string paymentLag_
Definition: trs.hpp:76
const std::vector< std::string > & paymentDates() const
Definition: trs.hpp:63
const std::string & currency() const
Definition: trs.hpp:55
const std::vector< std::string > & fxTerms() const
Definition: trs.hpp:66
std::string currency_
Definition: trs.hpp:74
const ScheduleData & scheduleData() const
Definition: trs.hpp:56
const std::string & initialPriceCurrency() const
Definition: trs.hpp:65
std::vector< std::string > paymentDates_
Definition: trs.hpp:78
boost::optional< bool > payUnderlyingCashFlowsImmediately() const
Definition: trs.hpp:67
const std::string & observationCalendar() const
Definition: trs.hpp:59
void fromXML(XMLNode *node) override
Definition: trs.cpp:51
XMLNode * toXML(XMLDocument &doc) const override
Definition: trs.cpp:75
const std::string & paymentCalendar() const
Definition: trs.hpp:62
std::string initialPriceCurrency_
Definition: trs.hpp:80
const std::string & observationLag() const
Definition: trs.hpp:57
std::string observationLag_
Definition: trs.hpp:76
ScheduleData scheduleData_
Definition: trs.hpp:75
std::string paymentCalendar_
Definition: trs.hpp:77
const std::string & paymentLag() const
Definition: trs.hpp:60
std::string observationCalendar_
Definition: trs.hpp:76
const std::string & paymentConvention() const
Definition: trs.hpp:61
Real initialPrice() const
Definition: trs.hpp:64
std::string paymentConvention_
Definition: trs.hpp:76
bool payer() const
Definition: trs.hpp:54
std::map< std::string, SimmCreditQualifierMapping > creditQualifierMapping_
Definition: trs.hpp:173
QuantLib::ext::shared_ptr< QuantExt::FxIndex > getFxIndex(const QuantLib::ext::shared_ptr< Market > market, const std::string &configuration, const std::string &domestic, const std::string &foreign, std::map< std::string, QuantLib::ext::shared_ptr< QuantExt::FxIndex > > &fxIndices, std::set< std::string > &missingFxIndexPairs) const
Definition: trs.cpp:262
AdditionalCashflowData additionalCashflowData_
Definition: trs.hpp:170
std::map< AssetClass, std::set< std::string > > underlyingIndices(const QuantLib::ext::shared_ptr< ReferenceDataManager > &referenceDataManager=nullptr) const override
Interface.
Definition: trs.cpp:151
std::vector< QuantLib::ext::shared_ptr< Trade > > underlying_
Definition: trs.hpp:165
std::vector< std::string > underlyingDerivativeId_
Definition: trs.hpp:167
QuantLib::Real notional() const override
Return the current notional in npvCurrency. See individual sub-classes for the precise definition.
Definition: trs.cpp:769
FundingData fundingData_
Definition: trs.hpp:169
void fromXML(XMLNode *node) override
Definition: trs.cpp:167
XMLNode * toXML(XMLDocument &doc) const override
Definition: trs.cpp:234
ReturnData returnData_
Definition: trs.hpp:168
void build(const QuantLib::ext::shared_ptr< EngineFactory > &) override
Definition: trs.cpp:292
std::string creditRiskCurrency_
Definition: trs.hpp:172
string npvCurrency_
Definition: trade.hpp:201
const std::string & sensitivityTemplate() const
Definition: trade.cpp:305
string sensitivityTemplate_
Definition: trade.hpp:206
const RequiredFixings & requiredFixings() const
Definition: trade.hpp:90
QuantLib::Real notional_
Definition: trade.hpp:202
virtual void fromXML(XMLNode *node) override
Definition: trade.cpp:34
void setSensitivityTemplate(const EngineBuilder &builder)
Definition: trade.cpp:295
virtual XMLNode * toXML(XMLDocument &doc) const override
Definition: trade.cpp:46
string & id()
Set the trade id.
Definition: trade.hpp:118
string tradeType_
Definition: trade.hpp:196
virtual const std::map< std::string, boost::any > & additionalData() const
returns all additional data returned by the trade once built
Definition: trade.cpp:151
RequiredFixings requiredFixings_
Definition: trade.hpp:223
QuantLib::ext::shared_ptr< InstrumentWrapper > instrument_
Definition: trade.hpp:197
void reset()
Reset trade, clear all base class data. This does not reset accumulated timings for this trade.
Definition: trade.cpp:130
string notionalCurrency_
Definition: trade.hpp:203
const string & tradeType() const
Definition: trade.hpp:133
std::map< std::string, boost::any > additionalData_
Definition: trade.hpp:224
Small XML Document wrapper class.
Definition: xmlutils.hpp:65
XMLNode * allocNode(const string &nodeName)
util functions that wrap rapidxml
Definition: xmlutils.cpp:132
static void addChildren(XMLDocument &doc, XMLNode *n, const string &names, const string &name, const vector< T > &values)
Definition: xmlutils.cpp:502
static string getAttribute(XMLNode *node, const string &attrName)
Definition: xmlutils.cpp:419
static void checkNode(XMLNode *n, const string &expectedName)
Definition: xmlutils.cpp:175
static vector< XMLNode * > getChildrenNodes(XMLNode *node, const string &name)
Returns all the children with a given name.
Definition: xmlutils.cpp:428
static string getChildValue(XMLNode *node, const string &name, bool mandatory=false, const string &defaultValue=string())
Definition: xmlutils.cpp:277
static bool getChildValueAsBool(XMLNode *node, const string &name, bool mandatory=false, bool defaultValue=true)
Definition: xmlutils.cpp:296
static XMLNode * getChildNode(XMLNode *n, const string &name="")
Definition: xmlutils.cpp:387
static string getNodeValue(XMLNode *node)
Get a node's value.
Definition: xmlutils.cpp:489
static int getChildValueAsInt(XMLNode *node, const string &name, bool mandatory=false, int defaultValue=0)
Definition: xmlutils.cpp:291
static vector< string > getChildrenValues(XMLNode *node, const string &names, const string &name, bool mandatory=false)
Definition: xmlutils.cpp:306
static XMLNode * addChild(XMLDocument &doc, XMLNode *n, const string &name)
Definition: xmlutils.cpp:181
static void appendNode(XMLNode *parent, XMLNode *child)
Definition: xmlutils.cpp:406
SafeStack< ValueType > value
Calendar parseCalendar(const string &s)
Convert text to QuantLib::Calendar.
Definition: parsers.cpp:157
QuantLib::ext::shared_ptr< FxIndex > parseFxIndex(const string &s, const Handle< Quote > &fxSpot, const Handle< YieldTermStructure > &sourceYts, const Handle< YieldTermStructure > &targetYts, const bool useConventions)
Convert std::string to QuantExt::FxIndex.
Currency parseCurrencyWithMinors(const string &s)
Convert text to QuantLib::Currency.
Definition: parsers.cpp:310
Date parseDate(const string &s)
Convert std::string to QuantLib::Date.
Definition: parsers.cpp:51
QuantLib::Real convertMinorToMajorCurrency(const std::string &s, QuantLib::Real value)
Convert a value from a minor ccy to major.
Definition: parsers.cpp:324
Currency parseCurrency(const string &s)
Convert text to QuantLib::Currency.
Definition: parsers.cpp:290
BusinessDayConvention parseBusinessDayConvention(const string &s)
Convert text to QuantLib::BusinessDayConvention.
Definition: parsers.cpp:173
Period parsePeriod(const string &s)
Convert text to QuantLib::Period.
Definition: parsers.cpp:171
bool parseBool(const string &s)
Convert text to bool.
Definition: parsers.cpp:144
PaymentLag parsePaymentLag(const string &s)
Convert text to PaymentLag.
Definition: parsers.cpp:628
Real parseReal(const string &s)
Convert text to Real.
Definition: parsers.cpp:112
translates between QuantLib::Index::name() and ORE names
@ data
Definition: log.hpp:77
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
#define ALOG(text)
Logging Macro (Level = Alert)
Definition: log.hpp:544
#define WLOG(text)
Logging Macro (Level = Warning)
Definition: log.hpp:550
market data related utilties
QuantLib::Date fixingDate(const QuantLib::Date &d, const QuantLib::Period obsLag, const QuantLib::Frequency freq, bool interpolated)
std::ostream & operator<<(std::ostream &out, EquityReturnType t)
std::string creditCurveNameFromSecuritySpecificCreditCurveName(const std::string &name)
Definition: marketdata.cpp:84
std::pair< std::string, SimmCreditQualifierMapping > getCmbLegCreditQualifierMapping(const CMBLegData &ld, const QuantLib::ext::shared_ptr< ReferenceDataManager > &refData, const std::string &tradeId, const std::string &tradeType)
Definition: legdata.cpp:2862
void addToRequiredFixings(const QuantLib::Leg &leg, const QuantLib::ext::shared_ptr< FixingDateGetter > &fixingDateGetter)
Size size(const ValueType &v)
Definition: value.cpp:145
TRS::FundingData::NotionalType parseTrsFundingNotionalType(const std::string &s)
Definition: trs.cpp:789
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
boost::bimap< std::string, TRS::FundingData::NotionalType > types
Definition: trs.cpp:783
void addTRSRequiredFixings(RequiredFixings &fixings, const std::vector< Leg > &returnLegs, const QuantLib::ext::shared_ptr< QuantExt::FxIndex > &ind=nullptr)
Definition: trs.cpp:42
std::string getCmbLegCreditRiskCurrency(const CMBLegData &ld, const QuantLib::ext::shared_ptr< ReferenceDataManager > &refData)
Definition: legdata.cpp:2851
boost::variant< QuantLib::Period, QuantLib::Natural > PaymentLag
Definition: types.hpp:32
QuantLib::ext::shared_ptr< QuantExt::FxIndex > buildFxIndex(const string &fxIndex, const string &domestic, const string &foreign, const QuantLib::ext::shared_ptr< Market > &market, const string &configuration, bool useXbsCurves)
Definition: marketdata.cpp:137
Schedule makeSchedule(const ScheduleDates &data)
Definition: schedule.cpp:263
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Structured Trade Error class.
string conversion utilities
string name
generic wrapper for trs (bond, convertible bond, equity, ...)