Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
commodityswap.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
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
29#include <ql/cashflows/cashflows.hpp>
34
35using namespace ore::data;
36using namespace QuantExt;
37using namespace QuantLib;
38using std::max;
39
40namespace ore {
41namespace data {
42
43void CommoditySwap::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
44
45 reset();
46
47 LOG("CommoditySwap::build() called for trade " << id());
48
49 // ISDA taxonomy, assuming Commodity follows the Equity template
50 additionalData_["isdaAssetClass"] = string("Commodity");
51 additionalData_["isdaBaseProduct"] = string("Swap");
52 additionalData_["isdaSubProduct"] = string("Price Return Basic Performance");
53 // skip the transaction level mapping for now
54 additionalData_["isdaTransaction"] = string("");
55
56 check();
57
58 // Arbitrarily choose NPV currency from 1st leg. Already checked that both leg currencies equal.
59 npvCurrency_ = legData_[0].currency();
60
61 // Set notional to N/A for now, but reset this for a commodity fixed respectively floating leg below.
62 notional_ = Null<Real>();
63 notionalCurrency_ = legData_[0].currency();
64
65 const QuantLib::ext::shared_ptr<Market> market = engineFactory->market();
66 QuantLib::ext::shared_ptr<EngineBuilder> builder = engineFactory->builder("CommoditySwap");
67 QuantLib::ext::shared_ptr<CommoditySwapEngineBuilder> engineBuilder =
68 QuantLib::ext::dynamic_pointer_cast<CommoditySwapEngineBuilder>(builder);
69 const string& configuration = builder->configuration(MarketContext::pricing);
70
71 // Build the commodity swap legs
72
73 // Build the floating legs first in case we need the quantities to build the fixed legs.
74 // Store the floating legs in the map with their "Tag" as key. This allows the fixed leg to find the floating leg
75 // with the matching "Tag" when retrieving the quantities if it needs them. Note the if all the tags are empty,
76 // the map entry gets overwritten and the fixed leg with empty tag matches a random floating leg with empty tag.
77 // This is by design i.e. use tags if you want to link specific legs.
78 map<string, Leg> floatingLegs;
79 vector<Size> legsIdx;
80 for (Size t = 0; t < legData_.size(); t++) {
81 const auto& legDatum = legData_.at(t);
82
83 const string& type = legDatum.legType();
84 if (type == "CommodityFixed")
85 continue;
86
87 // Build the leg and add it to legs_
88 buildLeg(engineFactory, legDatum, configuration);
89 legsIdx.push_back(t);
90
91 // Only add to map if CommodityFloatingLegData
92 if (auto cfld = QuantLib::ext::dynamic_pointer_cast<CommodityFloatingLegData>(legDatum.concreteLegData())) {
93 floatingLegs[cfld->tag()] = legs_.back();
94 }
95 }
96
97 // Build any fixed legs skipped above.
98 for (Size t = 0; t < legData_.size(); t++) {
99 const auto& legDatum = legData_.at(t);
100
101 // take a copy, since we might modify the leg datum below
102 auto effLegDatum = legDatum;
103
104 const string& type = effLegDatum.legType();
105 if (type != "CommodityFixed")
106 continue;
107
108 // Update the commodity fixed leg quantities if necessary.
109 auto cfld = QuantLib::ext::dynamic_pointer_cast<CommodityFixedLegData>(effLegDatum.concreteLegData());
110 QL_REQUIRE(cfld, "CommodityFixed leg should have valid CommodityFixedLegData");
111 if (cfld->quantities().empty()) {
112
113 auto it = floatingLegs.find(cfld->tag());
114 QL_REQUIRE(it != floatingLegs.end(), "Did not find a commodity floating leg corresponding to the" <<
115 " fixed leg with tag '" << cfld->tag() << "' from which to take the quantities.");
116
117 const Leg& leg = it->second;
118 vector<Real> quantities;
119 quantities.reserve(leg.size());
120 for (const auto& cf : leg) {
121 // If more options arise here, may think of visitor to get the commodity notional.
122 auto ucf = unpackIndexWrappedCashFlow(cf);
123 if (auto cicf = QuantLib::ext::dynamic_pointer_cast<CommodityIndexedCashFlow>(ucf)) {
124 quantities.push_back(cicf->periodQuantity());
125 } else if (auto ciacf = QuantLib::ext::dynamic_pointer_cast<CommodityIndexedAverageCashFlow>(ucf)) {
126 quantities.push_back(ciacf->periodQuantity());
127 } else {
128 QL_FAIL("Expected a commodity indexed cashflow while building commodity fixed" <<
129 " leg quantities for trade " << id() << ".");
130 }
131 }
132
133 cfld->setQuantities(quantities);
134 }
135 // overwrite payment dates if pay relative to future expiry of floating leg is specified
136 if (effLegDatum.paymentDates().empty() &&
137 cfld->commodityPayRelativeTo() == CommodityPayRelativeTo::FutureExpiryDate) {
138 auto it = floatingLegs.find(cfld->tag());
139 QL_REQUIRE(it != floatingLegs.end(),
140 "Did not find a commodity floating leg correpsonding to the fixed leg with tag '"
141 << cfld->tag() << "' from which to take the payment dates.");
142 vector<string> tmp;
143 for(auto const& cf: it->second) {
144 tmp.push_back(ore::data::to_string(cf->date()));
145 }
146 effLegDatum.paymentDates() = tmp;
147 }
148
149 // Build the leg and add it to legs_
150 buildLeg(engineFactory, effLegDatum, configuration);
151 legsIdx.push_back(t);
152 }
153
154 // Reposition the leg-based data to match the original order according to legData_
155 vector<Leg> legsTmp;
156 vector<bool> legPayersTmp;
157 vector<string> legCurrenciesTmp;
158 for (const Size idx : legsIdx) {
159 legsTmp.push_back(legs_.at(idx));
160 legPayersTmp.push_back(legPayers_.at(idx));
161 legCurrenciesTmp.push_back(legCurrencies_.at(idx));
162 }
163 legs_ = legsTmp;
164 legPayers_ = legPayersTmp;
165 legCurrencies_ = legCurrenciesTmp;
166
167 // Create the QuantLib swap instrument and assign pricing engine
168 auto swap = QuantLib::ext::make_shared<QuantLib::Swap>(legs_, legPayers_);
169 QuantLib::ext::shared_ptr<PricingEngine> engine = engineBuilder->engine(parseCurrency(npvCurrency_));
170 swap->setPricingEngine(engine);
171 setSensitivityTemplate(*engineBuilder);
172 instrument_ = QuantLib::ext::make_shared<VanillaInstrument>(swap);
173}
174
175const std::map<std::string,boost::any>& CommoditySwap::additionalData() const {
176 Size numLegs = legData_.size();
177 // use the build time as of date to determine current notionals
178 Date asof = Settings::instance().evaluationDate();
179 QuantLib::ext::shared_ptr<QuantLib::Swap> swap = QuantLib::ext::dynamic_pointer_cast<QuantLib::Swap>(instrument_->qlInstrument());
180 for (Size i = 0; i < numLegs; ++i) {
181 string legID = to_string(i+1);
182 additionalData_["legType[" + legID + "]"] = legData_[i].legType();
183 additionalData_["isPayer[" + legID + "]"] = legData_[i].isPayer();
184 additionalData_["currency[" + legID + "]"] = legData_[i].currency();
185 if (swap)
186 additionalData_["legNPV[" + legID + "]"] = swap->legNPV(i);
187 else
188 ALOG("commodity swap underlying instrument not set, skip leg npv reporting");
189 for (Size j = 0; j < legs_[i].size(); ++j) {
190 QuantLib::ext::shared_ptr<CashFlow> flow = legs_[i][j];
191 if (flow->date() > asof) {
192 std::string label = legID + ":" + std::to_string(j + 1);
193 // CommodityFloatingLeg consists of indexed or indexed average cash flows
194 QuantLib::ext::shared_ptr<CommodityIndexedCashFlow> indexedFlow =
195 QuantLib::ext::dynamic_pointer_cast<CommodityIndexedCashFlow>(unpackIndexWrappedCashFlow(flow));
196 if (indexedFlow) {
197 additionalData_["quantity[" + label + "]"] = indexedFlow->quantity();
198 additionalData_["periodQuantity[" + label + "]"] = indexedFlow->periodQuantity();
199 additionalData_["gearing[" + label + "]"] = indexedFlow->gearing();
200 if (indexedFlow->isAveragingFrontMonthCashflow(asof)) {
201 std::vector<Real> priceVec;
202 std::vector<std::string> indexVec;
203 std::vector<Date> indexExpiryVec, pricingDateVec;
204 double averagePrice = 0;
205 for (const auto& pd : indexedFlow->spotAveragingPricingDates()) {
206 if (pd > asof) {
207 auto index = indexedFlow->index();
208 auto pricingDate = indexedFlow->lastPricingDate();
209 indexVec.push_back(index->name());
210 indexExpiryVec.push_back(index->expiryDate());
211 pricingDateVec.push_back(pd);
212 priceVec.push_back(index->fixing(pricingDate));
213 averagePrice += priceVec.back();
214 }
215 else {
216 auto index = indexedFlow->spotIndex();
217 indexVec.push_back(index->name());
218 indexExpiryVec.push_back(index->expiryDate());
219 pricingDateVec.push_back(pd);
220 priceVec.push_back(index->fixing(pd));
221 averagePrice += priceVec.back();
222 }
223 }
224 averagePrice /= indexedFlow->spotAveragingPricingDates().size();
225 additionalData_["index[" + label + "]"] = indexVec;
226 additionalData_["indexExpiry[" + label + "]"] = indexExpiryVec;
227 additionalData_["price[" + label + "]"] = priceVec;
228 additionalData_["averagePrice[" + label + "]"] = averagePrice;
229 additionalData_["pricingDate[" + label + "]"] = pricingDateVec;
230 }
231 else {
232 additionalData_["index[" + label + "]"] = indexedFlow->index()->name();
233 additionalData_["indexExpiry[" + label + "]"] = indexedFlow->index()->expiryDate();
234 additionalData_["price[" + label + "]"] = indexedFlow->index()->fixing(indexedFlow->pricingDate());
235 additionalData_["pricingDate[" + label + "]"] = to_string(indexedFlow->pricingDate());
236 }
237 additionalData_["paymentDate[" + label + "]"] = to_string(indexedFlow->date());
238 }
239 QuantLib::ext::shared_ptr<CommodityIndexedAverageCashFlow> indexedAvgFlow =
240 QuantLib::ext::dynamic_pointer_cast<CommodityIndexedAverageCashFlow>(unpackIndexWrappedCashFlow(flow));
241 if (indexedAvgFlow) {
242 additionalData_["quantity[" + label + "]"] = indexedAvgFlow->quantity();
243 additionalData_["periodQuantity[" + label + "]"] = indexedAvgFlow->periodQuantity();
244 additionalData_["gearing[" + label + "]"] = indexedAvgFlow->gearing();
245 std::vector<Real> priceVec;
246 std::vector<std::string> indexVec;
247 std::vector<Date> indexExpiryVec, pricingDateVec;
248 double averagePrice = 0;
249 for (const auto& kv : indexedAvgFlow->indices()) {
250 indexVec.push_back(kv.second->name());
251 indexExpiryVec.push_back(kv.second->expiryDate());
252 pricingDateVec.push_back(kv.first);
253 priceVec.push_back(kv.second->fixing(kv.first));
254 averagePrice += priceVec.back();
255 }
256 averagePrice /= indexedAvgFlow->indices().size();
257 additionalData_["index[" + label + "]"] = indexVec;
258 additionalData_["indexExpiry[" + label + "]"] = indexExpiryVec;
259 additionalData_["price[" + label + "]"] = priceVec;
260 additionalData_["averagePrice[" + label + "]"] = averagePrice;
261 additionalData_["pricingDate[" + label + "]"] = pricingDateVec;
262 additionalData_["paymentDate[" + label + "]"] = to_string(indexedAvgFlow->date());
263 }
264 // CommodityFixedLeg consists of simple cash flows
265 Real flowAmount = 0.0;
266 try {
267 flowAmount = flow->amount();
268 } catch (std::exception& e) {
269 ALOG("flow amount could not be determined for trade " << id() << ", set to zero: " << e.what());
270 }
271 additionalData_["amount[" + label + "]"] = flowAmount;
272 additionalData_["paymentDate[" + label + "]"] = to_string(flow->date());
273 }
274 }
275 if (legs_[i].size() > 0) {
276 QuantLib::ext::shared_ptr<Coupon> coupon = QuantLib::ext::dynamic_pointer_cast<Coupon>(legs_[i][0]);
277 if (coupon) {
278 Real originalNotional = 0.0;
279 try { originalNotional = coupon->nominal(); }
280 catch(std::exception& e) {
281 ALOG("original nominal could not be determined for trade " << id() << ", set to zero: " << e.what());
282 }
283 additionalData_["originalNotional[" + legID + "]"] = originalNotional;
284 }
285 }
286 }
287 return additionalData_;
288}
289
290QuantLib::Real CommoditySwap::notional() const {
291 Date asof = Settings::instance().evaluationDate();
292 Real currentAmount = Null<Real>();
293 // Get maximum current cash flow amount (quantity * strike, quantity * spot/forward price) across legs
294 // include gearings and spreads; note that the swap is in a single currency.
295 for (Size i = 0; i < legs_.size(); ++i) {
296 for (Size j = 0; j < legs_[i].size(); ++j) {
297 QuantLib::ext::shared_ptr<CashFlow> flow = legs_[i][j];
298 // pick flow with earliest payment date on this leg
299 if (flow->date() > asof) {
300 if (currentAmount == Null<Real>())
301 currentAmount = flow->amount();
302 else // set on a previous leg already, set to maximum
303 currentAmount = std::max(currentAmount, flow->amount());
304 break; // move on to the next leg
305 }
306 }
307 }
308
309 if (currentAmount != Null<Real>()) {
310 return currentAmount;
311 } else {
312 ALOG("Error retrieving current notional for commodity swap " << id() << " as of " << io::iso_date(asof));
313 return Null<Real>();
314 }
315}
316
317std::map<AssetClass, std::set<std::string>>
318CommoditySwap::underlyingIndices(const QuantLib::ext::shared_ptr<ReferenceDataManager>& referenceDataManager) const {
319
320 std::map<AssetClass, std::set<std::string>> result;
321
322 for (auto ld : legData_) {
323 set<string> indices = ld.indices();
324 for (auto ind : indices) {
325 QuantLib::ext::shared_ptr<Index> index = parseIndex(ind);
326 // only handle commodity
327 if (auto ci = QuantLib::ext::dynamic_pointer_cast<QuantExt::CommodityIndex>(index)) {
328 result[AssetClass::COM].insert(ci->name());
329 }
330 }
331 }
332 return result;
333}
334
336 DLOG("CommoditySwap::fromXML called");
337 Trade::fromXML(node);
338 legData_.clear();
339
340 XMLNode* swapNode = XMLUtils::getChildNode(node, "SwapData");
341 QL_REQUIRE(swapNode, "No SwapData Node");
342
343 vector<XMLNode*> nodes = XMLUtils::getChildrenNodes(swapNode, "LegData");
344 for (Size i = 0; i < nodes.size(); i++) {
345 auto ld = createLegData();
346 ld->fromXML(nodes[i]);
347 legData_.push_back(*ld);
348 }
349}
350
352 XMLNode* node = Trade::toXML(doc);
353 XMLNode* swapNode = doc.allocNode("SwapData");
354 XMLUtils::appendNode(node, swapNode);
355 for (Size i = 0; i < legData_.size(); i++)
356 XMLUtils::appendNode(swapNode, legData_[i].toXML(doc));
357 return node;
358}
359
361 QL_REQUIRE(legData_.size() >= 2, "Expected at least two commodity legs but found " << legData_.size());
362 auto ccy = legData_[0].currency();
363 for (const auto& legDatum : legData_) {
364 QL_REQUIRE(legDatum.currency() == ccy, "Cross currency commodity swaps are not supported");
365 }
366}
367
368void CommoditySwap::buildLeg(const QuantLib::ext::shared_ptr<EngineFactory>& ef,
369 const LegData& legDatum, const string& configuration) {
370
371 auto legBuilder = ef->legBuilder(legDatum.legType());
372 Leg leg = legBuilder->buildLeg(legDatum, ef, requiredFixings_, configuration);
373 legs_.push_back(leg);
374 legPayers_.push_back(legDatum.isPayer());
375 legCurrencies_.push_back(legDatum.currency());
376
377 // Update maturity
378 maturity_ = max(CashFlows::maturityDate(leg), maturity_);
379
380}
381
382} // namespace data
383} // namespace ore
Engine builder for commodity swaps.
void buildLeg(const QuantLib::ext::shared_ptr< ore::data::EngineFactory > &ef, const ore::data::LegData &legDatum, const std::string &configuration)
std::vector< ore::data::LegData > legData_
virtual void fromXML(ore::data::XMLNode *node) override
QuantLib::Real notional() const override
Return the current notional in npvCurrency. See individual sub-classes for the precise definition.
virtual ore::data::XMLNode * toXML(ore::data::XMLDocument &doc) const override
QuantLib::ext::shared_ptr< ore::data::LegData > createLegData() const
void build(const QuantLib::ext::shared_ptr< ore::data::EngineFactory > &) override
std::map< ore::data::AssetClass, std::set< std::string > > underlyingIndices(const QuantLib::ext::shared_ptr< ore::data::ReferenceDataManager > &referenceDataManager=nullptr) const override
Add underlying Commodity names.
const std::map< std::string, boost::any > & additionalData() const override
returns all additional data returned by the trade once built
Serializable object holding leg data.
Definition: legdata.hpp:844
const string & currency() const
Definition: legdata.hpp:873
bool isPayer() const
Definition: legdata.hpp:872
const string & legType() const
Definition: legdata.hpp:890
string npvCurrency_
Definition: trade.hpp:201
std::vector< bool > legPayers_
Definition: trade.hpp:200
std::vector< string > legCurrencies_
Definition: trade.hpp:199
std::vector< QuantLib::Leg > legs_
Definition: trade.hpp:198
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
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
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 vector< XMLNode * > getChildrenNodes(XMLNode *node, const string &name)
Returns all the children with a given name.
Definition: xmlutils.cpp:428
static XMLNode * getChildNode(XMLNode *n, const string &name="")
Definition: xmlutils.cpp:387
static void appendNode(XMLNode *parent, XMLNode *child)
Definition: xmlutils.cpp:406
leg data for commodity leg types
Commodity Swap data model and serialization.
Pricing Engine Factory.
Logic for calculating required fixing dates on legs.
Currency parseCurrency(const string &s)
Convert text to QuantLib::Currency.
Definition: parsers.cpp:290
QuantLib::ext::shared_ptr< Index > parseIndex(const string &s)
Convert std::string to QuantLib::Index.
leg data model and serialization
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 ALOG(text)
Logging Macro (Level = Alert)
Definition: log.hpp:544
boost::shared_ptr< CashFlow > unpackIndexWrappedCashFlow(const boost::shared_ptr< CashFlow > &c)
RandomVariable max(RandomVariable x, const RandomVariable &y)
Size size(const ValueType &v)
Definition: value.cpp:145
Real originalNotional(const Leg &leg)
Definition: legdata.cpp:2449
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Map text representations to QuantLib/QuantExt types.
trade schedule data model and serialization
string conversion utilities