Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
swap.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2016 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
27#include <ql/cashflows/simplecashflow.hpp>
28
29#include <ql/time/calendars/target.hpp>
34
35#include <ql/instruments/swap.hpp>
36#include <ql/time/daycounters/actualactual.hpp>
37
38using namespace QuantLib;
39using namespace QuantExt;
40using std::make_pair;
41
42namespace ore {
43namespace data {
44
45void Swap::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
46 DLOG("Swap::build() called for trade " << id());
47
49
50 QL_REQUIRE(legData_.size() >= 1, "Swap must have at least 1 leg");
51 const QuantLib::ext::shared_ptr<Market> market = engineFactory->market();
52
53 // allow minor currencies in case first leg is equity
54 Currency currency = parseCurrencyWithMinors(legData_[0].currency());
55 string ccy_str = currency.code();
56
57 Size numLegs = legData_.size();
58 legPayers_ = vector<bool>(numLegs);
59 std::vector<QuantLib::Currency> currencies(numLegs);
60 std::vector<QuantLib::Currency> currenciesForMcSimulation;
61 legs_.resize(numLegs);
62
63 isXCCY_ = false;
64 isResetting_ = false;
65
66 for (Size i = 0; i < numLegs; ++i) {
67 // allow minor currencies for Equity legs as some exchanges trade in these, e.g LSE in pence - GBX or GBp
68 // minor currencies on other legs will fail here
69 if (legData_[i].legType() == "Equity")
70 currencies[i] = parseCurrencyWithMinors(legData_[i].currency());
71 else
72 currencies[i] = parseCurrency(legData_[i].currency());
73
74 if (currencies[i] != currency)
75 isXCCY_ = true;
76 isResetting_ = isResetting_ || (!legData_[i].isNotResetXCCY());
77 }
78
79
80 // Check if there is indexing is used, need to collect all underlying currrencies
81 // for AMC simulations, such a trade needs to be treated a x-ccy swap with both leg paying
82 // one currency.
83 auto addUnique = [](vector<Currency>& currencies, Currency ccy) {
84 if (std::find(currencies.begin(), currencies.end(), ccy) ==
85 currencies.end()) {
86 currencies.push_back(ccy);
87 }
88 };
89
90 for (Size i = 0; i < numLegs; ++i) {
91 addUnique(currenciesForMcSimulation, currencies[i]);
92 vector<Indexing> indexings = legData_[i].indexing();
93 if (!indexings.empty() && indexings.front().hasData()) {
94 Indexing indexing = indexings.front();
95 if (boost::starts_with(indexing.index(), "FX-")) {
96 auto index = parseFxIndex(indexing.index());
97 addUnique(currenciesForMcSimulation, index->targetCurrency());
98 addUnique(currenciesForMcSimulation, index->sourceCurrency());
99 }
100 }
101 }
102 isXCCY_ = isXCCY_ || currenciesForMcSimulation.size() > 1;
103 static std::set<std::string> eligibleForXbs = {"Fixed", "Floating"};
104 bool useXbsCurves = true;
105 for(Size i=0;i<numLegs;++i) {
106 useXbsCurves = useXbsCurves && (eligibleForXbs.find(legData_[i].legType()) != eligibleForXbs.end());
107 }
108
109 // The npv currency, notional currency and current notional are taken from the first leg that
110 // appears in the XML that has a notional. If no such leg exists the notional currency
111 // and current notional are left empty and the npv currency is set to the first leg's currency
112
116 if (!d.notionals().empty())
117 break;
118 }
119
120 if (notionalTakenFromLeg_ == legData_.size()) {
121 ALOG("no suitable leg found to set notional, set to null and notionalCurrency to empty string");
122 notional_ = Null<Real>();
124 // parse for currency in case first leg is Equity, we only want the major currency for NPV
125 npvCurrency_ = parseCurrencyWithMinors(legData_.front().currency()).code();
126 } else {
127 if (legData_[notionalTakenFromLeg_].schedule().hasData()) {
128 Schedule schedule = makeSchedule(legData_[notionalTakenFromLeg_].schedule());
129 auto notional =
131 legData_[notionalTakenFromLeg_].notionalDates(), schedule, 0.0);
132 Date today = Settings::instance().evaluationDate();
133 auto d = std::upper_bound(schedule.dates().begin(), schedule.dates().end(), today);
134 // forward starting => take first notional
135 // on or after last schedule date => zero notional
136 // in between => notional of current period
137 if (d == schedule.dates().begin())
138 notional_ = notional.at(0);
139 else if (d == schedule.dates().end())
140 notional_ = 0.0;
141 else
142 notional_ = notional.at(std::distance(schedule.dates().begin(), d) - 1);
143 } else {
144 notional_ = legData_[notionalTakenFromLeg_].notionals().at(0);
145 }
146 // parse for currency in case leg is Equity, we only want the major currency for NPV and Notional
149 DLOG("Notional is " << notional_ << " " << notionalCurrency_);
150 }
151
152 Currency npvCcy = parseCurrency(npvCurrency_);
153 DLOG("npv currency is " << npvCurrency_);
154
155 QuantLib::ext::shared_ptr<EngineBuilder> builder =
156 isXCCY_ ? engineFactory->builder("CrossCurrencySwap") : engineFactory->builder("Swap");
157 auto configuration = builder->configuration(MarketContext::pricing);
158
159 for (Size i = 0; i < numLegs; ++i) {
160 legPayers_[i] = legData_[i].isPayer();
161 auto legBuilder = engineFactory->legBuilder(legData_[i].legType());
162 legs_[i] = legBuilder->buildLeg(legData_[i], engineFactory, requiredFixings_, configuration, Null<Date>(),
163 useXbsCurves);
164 DLOG("Swap::build(): currency[" << i << "] = " << currencies[i]);
165
166 // add notional leg, if applicable
167 auto leg = buildNotionalLeg(legData_[i], legs_[i], requiredFixings_, engineFactory->market(), configuration);
168 applyIndexing(leg, legData_[i], engineFactory, requiredFixings_, Null<Date>(), useXbsCurves);
169 if (!leg.empty()) {
170 legs_.push_back(leg);
171 legPayers_.push_back(legPayers_[i]);
172 currencies.push_back(currencies[i]);
173 }
174 } // for legs
175
176 if (isXCCY_) {
177 QuantLib::ext::shared_ptr<QuantExt::CurrencySwap> swap(
178 new QuantExt::CurrencySwap(legs_, legPayers_, currencies, settlement_ == "Physical", isResetting_));
179 QuantLib::ext::shared_ptr<CrossCurrencySwapEngineBuilderBase> swapBuilder =
180 QuantLib::ext::dynamic_pointer_cast<CrossCurrencySwapEngineBuilderBase>(builder);
181 QL_REQUIRE(swapBuilder, "No Builder found for CrossCurrencySwap " << id());
182 swap->setPricingEngine(swapBuilder->engine(currenciesForMcSimulation, npvCcy));
183 setSensitivityTemplate(*swapBuilder);
184 // take the first legs currency as the npv currency (arbitrary choice)
185 instrument_.reset(new VanillaInstrument(swap));
186 } else {
187 QuantLib::ext::shared_ptr<QuantLib::Swap> swap(new QuantLib::Swap(legs_, legPayers_));
188 QuantLib::ext::shared_ptr<SwapEngineBuilderBase> swapBuilder =
189 QuantLib::ext::dynamic_pointer_cast<SwapEngineBuilderBase>(builder);
190 QL_REQUIRE(swapBuilder, "No Builder found for Swap " << id());
191 swap->setPricingEngine(swapBuilder->engine(npvCcy, envelope().additionalField("discount_curve", false),
192 envelope().additionalField("security_spread", false)));
193 setSensitivityTemplate(*swapBuilder);
194 instrument_.reset(new VanillaInstrument(swap));
195 }
196
197 DLOG("Set instrument wrapper");
198
199 // set Leg Currencies
200 legCurrencies_ = vector<string>(currencies.size());
201 for (Size i = 0; i < currencies.size(); i++)
202 legCurrencies_[i] = currencies[i].code();
203
204 // set maturity
205 maturity_ = Date::minDate();
206 Date startDate = Date::maxDate();
207 for (auto const& l : legs_) {
208 if (!l.empty()) {
209 maturity_ = std::max(maturity_, l.back()->date());
210 startDate = std::min(startDate, l.front()->date());
211 QuantLib::ext::shared_ptr<Coupon> coupon = QuantLib::ext::dynamic_pointer_cast<Coupon>(l.front());
212 if (coupon)
213 startDate = std::min(startDate, coupon->accrualStartDate());
214 }
215 }
216
217 additionalData_["startDate"] = to_string(startDate);
218}
219
221 // ISDA taxonomy
222 additionalData_["isdaAssetClass"] = string("Interest Rate");
223 additionalData_["isdaBaseProduct"] = string(isXCCY_ ? "Cross Currency" : "IR Swap");
224 additionalData_["isdaSubProduct"] = isdaSubProductSwap(id(), legData_);
225 additionalData_["isdaTransaction"] = string("");
226}
227
228const std::map<std::string,boost::any>& Swap::additionalData() const {
229 Size numLegs = legData_.size();
230 // use the build time as of date to determine current notionals
231 QuantLib::ext::shared_ptr<QuantLib::Swap> swap = QuantLib::ext::dynamic_pointer_cast<QuantLib::Swap>(instrument_->qlInstrument());
232 QuantLib::ext::shared_ptr<QuantExt::CurrencySwap> cswap = QuantLib::ext::dynamic_pointer_cast<QuantExt::CurrencySwap>(instrument_->qlInstrument());
233 std::map<std::string, Real> legNpv; // by currency
234 for (Size i = 0; i < numLegs; ++i) {
235 string legID = to_string(i+1);
236 additionalData_["legType[" + legID + "]"] = legData_[i].legType();
237 additionalData_["isPayer[" + legID + "]"] = legData_[i].isPayer();
238 additionalData_["notionalCurrency[" + legID + "]"] = legData_[i].currency();
239 if (!isXCCY_) {
240 if (swap)
241 additionalData_["legNPV[" + legID + "]"] = swap->legNPV(i);
242 else
243 ALOG("single currency swap underlying instrument not set, skip leg npv reporting");
244 }
245 else {
246 if (cswap) {
247 // The currency swap has more legs than the swap wrapper (additional notional legs), so aggregate by currency
248 Real legNpv = 0;
249 Real legNpvInCcy = 0;
250 for (Size j = 0; j < cswap->legs().size(); ++j) {
251 if (cswap->legCurrency(j).code() == legData_[i].currency()) {
252 legNpv += cswap->legNPV(j);
253 legNpvInCcy += cswap->inCcyLegNPV(j);
254 }
255 }
256 additionalData_["legNPV[" + legID + "]"] = legNpv;
257 additionalData_["legNPVCCY[" + legID + "]"] = legNpvInCcy;
258 }
259 else
260 ALOG("cross currency swap underlying instrument not set, skip leg npv reporting");
261 }
263 }
264 return additionalData_;
265}
266
267QuantLib::Real Swap::notional() const {
268 // try to get the notional from the additional results of the instrument
269 try {
270 return instrument_->qlInstrument(true)->result<Real>("currentNotional");
271 } catch (const std::exception& e) {
272 WLOG("swap engine does not provide current notional: " << e.what() << ", using fallback");
273 // Try getting current notional from coupons
274 if (notionalTakenFromLeg_ < legs_.size()) {
276 if (fabs(n) > QL_EPSILON) {
277 return n;
278 }
279 }
280 // else return the face value
281 WLOG("swap does not provide coupon notionals, using face value");
282 return notional_;
283 }
284}
285
286std::string Swap::notionalCurrency() const {
287 // try to get the notional ccy from the additional results of the instrument
288 try {
289 return instrument_->qlInstrument(true)->result<std::string>("notionalCurrency");
290 } catch (const std::exception& e) {
291 if (strcmp(e.what(), "notionalCurrency not provided"))
292 WLOG("swap engine does not provide notional ccy: " << e.what() << ", using fallback");
293 return notionalCurrency_;
294 }
295}
296
297map<AssetClass, set<string>>
298Swap::underlyingIndices(const QuantLib::ext::shared_ptr<ReferenceDataManager>& referenceDataManager) const {
299
300 map<AssetClass, set<string>> result;
301 for (const auto& ld : legData_) {
302 for (auto ind : ld.indices()) {
303 // only handle equity and commodity for now
304 if (ind.substr(0, 5) != "COMM-" && ind.substr(0, 3) != "EQ-")
305 continue;
306
307 QuantLib::ext::shared_ptr<Index> index = parseIndex(ind);
308
309 if (auto ei = QuantLib::ext::dynamic_pointer_cast<EquityIndex2>(index)) {
310 result[AssetClass::EQ].insert(ei->name());
311 } else if (auto ci = QuantLib::ext::dynamic_pointer_cast<QuantExt::CommodityIndex>(index)) {
312 result[AssetClass::COM].insert(ci->name());
313 }
314 }
315 }
316
317 if (auto s = envelope().additionalField("security_spread", false); !s.empty())
318 result[AssetClass::BOND] = {s};
319
320 return result;
321}
322
323std::string isdaSubProductSwap(const std::string& tradeId, const vector<LegData>& legData) {
324
325 Size nFixed = 0;
326 Size nFloating = 0;
327 for (Size i = 0; i < legData.size(); ++i) {
328 std::string type = legData[i].legType();
329 if (type == "Fixed" ||
330 type == "ZeroCouponFixed" ||
331 type == "Cashflow"||
332 type == "CommodityFixed")
333 nFixed++;
334 else if (type == "Floating" ||
335 type == "CPI" ||
336 type == "YY" ||
337 type == "CMS" ||
338 type == "DigitalCMS" ||
339 type == "CMSSpread" ||
340 type == "DigitalCMSSpread" ||
341 type == "CMB" ||
342 type == "Equity"||
343 type == "DurationAdjustedCMS"||
344 type == "FormulaBased"||
345 type =="CommodityFloating"||
346 type =="EquityMargin")
347 nFloating++;
348 else {
349 ALOG("leg type " << type << " not mapped for trade " << tradeId);
350 }
351 }
352
353 if (nFixed == 0)
354 return string("Basis");
355 else if (nFloating >= 1)
356 return string("Fixed Float");
357 else
358 return string("Fixed Fixed");
359}
360
362 Trade::fromXML(node);
363 legData_.clear();
364 XMLNode* swapNode = XMLUtils::getChildNode(node, tradeType() + "Data");
365 // backwards compatibility
366 if (swapNode == nullptr) {
367 swapNode = XMLUtils::getChildNode(node, "SwapData");
368 }
369 QL_REQUIRE(swapNode, "Swap::fromXML(): expected '" << tradeType() << "Data'"
370 << (tradeType() == "Swap" ? "" : " or 'SwapData'"));
371
372 settlement_ = XMLUtils::getChildValue(swapNode, "Settlement", false);
373 if (settlement_ == "")
374 settlement_ = "Physical";
375
376 vector<XMLNode*> nodes = XMLUtils::getChildrenNodes(swapNode, "LegData");
377 for (Size i = 0; i < nodes.size(); i++) {
378 auto ld = createLegData();
379 ld->fromXML(nodes[i]);
380 legData_.push_back(*QuantLib::ext::static_pointer_cast<LegData>(ld));
381 }
382}
383
384QuantLib::ext::shared_ptr<LegData> Swap::createLegData() const { return QuantLib::ext::make_shared<LegData>(); }
385
387 XMLNode* node = Trade::toXML(doc);
388 XMLNode* swapNode = doc.allocNode(tradeType() + "Data");
389 XMLUtils::appendNode(node, swapNode);
390
391 if (settlement_ == "Cash")
392 XMLUtils::addChild(doc, swapNode, "Settlement", settlement_);
393 for (Size i = 0; i < legData_.size(); i++)
394 XMLUtils::appendNode(swapNode, legData_[i].toXML(doc));
395 return node;
396}
397
398} // namespace data
399} // namespace ore
Engine builder for Swaps.
Serializable object holding indexing data.
Definition: indexing.hpp:39
const string & index() const
Definition: indexing.hpp:60
Serializable object holding leg data.
Definition: legdata.hpp:844
const vector< double > & notionals() const
Definition: legdata.hpp:875
bool isResetting_
Definition: swap.hpp:87
std::string notionalCurrency() const override
Definition: swap.cpp:286
string settlement_
Definition: swap.hpp:83
bool isXCCY_
Definition: swap.hpp:84
virtual QuantLib::ext::shared_ptr< LegData > createLegData() const
Definition: swap.cpp:384
vector< LegData > legData_
Definition: swap.hpp:82
QuantLib::Real notional() const override
Return the current notional in npvCurrency. See individual sub-classes for the precise definition.
Definition: swap.cpp:267
virtual void fromXML(XMLNode *node) override
Definition: swap.cpp:361
virtual XMLNode * toXML(XMLDocument &doc) const override
Definition: swap.cpp:386
Size notionalTakenFromLeg_
Definition: swap.hpp:88
std::map< AssetClass, std::set< std::string > > underlyingIndices(const QuantLib::ext::shared_ptr< ReferenceDataManager > &referenceDataManager=nullptr) const override
Add underlying index names.
Definition: swap.cpp:298
virtual void setIsdaTaxonomyFields()
Definition: swap.cpp:220
virtual void build(const QuantLib::ext::shared_ptr< EngineFactory > &) override
Build QuantLib/QuantExt instrument, link pricing engine.
Definition: swap.cpp:45
const std::map< std::string, boost::any > & additionalData() const override
returns all additional data returned by the trade once built
Definition: swap.cpp:228
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
void setSensitivityTemplate(const EngineBuilder &builder)
Definition: trade.cpp:295
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
void setLegBasedAdditionalData(const Size legNo, Size resultLegId=Null< Size >()) const
Definition: trade.cpp:153
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
Logic for calculating required fixing dates on legs.
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
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.
Map text representations to QuantLib/QuantExt types.
Leg Builders.
leg data model and serialization
Classes and functions for log message handling.
@ 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
vector< T > buildScheduledVectorNormalised(const vector< T > &values, const vector< string > &dates, const Schedule &schedule, const T &defaultValue, const bool checkAllValuesAppearInResult=false)
Definition: legdata.hpp:1139
Real currentNotional(const Leg &leg)
Definition: legdata.cpp:2435
void applyIndexing(Leg &leg, const LegData &data, const QuantLib::ext::shared_ptr< EngineFactory > &engineFactory, RequiredFixings &requiredFixings, const QuantLib::Date &openEndDateReplacement, const bool useXbsCurves)
Definition: legdata.cpp:2633
std::string isdaSubProductSwap(const std::string &tradeId, const vector< LegData > &legData)
Definition: swap.cpp:323
Size size(const ValueType &v)
Definition: value.cpp:145
Leg buildNotionalLeg(const LegData &data, const Leg &leg, RequiredFixings &requiredFixings, const QuantLib::ext::shared_ptr< Market > &market, const std::string &configuration)
Definition: legdata.cpp:2729
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
Schedule makeSchedule(const ScheduleDates &data)
Definition: schedule.cpp:263
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Swap trade data model and serialization.
string conversion utilities