Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
riskparticipationagreement.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
22
25
30
31#include <ql/cashflows/fixedratecoupon.hpp>
32#include <ql/cashflows/floatingratecoupon.hpp>
33
34namespace ore {
35namespace data {
36
37void RiskParticipationAgreement::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
38
39 LOG("RiskParticipationAgreement::build() for id \"" << id() << "\" called.");
40
41 // ISDA taxonomy
42 additionalData_["isdaAssetClass"] = string("Interest Rate");
43 additionalData_["isdaBaseProduct"] = string("Exotic");
44 additionalData_["isdaSubProduct"] = string("");
45 additionalData_["isdaTransaction"] = string("");
46
47 // do some checks
48
49 QL_REQUIRE(!protectionFee_.empty(), "protection fees must not be empty");
50
51 QL_REQUIRE(underlying_.empty() || tlockData_.empty(),
52 "RiskParticipationAgreement::build(): both LegData and TreasuryLockData given in Underlying node.");
53
54 if (!underlying_.empty())
55 buildWithSwapUnderlying(engineFactory);
56 else if (!tlockData_.empty())
57 buildWithTlockUnderlying(engineFactory);
58 else {
59 QL_FAIL("RiskParticipationAgreement::build(): Underlying node must not be empty, LegData or TreasuryLockData "
60 "required as subnode");
61 }
62
63 // set start date
64 additionalData_["startDate"] = to_string(protectionStart_);
65}
66
67void RiskParticipationAgreement::buildWithSwapUnderlying(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
68 npvCurrency_ = notionalCurrency_ = underlying_.front().currency();
69
70 bool isXccy = false;
71 for (auto const& u : underlying_) {
72 isXccy = isXccy || u.currency() != npvCurrency_;
73 }
74
75 for (auto const& p : protectionFee_)
76 QL_REQUIRE(p.isPayer() == protectionFee_.front().isPayer(),
77 "the protection fee legs must all be pay or all receive");
78
79 /* determine product variant used to retrieve engine builder
80
81 RiskParticipationAgreement_Vanilla:
82 - exactly one fixed and one floating leg with opposite payer flags
83 - only fixed, ibor, ois (comp, avg) coupons allowed, no cap / floors, no in arrears fixings for ibor
84
85 RiskParticipationAgreement_Structured:
86 - arbitrary number of fixed, floating, cashflow legs
87 - only fixed, ibor coupons, ois (comp, avg), simple cashflows allowed, but possibly capped / floored,
88 as naked option, with in arrears fixing for ibor
89 - with optionData (i.e. callable underlying), as naked option (i.e. swaption)
90
91 RiskParticipationAgreement_Vanilla_XCcy:
92 - two legs in different currencies with arbitrary coupons allowed, no optionData though
93 */
94
95 std::set<std::string> legTypes;
96 std::set<bool> legPayers;
97 bool hasCapFloors = false, hasIborInArrears = false;
98 for (auto const& l : underlying_) {
99 QL_REQUIRE(l.legType() == "Fixed" || l.legType() == "Floating" || l.legType() == "Cashflow",
100 "RiskParticipationAgreement: leg type " << l.legType()
101 << " not supported, expected Fixed, Floating, Cashflow");
102 legTypes.insert(l.legType());
103 legPayers.insert(l.isPayer());
104 if (auto c = QuantLib::ext::dynamic_pointer_cast<FloatingLegData>(l.concreteLegData())) {
105 hasCapFloors = hasCapFloors || !c->caps().empty();
106 hasCapFloors = hasCapFloors || !c->floors().empty();
107 hasIborInArrears = hasIborInArrears || (c->isInArrears() && !isOvernightIndex(c->index()));
108 }
109 }
110
111 std::string productVariant;
112 if (isXccy) {
113 productVariant = "RiskParticipationAgreement_Vanilla_XCcy";
114 QL_REQUIRE(!optionData_, "XCcy Risk Participation Agreement does not allow for OptionData");
115 } else if (underlying_.size() == 2 &&
116 (legTypes == std::set<std::string>{"Fixed", "Floating"} ||
117 legTypes == std::set<std::string>{"Cashflow", "Floating"}) &&
118 legPayers == std::set<bool>{false, true} && !hasCapFloors && !hasIborInArrears && !optionData_) {
119 productVariant = "RiskParticipationAgreement_Vanilla";
120 } else {
121 productVariant = "RiskParticipationAgreement_Structured";
122 }
123
124 // get engine builder
125
126 DLOG("get engine builder for product variant " << productVariant);
127 auto builder = QuantLib::ext::dynamic_pointer_cast<RiskParticipationAgreementEngineBuilderBase>(
128 engineFactory->builder(productVariant));
129 QL_REQUIRE(builder, "wrong builder, expected RiskParticipationAgreementEngineBuilderBase");
130 auto configuration = builder->configuration(MarketContext::pricing);
131
132 // build underlying legs and protection fee legs
133
134 std::vector<Leg> underlyingLegs, protectionFeeLegs;
135 std::vector<bool> underlyingPayer, protectionPayer;
136 std::vector<string> underlyingCcys, protectionCcys;
137 for (auto const& l : underlying_) {
138 auto legBuilder = engineFactory->legBuilder(l.legType());
139 underlyingLegs.push_back(legBuilder->buildLeg(l, engineFactory, requiredFixings_, configuration));
140 underlyingPayer.push_back(l.isPayer());
141 underlyingCcys.push_back(l.currency());
142 auto leg = buildNotionalLeg(l, underlyingLegs.back(), requiredFixings_, engineFactory->market(), configuration);
143 if (!leg.empty()) {
144 underlyingLegs.push_back(leg);
145 underlyingPayer.push_back(l.isPayer());
146 underlyingCcys.push_back(l.currency());
147 }
148 }
149 for (auto const& l : protectionFee_) {
150 auto legBuilder = engineFactory->legBuilder(l.legType());
151 protectionFeeLegs.push_back(legBuilder->buildLeg(l, engineFactory, requiredFixings_, configuration));
152 protectionPayer.push_back(l.isPayer());
153 protectionCcys.push_back(protectionFeeLegs.back().empty() ? npvCurrency_ : l.currency());
154 }
155
156 // build exercise, if option data is present
157
158 QuantLib::ext::shared_ptr<QuantLib::Exercise> exercise;
159 bool exerciseIsLong = true;
160 std::vector<QuantLib::ext::shared_ptr<CashFlow>> vectorPremium;
161 if (optionData_) {
162 ExerciseBuilder eb(*optionData_, underlyingLegs);
163 exercise = eb.exercise();
164 exerciseIsLong = parsePositionType((*optionData_).longShort()) == QuantLib::Position::Long;
165 for (const auto& premium : (*optionData_).premiumData().premiumData()) {
166 QL_REQUIRE((premium.ccy == underlyingCcys[0]) && (premium.ccy == underlyingCcys[1]),
167 "premium currency must be the same than the swaption legs");
168 vectorPremium.push_back(QuantLib::ext::make_shared<SimpleCashFlow>(premium.amount, premium.payDate));
169 }
170 }
171
172 // build ql instrument
173
174 auto qleInstr = QuantLib::ext::make_shared<QuantExt::RiskParticipationAgreement>(
175 underlyingLegs, underlyingPayer, underlyingCcys, protectionFeeLegs, protectionPayer.front(), protectionCcys,
177 exerciseIsLong, vectorPremium, nakedOption_);
178
179 // wrap instrument
180
181 instrument_ = QuantLib::ext::make_shared<VanillaInstrument>(qleInstr);
182
183 // set trade members
184
185 notional_ = 0.0;
186 for (Size i = 0; i < underlyingLegs.size(); ++i) {
187 notional_ = std::max(notional_, currentNotional(underlyingLegs[i]) *
188 engineFactory->market()
189 ->fxRate(underlyingCcys[i] + notionalCurrency_,
190 engineFactory->configuration(MarketContext::pricing))
191 ->value());
192 }
193 legs_ = underlyingLegs;
194 legCurrencies_ = underlyingCcys;
195 legPayers_ = underlyingPayer;
196 legs_.insert(legs_.end(), protectionFeeLegs.begin(), protectionFeeLegs.end());
197 legCurrencies_.insert(legCurrencies_.end(), protectionCcys.begin(), protectionCcys.end());
198 legPayers_.insert(legPayers_.end(), protectionPayer.begin(), protectionPayer.end());
199 maturity_ = qleInstr->maturity();
200
201 // set pricing engine
202 qleInstr->setPricingEngine(builder->engine(id(), this));
203 setSensitivityTemplate(*builder);
204}
205
206namespace {
207DayCounter getDayCounter(const Leg& l) {
208 for (auto const& c : l) {
209 if (auto cpn = QuantLib::ext::dynamic_pointer_cast<Coupon>(c))
210 return cpn->dayCounter();
211 }
212 QL_FAIL("RiskParticipationAgreement: could not deduce DayCounter from underlying bond, no coupons found in "
213 "bond "
214 "cashflows ("
215 << l.size() << ")");
216}
217
218} // namespace
219
220void RiskParticipationAgreement::buildWithTlockUnderlying(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
221 std::string productVariant = "RiskParticipationAgreement_TLock";
222
223 // get bond reference data and build bond
224
225 tlockData_.bondData() = tlockData_.originalBondData();
226 tlockData_.bondData().populateFromBondReferenceData(engineFactory->referenceData());
227 ore::data::Bond tmp(Envelope(), tlockData_.bondData());
228 tmp.build(engineFactory);
229 auto bond = QuantLib::ext::dynamic_pointer_cast<QuantLib::Bond>(tmp.instrument()->qlInstrument());
230 QL_REQUIRE(bond != nullptr, "RiskParticipationAgreement: could not build tlock underlying, cast failed (internal "
231 "error that dev needs to look at)");
232
233 // set currency and notional
234
235 npvCurrency_ = notionalCurrency_ = tlockData_.bondData().currency();
236 notional_ = tlockData_.bondData().bondNotional();
237
238 // checks
239
240 for (auto const& p : protectionFee_)
241 QL_REQUIRE(p.isPayer() == protectionFee_.front().isPayer(),
242 "the protection fee legs must all be pay or all receive");
243
244 // get engine builder
245
246 DLOG("get engine builder for product variant " << productVariant);
247 auto builder = QuantLib::ext::dynamic_pointer_cast<RiskParticipationAgreementEngineBuilderBase>(
248 engineFactory->builder(productVariant));
249 QL_REQUIRE(builder, "wrong builder, expected RiskParticipationAgreementEngineBuilderBase");
250 auto configuration = builder->configuration(MarketContext::pricing);
251
252 // build ql instrument and set the pricing engine
253
254 bool payer = tlockData_.payer();
255 Real referenceRate = tlockData_.referenceRate();
256 DayCounter dayCounter =
257 tlockData_.dayCounter().empty() ? getDayCounter(bond->cashflows()) : parseDayCounter(tlockData_.dayCounter());
258 Date terminationDate = parseDate(tlockData_.terminationDate());
259 Size paymentGap = tlockData_.paymentGap();
260 Calendar paymentCalendar = parseCalendar(tlockData_.paymentCalendar());
261
262 std::vector<Leg> protectionFeeLegs;
263 std::vector<bool> protectionPayer;
264 std::vector<string> protectionCcys;
265 for (auto const& l : protectionFee_) {
266 auto legBuilder = engineFactory->legBuilder(l.legType());
267 protectionFeeLegs.push_back(legBuilder->buildLeg(l, engineFactory, requiredFixings_, configuration));
268 protectionPayer.push_back(l.isPayer());
269 protectionCcys.push_back(protectionFeeLegs.back().empty() ? npvCurrency_ : l.currency());
270 }
271
272 Date paymentDate = paymentCalendar.advance(terminationDate, paymentGap * Days);
273 auto qleInstr = QuantLib::ext::make_shared<QuantExt::RiskParticipationAgreementTLock>(
274 bond, notional_, payer, referenceRate, dayCounter, terminationDate, paymentDate, protectionFeeLegs,
275 protectionPayer.front(), protectionCcys, participationRate_, protectionStart_, protectionEnd_, settlesAccrual_,
277
278 // wrap instrument
279
280 instrument_ = QuantLib::ext::make_shared<VanillaInstrument>(qleInstr);
281
282 // set trade members
283
284 legs_ = {bond->cashflows()};
285 legCurrencies_ = {npvCurrency_};
286 legPayers_ = {payer};
287 legs_.insert(legs_.end(), protectionFeeLegs.begin(), protectionFeeLegs.end());
288 legCurrencies_.insert(legCurrencies_.end(), protectionCcys.begin(), protectionCcys.end());
289 legPayers_.insert(legPayers_.end(), protectionPayer.begin(), protectionPayer.end());
290 maturity_ = qleInstr->maturity();
291
292 // set pricing engine
293
294 qleInstr->setPricingEngine(builder->engine(id(), this));
295 setSensitivityTemplate(*builder);
296}
297
298void RiskParticipationAgreement::fromXML(XMLNode* node) {
299 Trade::fromXML(node);
300 XMLNode* n = XMLUtils::getChildNode(node, "RiskParticipationAgreementData");
301 QL_REQUIRE(n, "RiskParticipationAgreement::fromXML(): RiskParticipationAgreementData not found");
302 participationRate_ = XMLUtils::getChildValueAsDouble(n, "ParticipationRate", true);
303 protectionStart_ = parseDate(XMLUtils::getChildValue(n, "ProtectionStart", true));
304 protectionEnd_ = parseDate(XMLUtils::getChildValue(n, "ProtectionEnd", true));
305 creditCurveId_ = XMLUtils::getChildValue(n, "CreditCurveId", true);
306 issuerId_ = XMLUtils::getChildValue(n, "IssuerId", false); // defaults to empty string
307 settlesAccrual_ = XMLUtils::getChildValueAsBool(n, "SettlesAccrual", false); // defaults to true
308 tryParseReal(XMLUtils::getChildValue(n, "FixedRecoveryRate"), fixedRecoveryRate_); // defaults to null
309
310 underlying_.clear();
311 XMLNode* u = XMLUtils::getChildNode(n, "Underlying");
312 QL_REQUIRE(u, "RiskParticipationAgreement::fromXML(): Underlying not found");
313
314 vector<XMLNode*> nodes = XMLUtils::getChildrenNodes(u, "LegData");
315 for (auto const n : nodes) {
316 LegData ld;
317 ld.fromXML(n);
318 underlying_.push_back(ld);
319 }
320
321 if (auto tmp = XMLUtils::getChildNode(u, "OptionData")) {
322 optionData_ = OptionData();
323 (*optionData_).fromXML(tmp);
324 }
325
326 nakedOption_ = XMLUtils::getChildValueAsBool(u, "NakedOption", false, false);
327
328 if (auto tmp = XMLUtils::getChildNode(u, "TreasuryLockData")) {
329 tlockData_.fromXML(tmp);
330 }
331
332 protectionFee_.clear();
333 XMLNode* p = XMLUtils::getChildNode(n, "ProtectionFee");
334 QL_REQUIRE(p, "RiskParticipationAgreement::fromXML(): ProtectionFee not found");
335 vector<XMLNode*> nodes2 = XMLUtils::getChildrenNodes(p, "LegData");
336 for (auto const n : nodes2) {
337 LegData ld; // we do not allow ORE+ leg types anyway
338 ld.fromXML(n);
339 protectionFee_.push_back(ld);
340 }
341}
342
343XMLNode* RiskParticipationAgreement::toXML(XMLDocument& doc) const {
344 XMLNode* node = Trade::toXML(doc);
345 XMLNode* n = doc.allocNode("RiskParticipationAgreementData");
346 XMLUtils::appendNode(node, n);
347 XMLUtils::addChild(doc, n, "ParticipationRate", participationRate_);
348 XMLUtils::addChild(doc, n, "ProtectionStart", ore::data::to_string(protectionStart_));
349 XMLUtils::addChild(doc, n, "ProtectionEnd", ore::data::to_string(protectionEnd_));
350 XMLUtils::addChild(doc, n, "CreditCurveId", creditCurveId_);
351 XMLUtils::addChild(doc, n, "IssuerId", issuerId_);
352 XMLUtils::addChild(doc, n, "SettlesAccrual", settlesAccrual_);
353 if (fixedRecoveryRate_ != Null<Real>())
354 XMLUtils::addChild(doc, n, "FixedRecoveryRate", fixedRecoveryRate_);
355 XMLNode* p = doc.allocNode("ProtectionFee");
356 XMLNode* u = doc.allocNode("Underlying");
357 XMLUtils::appendNode(n, p);
358 XMLUtils::appendNode(n, u);
359 if (optionData_)
360 XMLUtils::appendNode(u, (*optionData_).toXML(doc));
361 if (nakedOption_)
362 XMLUtils::addChild(doc, n, "NakedOption", nakedOption_);
363 for (auto& l : protectionFee_)
364 XMLUtils::appendNode(p, l.toXML(doc));
365 for (auto& l : underlying_)
366 XMLUtils::appendNode(u, l.toXML(doc));
367 if (!tlockData_.empty()) {
368 XMLUtils::appendNode(u, tlockData_.toXML(doc));
369 }
370 return node;
371}
372
373} // namespace data
374} // namespace ore
builder that returns an engine to price a bond instrument
const boost::shared_ptr< Exercise > & exercise() const
const std::vector< Leg > underlying_
const std::vector< Leg > protectionFee_
const std::vector< std::string > & underlyingCcys() const
const std::vector< bool > & underlyingPayer() const
Serializable Bond.
Definition: bond.hpp:153
virtual void build(const QuantLib::ext::shared_ptr< EngineFactory > &) override
Trade interface.
Definition: bond.cpp:228
Serializable object holding generic trade data, reporting dimensions.
Definition: envelope.hpp:51
QuantLib::ext::shared_ptr< QuantLib::Exercise > exercise() const
Definition: optiondata.hpp:157
Serializable object holding leg data.
Definition: legdata.hpp:844
virtual void fromXML(XMLNode *node) override
Definition: legdata.cpp:759
Serializable object holding option data.
Definition: optiondata.hpp:42
void build(const QuantLib::ext::shared_ptr< ore::data::EngineFactory > &) override
const QuantLib::ext::shared_ptr< InstrumentWrapper > & instrument() const
Definition: trade.hpp:141
Small XML Document wrapper class.
Definition: xmlutils.hpp:65
XMLNode * allocNode(const string &nodeName)
util functions that wrap rapidxml
Definition: xmlutils.cpp:132
Pricing Engine Factory.
Logic for calculating required fixing dates on legs.
Calendar parseCalendar(const string &s)
Convert text to QuantLib::Calendar.
Definition: parsers.cpp:157
bool isOvernightIndex(const string &indexName)
Return true if the indexName is that of an overnight index, otherwise false.
bool tryParseReal(const string &s, QuantLib::Real &result)
Attempt to convert text to Real.
Definition: parsers.cpp:126
Date parseDate(const string &s)
Convert std::string to QuantLib::Date.
Definition: parsers.cpp:51
Position::Type parsePositionType(const std::string &s)
Convert text to QuantLib::Position::Type.
Definition: parsers.cpp:404
DayCounter parseDayCounter(const string &s)
Convert text to QuantLib::DayCounter.
Definition: parsers.cpp:209
@ 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
Real currentNotional(const Leg &leg)
Definition: legdata.cpp:2435
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
Serializable Credit Default Swap.
Definition: namespaces.docs:23
risk participation agreement data model and serialization
string conversion utilities