Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
compositetrade.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2019 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 ORE is free software: you can redistribute it and/or modify it
8 under the terms of the Modified BSD License. You should have received a
9 copy of the license along with this program.
10 The license is also available online at <http://opensourcerisk.org>
11 This program is distributed on the basis that it will form a useful
12 contribution to risk analytics and model standardisation, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
15*/
16
24
25#include <boost/algorithm/string/case_conv.hpp>
26
28
29namespace ore {
30namespace data {
32
33void CompositeTrade::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
34 DLOG("Building Composite Trade: " << id());
36 QuantLib::ext::shared_ptr<MultiCcyCompositeInstrument> compositeInstrument =
37 QuantLib::ext::make_shared<MultiCcyCompositeInstrument>();
38 fxRates_.clear();
39 fxRatesNotional_.clear();
40 legs_.clear();
41
42 populateFromReferenceData(engineFactory->referenceData());
43
44
45 for (const QuantLib::ext::shared_ptr<Trade>& trade : trades_) {
46
47 trade->reset();
48 trade->build(engineFactory);
49 trade->validate();
50
51 if (sensitivityTemplate_.empty())
52 setSensitivityTemplate(trade->sensitivityTemplate());
53
54 Handle<Quote> fx = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(1.0));
55 if (trade->npvCurrency() != npvCurrency_)
56 fx = engineFactory->market()->fxRate(trade->npvCurrency() + npvCurrency_);
57 fxRates_.push_back(fx);
58
59 Handle<Quote> fxNotional = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(1.0));
60 if (trade->notionalCurrency().empty()) {
61 // trade is not guaranteed to provide a non-null notional, but if it does we require a notional currency
62 if (trade->notional() != Null<Real>()) {
64 trade, "Error building composite trade '" + id() + "'",
65 "Component trade '" + trade->id() + "' does not provide notional currency for notional " +
66 std::to_string(trade->notional()) + ". Assuming " + npvCurrency_ + ".")
67 .log();
68 }
69 } else if (trade->notionalCurrency() != npvCurrency_)
70 fxNotional = engineFactory->market()->fxRate(trade->notionalCurrency() + npvCurrency_);
71 fxRatesNotional_.push_back(fxNotional);
72
73 QuantLib::ext::shared_ptr<InstrumentWrapper> instrumentWrapper = trade->instrument();
74 Real effectiveMultiplier = instrumentWrapper->multiplier();
75 if (auto optionWrapper = QuantLib::ext::dynamic_pointer_cast<ore::data::OptionWrapper>(instrumentWrapper)) {
76 effectiveMultiplier *= optionWrapper->isLong() ? 1.0 : -1.0;
77 }
78
79 compositeInstrument->add(instrumentWrapper->qlInstrument(), effectiveMultiplier, fx);
80 for (Size i = 0; i < instrumentWrapper->additionalInstruments().size(); ++i) {
81 compositeInstrument->add(instrumentWrapper->additionalInstruments()[i],
82 instrumentWrapper->additionalMultipliers()[i]);
83 }
84
85 bool isDuplicate = false;
86 try {
87 if (instrumentWrapper->additionalResults().find("cashFlowResults") != trade->instrument()->additionalResults().end())
88 isDuplicate = true;
89 } catch (...) {}
90 if (!isDuplicate) {
91 // For cashflows
92 legs_.insert(legs_.end(), trade->legs().begin(), trade->legs().end());
93 legPayers_.insert(legPayers_.end(), trade->legPayers().begin(), trade->legPayers().end());
94 legCurrencies_.insert(legCurrencies_.end(), trade->legCurrencies().begin(), trade->legCurrencies().end());
95 }
96
97 maturity_ = std::max(maturity_, trade->maturity());
98 }
99 instrument_ = QuantLib::ext::shared_ptr<InstrumentWrapper>(new VanillaInstrument(compositeInstrument));
100
102
103 // set required fixings
104 for (auto const& t : trades_)
105 requiredFixings_.addData(t->requiredFixings());
106}
107
108QuantLib::Real CompositeTrade::notional() const {
109 vector<Real> notionals;
110 vector<Handle<Quote>> fxRates;
111 // trade is not guaranteed to provide a non-null notional
112 for (const QuantLib::ext::shared_ptr<Trade>& trade : trades_)
113 notionals.push_back(trade->notional() != Null<Real>() ? trade->notional() : 0.0);
114
115 // need to convert the component notionals to the composite currency.
116 auto notionalConverter = [](const Real ntnl, const Handle<Quote>& fx) { return (ntnl * fx->value()); };
117 std::transform(begin(notionals), end(notionals), begin(fxRates_), begin(notionals), notionalConverter);
118 return calculateNotional(notionals);
119}
120
122 QL_REQUIRE("CompositeTrade" == XMLUtils::getChildValue(node, "TradeType", true),
123 "Wrong trade type in composite trade builder.");
124 Trade::fromXML(node);
125 this->id() = XMLUtils::getAttribute(node, "id");
126 // We read the data particular to composite trades
127 XMLNode* compNode = XMLUtils::getChildNode(node, "CompositeTradeData");
128 QL_REQUIRE(compNode, "Could not find CompositeTradeData node.");
129 currency_ = XMLUtils::getChildValue(compNode, "Currency", true);
130 // The notional logic is as follows:
131 // If the notional override is specified then it is used, regardless of the "NotionalCalculation" field
132 // Otherwise we calculate the notional as per the calculation specified
133 if (XMLUtils::getChildNode(compNode, "NotionalOverride") != nullptr) {
134 notionalOverride_ = XMLUtils::getChildValueAsDouble(compNode, "NotionalOverride");
135 QL_REQUIRE(notionalOverride_ >= 0, "Non-negative notional expected.");
136 DLOG("Using override notional of " << notionalOverride_);
137 notionalCalculation_ = "Override";
138 } else {
139 // Convert everything to proper case to match xml schema
140 notionalCalculation_ = boost::to_lower_copy(XMLUtils::getChildValue(compNode, "NotionalCalculation"));
142 QL_REQUIRE(notionalCalculation_ != "Override", "Notional override value has not been provided.");
143 }
144
145 if (XMLUtils::getChildNode(compNode, "PortfolioBasket")) {
146 portfolioBasket_ = XMLUtils::getChildValueAsBool(node, "PortfolioBasket", false);
147 } else {
148 portfolioBasket_ = false;
149 }
150
151 portfolioId_ = XMLUtils::getChildValue(compNode, "BasketName", false);
152
153 XMLNode* tradesNode = XMLUtils::getChildNode(compNode, "Components");
154 if (portfolioBasket_ && portfolioId_.empty()) {
155 QL_REQUIRE(tradesNode, "Required a Portfolio Id or a Components Node.");
156 }
157 if ((portfolioBasket_ && portfolioId_.empty()) || (!portfolioBasket_)) {
158
159 vector<XMLNode*> nodes = XMLUtils::getChildrenNodes(tradesNode, "Trade");
160 for (Size i = 0; i < nodes.size(); i++) {
161 string tradeType = XMLUtils::getChildValue(nodes[i], "TradeType", true);
162 string id = XMLUtils::getAttribute(nodes[i], "id");
163 if (id == "") {
164 WLOG("Empty component trade id being overwritten in composite trade " << this->id() << ".");
165 }
166 id = this->id() + "_" + std::to_string(i);
167 DLOG("Parsing composite trade " << this->id() << " node " << i << " with id: " << id);
168
169 QuantLib::ext::shared_ptr<Trade> trade;
170 try {
171 trade = TradeFactory::instance().build(tradeType);
172 trade->id() = id;
173 Envelope componentEnvelope;
174 if (XMLNode* envNode = XMLUtils::getChildNode(nodes[i], "Envelope")) {
175 componentEnvelope.fromXML(envNode);
176 }
177 Envelope env = this->envelope();
178 // the component trade's envelope is the main trade's envelope with possibly overwritten add fields
179 for (auto const& [k, v] : componentEnvelope.fullAdditionalFields()) {
180 env.setAdditionalField(k,v);
181 }
182
183 trade->setEnvelope(env);
184 trade->fromXML(nodes[i]);
185 trades_.push_back(trade);
186 DLOG("Added Trade " << id << " (" << trade->id() << ")"
187 << " type:" << tradeType << " to composite trade " << this->id() << ".");
188 } catch (const std::exception& e) {
190 id, this->tradeType(),
191 "Failed to build subtrade with id '" + id + "' inside composite trade: ", e.what())
192 .log();
193 }
194 }
195 LOG("Finished Parsing XML doc");
196 }
197}
198
200 XMLNode* node = Trade::toXML(doc);
201 XMLNode* compNode = doc.allocNode("CompositeTradeData");
202 XMLUtils::appendNode(node, compNode);
203
204 XMLUtils::addChild(doc, compNode, "Currency", currency_);
205 if (notionalCalculation_ == "Override")
206 XMLUtils::addChild(doc, compNode, "NotionalOverride", notionalOverride_);
207 XMLUtils::addChild(doc, compNode, "NotionalCalculation", notionalCalculation_);
208
209 XMLNode* tradesNode = doc.allocNode("Components");
210 XMLUtils::appendNode(compNode, tradesNode);
211 for (auto trade : trades_) {
212 XMLNode* subTradeNode = trade->toXML(doc);
213 XMLUtils::appendNode(tradesNode, subTradeNode);
214 }
215 return node;
216}
217
218Real CompositeTrade::calculateNotional(const vector<Real>& notionals) const {
219 if (notionalCalculation_ == "Sum" || notionalCalculation_ == "")
220 return std::accumulate(notionals.begin(), notionals.end(), 0.0);
221 else if (notionalCalculation_ == "Mean" || notionalCalculation_ == "Average")
222 return std::accumulate(notionals.begin(), notionals.end(), 0.0) / notionals.size();
223 else if (notionalCalculation_ == "First")
224 return notionals[0];
225 else if (notionalCalculation_ == "Last")
226 return notionals.back();
227 else if (notionalCalculation_ == "Min")
228 return *std::min_element(notionals.begin(), notionals.end());
229 else if (notionalCalculation_ == "Max")
230 return *std::min_element(notionals.begin(), notionals.end());
231 else if (notionalCalculation_ == "Override")
232 return notionalOverride_;
233 else
234 QL_FAIL("Unsupported notional calculation type.");
235}
236
237map<string, RequiredFixings::FixingDates> CompositeTrade::fixings(const Date& settlementDate) const {
238
239 map<string, RequiredFixings::FixingDates> result;
240 for (const auto& t : trades_) {
241 auto fixings = t->fixings(settlementDate);
242 for (const auto& [indexName, fixingDates] : fixings) {
243 result[indexName].addDates(fixingDates);
244 }
245 }
246 return result;
247}
248
249std::map<AssetClass, std::set<std::string>>
250CompositeTrade::underlyingIndices(const QuantLib::ext::shared_ptr<ReferenceDataManager>& referenceDataManager) const {
251
252 map<AssetClass, std::set<std::string>> result;
253 for (const auto& t : trades_) {
254 auto underlyings = t->underlyingIndices(referenceDataManager);
255 for (const auto& kv : underlyings) {
256 result[kv.first].insert(kv.second.begin(), kv.second.end());
257 }
258 }
259 return result;
260}
261
262const std::map<std::string, boost::any>& CompositeTrade::additionalData() const {
263 additionalData_.clear();
264 Size counter = 0;
265 for (auto const& t : trades_) {
266 for (auto const& d : t->additionalData()) {
267 additionalData_[d.first + "_" + std::to_string(counter)] = d.second;
268 }
269 ++counter;
270 }
271 return additionalData_;
272}
273
274void CompositeTrade::populateFromReferenceData(const QuantLib::ext::shared_ptr<ReferenceDataManager>& referenceData) {
275
276 if (!portfolioId_.empty() && referenceData != nullptr &&
277 (referenceData->hasData(PortfolioBasketReferenceDatum::TYPE, portfolioId_))) {
278 auto ptfRefData = QuantLib::ext::dynamic_pointer_cast<PortfolioBasketReferenceDatum>(
279 referenceData->getData(PortfolioBasketReferenceDatum::TYPE, portfolioId_));
280 QL_REQUIRE(ptfRefData, "could not cast to PortfolioBasketReferenceDatum, this is unexpected");
281 getTradesFromReferenceData(ptfRefData);
282 } else {
283 DLOG("Could not get PortfolioBasketReferenceDatum for Id " << portfolioId_ << " leave data in trade unchanged");
284 }
285
286}
287
289 const QuantLib::ext::shared_ptr<PortfolioBasketReferenceDatum>& ptfReferenceDatum) {
290
291 DLOG("populating portfolio basket data from reference data");
292 QL_REQUIRE(ptfReferenceDatum, "populateFromReferenceData(): empty cbo reference datum given");
293
294 auto refData = ptfReferenceDatum->getTrades();
295 trades_.clear();
296 for (Size i = 0; i < refData.size(); i++) {
297 trades_.push_back(refData[i]);
298 }
299 LOG("Finished Parsing XML doc");
300
301}
302
303} // namespace data
304} // namespace oreplus
vector< Handle< Quote > > fxRatesNotional_
std::map< std::string, RequiredFixings::FixingDates > fixings(const QuantLib::Date &settlementDate) const override
Real calculateNotional(const vector< Real > &tradeNotionals) const
calculates the CompositeTrade notional, when supplied with the notionals of the subtrades
void getTradesFromReferenceData(const QuantLib::ext::shared_ptr< PortfolioBasketReferenceDatum > &ptfReferenceDatum)
vector< Handle< Quote > > fxRates_
QuantLib::Real notional() const override
Return the current notional in npvCurrency. See individual sub-classes for the precise definition.
vector< QuantLib::ext::shared_ptr< Trade > > trades_
virtual void fromXML(XMLNode *node) override
virtual XMLNode * toXML(XMLDocument &doc) const override
void populateFromReferenceData(const QuantLib::ext::shared_ptr< ReferenceDataManager > &referenceDataManager)
Size size() const
returns the number of subtrades in the strategy
std::map< AssetClass, std::set< std::string > > underlyingIndices(const QuantLib::ext::shared_ptr< ReferenceDataManager > &referenceDataManager) const override
virtual void build(const QuantLib::ext::shared_ptr< EngineFactory > &) override
Build QuantLib/QuantExt instrument, link pricing engine.
const std::map< std::string, boost::any > & additionalData() const override
returns all additional data returned by the trade once built
Serializable object holding generic trade data, reporting dimensions.
Definition: envelope.hpp:51
virtual void fromXML(XMLNode *node) override
Definition: envelope.cpp:27
void setAdditionalField(const std::string &key, const boost::any &value)
Definition: envelope.cpp:133
const map< string, boost::any > & fullAdditionalFields() const
Definition: envelope.hpp:105
void log() const
generate Boost log record to pass to corresponding sinks
Definition: log.cpp:491
static constexpr const char * TYPE
void addData(const RequiredFixings &requiredFixings)
Utility class for Structured Trade errors, contains the Trade ID and Type.
string npvCurrency_
Definition: trade.hpp:201
std::vector< bool > legPayers_
Definition: trade.hpp:200
string sensitivityTemplate_
Definition: trade.hpp:206
std::vector< string > legCurrencies_
Definition: trade.hpp:199
std::vector< QuantLib::Leg > legs_
Definition: trade.hpp:198
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
RequiredFixings requiredFixings_
Definition: trade.hpp:223
const Envelope & envelope() const
Definition: trade.hpp:135
QuantLib::ext::shared_ptr< InstrumentWrapper > instrument_
Definition: trade.hpp:197
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
Vanilla Instrument Wrapper.
Small XML Document wrapper class.
Definition: xmlutils.hpp:65
XMLNode * allocNode(const string &nodeName)
util functions that wrap rapidxml
Definition: xmlutils.cpp:132
XML Utilities Class.
Definition: xmlutils.hpp:119
static string getAttribute(XMLNode *node, const string &attrName)
Definition: xmlutils.cpp:419
static vector< XMLNode * > getChildrenNodes(XMLNode *node, const string &name)
Returns all the children with a given name.
Definition: xmlutils.cpp:428
static Real getChildValueAsDouble(XMLNode *node, const string &name, bool mandatory=false, double defaultValue=0.0)
Definition: xmlutils.cpp:286
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 XMLNode * addChild(XMLDocument &doc, XMLNode *n, const string &name)
Definition: xmlutils.cpp:181
static void appendNode(XMLNode *parent, XMLNode *child)
Definition: xmlutils.cpp:406
Composite trades operate as a mini portfolio. Their intended use is for strategies like straddles.
Classes and functions for log message handling.
@ data
Definition: log.hpp:77
#define LOG(text)
Logging Macro (Level = Notice)
Definition: log.hpp:552
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
#define WLOG(text)
Logging Macro (Level = Warning)
Definition: log.hpp:550
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Wrapper for option instruments, tracks whether option has been exercised or not.
Structured Trade Error class.
string conversion utilities
Trade Factory.