Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
balanceguaranteedswap.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
21#include <qle/instruments/balanceguaranteedswap.hpp>
22
27
28#include <ql/cashflows/fixedratecoupon.hpp>
29#include <ql/cashflows/floatingratecoupon.hpp>
30
31using namespace QuantLib;
32
33namespace ore {
34namespace data {
35
37 XMLUtils::checkNode(node, "Tranche");
38 description_ = XMLUtils::getChildValue(node, "Description", false);
39 securityId_ = XMLUtils::getChildValue(node, "SecurityId", true);
40 seniority_ = XMLUtils::getChildValueAsInt(node, "Seniority", true);
41 notionals_ = XMLUtils::getChildrenValuesWithAttributes<Real>(node, "Notionals", "Notional", "startDate",
43}
44
46 XMLNode* node = doc.allocNode("Tranche");
47 XMLUtils::addChild(doc, node, "Description", description_);
48 XMLUtils::addChild(doc, node, "SecurityId", securityId_);
49 XMLUtils::addChildrenWithOptionalAttributes(doc, node, "Notionals", "Notional", notionals_, "startDate",
51 return node;
52}
53
54void BalanceGuaranteedSwap::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
55
56 LOG("BalanceGuaranteedSwap::build() for id \"" << id() << "\" called.");
57
58 // ISDA taxonomy
59 additionalData_["isdaAssetClass"] = string("Interest Rate");
60 additionalData_["isdaBaseProduct"] = string("Exotic");
61 additionalData_["isdaSubProduct"] = string("");
62 additionalData_["isdaTransaction"] = string("");
63
64 QuantLib::Schedule schedule = makeSchedule(this->schedule());
65
66 std::vector<BGSTrancheData> sortedTranches(tranches());
67 std::sort(sortedTranches.begin(), sortedTranches.end(),
68 [](const BGSTrancheData& x, const BGSTrancheData& y) { return x.seniority() < y.seniority(); });
69 std::vector<std::vector<Real>> trancheNotionals;
70 Size referencedTranche = Null<Size>(), counter = 0;
71 for (auto const& t : sortedTranches) {
72 if (t.securityId() == referenceSecurity()) {
73 QL_REQUIRE(referencedTranche == Null<Size>(),
74 "there is more than one tranche with id \"" << referenceSecurity() << "\"");
75 referencedTranche = counter;
76 }
77 trancheNotionals.push_back(buildScheduledVectorNormalised(t.notionals(), t.notionalDates(), schedule, 0.0));
78 ++counter;
79 }
80 QL_REQUIRE(referencedTranche != Null<Size>(), "referenced tranche not found");
81
82 QL_REQUIRE(swap_.size() == 2, "swap must have 2 legs");
83 QL_REQUIRE(swap_[0].currency() == swap_[1].currency(), "swap must be single currency");
84
85 string ccy_str = swap_[0].currency();
86 Currency currency = parseCurrency(ccy_str);
87
88 Size fixedLegIndex, floatingLegIndex;
89 if (swap_[0].legType() == "Floating" && swap_[1].legType() == "Fixed") {
90 floatingLegIndex = 0;
91 fixedLegIndex = 1;
92 } else if (swap_[1].legType() == "Floating" && swap_[0].legType() == "Fixed") {
93 floatingLegIndex = 1;
94 fixedLegIndex = 0;
95 } else {
96 QL_FAIL("Invalid leg types " << swap_[0].legType() << " + " << swap_[1].legType());
97 }
98
99 QuantLib::ext::shared_ptr<FixedLegData> fixedLegData =
100 QuantLib::ext::dynamic_pointer_cast<FixedLegData>(swap_[fixedLegIndex].concreteLegData());
101 QuantLib::ext::shared_ptr<FloatingLegData> floatingLegData =
102 QuantLib::ext::dynamic_pointer_cast<FloatingLegData>(swap_[floatingLegIndex].concreteLegData());
103
104 QL_REQUIRE(fixedLegData != nullptr, "expected fixed leg data");
105 QL_REQUIRE(floatingLegData != nullptr, "expected floating leg data");
106
107 QuantLib::ext::shared_ptr<EngineBuilder> tmp = engineFactory->builder("BalanceGuaranteedSwap");
108 auto builder = QuantLib::ext::dynamic_pointer_cast<FlexiSwapBGSEngineBuilderBase>(tmp);
109 QL_REQUIRE(builder, "No BGS Builder found for \"" << id() << "\"");
110
111 Schedule fixedSchedule = makeSchedule(swap_[fixedLegIndex].schedule());
112 Schedule floatingSchedule = makeSchedule(swap_[floatingLegIndex].schedule());
113 vector<Real> fixedRate =
114 buildScheduledVectorNormalised(fixedLegData->rates(), fixedLegData->rateDates(), fixedSchedule, 0.0);
115 vector<Real> spreads = buildScheduledVectorNormalised(floatingLegData->spreads(), floatingLegData->spreadDates(),
116 floatingSchedule, 0.0);
117 vector<Real> gearings = buildScheduledVectorNormalised(floatingLegData->gearings(), floatingLegData->gearingDates(),
118 floatingSchedule, 1.0);
119 vector<Real> caps = buildScheduledVectorNormalised(floatingLegData->caps(), floatingLegData->capDates(),
120 floatingSchedule, (Real)Null<Real>());
121 vector<Real> floors = buildScheduledVectorNormalised(floatingLegData->floors(), floatingLegData->floorDates(),
122 floatingSchedule, (Real)Null<Real>());
123 std::string floatingIndex = floatingLegData->index();
124 DayCounter fixedDayCounter = parseDayCounter(swap_[fixedLegIndex].dayCounter());
125 Handle<IborIndex> index =
126 engineFactory->market()->iborIndex(floatingIndex, builder->configuration(MarketContext::pricing));
127 DayCounter floatingDayCounter = parseDayCounter(swap_[floatingLegIndex].dayCounter());
128 BusinessDayConvention paymentConvention = parseBusinessDayConvention(swap_[floatingLegIndex].paymentConvention());
129 VanillaSwap::Type type = swap_[fixedLegIndex].isPayer() ? VanillaSwap::Payer : VanillaSwap::Receiver;
130
131 auto bgSwap = QuantLib::ext::make_shared<QuantExt::BalanceGuaranteedSwap>(
132 type, trancheNotionals, schedule, referencedTranche, fixedSchedule, fixedRate, fixedDayCounter,
133 floatingSchedule, *index, gearings, spreads, caps, floors, floatingDayCounter, paymentConvention);
134
135 auto fixLeg = bgSwap->leg(0);
136 auto fltLeg = bgSwap->leg(1);
137
138 // check that nominal schedule in legs is identical with the one we extracted from the tranche notionals
139 Size legRatio = fltLeg.size() / fixLeg.size(); // no remainder by construction of a bg swap
140 vector<Real> legFixedNominal = buildScheduledVectorNormalised(
141 swap_[fixedLegIndex].notionals(), swap_[fixedLegIndex].notionalDates(), fixedSchedule, 0.0);
142 vector<Real> legFloatingNominal = buildScheduledVectorNormalised(
143 swap_[floatingLegIndex].notionals(), swap_[floatingLegIndex].notionalDates(), floatingSchedule, 0.0);
144 for (Size i = 0; i < legFixedNominal.size(); ++i) {
145 QL_REQUIRE(close_enough(bgSwap->trancheNominal(referencedTranche, fixedSchedule[i]), legFixedNominal[i]),
146 "fixed leg notional at " << i << " (" << legFixedNominal[i] << ") does not match tranche notional ("
147 << bgSwap->trancheNominal(referencedTranche, fixedSchedule[i])
148 << "), referenced tranche is " << referencedTranche);
149 }
150 for (Size i = 0; i < legFloatingNominal.size(); ++i) {
151 // this is how we build the float notional schedule in the BGS internally as well, i.e. derived from the fixed
152 // side
153 QL_REQUIRE(close_enough(legFloatingNominal[i], legFixedNominal[i / legRatio]),
154 "floating leg notional at " << i << " (" << legFloatingNominal[i]
155 << ") does not match fixed leg notional at " << (i / legRatio) << " ("
156 << legFixedNominal[i / legRatio] << ")");
157 }
158
159 // set coupon pricers if needed (for flow report, discounting swap engine, not used in LGM engine)
160
161 bool hasCapsFloors = false;
162 for (auto const& k : caps) {
163 if (k != Null<Real>())
164 hasCapsFloors = true;
165 }
166 for (auto const& k : floors) {
167 if (k != Null<Real>())
168 hasCapsFloors = true;
169 }
170 if (hasCapsFloors) {
171 QuantLib::ext::shared_ptr<EngineBuilder> cfBuilder = engineFactory->builder("CapFlooredIborLeg");
172 QL_REQUIRE(cfBuilder, "No builder found for CapFlooredIborLeg");
173 QuantLib::ext::shared_ptr<CapFlooredIborLegEngineBuilder> cappedFlooredIborBuilder =
174 QuantLib::ext::dynamic_pointer_cast<CapFlooredIborLegEngineBuilder>(cfBuilder);
175 QL_REQUIRE(cappedFlooredIborBuilder != nullptr, "expected CapFlooredIborLegEngineBuilder");
176 QuantLib::ext::shared_ptr<FloatingRateCouponPricer> couponPricer =
177 cappedFlooredIborBuilder->engine(IndexNameTranslator::instance().oreName(index->name()));
178 QuantLib::setCouponPricer(fltLeg, couponPricer);
179 }
180
181 // determine expiries and strikes for calibration basket (simple approach, a la summit)
182
183 std::vector<Date> expiryDates;
184 std::vector<Real> strikes;
185 Date today = Settings::instance().evaluationDate();
186 for (Size i = 0; i < fltLeg.size(); ++i) {
187 auto fltcpn = QuantLib::ext::dynamic_pointer_cast<FloatingRateCoupon>(fltLeg[i]);
188 if (fltcpn != nullptr && fltcpn->fixingDate() > today && i % legRatio == 0) {
189 expiryDates.push_back(fltcpn->fixingDate());
190 auto fixcpn = QuantLib::ext::dynamic_pointer_cast<FixedRateCoupon>(fixLeg[i]);
191 QL_REQUIRE(fixcpn != nullptr, "BalanceGuaranteedSwap Builder: expected fixed rate coupon");
192 strikes.push_back(fixcpn->rate() - fltcpn->spread());
193 }
194 }
195
196 // set pricing engine, init instrument and other trade members
197
198 bgSwap->setPricingEngine(
199 builder->engine(id(), referenceSecurity(), ccy_str, expiryDates, bgSwap->maturityDate(), strikes));
200 setSensitivityTemplate(*builder);
201
202 // add required fixings
203 addToRequiredFixings(fltLeg, QuantLib::ext::make_shared<FixingDateGetter>(requiredFixings_));
204
205 // FIXME this won't work for exposure, currently not supported
206 instrument_ = QuantLib::ext::make_shared<VanillaInstrument>(bgSwap);
207
208 npvCurrency_ = ccy_str;
209 notional_ = std::max(currentNotional(fixLeg), currentNotional(fltLeg));
210 notionalCurrency_ = ccy_str;
211 legCurrencies_ = vector<string>(2, ccy_str);
212 legs_ = {fixLeg, fltLeg};
213 legPayers_ = {swap_[fixedLegIndex].isPayer(), swap_[floatingLegIndex].isPayer()};
214 maturity_ = bgSwap->maturityDate();
215}
216
218 Trade::fromXML(node);
219 XMLNode* swapNode = XMLUtils::getChildNode(node, "BalanceGuaranteedSwapData");
220 QL_REQUIRE(swapNode, "BalanceGuaranteedSwap::fromXML(): BalanceGuaranteedSwapData not found");
221
222 referenceSecurity_ = XMLUtils::getChildValue(swapNode, "ReferenceSecurity");
223
224 XMLNode* tranchesNode = XMLUtils::getChildNode(swapNode, "Tranches");
225 QL_REQUIRE(tranchesNode, "BalanceGuaranteedSwap::fromXML(): Tranches node not found");
226 tranches_.clear();
227 vector<XMLNode*> trancheNodes = XMLUtils::getChildrenNodes(tranchesNode, "Tranche");
228 for (Size i = 0; i < trancheNodes.size(); ++i) {
230 td.fromXML(trancheNodes[i]);
231 tranches_.push_back(td);
232 }
233
234 XMLNode* scheduleNode = XMLUtils::getChildNode(tranchesNode, "ScheduleData");
235 schedule_.fromXML(scheduleNode);
236
237 swap_.clear();
238 vector<XMLNode*> nodes = XMLUtils::getChildrenNodes(swapNode, "LegData");
239 for (Size i = 0; i < nodes.size(); ++i) {
240 LegData ld; // we do not allow ORE+ leg types anyway
241 ld.fromXML(nodes[i]);
242 swap_.push_back(ld);
243 }
244}
245
247 XMLNode* node = Trade::toXML(doc);
248 XMLUtils::addChild(doc, node, "ReferenceSecurity", referenceSecurity_);
249
250 XMLNode* tranchesNode = doc.allocNode("Tranches");
251 XMLUtils::appendNode(node, tranchesNode);
252 for (Size i = 0; i < tranches_.size(); ++i) {
253 XMLUtils::appendNode(tranchesNode, tranches_[i].toXML(doc));
254 }
255
256 XMLUtils::appendNode(tranchesNode, schedule_.toXML(doc));
257
258 XMLNode* swapNode = doc.allocNode("BalanceGuaranteedSwapData");
259 XMLUtils::appendNode(node, swapNode);
260 for (Size i = 0; i < swap_.size(); ++i)
261 XMLUtils::appendNode(swapNode, swap_[i].toXML(doc));
262 return node;
263}
264
265} // namespace data
266} // namespace ore
Balance Guaranteed Swap data model and serialization.
builder that returns an engine to price capped floored ibor legs
Serializable Tranche for use in Balance Guaranteed Swaps.
std::vector< std::string > notionalDates_
std::vector< QuantLib::Real > notionals_
virtual void fromXML(ore::data::XMLNode *node) override
virtual ore::data::XMLNode * toXML(ore::data::XMLDocument &doc) const override
const std::string & referenceSecurity() const
std::vector< ore::data::LegData > swap_
std::vector< BGSTrancheData > tranches_
virtual void fromXML(ore::data::XMLNode *node) override
const ore::data::ScheduleData schedule() const
const std::vector< BGSTrancheData > & tranches() const
virtual ore::data::XMLNode * toXML(ore::data::XMLDocument &doc) const override
void build(const QuantLib::ext::shared_ptr< ore::data::EngineFactory > &) override
Serializable object holding leg data.
Definition: legdata.hpp:844
virtual void fromXML(XMLNode *node) override
Definition: legdata.cpp:759
virtual void fromXML(XMLNode *node) override
Definition: schedule.cpp:179
virtual XMLNode * toXML(XMLDocument &doc) const override
Definition: schedule.cpp:198
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
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 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 XMLNode * getChildNode(XMLNode *n, const string &name="")
Definition: xmlutils.cpp:387
static void addChildrenWithOptionalAttributes(XMLDocument &doc, XMLNode *n, const string &names, const string &name, const vector< T > &values, const string &attrName, const vector< string > &attrs)
Definition: xmlutils.cpp:542
static int getChildValueAsInt(XMLNode *node, const string &name, bool mandatory=false, int defaultValue=0)
Definition: xmlutils.cpp:291
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
Logic for calculating required fixing dates on legs.
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
Real parseReal(const string &s)
Convert text to Real.
Definition: parsers.cpp:112
DayCounter parseDayCounter(const string &s)
Convert text to QuantLib::DayCounter.
Definition: parsers.cpp:209
translates between QuantLib::Index::name() and ORE names
Classes and functions for log message handling.
@ data
Definition: log.hpp:77
#define LOG(text)
Logging Macro (Level = Notice)
Definition: log.hpp:552
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
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 addToRequiredFixings(const QuantLib::Leg &leg, const QuantLib::ext::shared_ptr< FixingDateGetter > &fixingDateGetter)
Schedule makeSchedule(const ScheduleDates &data)
Definition: schedule.cpp:263
Serializable Credit Default Swap.
Definition: namespaces.docs:23
vector< Real > strikes