Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
bondbasket.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2020 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
19
21
25
27
28#include <ql/currencies/europe.hpp>
29#include <ql/errors.hpp>
30#include <ql/termstructures/credit/flathazardrate.hpp>
32#include <ql/math/functional.hpp>
33#include <ql/quotes/derivedquote.hpp>
34
35using namespace QuantLib;
36using std::string;
37using std::vector;
38
39namespace ore {
40namespace data {
41
42static DefaultProbKey dummyDefaultProbKey() {
43 Currency currency = QuantLib::EURCurrency();
44 Seniority seniority = NoSeniority;
45 QuantLib::ext::shared_ptr<DefaultType> defaultType(new DefaultType());
46 vector<QuantLib::ext::shared_ptr<DefaultType>> defaultTypes(1, defaultType);
47 DefaultProbKey key(defaultTypes, currency, seniority);
48 return key;
49}
50
51static Issuer dummyIssuer(Handle<DefaultProbabilityTermStructure> dts) {
52 Issuer::key_curve_pair curve(dummyDefaultProbKey(), dts);
53 return Issuer(vector<Issuer::key_curve_pair>(1, curve));
54}
55
56QuantLib::ext::shared_ptr<QuantExt::BondBasket> BondBasket::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory,
57 const QuantLib::Currency& baseCcy,
58 const string& reinvestmentEndDate) {
59 DLOG("BondBasket::build() called");
60
61 QuantLib::ext::shared_ptr<Pool> pool(new Pool());
62 vector<Issuer> issuerPair;
63 set<Currency> currencies_unique;
64
65 map<string, QuantLib::ext::shared_ptr<QuantLib::Bond>> qlBonds;
66 map<string, double> recoveries;
67 map<string, double> multipliers;
68 map<string, Handle<YieldTermStructure>> yieldTermStructures;
69 map<string, Currency> currencies;
70
71 const QuantLib::ext::shared_ptr<Market> market = engineFactory->market();
72
73 for (size_t i = 0; i < bonds_.size(); i++) {
74 DLOG("BondBasket::build() -- processing issuer number " << i);
75
76 bonds_[i]->build(engineFactory);
77 string creditId = bonds_[i]->bondData().creditCurveId();
78 string securityId = bonds_[i]->bondData().securityId();
79
80 Handle<DefaultProbabilityTermStructure> defaultOriginal =
81 securitySpecificCreditCurve(market, securityId, creditId)->curve();
82 Handle<DefaultProbabilityTermStructure> defaultTS;
83 Handle<Quote> recovery;
84 try {
85 recovery = market->recoveryRate(securityId, Market::defaultConfiguration);
86 } catch (...) {
87 }
88
89 try {
90
91 Real rr = recovery.empty() ? 0.0 : recovery->value();
92 auto m = [rr](Real x) { return x / (1.0 - rr); };
93 Handle<Quote> scaledSpread(QuantLib::ext::make_shared<DerivedQuote<decltype(m)>>(
94 market->securitySpread(securityId, Market::defaultConfiguration), m));
95
96 defaultTS = Handle<DefaultProbabilityTermStructure>(
97 QuantLib::ext::make_shared<QuantExt::HazardSpreadedDefaultTermStructure>(defaultOriginal, scaledSpread));
98
99 bonds_[i]->instrument()->qlInstrument()->registerWith(scaledSpread);
100 recoveries[bonds_[i]->id()] = rr;
101
102 } catch (...) {
103 defaultTS = defaultOriginal;
104 recoveries[bonds_[i]->id()] = 0.0;
105 }
106
107
108 issuerPair.push_back(dummyIssuer(defaultTS));
109 pool->add(bonds_[i]->id(), issuerPair[i]);
110 currencies_unique.insert(parseCurrency(bonds_[i]->bondData().currency()));
111 requiredFixings_.addData(bonds_[i]->requiredFixings());
112
113 QuantLib::ext::shared_ptr<QuantLib::Bond> qlBond = QuantLib::ext::dynamic_pointer_cast<QuantLib::Bond>(bonds_[i]->instrument()->qlInstrument());
114 QL_REQUIRE(qlBond, "ql bond expected " << bonds_[i]->id());
115 qlBonds[bonds_[i]->id()]= qlBond;
116
117 multipliers[bonds_[i]->id()] = bonds_[i]->instrument()->multiplier();
118 yieldTermStructures[bonds_[i]->id()] = market->discountCurve(bonds_[i]->bondData().currency());
119 currencies[bonds_[i]->id()] = parseCurrency(bonds_[i]->bondData().currency());
120
121 }
122
123 DLOG("pool names");
124 for (size_t i = 0; i < pool->names().size(); i++) {
125 DLOG("name: " << pool->names()[i] << ", included: " << pool->has(bonds_[i]->id()));
126 }
127
128 //create a fx index map for each requred currency...
129 for (auto it = currencies_unique.begin(); it!=currencies_unique.end(); ++it){
130 if(it->code() != baseCcy.code()){
131 string source = it->code();
132 string target = baseCcy.code();
133 Handle<YieldTermStructure> sorTS = market->discountCurve(source);
134 Handle<YieldTermStructure> tarTS = market->discountCurve(target);
135 Handle<Quote> spot = market->fxSpot(source + target);
136 Calendar cal = NullCalendar();
137 Natural fixingDays = 0;
138
139 string name = source + target + "Index";
140
141 QuantLib::ext::shared_ptr<QuantExt::FxIndex> fxi = QuantLib::ext::make_shared<QuantExt::FxIndex>(
142 name, fixingDays, parseCurrency(source), baseCcy, cal, spot, sorTS, tarTS);
143
144 fxIndexMap_[source] = fxi;
145
146 DLOG("BondBasket::build() -- created FX Index for " << source + target);
147 }
148 }
149
150 reinvestment_ = Date().minDate();
151 if(reinvestmentEndDate != "")
152 reinvestment_ = ore::data::parseDate(reinvestmentEndDate);
153
154 setReinvestmentScalar();
155
156 QuantLib::ext::shared_ptr<QuantExt::BondBasket> basket(
157 new QuantExt::BondBasket(qlBonds, recoveries, multipliers, yieldTermStructures, currencies,
158 pool, baseCcy, fxIndexMap_, reinvestment_, reinvestmentScalar_, flowType_));
159
160 DLOG("BondBasket::build() -- completed");
161
162 return basket;
163}
164
165bool BondBasket::isFeeFlow(const ext::shared_ptr<QuantLib::CashFlow>& cf, const std::string& name) {
166
167 //in order to identify as fees, the method expects, (upfront) fees in the form of CashflowData legdata within xml representation
168 //this could be dangerous, as 5% upfront and 5% amortisation at the same date could trigger wrong assessment
169
170 bool result = false;
171 for(auto& bond : bonds_){
172 if(bond->id() == name){
173 auto oreBondData = QuantLib::ext::make_shared<ore::data::BondData>(bond->bondData());
174 if(oreBondData){
175 for (auto legData : oreBondData->coupons()){
176 auto cfData = QuantLib::ext::dynamic_pointer_cast<ore::data::CashflowData>(legData.concreteLegData());
177 if(cfData){
178 auto amounts = cfData->amounts();
179 auto dates = cfData->dates();
180 for(size_t d = 0; d < dates.size(); d++){
181 if(cf->date() == ore::data::parseDate(dates[d])){
182 if(cf->amount() == amounts[d])
183 result = true;
184 }
185 }
186 }
187 }
188 }
189 }
190 }
191 return result;
192 }
193
194void BondBasket::setReinvestmentScalar(){
195
196 Date today = Settings::instance().evaluationDate();
197
198 for(auto& bond : bonds_){
199
200 string name = bond->id();
201 if(bond->maturity() <= reinvestment_)
202 ALOG("bond " << name << " maturity " << io::iso_date(bond->maturity()) << " <= " << "reinvestment end " << io::iso_date(reinvestment_));
203
204 //identify adjustment factor (scalars) -> rescale to todays notional
205 const vector<Leg>& legs = bond->legs();
206 if(legs.size() > 1)
207 ALOG("bond " << name << " has more than one leg, only the first is considered.")
208 const Leg& leg = legs[0];
209
210 size_t jmax = 0; //index for first coupon date after reinvestment period end date
211 for (size_t j = 0; j < leg.size(); j++) {
212 QuantLib::ext::shared_ptr<QuantLib::Coupon> ptrCoupon =
213 QuantLib::ext::dynamic_pointer_cast<QuantLib::Coupon>(leg[j]);
214 if(ptrCoupon){
215 if(ptrCoupon->date() > reinvestment_){
216 jmax = j;
217 break;
218 }
219 }
220 }
221
222 double baseNotional = bond->notional(); //notional as of today, as set in the ore::data::Bond::build
223 vector<double>scalars(leg.size(), 1.0);
224 vector<string>flowType(leg.size(), "");
225 double currentScalar = 1.0;
226
227 for (size_t j = 0; j < leg.size(); j++) {
228
229 QuantLib::ext::shared_ptr<QuantLib::Coupon> ptrCoupon =
230 QuantLib::ext::dynamic_pointer_cast<QuantLib::Coupon>(leg[j]);
231 flowType[j] = "xnl"; //notional
232 if(isFeeFlow(leg[j], name))
233 flowType[j] = "fee"; //fees
234
235 if(ptrCoupon){
236 flowType[j] = "int"; //interest
237 if(j <= jmax && ptrCoupon->date() >= today){
238 double periodNotional = ptrCoupon->nominal();
239
240 if(periodNotional < 1e-10)
241 ALOG(name << " amortises too early: periodNotional " << periodNotional
242 << " cpnDate " << io::iso_date(ptrCoupon->date())
243 << " (first period after) reinvestment end " << io::iso_date(reinvestment_));
244
245 if(periodNotional < baseNotional && periodNotional > 1e-10)
246 currentScalar = baseNotional/periodNotional;
247 }
248 }
249 scalars[j] = currentScalar;
250 }
251 reinvestmentScalar_[name] = scalars;
253 }
254}
255
256void BondBasket::clear() {
257
258 bonds_.clear();
259}
260
261void BondBasket::fromXML(XMLNode* node) {
262
263 clear();
264
265 XMLUtils::checkNode(node, "BondBasketData");
266 bonds_.clear();
267 for (XMLNode* child = XMLUtils::getChildNode(node, "Trade"); child; child = XMLUtils::getNextSibling(child)) {
268 string id = XMLUtils::getAttribute(child, "id");
269 auto bonddata = QuantLib::ext::make_shared<ore::data::Bond>();
270 bonddata->fromXML(child);
271 bonddata->id() = id;
272 bonds_.push_back(bonddata);
273 }
274}
275
276XMLNode* BondBasket::toXML(ore::data::XMLDocument& doc) const {
277 XMLNode* node = doc.allocNode("BondBasketData");
278 for (Size i = 0; i < bonds_.size(); i++) {
279 XMLUtils::appendNode(node, bonds_[i]->toXML(doc));
280 }
281 return node;
282}
283
284std::map<AssetClass, std::set<std::string>> BondBasket::underlyingIndices(const QuantLib::ext::shared_ptr<ReferenceDataManager>& referenceDataManager) const {
285 std::map<AssetClass, std::set<std::string>> result;
286 result[AssetClass::BOND] = {};
287 for ( auto b : bonds_)
288 result[AssetClass::BOND].insert(b->bondData().securityId());
289 return result;
290}
291
292} // namespace data
293} // namespace orep
credit bond basket data model and serialization
const std::map< std::string, boost::shared_ptr< QuantExt::FxIndex > > fxIndexMap_
const std::map< std::string, std::vector< double > > reinvestmentScalar_
const Currency currency(const std::string &name) const
const boost::shared_ptr< QuantLib::Pool > & pool() const
const std::map< std::string, std::vector< std::string > > flowType_
const std::string flowType(const std::string &name, int idx) const
QuantLib::ext::shared_ptr< QuantExt::BondBasket > build(const QuantLib::ext::shared_ptr< EngineFactory > &engineFactory, const QuantLib::Currency &ccy, const std::string &reinvestmentEndDate)
Definition: bondbasket.cpp:56
Small XML Document wrapper class.
Definition: xmlutils.hpp:65
XMLNode * allocNode(const string &nodeName)
util functions that wrap rapidxml
Definition: xmlutils.cpp:132
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
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
market data related utilties
Size size(const ValueType &v)
Definition: value.cpp:145
QuantLib::Handle< QuantExt::CreditCurve > securitySpecificCreditCurve(const QuantLib::ext::shared_ptr< Market > &market, const std::string &securityId, const std::string &creditCurveId, const std::string &configuration)
Definition: marketdata.cpp:98
Serializable Credit Default Swap.
Definition: namespaces.docs:23
string conversion utilities
string name