Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
bondtotalreturnswap.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
34
35#include <ql/cashflows/simplecashflow.hpp>
36#include <ql/instruments/bond.hpp>
37#include <ql/instruments/bonds/zerocouponbond.hpp>
38#include <ql/quotes/simplequote.hpp>
39#include <ql/time/calendars/weekendsonly.hpp>
40
41#include <boost/lexical_cast.hpp>
42#include <boost/make_shared.hpp>
43
44using namespace QuantLib;
45using namespace QuantExt;
46
47namespace ore {
48namespace data {
49
50void BondTRS::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
51 DLOG("BondTRS::build() called for trade " << id());
52
53 // ISDA taxonomy
54 additionalData_["isdaAssetClass"] = string("Credit");
55 additionalData_["isdaBaseProduct"] = string("Total Return Swap");
56 additionalData_["isdaSubProduct"] = string("");
57 additionalData_["isdaTransaction"] = string("");
58
59 const QuantLib::ext::shared_ptr<Market> market = engineFactory->market();
60 QuantLib::ext::shared_ptr<EngineBuilder> builder_trs = engineFactory->builder("BondTRS");
61 bondData_ = originalBondData_;
62 bondData_.populateFromBondReferenceData(engineFactory->referenceData());
63
64 Schedule schedule = makeSchedule(scheduleData_);
65 Calendar calendar = parseCalendar(bondData_.calendar());
66
67 auto configuration = builder_trs->configuration(MarketContext::pricing);
68 auto legBuilder = engineFactory->legBuilder(fundingLegData_.legType());
69
70 // check currency restrictions
71
72 QL_REQUIRE(fundingLegData_.currency() != bondData_.currency() || fxIndex_.empty(),
73 "if funding leg ccy (" << fundingLegData_.currency() << ") = bond ccy (" << bondData_.currency()
74 << "), no fx index must be given");
75 QL_REQUIRE(fundingLegData_.currency() == bondData_.currency() || !fxIndex_.empty(),
76 "if funding leg ccy (" << fundingLegData_.currency() << ") != bond ccy (" << bondData_.currency()
77 << "), a fx index must be given");
78
79 npvCurrency_ = fundingLegData_.currency();
80 notionalCurrency_ = bondData_.currency();
81
82 // build return leg valuation and payment schedule
83 DLOG("build valuation and payment dates vectors");
84
85 Period observationLag = observationLag_.empty() ? 0 * Days : parsePeriod(observationLag_);
86 Calendar observationCalendar = parseCalendar(observationCalendar_);
87 BusinessDayConvention observationConvention =
88 observationConvention_.empty() ? Unadjusted : parseBusinessDayConvention(observationConvention_);
89
90 PaymentLag paymentLag = parsePaymentLag(paymentLag_);
91 Period plPeriod = boost::apply_visitor(PaymentLagPeriod(), paymentLag);
92 Calendar paymentCalendar = parseCalendar(paymentCalendar_);
93 BusinessDayConvention paymentConvention =
94 paymentConvention_.empty() ? Unadjusted : parseBusinessDayConvention(paymentConvention_);
95
96 std::vector<Date> valuationDates, paymentDates;
97
98 for (auto const& d : schedule.dates()) {
99 valuationDates.push_back(observationCalendar.advance(d, -observationLag, observationConvention));
100 if (d != schedule.dates().front())
101 paymentDates.push_back(paymentCalendar.advance(d, plPeriod, paymentConvention));
102 }
103
104 if (!paymentDates_.empty()) {
105 paymentDates.clear();
106 QL_REQUIRE(paymentDates_.size() + 1 == valuationDates.size(),
107 "paymentDates size (" << paymentDates_.size() << ") does no match valuatioDates size ("
108 << valuationDates.size() << ") minus 1");
109 for (auto const& s : paymentDates_)
110 paymentDates.push_back(parseDate(s));
111 }
112
113 DLOG("valuation schedule:");
114 for (auto const& d : valuationDates)
116
117 DLOG("payment schedule:");
118 for (auto const& d : paymentDates)
120
121 // build fx index for composite bond trs
122
123 QuantLib::ext::shared_ptr<FxIndex> fxIndex;
124 if (!fxIndex_.empty()) {
125 fxIndex =
126 buildFxIndex(fxIndex_, fundingLegData_.currency(), bondData_.currency(), engineFactory->market(),
127 engineFactory->configuration(MarketContext::pricing));
128
129 }
130
131 // build bond index (absolute prices, conditional on survival set to false)
132 BondIndexBuilder bondIndexBuilder(bondData_, useDirtyPrices_, false, NullCalendar(), false, engineFactory);
133 auto bondIndex = bondIndexBuilder.bondIndex();
134
135 // compute initial price taking into account the possible scaling with priceQuoteBaseValue and 100.0
136 Real effectiveInitialPrice = bondIndexBuilder.priceAdjustment(initialPrice_);
137 if (effectiveInitialPrice != Null<Real>())
138 effectiveInitialPrice = effectiveInitialPrice / 100.0;
139
140 // add indexing data from the bond trs leg, if this is desired
141
142 if (fundingLegData_.indexingFromAssetLeg()) {
143 DLOG("adding indexing information from trs leg to funding leg");
144
145 std::vector<string> stringValuationDates;
146 for (auto const& d : valuationDates)
147 stringValuationDates.push_back(ore::data::to_string(d));
148 ScheduleData valuationSchedule(ScheduleDates("", "", "", stringValuationDates, ""));
149
150 // add bond indexing
151 Indexing bondIndexing("BOND-" + bondIndex->securityName(), "", bondIndex->dirty(), bondIndex->relative(),
152 bondIndex->conditionalOnSurvival(), bondData_.bondNotional(), effectiveInitialPrice,
153 Null<Real>(), valuationSchedule, 0, "", "U", false);
154 fundingLegData_.indexing().push_back(bondIndexing);
155
156 // add fx indexing, if applicable
157 if (!fxIndex_.empty()) {
158 Indexing fxIndexing(fxIndex_, "", false, false, false, 1.0, Null<Real>(), Null<Real>(), valuationSchedule,
159 0, "", "U", false);
160 fundingLegData_.indexing().push_back(fxIndexing);
161 }
162
163 // set notional node to 1.0
164 fundingLegData_.notionals() = std::vector<Real>(1, 1.0);
165 fundingLegData_.notionalDates() = std::vector<std::string>();
166
167 // reset flag that told us to pull the indexing information from the equity leg
168 fundingLegData_.indexingFromAssetLeg() = false;
169 }
170
171 // build funding leg (consisting of a coupon leg and (possibly) a notional leg
172
173 Leg fundingLeg = legBuilder->buildLeg(fundingLegData_, engineFactory, requiredFixings_, configuration);
174 Leg fundingNotionalLeg;
175 if (fundingLegData_.notionalInitialExchange() || fundingLegData_.notionalFinalExchange() ||
176 fundingLegData_.notionalAmortizingExchange()) {
177 Natural fundingLegPayLag = 0;
178 fundingNotionalLeg =
179 makeNotionalLeg(fundingLeg, fundingLegData_.notionalInitialExchange(),
180 fundingLegData_.notionalFinalExchange(), fundingLegData_.notionalAmortizingExchange(),
181 fundingLegPayLag, parseBusinessDayConvention(fundingLegData_.paymentConvention()),
182 parseCalendar(fundingLegData_.paymentCalendar()));
183 }
184
185 QL_REQUIRE(fundingLegData_.isPayer() != payTotalReturnLeg_,
186 "funding leg and total return lag are both rec or both pay");
187 DLOG("Before bondTRS");
188 auto bondTRS = QuantLib::ext::make_shared<QuantExt::BondTRS>(
189 bondIndex, bondData_.bondNotional(), effectiveInitialPrice,
190 std::vector<QuantLib::Leg>{fundingLeg, fundingNotionalLeg}, payTotalReturnLeg_, valuationDates, paymentDates,
191 fxIndex, payBondCashFlowsImmediately_, parseCurrency(fundingLegData_.currency()),
192 parseCurrency(bondData_.currency()));
193 DLOG("After bondTRS");
194 QuantLib::ext::shared_ptr<BondTRSEngineBuilder> trsBondBuilder =
195 QuantLib::ext::dynamic_pointer_cast<BondTRSEngineBuilder>(builder_trs);
196 QL_REQUIRE(trsBondBuilder, "No Builder found for BondTRS: " << id());
197 bondTRS->setPricingEngine(trsBondBuilder->engine(fundingLegData_.currency()));
198 setSensitivityTemplate(*trsBondBuilder);
199 instrument_.reset(new VanillaInstrument(bondTRS));
200 // maturity_ = std::max(valuationDates.back(), paymentDates.back());
201 maturity_ = bondIndex->bond()->maturityDate();
202 notional_ = bondIndex->bond()->notional() * bondData_.bondNotional();
203
204 // cashflows will be generated as additional results in the pricing engine
205
206 legs_ = {};
207 legCurrencies_ = {};
208 legPayers_ = {};
209
210 // add required bond and fx fixings for return calculation
211 addToRequiredFixings(bondTRS->returnLeg(), QuantLib::ext::make_shared<FixingDateGetter>(requiredFixings_));
212 bondIndexBuilder.addRequiredFixings(requiredFixings_, bondTRS->returnLeg());
213
214 // add required fx fixings for bond cashflow conversion (see the engine for details)
215
216 if (!fxIndex_.empty()) {
217 for (auto const& c : bondIndex->bond()->cashflows()) {
218 requiredFixings_.addFixingDate(fxIndex->fixingCalendar().adjust(c->date(), Preceding), fxIndex_, c->date());
219 }
220 }
221
222 if (bondData_.isInflationLinked() && useDirtyPrices()) {
223 auto inflationIndices = extractAllInflationUnderlyingFromBond(bondIndex->bond());
224 for (const auto& cf : bondTRS->returnLeg()) {
225 auto tcf = QuantLib::ext::dynamic_pointer_cast<TRSCashFlow>(cf);
226 if (tcf != nullptr) {
227 for (const auto& [key, index] : inflationIndices) {
228 auto [name, interpolation, couponFrequency, observationLag] = key;
229 std::string oreName = IndexNameTranslator::instance().oreName(name);
230 requiredFixings_.addZeroInflationFixingDate(
231 tcf->fixingStartDate() - observationLag, oreName, false, index->frequency(),
232 index->availabilityLag(), interpolation, couponFrequency, Date::maxDate(), false, false);
233 }
234 }
235 }
236 }
237}
238
239void BondTRS::fromXML(XMLNode* node) {
240 Trade::fromXML(node);
241 XMLNode* bondTRSNode = XMLUtils::getChildNode(node, "BondTRSData");
242 QL_REQUIRE(bondTRSNode, "No BondTRSData Node");
243 originalBondData_.fromXML(XMLUtils::getChildNode(bondTRSNode, "BondData"));
244 bondData_ = originalBondData_;
245
246 XMLNode* bondTRSDataNode = XMLUtils::getChildNode(bondTRSNode, "TotalReturnData");
247 QL_REQUIRE(bondTRSDataNode, "No bondTRSDataNode Node");
248
249 payTotalReturnLeg_ = parseBool(XMLUtils::getChildValue(bondTRSDataNode, "Payer", true));
250
251 scheduleData_.fromXML(XMLUtils::getChildNode(bondTRSDataNode, "ScheduleData"));
252
253 observationLag_ = XMLUtils::getChildValue(bondTRSDataNode, "ObservationLag");
254 observationConvention_ = XMLUtils::getChildValue(bondTRSDataNode, "ObservationConvention");
255 observationCalendar_ = XMLUtils::getChildValue(bondTRSDataNode, "ObservationCalendar");
256
257 paymentLag_ = XMLUtils::getChildValue(bondTRSDataNode, "PaymentLag");
258 paymentConvention_ = XMLUtils::getChildValue(bondTRSDataNode, "PaymentConvention");
259 paymentCalendar_ = XMLUtils::getChildValue(bondTRSDataNode, "PaymentCalendar");
260 paymentDates_ = XMLUtils::getChildrenValues(bondTRSDataNode, "PaymentDates", "PaymentDate");
261
262 initialPrice_ = Null<Real>();
263 if (auto n = XMLUtils::getChildNode(bondTRSDataNode, "InitialPrice"))
264 initialPrice_ = parseReal(XMLUtils::getNodeValue(n));
265 std::string priceType = XMLUtils::getChildValue(bondTRSDataNode, "PriceType", true);
266 if (priceType == "Dirty")
267 useDirtyPrices_ = true;
268 else if (priceType == "Clean")
269 useDirtyPrices_ = false;
270 else {
271 QL_FAIL("PriceType (" << priceType << ") must be Clean or Dirty");
272 }
273
274 XMLNode* fxt = XMLUtils::getChildNode(bondTRSDataNode, "FXTerms");
275 if (fxt)
276 fxIndex_ = XMLUtils::getChildValue(fxt, "FXIndex", true);
277
279 XMLUtils::getChildValueAsBool(bondTRSDataNode, "PayBondCashFlowsImmediately", false, false);
280
281 XMLNode* bondTRSFundingNode = XMLUtils::getChildNode(bondTRSNode, "FundingData");
282 XMLNode* fLegNode = XMLUtils::getChildNode(bondTRSFundingNode, "LegData");
283 fundingLegData_ = LegData();
284 fundingLegData_.fromXML(fLegNode);
285}
286
287XMLNode* BondTRS::toXML(XMLDocument& doc) const {
288 XMLNode* node = Trade::toXML(doc);
289 XMLNode* bondTRSNode = doc.allocNode("BondTRSData");
290 XMLUtils::appendNode(node, bondTRSNode);
291 XMLUtils::appendNode(bondTRSNode, originalBondData_.toXML(doc));
292
293 XMLNode* trsDataNode = doc.allocNode("TotalReturnData");
294 XMLUtils::appendNode(bondTRSNode, trsDataNode);
295 XMLUtils::addChild(doc, trsDataNode, "Payer", payTotalReturnLeg_);
296
297 if (initialPrice_ != Null<Real>())
298 XMLUtils::addChild(doc, trsDataNode, "InitialPrice", initialPrice_);
299 XMLUtils::addChild(doc, trsDataNode, "PriceType", useDirtyPrices_ ? "Dirty" : "Clean");
300
301 if (!observationLag_.empty())
302 XMLUtils::addChild(doc, trsDataNode, "ObservationLag", observationLag_);
303 if (!observationConvention_.empty())
304 XMLUtils::addChild(doc, trsDataNode, "ObservationConvention", observationConvention_);
305 if (!observationCalendar_.empty())
306 XMLUtils::addChild(doc, trsDataNode, "ObservationCalendar", observationCalendar_);
307
308 if (!paymentLag_.empty())
309 XMLUtils::addChild(doc, trsDataNode, "PaymentLag", paymentLag_);
310 if (!paymentConvention_.empty())
311 XMLUtils::addChild(doc, trsDataNode, "PaymentConvention", paymentConvention_);
312 if (!paymentCalendar_.empty())
313 XMLUtils::addChild(doc, trsDataNode, "PaymentCalendar", paymentCalendar_);
314 if (!paymentDates_.empty())
315 XMLUtils::addChildren(doc, trsDataNode, "PaymentDates", "PaymentDate", paymentDates_);
316
317 if (!fxIndex_.empty()) {
318 XMLNode* fxNode = doc.allocNode("FXTerms");
319 XMLUtils::addChild(doc, fxNode, "FXIndex", fxIndex_);
320 XMLUtils::appendNode(trsDataNode, fxNode);
321 }
322
323 XMLUtils::appendNode(trsDataNode, scheduleData_.toXML(doc));
324
325 XMLUtils::addChild(doc, trsDataNode, "PayBondCashFlowsImmediately", payBondCashFlowsImmediately_);
326
327 XMLNode* fundingDataNode = doc.allocNode("FundingData");
328 XMLUtils::appendNode(bondTRSNode, fundingDataNode);
329 XMLUtils::appendNode(fundingDataNode, fundingLegData_.toXML(doc));
330 return node;
331}
332
333std::map<AssetClass, std::set<std::string>>
334BondTRS::underlyingIndices(const QuantLib::ext::shared_ptr<ReferenceDataManager>& referenceDataManager) const {
335 std::map<AssetClass, std::set<std::string>> result;
336 result[AssetClass::BOND] = {bondData_.securityId()};
337 return result;
338}
339
340} // namespace data
341} // namespace ore
Interface for building a bond index.
bond utilities
builder that returns an engine to price a bond instrument
bool payBondCashFlowsImmediately_
boost::shared_ptr< QuantExt::FxIndex > fxIndex_
const std::vector< Date > & valuationDates() const
const std::vector< Leg > & fundingLeg() const
const std::vector< Date > & paymentDates() const
std::vector< Date > paymentDates_
const boost::shared_ptr< QuantExt::FxIndex > & fxIndex() const
const boost::shared_ptr< QuantExt::BondIndex > & bondIndex() const
QuantLib::Real priceAdjustment(QuantLib::Real price)
QuantLib::ext::shared_ptr< QuantExt::BondIndex > bondIndex() const
void addRequiredFixings(RequiredFixings &requiredFixings, Leg leg={})
virtual void build(const QuantLib::ext::shared_ptr< EngineFactory > &) override
Serializable object holding indexing data.
Definition: indexing.hpp:39
Serializable object holding leg data.
Definition: legdata.hpp:844
Serializable schedule data.
Definition: schedule.hpp:202
Serializable object holding schedule Dates data.
Definition: schedule.hpp:110
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.
Calendar parseCalendar(const string &s)
Convert text to QuantLib::Calendar.
Definition: parsers.cpp:157
Date parseDate(const string &s)
Convert std::string to QuantLib::Date.
Definition: parsers.cpp:51
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
Period parsePeriod(const string &s)
Convert text to QuantLib::Period.
Definition: parsers.cpp:171
bool parseBool(const string &s)
Convert text to bool.
Definition: parsers.cpp:144
PaymentLag parsePaymentLag(const string &s)
Convert text to PaymentLag.
Definition: parsers.cpp:628
Real parseReal(const string &s)
Convert text to Real.
Definition: parsers.cpp:112
translates between QuantLib::Index::name() and ORE names
Map text representations to QuantLib/QuantExt types.
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
Calendar calendar
Definition: utilities.cpp:441
void addToRequiredFixings(const QuantLib::Leg &leg, const QuantLib::ext::shared_ptr< FixingDateGetter > &fixingDateGetter)
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
boost::variant< QuantLib::Period, QuantLib::Natural > PaymentLag
Definition: types.hpp:32
Leg makeNotionalLeg(const Leg &refLeg, const bool initNomFlow, const bool finalNomFlow, const bool amortNomFlow, const Natural notionalPaymentLag, const BusinessDayConvention paymentConvention, const Calendar paymentCalendar, const bool excludeIndexing)
Definition: legdata.cpp:1566
QuantLib::ext::shared_ptr< QuantExt::FxIndex > buildFxIndex(const string &fxIndex, const string &domestic, const string &foreign, const QuantLib::ext::shared_ptr< Market > &market, const string &configuration, bool useXbsCurves)
Definition: marketdata.cpp:137
Schedule makeSchedule(const ScheduleDates &data)
Definition: schedule.cpp:263
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Map text representations to QuantLib/QuantExt types.
string name