Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
Public Member Functions | Static Public Member Functions | Private Member Functions | Private Attributes | List of all members
SyntheticCDO Class Reference

Serializable CDS Index Tranche (Synthetic CDO) More...

#include <ored/portfolio/cdo.hpp>

+ Inheritance diagram for SyntheticCDO:
+ Collaboration diagram for SyntheticCDO:

Public Member Functions

 SyntheticCDO ()
 
 SyntheticCDO (const Envelope &env, const LegData &leg, const string &qualifier, const BasketData &basketData, double attachmentPoint, double detachmentPoint, const bool settlesAccrual=true, const QuantExt::CreditDefaultSwap::ProtectionPaymentTime protectionPaymentTime=QuantExt::CreditDefaultSwap::ProtectionPaymentTime::atDefault, const string &protectionStart=string(), const string &upfrontDate=string(), const Real upfrontFee=Null< Real >(), const bool rebatesAccrual=true, Real recoveryRate=Null< Real >())
 
virtual void build (const QuantLib::ext::shared_ptr< EngineFactory > &) override
 
const string & qualifier () const
 Inspectors. More...
 
const LegDataleg () const
 
const BasketDatabasketData () const
 
const double & attachmentPoint () const
 
const double & detachmentPoint () const
 
QuantExt::CreditDefaultSwap::ProtectionPaymentTime protectionPaymentTime () const
 
const string & protectionStart () const
 
const string & upfrontDate () const
 
const double & upfrontFee () const
 
bool settlesAccrual () const
 
bool rebatesAccrual () const
 
const Real & recoveryRate () const
 
bool useSensitivitySimplification () const
 
const std::map< std::string, Real > & basketConstituents () const
 
virtual void fromXML (XMLNode *node) override
 
virtual XMLNodetoXML (XMLDocument &doc) const override
 
std::string creditCurveIdWithTerm () const
 
void setIndexStartDateHint (const QuantLib::Date &d) const
 
const QuantLib::Date & indexStartDateHint () const
 
- Public Member Functions inherited from Trade
 Trade ()
 Default constructor. More...
 
 Trade (const string &tradeType, const Envelope &env=Envelope(), const TradeActions &ta=TradeActions())
 Base class constructor. More...
 
virtual ~Trade ()
 Default destructor. More...
 
virtual void build (const QuantLib::ext::shared_ptr< EngineFactory > &)=0
 
virtual std::map< std::string, RequiredFixings::FixingDatesfixings (const QuantLib::Date &settlementDate=QuantLib::Date()) const
 
const RequiredFixingsrequiredFixings () const
 
virtual std::map< AssetClass, std::set< std::string > > underlyingIndices (const QuantLib::ext::shared_ptr< ReferenceDataManager > &referenceDataManager=nullptr) const
 
void reset ()
 Reset trade, clear all base class data. This does not reset accumulated timings for this trade. More...
 
void resetPricingStats (const std::size_t numberOfPricings=0, const boost::timer::nanosecond_type cumulativePricingTime=0)
 Reset accumulated timings to given values. More...
 
string & id ()
 Set the trade id. More...
 
void setEnvelope (const Envelope &envelope)
 Set the envelope with counterparty and portfolio info. More...
 
void setAdditionalData (const std::map< std::string, boost::any > &additionalData)
 
TradeActionstradeActions ()
 Set the trade actions. More...
 
const string & id () const
 
const string & tradeType () const
 
const Envelopeenvelope () const
 
const set< string > & portfolioIds () const
 
const TradeActionstradeActions () const
 
const QuantLib::ext::shared_ptr< InstrumentWrapper > & instrument () const
 
const std::vector< QuantLib::Leg > & legs () const
 
const std::vector< string > & legCurrencies () const
 
const std::vector< bool > & legPayers () const
 
const string & npvCurrency () const
 
virtual QuantLib::Real notional () const
 Return the current notional in npvCurrency. See individual sub-classes for the precise definition. More...
 
virtual string notionalCurrency () const
 
const Date & maturity () const
 
virtual bool isExpired (const Date &d)
 
const string & issuer () const
 
template<typename T >
additionalDatum (const std::string &tag) const
 returns any additional datum. More...
 
virtual const std::map< std::string, boost::any > & additionalData () const
 returns all additional data returned by the trade once built More...
 
const std::string & sensitivityTemplate () const
 
void validate () const
 Utility to validate that everything that needs to be set in this base class is actually set. More...
 
virtual bool hasCashflows () const
 
boost::timer::nanosecond_type getCumulativePricingTime () const
 Get cumulative timing spent on pricing. More...
 
std::size_t getNumberOfPricings () const
 Get number of pricings. More...
 
- Public Member Functions inherited from XMLSerializable
virtual ~XMLSerializable ()
 
virtual void fromXML (XMLNode *node)=0
 
virtual XMLNodetoXML (XMLDocument &doc) const =0
 
void fromFile (const std::string &filename)
 
void toFile (const std::string &filename) const
 
void fromXMLString (const std::string &xml)
 Parse from XML string. More...
 
std::string toXMLString () const
 Parse from XML string. More...
 

Static Public Member Functions

static vector< Time > extractTimeGridDefaultCurve (const QuantLib::Handle< QuantLib::DefaultProbabilityTermStructure > &dpts)
 
static QuantLib::Handle< QuantLib::DefaultProbabilityTermStructure > buildCalibratedConstiuentCurve (const QuantLib::Handle< QuantLib::DefaultProbabilityTermStructure > &curve, const QuantLib::ext::shared_ptr< SimpleQuote > &calibrationFactor)
 

Private Member Functions

bool isIndexTranche () const
 

Private Attributes

string qualifier_
 
LegData legData_
 
BasketData basketData_
 
double attachmentPoint_
 
double detachmentPoint_
 
bool settlesAccrual_
 
QuantExt::CreditDefaultSwap::ProtectionPaymentTime protectionPaymentTime_
 
string protectionStart_
 
string upfrontDate_
 
double upfrontFee_
 
bool rebatesAccrual_
 
Real recoveryRate_
 
std::map< std::string, double > basketConstituents_
 
bool useSensitivitySimplification_
 
QuantLib::Date indexStartDateHint_
 

Additional Inherited Members

- Protected Member Functions inherited from Trade
Date addPremiums (std::vector< QuantLib::ext::shared_ptr< Instrument > > &instruments, std::vector< Real > &multipliers, const Real tradeMultiplier, const PremiumData &premiumData, const Real premiumMultiplier, const Currency &tradeCurrency, const QuantLib::ext::shared_ptr< EngineFactory > &factory, const string &configuration)
 
void setLegBasedAdditionalData (const Size legNo, Size resultLegId=Null< Size >()) const
 
void setSensitivityTemplate (const EngineBuilder &builder)
 
void setSensitivityTemplate (const std::string &id)
 
- Protected Attributes inherited from Trade
string tradeType_
 
QuantLib::ext::shared_ptr< InstrumentWrapperinstrument_
 
std::vector< QuantLib::Leg > legs_
 
std::vector< string > legCurrencies_
 
std::vector< boollegPayers_
 
string npvCurrency_
 
QuantLib::Real notional_
 
string notionalCurrency_
 
Date maturity_
 
string issuer_
 
string sensitivityTemplate_
 
bool sensitivityTemplateSet_ = false
 
std::size_t savedNumberOfPricings_ = 0
 
boost::timer::nanosecond_type savedCumulativePricingTime_ = 0
 
RequiredFixings requiredFixings_
 
std::map< std::string, boost::any > additionalData_
 

Detailed Description

Serializable CDS Index Tranche (Synthetic CDO)

Definition at line 40 of file cdo.hpp.

Constructor & Destructor Documentation

◆ SyntheticCDO() [1/2]

Definition at line 42 of file cdo.hpp.

42 : Trade("SyntheticCDO"),
43 attachmentPoint_(Null<Real>()), detachmentPoint_(Null<Real>()), settlesAccrual_(true),
44 protectionPaymentTime_(QuantExt::CreditDefaultSwap::ProtectionPaymentTime::atDefault),
45 upfrontFee_(Null<Real>()), rebatesAccrual_(true), recoveryRate_(Null<Real>()),
QuantLib::CreditDefaultSwap::ProtectionPaymentTime protectionPaymentTime_
bool useSensitivitySimplification_
Definition: cdo.hpp:121
double detachmentPoint_
Definition: cdo.hpp:109
double attachmentPoint_
Definition: cdo.hpp:108
Trade()
Default constructor.
Definition: trade.hpp:59

◆ SyntheticCDO() [2/2]

SyntheticCDO ( const Envelope env,
const LegData leg,
const string &  qualifier,
const BasketData basketData,
double  attachmentPoint,
double  detachmentPoint,
const bool  settlesAccrual = true,
const QuantExt::CreditDefaultSwap::ProtectionPaymentTime  protectionPaymentTime = QuantExt::CreditDefaultSwap::ProtectionPaymentTime::atDefault,
const string &  protectionStart = string(),
const string &  upfrontDate = string(),
const Real  upfrontFee = Null<Real>(),
const bool  rebatesAccrual = true,
Real  recoveryRate = Null<Real>() 
)

Definition at line 47 of file cdo.hpp.

54 : Trade("SyntheticCDO", env), qualifier_(qualifier), legData_(leg), basketData_(basketData),
55 attachmentPoint_(attachmentPoint), detachmentPoint_(detachmentPoint), settlesAccrual_(settlesAccrual),
56 protectionPaymentTime_(protectionPaymentTime), protectionStart_(protectionStart), upfrontDate_(upfrontDate),
57 upfrontFee_(upfrontFee), rebatesAccrual_(rebatesAccrual), recoveryRate_(recoveryRate),
Real recoveryRate() const
BasketData basketData_
Definition: cdo.hpp:107
+ Here is the call graph for this function:

Member Function Documentation

◆ build()

void build ( const QuantLib::ext::shared_ptr< EngineFactory > &  )
overridevirtual

Build QuantLib/QuantExt instrument, link pricing engine. If build() is called multiple times, reset() should be called between these calls.

Implements Trade.

Definition at line 75 of file cdo.cpp.

75 {
76
77 DLOG("SyntheticCDO::build() called for trade " << id());
78
79 // ISDA taxonomy
80 additionalData_["isdaAssetClass"] = string("Credit");
81 additionalData_["isdaBaseProduct"] = string("Index Tranche");
82 QuantLib::ext::shared_ptr<ReferenceDataManager> refData = engineFactory->referenceData();
83 if (refData && refData->hasData("CreditIndex", qualifier_)) {
84 auto refDatum = refData->getData("CreditIndex", qualifier_);
85 QuantLib::ext::shared_ptr<CreditIndexReferenceDatum> creditIndexRefDatum =
86 QuantLib::ext::dynamic_pointer_cast<CreditIndexReferenceDatum>(refDatum);
87 additionalData_["isdaSubProduct"] = creditIndexRefDatum->indexFamily();
88 if (creditIndexRefDatum->indexFamily() == "") {
89 ALOG("IndexFamily is blank in credit index reference data for entity " << qualifier_);
90 }
91 } else {
92 ALOG("Credit index reference data missing for entity " << qualifier_ << ", isdaSubProduct left blank");
93 }
94 // skip the transaction level mapping for now
95 additionalData_["isdaTransaction"] = string("");
96
97 Date protectionStartDate = protectionStart_ == "" ? Date() : parseDate(protectionStart_);
98 Date upfrontDate = upfrontDate_ == "" ? Date() : parseDate(upfrontDate_);
99 Leg leg = makeFixedLeg(legData_);
100 Protection::Side side = legData_.isPayer() ? Protection::Buyer : Protection::Seller;
101 Schedule schedule = makeSchedule(legData_.schedule());
102 Real fixedRecovery = recoveryRate_;
103 auto fixedLegData = QuantLib::ext::dynamic_pointer_cast<FixedLegData>(legData_.concreteLegData());
104 QL_REQUIRE(fixedLegData, "Expected FixedLegData but got " << legData_.legType());
105 Real runningRate = fixedLegData->rates().front();
106 DayCounter dayCounter = parseDayCounter(legData_.dayCounter());
107 BusinessDayConvention bdc = parseBusinessDayConvention(legData_.paymentConvention());
108 Currency ccy = parseCurrency(legData_.currency());
109
110 // In general for CDS index trades, the standard day counter is Actual/360 and the final
111 // period coupon accrual includes the maturity date.
112 Actual360 standardDayCounter;
113 DayCounter lastPeriodDayCounter = dayCounter == standardDayCounter ? Actual360(true) : dayCounter;
114
115
116 // Set some trade variables
118 maturity_ = leg.back()->date();
120 legs_ = {leg};
123
124 // Checks for upfront date and upfront fee
125 QL_REQUIRE(upfrontDate == Date() || upfrontFee_ != Null<Real>(),
126 "If upfront date is given (" << upfrontDate << "), upfront fee must be given.");
127 QL_REQUIRE(upfrontDate != Date() || upfrontFee_ == Null<Real>() || close_enough(upfrontFee_, 0.0),
128 "If no upfront date is given, no upfront fee should be given but got " << upfrontFee_ << ".");
129
130 // Get the original total notional using the contractual attachment point and detachment point and the contractual
131 // tranche notional. We will calculate the corresponding current amounts below from the basket or reference data.
132 QL_REQUIRE(attachmentPoint_ < detachmentPoint_, "Detachment point should be greater than attachment point.");
133 Real origTrancheNtl = legData_.notionals().front();
134 Real origTotalNtl = origTrancheNtl / (detachmentPoint_ - attachmentPoint_);
135 Real origEquityNtl = origTotalNtl * attachmentPoint_;
136 Real origSeniorNtl = origTotalNtl * (1.0 - detachmentPoint_);
137
138 DLOG("Original tranche notional: " << origTrancheNtl);
139 DLOG("Original equity notional: " << origEquityNtl);
140 DLOG("Original senior notional: " << origSeniorNtl);
141 DLOG("Original attachment point: " << attachmentPoint_);
142 DLOG("Original detachment point: " << detachmentPoint_);
143 DLOG("Original total notional: " << origTotalNtl);
144
145 // There may have been credit events up to the valuation date. Record the cumulative lost and recovered notional.
146 Real lostNotional = 0.0;
147 Real recoveredNotional = 0.0;
148 // clear the basketNotionals and credit names
149
150 vector<Real> basketNotionals;
151
152 vector<string> creditCurves;
153
154
155
156 if (basketData_.constituents().size() > 0) {
157
158 const auto& constituents = basketData_.constituents();
159 DLOG("Building constituents from basket data containing " << constituents.size() << " elements.");
160
161 Real totalRemainingNtl = 0.0;
162 Real totalPriorNtl = 0.0;
163
164 for (const auto& c : constituents) {
165 Real ntl = Null<Real>(), priorNotional = Null<Real>();
166 const auto& creditCurve = c.creditCurveId();
167 if (c.weightInsteadOfNotional()) {
168 ntl = c.weight() * origTotalNtl;
169 priorNotional = c.priorWeight();
170 if (priorNotional != Null<Real>()) {
171 priorNotional *= origTotalNtl;
172 }
173 } else {
174 ntl = c.notional();
175 priorNotional = c.priorNotional();
176 QL_REQUIRE(c.currency() == npvCurrency_, "The currency of basket constituent "
177 << creditCurve << " is " << c.currency()
178 << " and does not equal the trade leg currency "
179 << npvCurrency_);
180 }
181
182 if (!close(0.0, ntl) && ntl > 0.0) {
183 if (std::find(creditCurves.begin(), creditCurves.end(), creditCurve) == creditCurves.end()) {
184 DLOG("Adding underlying " << creditCurve << " with notional " << ntl);
185 creditCurves.push_back(creditCurve);
186 basketNotionals.push_back(ntl);
187 totalRemainingNtl += ntl;
188 } else {
189 StructuredTradeErrorMessage(id(), "Synthetic CDO", "Error building trade",
190 ("Invalid Basket: found a duplicate credit curve " + creditCurve +
191 ", skip it. Check the basket data for possible errors.")
192 .c_str())
193 .log();
194 }
195 } else {
196 DLOG("Underlying " << creditCurve << " notional is " << ntl << " so assuming a credit event occured.");
197 QL_REQUIRE(priorNotional != Null<Real>(),
198 "Expecting a valid prior notional for name " << creditCurve << ".");
199 auto recovery = c.recovery();
200 QL_REQUIRE(recovery != Null<Real>(), "Expecting a valid recovery for name " << creditCurve << ".");
201 validateWeightRec(recovery, creditCurve, "recovery");
202 lostNotional += (1.0 - recovery) * priorNotional;
203 recoveredNotional += recovery * priorNotional;
204 totalPriorNtl += priorNotional;
205 }
206 }
207
208 Real totalNtl = totalRemainingNtl + totalPriorNtl;
209 DLOG("All Underlyings added, total remaining notional = " << totalRemainingNtl);
210 DLOG("All Underlyings added, total prior notional = " << totalPriorNtl);
211 DLOG("All Underlyings added, total notional = " << totalNtl);
212
213 QL_REQUIRE(creditCurves.size() == basketNotionals.size(), "numbers of defaults curves ("
214 << creditCurves.size() << ") and notionals ("
215 << basketNotionals.size() << ") doesnt match");
216 Real notionalCorrectionFactor = origTotalNtl / totalNtl;
217 // Scaling to Notional if relative error is close less than 10^-4
218 if (!close(totalNtl, origTotalNtl) && (abs(notionalCorrectionFactor - 1.0) <= 1e-4)) {
219 ALOG("Trade " << id() << ", sum of notionals(" << totalNtl << ") is very close to total original notional ("
220 << origTotalNtl << "), will scale each notional by " << notionalCorrectionFactor
221 << ", check the basket data for possible errors.");
222 totalRemainingNtl = 0;
223 for (Size i = 0; i < basketNotionals.size(); i++) {
224 Real scaledNotional = basketNotionals[i] * notionalCorrectionFactor;
225 TLOG("Trade " << id() << ", Issuer" << creditCurves[i] << " unscaled Notional: " << basketNotionals[i]
226 << ", scaled Notional: " << scaledNotional);
227 basketNotionals[i] = scaledNotional;
228 totalRemainingNtl += scaledNotional;
229 }
230 lostNotional *= notionalCorrectionFactor;
231 recoveredNotional *= notionalCorrectionFactor;
232 totalNtl *= notionalCorrectionFactor;
233 }
234
235 if (!close(totalRemainingNtl, origTotalNtl) && totalRemainingNtl > origTotalNtl) {
236 StructuredTradeErrorMessage(id(), "Synthetic CDO", "Error building trade",
237 ("Total remaining notional (" + std::to_string(totalRemainingNtl) +
238 ") is greater than total original notional (" +
239 std::to_string(origTotalNtl) +
240 "), check the basket data for possible errors.")
241 .c_str())
242 .log();
243 }
244
245 if (!close(totalNtl, origTotalNtl)) {
246 StructuredTradeErrorMessage(id(), "Synthetic CDO", "Error building trade",
247 ("Expected the total notional (" + std::to_string(totalNtl) + " = " +
248 std::to_string(totalRemainingNtl) + " + " + std::to_string(totalPriorNtl) +
249 ") to equal the total original notional (" + std::to_string(origTotalNtl) +
250 "), check the basket data for possible errors.")
251 .c_str())
252 .log();
253 }
254
255 DLOG("Finished building constituents using basket data.");
256
257 } else {
258
259 DLOG("Building constituents using CreditIndexReferenceDatum for ID " << qualifier_);
260
261 QL_REQUIRE(engineFactory->referenceData(),
262 "Trade " << id() << " has no basket data and there is no reference data manager.");
263 QL_REQUIRE(engineFactory->referenceData()->hasData(CreditIndexReferenceDatum::TYPE, qualifier_),
264 "Trade " << id() << " needs credit index reference data for ID " << qualifier_);
265 auto crd = QuantLib::ext::dynamic_pointer_cast<CreditIndexReferenceDatum>(
266 engineFactory->referenceData()->getData(CreditIndexReferenceDatum::TYPE, qualifier_));
267
268 Real totalRemainingWeight = 0.0;
269 Real totalPriorWeight = 0.0;
270 for (const auto& c : crd->constituents()) {
271
272 const auto& name = c.name();
273 auto weight = c.weight();
274 validateWeightRec(weight, name, "weight");
275
276 if (!close(0.0, weight)) {
277 DLOG("Adding underlying " << name << " with weight " << weight);
278 creditCurves.push_back(name);
279 basketNotionals.push_back(weight * origTotalNtl);
280 totalRemainingWeight += weight;
281 } else {
282 DLOG("Underlying " << name << " has weight " << weight << " so assuming a credit event occured.");
283 auto priorWeight = c.priorWeight();
284 QL_REQUIRE(priorWeight != Null<Real>(), "Expecting a valid prior weight for name " << name << ".");
285 validateWeightRec(priorWeight, name, "prior weight");
286 auto recovery = c.recovery();
287 QL_REQUIRE(recovery != Null<Real>(), "Expecting a valid recovery for name " << name << ".");
288 validateWeightRec(recovery, name, "recovery");
289 lostNotional += (1.0 - recovery) * priorWeight * origTotalNtl;
290 recoveredNotional += recovery * priorWeight * origTotalNtl;
291 totalPriorWeight += priorWeight;
292 }
293 }
294
295 Real totalWeight = totalRemainingWeight + totalPriorWeight;
296 DLOG("All Underlyings added, total remaining weight = " << totalRemainingWeight);
297 DLOG("All Underlyings added, total prior weight = " << totalPriorWeight);
298 DLOG("All Underlyings added, total weight = " << totalWeight);
299
300 if (!close(totalRemainingWeight, 1.0) && totalRemainingWeight > 1.0) {
301 ALOG("Total remaining weight is greater than 1, possible error in CreditIndexReferenceDatum");
302 }
303
304 if (!close(totalWeight, 1.0)) {
305 ALOG("Expected the total weight (" << totalWeight << " = " << totalRemainingWeight << " + "
306 << totalPriorWeight
307 << ") to equal 1, possible error in CreditIndexReferenceDatum");
308 }
309
310 DLOG("Finished building constituents using CreditIndexReferenceDatum for ID " << qualifier_);
311 }
312
313 // Lost notional eats into junior tranches and recovered amount reduces senior tranches.
314 // Throw exception if tranche has been completely written down. Should be removed from portfolio or will be
315 // ignored if continue on error is true. Maybe better to have instrument that gives an NPV of 0.
316 Real currTotalNtl = accumulate(basketNotionals.begin(), basketNotionals.end(), 0.0);
317 QL_REQUIRE(!close(currTotalNtl, 0.0), "Trade " << id() << " has a current total notional of 0.0.");
318 Real currEquityNtl = max(origEquityNtl - lostNotional, 0.0);
319 Real currSeniorNtl = max(origSeniorNtl - recoveredNotional, 0.0);
320 Real currTrancheNtl = origTrancheNtl - max(min(recoveredNotional - origSeniorNtl, origTrancheNtl), 0.0) -
321 max(min(lostNotional - origEquityNtl, origTrancheNtl), 0.0);
322 QL_REQUIRE(!close(currTrancheNtl, 0.0), "Trade " << id() << " has a current tranche notional of 0.0.");
323 Real adjAttachPoint = currEquityNtl / currTotalNtl;
324 Real adjDetachPoint = (currEquityNtl + currTrancheNtl) / currTotalNtl;
325 notional_ = currTrancheNtl;
326
327 DLOG("Current tranche notional: " << currTrancheNtl);
328 DLOG("Current equity notional: " << currEquityNtl);
329 DLOG("Current senior notional: " << currSeniorNtl);
330 DLOG("Current attachment point: " << adjAttachPoint);
331 DLOG("Current detachment point: " << adjDetachPoint);
332 DLOG("Current total notional: " << currTotalNtl);
333
334
335 const auto& market = engineFactory->market();
336 auto cdoEngineBuilder = QuantLib::ext::dynamic_pointer_cast<CdoEngineBuilder>(engineFactory->builder("SyntheticCDO"));
337 QL_REQUIRE(cdoEngineBuilder, "Trade " << id() << " needs a valid CdoEngineBuilder.");
338 const string& config = cdoEngineBuilder->configuration(MarketContext::pricing);
339
340
341 std::vector<Handle<DefaultProbabilityTermStructure>> dpts;
342 vector<Real> recoveryRates;
343
344 if (fixedRecovery != Null<Real>()) {
345 LOG("Set all recovery rates to " << fixedRecovery);
346 }
347 for (Size i = 0; i < creditCurves.size(); ++i) {
348 const string& cc = creditCurves[i];
349 Real mktRecoveryRate = market->recoveryRate(cc, config)->value();
350 recoveryRates.push_back(fixedRecovery != Null<Real>() ? fixedRecovery : mktRecoveryRate);
351 auto originalCurve = market->defaultCurve(cc, config)->curve();
352 dpts.push_back(originalCurve);
353 }
354
355 // TODO check if underlying is index cds
356
357 // Calibrate the underlying constituent curves so that the index cds pricing with underlying curves matches the
358 // prices of the index cds with flat index curve.
359
360 QuantLib::ext::shared_ptr<SimpleQuote> calibrationFactor = QuantLib::ext::make_shared<SimpleQuote>(1.0);
361
362 bool calibrateConstiuentCurves = cdoEngineBuilder->calibrateConstituentCurve() && isIndexTranche();
363
364 if (calibrateConstiuentCurves) {
365 // Adjustment factor is a simplified version of the O'Kane's Forward Default Probability Multiplier
366 // O'Kane 2008 - Modelling Single-name and Multi-name Credit Derivatives
367 // Chapter 10.6
368 LOG("Use calibrated constiuent curves");
369 QuantLib::ext::shared_ptr<IndexCreditDefaultSwap> indexCDS;
370 Real cdsFairSpreads = 0.0;
371 Real cdsNpvs = 0.0;
372 try {
373 Handle<YieldTermStructure> yts =
374 market->discountCurve(ccy.code(), cdoEngineBuilder->configuration(MarketContext::pricing));
375
376 Date cdsStartDate = indexStartDateHint() == Date() ? schedule.dates().front() : indexStartDateHint();
377
378 Schedule cdsSchedule(cdsStartDate, schedule.dates().back(),
379 schedule.tenor(), schedule.calendar(), Following,
380 schedule.terminationDateBusinessDayConvention(), schedule.rule(), false);
381 indexCDS = QuantLib::ext::make_shared<QuantExt::IndexCreditDefaultSwap>(
382 side, currTotalNtl, basketNotionals, 0.0, runningRate, cdsSchedule, bdc, dayCounter, settlesAccrual_,
383 protectionPaymentTime_, Date(), Date(), QuantLib::ext::shared_ptr<Claim>(),
384 lastPeriodDayCounter, rebatesAccrual_);
385 Handle<DefaultProbabilityTermStructure> indexCreditCurve =
386 indexCdsDefaultCurve(market, creditCurveIdWithTerm(), config)->curve();
387 Handle<Quote> indexCdsRecovery = market->recoveryRate(creditCurveIdWithTerm(), config);
388 auto indexPricingEngine =
389 QuantLib::ext::make_shared<QuantExt::MidPointIndexCdsEngine>(indexCreditCurve, indexCdsRecovery->value(), yts);
390 indexCDS->setPricingEngine(indexPricingEngine);
391 } catch (const std::exception& e) {
392 indexCDS = nullptr;
393 WLOG("CDO constiuent calibration failed to build index cds. Got "
394 << e.what());
395 }
396 if (indexCDS) {
397 cdsFairSpreads = indexCDS->fairSpreadClean();
398 cdsNpvs = indexCDS->NPV();
399 }
400
401 auto it = engineFactory->engineData()->globalParameters().find("RunType");
402 if (it != engineFactory->engineData()->globalParameters().end() && it->second != "PortfolioAnalyser") {
403
404 Handle<YieldTermStructure> yts =
405 market->discountCurve(ccy.code(), cdoEngineBuilder->configuration(MarketContext::pricing));
406
407 std::vector<Handle<DefaultProbabilityTermStructure>> wrapperCurves;
408 DLOG("Building wrapper curves for calibration");
409 for (size_t i = 0; i < creditCurves.size(); ++i) {
410 try {
411 const string& cc = creditCurves[i];
412 auto originalCurve = market->defaultCurve(cc, config)->curve();
413 wrapperCurves.push_back(
414 buildCalibratedConstiuentCurve(originalCurve, calibrationFactor));
415 } catch (const std::exception& e) {
416
417 WLOG("CDO constiuent calibration failed during building wrapper curve for "
418 << creditCurves[i] << ", skip this curve. Got "
419 << e.what());
420 }
421 }
422
423 if (wrapperCurves.size() == dpts.size() && indexCDS) {
424 LOG("Start bootstraping of the calibration factors");
425 dpts.swap(wrapperCurves);
426
427 auto cdsPricingEngineUnderlyingCurves =
428 QuantLib::ext::make_shared<QuantExt::MidPointIndexCdsEngine>(dpts, recoveryRates, yts);
429
430 indexCDS->setPricingEngine(cdsPricingEngineUnderlyingCurves);
431
432 try {
433 auto targetFunction = [&cdsNpvs, &calibrationFactor, &indexCDS](const double& factor) {
434 calibrationFactor->setValue(factor);
435 return cdsNpvs - indexCDS->NPV();
436 };
437
438 Brent solver;
439 double indexAdjustmentForUnderlyingCurves =
440 solver.solve(targetFunction, 1e-8, cdsFairSpreads / indexCDS->fairSpreadClean(), 0.001, 2);
441
442 DLOG("Calibration of indexterm " << io::iso_date(indexCDS->maturity())
443 << "successful, found solution "
444 << indexAdjustmentForUnderlyingCurves);
445 calibrationFactor->setValue(indexAdjustmentForUnderlyingCurves);
446 } catch (const std::exception& e) {
447 WLOG("Calibration failed, at pillar " << io::iso_date(indexCDS->maturity())
448 << ", set calibration factor to 1 (uncalibrated), got "
449 << e.what());
450 calibrationFactor->setValue(1.0);
451 }
452
453 LOG("Calibration results for creditCurve:" << creditCurveIdWithTerm());
454 LOG("Expiry \t CalibrationFactor \t NpvIntrinsic \t NpvIndexCurve \t NpvError \t "
455 "FairSpreadIntrinsic "
456 "\t FairSpreadIndexCurve \t FairSreadError");
457
458 LOG(io::iso_date(indexCDS->maturity())
459 << "\t" << calibrationFactor->value() << "\t" << indexCDS->NPV() << "\t" << cdsNpvs << "\t"
460 << indexCDS->NPV() - cdsNpvs << "\t" << indexCDS->fairSpreadClean() << "\t" << cdsFairSpreads
461 << "\t" << indexCDS->fairSpreadClean() - cdsFairSpreads);
462 }
463 }
464 }
465
466 // Create the instruments.
467 auto pool = QuantLib::ext::make_shared<Pool>();
468
469 CreditPortfolioSensitivityDecomposition sensitivityDecomposition = cdoEngineBuilder->sensitivityDecomposition();
470 useSensitivitySimplification_ = sensitivityDecomposition != CreditPortfolioSensitivityDecomposition::Underlying;
471 Handle<DefaultProbabilityTermStructure> clientCurve;
472 Handle<DefaultProbabilityTermStructure> baseCurve;
473 vector<Time> baseCurveTimes;
474 vector<Real> expLoss;
475
476 if (cdoEngineBuilder->optimizedSensitivityCalculation()) {
478 }
479
480 for (Size i = 0; i < creditCurves.size(); ++i) {
481 const string& cc = creditCurves[i];
482 DefaultProbKey key = NorthAmericaCorpDefaultKey(ccy, SeniorSec, Period(), 1.0);
483 Real recoveryRate = recoveryRates[i];
484 auto defaultCurve = dpts[i];
485 expLoss.push_back((1 - recoveryRate) * defaultCurve->defaultProbability(maturity_, true) * basketNotionals[i]);
486 std::pair<DefaultProbKey, Handle<DefaultProbabilityTermStructure>> p(key, defaultCurve);
487 vector<pair<DefaultProbKey, Handle<DefaultProbabilityTermStructure>>> probabilities(1, p);
488 // Empty default set. Adjustments have been made above to account for existing credit events.
489 Issuer issuer(probabilities, DefaultEventSet());
490 pool->add(cc, issuer, key);
491 DLOG("Issuer " << cc << " added to the pool.");
492 }
493
494 // If we use the simplification, we need a list of all credit curves and their weight to the basket
495
496 if (sensitivityDecomposition == CreditPortfolioSensitivityDecomposition::LossWeighted) {
497 basketConstituents_.clear();
498 Real totalWeight = std::accumulate(expLoss.begin(), expLoss.end(), 0.0);
499 for (Size basketIdx = 0; basketIdx < creditCurves.size(); basketIdx++) {
500 string& creditCurve = creditCurves[basketIdx];
501 Real weight = expLoss[basketIdx];
502 if (basketConstituents_.count(creditCurve) == 0) {
503 basketConstituents_[creditCurve] = weight / totalWeight;
504 } else {
505 basketConstituents_[creditCurve] += weight / totalWeight;
506 }
507 }
508 } else if (sensitivityDecomposition == CreditPortfolioSensitivityDecomposition::NotionalWeighted) {
509 basketConstituents_.clear();
510 Real totalWeight = std::accumulate(basketNotionals.begin(), basketNotionals.end(), 0.0);
511 for (Size basketIdx = 0; basketIdx < creditCurves.size(); basketIdx++) {
512 string& creditCurve = creditCurves[basketIdx];
513 Real weight = basketNotionals[basketIdx];
514 if (basketConstituents_.count(creditCurve) == 0) {
515 basketConstituents_[creditCurve] = weight / totalWeight;
516 } else {
517 basketConstituents_[creditCurve] += weight / totalWeight;
518 }
519 }
520 } else if (sensitivityDecomposition == CreditPortfolioSensitivityDecomposition::DeltaWeighted) {
521 basketConstituents_.clear();
522 Real totalWeight = 0;
523 for (Size basketIdx = 0; basketIdx < creditCurves.size(); basketIdx++) {
524 string& creditCurve = creditCurves[basketIdx];
525 Real notional = basketNotionals[basketIdx];
526 auto defaultCurve = market->defaultCurve(creditCurve, config)->curve();
527 Real constituentSurvivalProb = defaultCurve->survivalProbability(maturity_);
528 Time t = defaultCurve->timeFromReference(maturity_);
529 Real CR01 = t * constituentSurvivalProb * notional;
530 if (basketConstituents_.find(creditCurve) == basketConstituents_.end())
531 basketConstituents_.emplace(creditCurve, CR01);
532 totalWeight += CR01;
533 }
534 // Normalize
535 for (auto& decompWeight : basketConstituents_) {
536 decompWeight.second /= totalWeight;
537 }
538 }
539
540 // We will use a homogeneous pool loss model below if all notionals and recoveries are the same.
541 bool homogeneous = all_of(basketNotionals.begin(), basketNotionals.end(),
542 [&basketNotionals](Real ntl) { return close_enough(ntl, basketNotionals[0]); });
543 homogeneous = homogeneous && all_of(recoveryRates.begin(), recoveryRates.end(),
544 [&recoveryRates](Real rr) { return close_enough(rr, recoveryRates[0]); });
545
546
547 // vanilla holds the representation of the CDO, i.e. 1 * [0, Detach] instrument - 1 * [0, Attach] instrument,
548 // without the upfront fee payment which is added below to create the full final instrument.
549 QuantLib::ext::shared_ptr<Instrument> vanilla;
550
551 // Tranche from 0 to detachment point.
552 // If detachment point is 1.0, build an index CDS, i.e. [0, 100%] tranche. Otherwise an actual tranche.
553 QuantLib::ext::shared_ptr<Instrument> cdoD;
554 if (!close_enough(adjDetachPoint, 1.0)) {
555 DLOG("Building detachment tranche [0," << adjDetachPoint << "].");
556 // Set up the basket loss model.
557 auto basket =
558 QuantLib::ext::make_shared<QuantExt::Basket>(schedule[0], creditCurves, basketNotionals, pool, 0.0, adjDetachPoint);
559 basket->setLossModel(
560 cdoEngineBuilder->lossModel(qualifier(), recoveryRates, adjDetachPoint,
561 maturity_, homogeneous));
562
563 auto cdoDetach =
564 QuantLib::ext::make_shared<QuantExt::SyntheticCDO>(basket, side, schedule, 0.0, runningRate, dayCounter, bdc,
565 settlesAccrual_, protectionPaymentTime_, protectionStartDate, parseDate(upfrontDate_), boost::none, Null<Real>(), lastPeriodDayCounter);
566
567 cdoDetach->setPricingEngine(
568 cdoEngineBuilder->engine(ccy, false, {}, calibrationFactor, fixedRecovery));
569 setSensitivityTemplate(*cdoEngineBuilder);
570 cdoD = cdoDetach;
571
572 DLOG("Detachment tranche [0," << adjDetachPoint << "] built.");
573
574 } else {
575 DLOG("Detachment point is 1.0 so building an index CDS for [0,1.0] 'tranche'.");
576
577 /*auto cdsBuilder = QuantLib::ext::dynamic_pointer_cast<IndexCreditDefaultSwapEngineBuilder>(
578 engineFactory->builder("IndexCreditDefaultSwap"));
579 QL_REQUIRE(cdsBuilder, "Trade " << id() << " needs a IndexCreditDefaultSwapEngineBuilder.");
580 */
581 auto cds = QuantLib::ext::make_shared<QuantExt::IndexCreditDefaultSwap>(
582 side, currTotalNtl, basketNotionals, 0.0, runningRate, schedule, bdc, dayCounter, settlesAccrual_,
583 protectionPaymentTime_, protectionStartDate, Date(), QuantLib::ext::shared_ptr<Claim>(),
584 lastPeriodDayCounter, rebatesAccrual_, protectionStartDate, 3);
585
586 cds->setPricingEngine(
587 cdoEngineBuilder->engine(ccy, true, creditCurves, calibrationFactor, fixedRecovery));
588 setSensitivityTemplate(*cdoEngineBuilder);
589 cdoD = cds;
590
591 DLOG("Index CDS for [0,1.0] 'tranche' built.");
592 }
593
594 // Tranche from 0 to attachment point.
595 // If attachment point is 0.0, the instrument is simply the 0 to detachment point CDO built above. If attachment
596 // point is greater than 0, we build the 0 to attachment point tranche and the value of the CDO is the
597 // difference between the detachment point CDO and attachment point CDO.
598 if (close_enough(adjAttachPoint, 0.0)) {
599 DLOG("Attachment point is 0 so the instrument is built.");
600 vanilla = cdoD;
601 } else {
602 DLOG("Building attachment tranche [0," << adjAttachPoint << "].");
603
604 // Set up the basket loss model.
605 auto basket =
606 QuantLib::ext::make_shared<QuantExt::Basket>(schedule[0], creditCurves, basketNotionals, pool, 0.0, adjAttachPoint);
607 basket->setLossModel(cdoEngineBuilder->lossModel(qualifier(), recoveryRates, adjAttachPoint,
608 maturity_, homogeneous));
609
610 auto cdoA =
611 QuantLib::ext::make_shared<QuantExt::SyntheticCDO>(basket, side, schedule, 0.0, runningRate, dayCounter, bdc, settlesAccrual_, protectionPaymentTime_,
612 protectionStartDate, parseDate(upfrontDate_), boost::none, fixedRecovery, lastPeriodDayCounter);
613
614 cdoA->setPricingEngine(
615 cdoEngineBuilder->engine(ccy, false, {}, calibrationFactor, fixedRecovery));
616 setSensitivityTemplate(*cdoEngineBuilder);
617
618 DLOG("Attachment tranche [0," << adjAttachPoint << "] built.");
619
620 DLOG("Building attachment and detachment composite instrument.");
621
622 auto composite = QuantLib::ext::make_shared<CompositeInstrument>();
623 composite->add(cdoD);
624 composite->subtract(cdoA);
625 vanilla = composite;
626
627 DLOG("Attachment and detachment composite instrument built.");
628 }
629
630 // Add the upfront fee payment here. Positive number indicates payment from buyer of protection to seller of
631 // protection and a negative value indicates a payment from seller of protection to buyer of protection. The
632 // upfront fee provided is interpreted as a fractional amount of the original tranche notional.
633 if (upfrontDate != Date()) {
634 vector<QuantLib::ext::shared_ptr<Instrument>> insts;
635 vector<Real> mults;
636 Real upfrontAmount = upfrontFee_ * origTrancheNtl;
637 string configuration = cdoEngineBuilder->configuration(MarketContext::pricing);
638 maturity_ =
639 std::max(maturity_, addPremiums(insts, mults, 1.0, PremiumData(upfrontAmount, ccy.code(), upfrontDate),
640 side == Protection::Buyer ? -1.0 : 1.0, ccy, engineFactory, configuration));
641
642 instrument_ = QuantLib::ext::make_shared<VanillaInstrument>(vanilla, 1.0, insts, mults);
643
644 } else {
645 // If no upfront payment, the instrument is simply the vanilla instrument.
646 instrument_ = QuantLib::ext::make_shared<VanillaInstrument>(vanilla);
647 }
648
649 additionalData_["originalNotional"] = origTrancheNtl;
650 additionalData_["currentNotional"] = currTrancheNtl;
651
652 DLOG("CDO instrument built");
653}
const boost::shared_ptr< QuantExt::Basket > & basket() const
const std::vector< BasketConstituent > & constituents() const
Definition: basketdata.cpp:263
const string & paymentConvention() const
Definition: legdata.hpp:878
const string & currency() const
Definition: legdata.hpp:873
const ScheduleData & schedule() const
Definition: legdata.hpp:874
bool isPayer() const
Definition: legdata.hpp:872
const string & legType() const
Definition: legdata.hpp:890
const string & dayCounter() const
Definition: legdata.hpp:877
QuantLib::ext::shared_ptr< LegAdditionalData > concreteLegData() const
Definition: legdata.hpp:891
const vector< double > & notionals() const
Definition: legdata.hpp:875
bool isIndexTranche() const
Definition: cdo.hpp:103
const string & upfrontDate() const
Definition: cdo.hpp:71
const LegData & leg() const
Definition: cdo.hpp:65
const string & qualifier() const
Inspectors.
Definition: cdo.hpp:64
static QuantLib::Handle< QuantLib::DefaultProbabilityTermStructure > buildCalibratedConstiuentCurve(const QuantLib::Handle< QuantLib::DefaultProbabilityTermStructure > &curve, const QuantLib::ext::shared_ptr< SimpleQuote > &calibrationFactor)
Definition: cdo.cpp:755
const QuantLib::Date & indexStartDateHint() const
Definition: cdo.hpp:99
std::map< std::string, double > basketConstituents_
Definition: cdo.hpp:120
std::string creditCurveIdWithTerm() const
Definition: cdo.cpp:778
const string & issuer() const
Definition: trade.hpp:161
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
Date addPremiums(std::vector< QuantLib::ext::shared_ptr< Instrument > > &instruments, std::vector< Real > &multipliers, const Real tradeMultiplier, const PremiumData &premiumData, const Real premiumMultiplier, const Currency &tradeCurrency, const QuantLib::ext::shared_ptr< EngineFactory > &factory, const string &configuration)
Definition: trade.cpp:58
void setSensitivityTemplate(const EngineBuilder &builder)
Definition: trade.cpp:295
virtual QuantLib::Real notional() const
Return the current notional in npvCurrency. See individual sub-classes for the precise definition.
Definition: trade.hpp:153
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
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
DayCounter parseDayCounter(const string &s)
Convert text to QuantLib::DayCounter.
Definition: parsers.cpp:209
#define LOG(text)
Logging Macro (Level = Notice)
Definition: log.hpp:552
#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
#define TLOG(text)
Logging Macro (Level = Data)
Definition: log.hpp:556
RandomVariable max(RandomVariable x, const RandomVariable &y)
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
RandomVariable abs(RandomVariable x)
RandomVariable min(RandomVariable x, const RandomVariable &y)
QuantLib::Handle< QuantExt::CreditCurve > indexCdsDefaultCurve(const QuantLib::ext::shared_ptr< Market > &market, const std::string &creditCurveId, const std::string &config)
Definition: marketdata.cpp:243
std::vector< Handle< DefaultProbabilityTermStructure > > buildPerformanceOptimizedDefaultCurves(const std::vector< Handle< DefaultProbabilityTermStructure > > &curves)
Definition: cdo.cpp:40
CreditPortfolioSensitivityDecomposition
Enumeration CreditPortfolioSensitivityDecomposition.
Definition: parsers.hpp:568
Leg makeFixedLeg(const LegData &data, const QuantLib::Date &openEndDateReplacement)
Definition: legdata.cpp:950
Schedule makeSchedule(const ScheduleDates &data)
Definition: schedule.cpp:263
string name
+ Here is the call graph for this function:

◆ qualifier()

const string & qualifier ( ) const

Inspectors.

Definition at line 64 of file cdo.hpp.

64{ return qualifier_; }

◆ leg()

const LegData & leg ( ) const

Definition at line 65 of file cdo.hpp.

65{ return legData_; }

◆ basketData()

const BasketData & basketData ( ) const

Definition at line 66 of file cdo.hpp.

66{ return basketData_; }

◆ attachmentPoint()

const double & attachmentPoint ( ) const

Definition at line 67 of file cdo.hpp.

67{ return attachmentPoint_; }

◆ detachmentPoint()

const double & detachmentPoint ( ) const

Definition at line 68 of file cdo.hpp.

68{ return detachmentPoint_; }

◆ protectionPaymentTime()

QuantExt::CreditDefaultSwap::ProtectionPaymentTime protectionPaymentTime ( ) const

Definition at line 69 of file cdo.hpp.

69{ return protectionPaymentTime_; }
+ Here is the caller graph for this function:

◆ protectionStart()

const string & protectionStart ( ) const

Definition at line 70 of file cdo.hpp.

70{ return protectionStart_; }
+ Here is the caller graph for this function:

◆ upfrontDate()

const string & upfrontDate ( ) const

Definition at line 71 of file cdo.hpp.

71{ return upfrontDate_; }
+ Here is the caller graph for this function:

◆ upfrontFee()

const double & upfrontFee ( ) const

Definition at line 72 of file cdo.hpp.

72{ return upfrontFee_; }
+ Here is the caller graph for this function:

◆ settlesAccrual()

bool settlesAccrual ( ) const

Definition at line 73 of file cdo.hpp.

73{ return settlesAccrual_; }
+ Here is the caller graph for this function:

◆ rebatesAccrual()

bool rebatesAccrual ( ) const

Definition at line 74 of file cdo.hpp.

74{ return rebatesAccrual_; }
+ Here is the caller graph for this function:

◆ recoveryRate()

const Real & recoveryRate ( ) const

Definition at line 75 of file cdo.hpp.

75{ return recoveryRate_; }

◆ useSensitivitySimplification()

bool useSensitivitySimplification ( ) const

Definition at line 76 of file cdo.hpp.

◆ basketConstituents()

const std::map< std::string, Real > & basketConstituents ( ) const

Definition at line 77 of file cdo.hpp.

77{ return basketConstituents_; }

◆ fromXML()

void fromXML ( XMLNode node)
overridevirtual

Reimplemented from Trade.

Definition at line 655 of file cdo.cpp.

655 {
656 Trade::fromXML(node);
657 XMLNode* cdoNode = XMLUtils::getChildNode(node, "CdoData");
658 QL_REQUIRE(cdoNode, "No CdoData Node");
659 qualifier_ = XMLUtils::getChildValue(cdoNode, "Qualifier", true);
660 protectionStart_ = XMLUtils::getChildValue(cdoNode, "ProtectionStart", true);
661 upfrontDate_ = XMLUtils::getChildValue(cdoNode, "UpfrontDate", false);
662
663 // zero if empty or missing
664 upfrontFee_ = Null<Real>();
665 string strUpfrontFee = XMLUtils::getChildValue(cdoNode, "UpfrontFee", false);
666 if (!strUpfrontFee.empty()) {
667 upfrontFee_ = parseReal(strUpfrontFee);
668 }
669 settlesAccrual_ = XMLUtils::getChildValueAsBool(cdoNode, "SettlesAccrual", false); // default = Y
670 rebatesAccrual_ = XMLUtils::getChildValueAsBool(cdoNode, "RebatesAccrual", false); // default = Y
671 protectionPaymentTime_ = QuantExt::CreditDefaultSwap::ProtectionPaymentTime::atDefault; // set default
672
673 // Recovery rate is Null<Real>() on a standard CDO i.e. if "FixedRecoveryRate" field is not populated.
674 recoveryRate_ = Null<Real>();
675 string strRecoveryRate = XMLUtils::getChildValue(node, "FixedRecoveryRate", false);
676 if (!strRecoveryRate.empty()) {
677 recoveryRate_ = parseReal(strRecoveryRate);
678 }
679
680 // for backwards compatibility only
681 if (auto c = XMLUtils::getChildNode(cdoNode, "PaysAtDefaultTime"))
682 if (!parseBool(XMLUtils::getNodeValue(c)))
683 protectionPaymentTime_ = QuantExt::CreditDefaultSwap::ProtectionPaymentTime::atPeriodEnd;
684 // new node overrides deprecated one, if both should be given
685 if (auto c = XMLUtils::getChildNode(cdoNode, "ProtectionPaymentTime")) {
686 if (XMLUtils::getNodeValue(c) == "atDefault")
687 protectionPaymentTime_ = QuantExt::CreditDefaultSwap::ProtectionPaymentTime::atDefault;
688 else if (XMLUtils::getNodeValue(c) == "atPeriodEnd")
689 protectionPaymentTime_ = QuantExt::CreditDefaultSwap::ProtectionPaymentTime::atPeriodEnd;
690 else if (XMLUtils::getNodeValue(c) == "atMaturity")
691 protectionPaymentTime_ = QuantExt::CreditDefaultSwap::ProtectionPaymentTime::atMaturity;
692 else {
693 QL_FAIL("protection payment time '" << XMLUtils::getNodeValue(c)
694 << "' not known, expected atDefault, atPeriodEnd, atMaturity");
695 }
696 }
697 attachmentPoint_ = XMLUtils::getChildValueAsDouble(cdoNode, "AttachmentPoint", true);
698 detachmentPoint_ = XMLUtils::getChildValueAsDouble(cdoNode, "DetachmentPoint", true);
699 XMLNode* legNode = XMLUtils::getChildNode(cdoNode, "LegData");
700 legData_.fromXML(legNode);
701 XMLNode* basketNode = XMLUtils::getChildNode(cdoNode, "BasketData");
702 if (basketNode)
703 basketData_.fromXML(basketNode);
704}
void fromXML(ore::data::XMLNode *node) override
Definition: basketdata.cpp:265
virtual void fromXML(XMLNode *node) override
Definition: legdata.cpp:759
bool parseBool(const string &s)
Convert text to bool.
Definition: parsers.cpp:144
Real parseReal(const string &s)
Convert text to Real.
Definition: parsers.cpp:112
rapidxml::xml_node< char > XMLNode
Definition: xmlutils.hpp:60
+ Here is the call graph for this function:

◆ toXML()

XMLNode * toXML ( XMLDocument doc) const
overridevirtual

Reimplemented from Trade.

Definition at line 706 of file cdo.cpp.

706 {
707 XMLNode* node = Trade::toXML(doc);
708 XMLNode* cdoNode = doc.allocNode("CdoData");
709 XMLUtils::appendNode(node, cdoNode);
710 XMLUtils::addChild(doc, cdoNode, "Qualifier", qualifier_);
711 XMLUtils::addChild(doc, cdoNode, "ProtectionStart", protectionStart_);
712 if (!upfrontDate_.empty()) {
713 XMLUtils::addChild(doc, cdoNode, "UpfrontDate", upfrontDate_);
714 }
715 if (upfrontFee_ != Null<Real>()) {
716 XMLUtils::addChild(doc, cdoNode, "UpfrontFee", upfrontFee_);
717 }
718 XMLUtils::addChild(doc, cdoNode, "SettlesAccrual", settlesAccrual_);
719 if (!rebatesAccrual_)
720 XMLUtils::addChild(doc, node, "RebatesAccrual", rebatesAccrual_);
721 if (protectionPaymentTime_ == QuantExt::CreditDefaultSwap::ProtectionPaymentTime::atDefault)
722 XMLUtils::addChild(doc, cdoNode, "ProtectionPaymentTime", "atDefault");
723 else if (protectionPaymentTime_ == QuantExt::CreditDefaultSwap::ProtectionPaymentTime::atPeriodEnd)
724 XMLUtils::addChild(doc, cdoNode, "ProtectionPaymentTime", "atPeriodEnd");
725 else if (protectionPaymentTime_ == QuantExt::CreditDefaultSwap::ProtectionPaymentTime::atMaturity)
726 XMLUtils::addChild(doc, cdoNode, "ProtectionPaymentTime", "atMaturity");
727 else {
728 QL_FAIL("CreditDefaultSwapData::toXML(): unexpected protectionPaymentTime_");
729 }
730 if (recoveryRate_ != Null<Real>())
731 XMLUtils::addChild(doc, node, "FixedRecoveryRate", recoveryRate_);
732 XMLUtils::addChild(doc, cdoNode, "AttachmentPoint", attachmentPoint_);
733 XMLUtils::addChild(doc, cdoNode, "DetachmentPoint", detachmentPoint_);
734 XMLUtils::appendNode(cdoNode, legData_.toXML(doc));
735 XMLUtils::appendNode(cdoNode, basketData_.toXML(doc));
736 return node;
737}
ore::data::XMLNode * toXML(ore::data::XMLDocument &doc) const override
Definition: basketdata.cpp:277
virtual XMLNode * toXML(XMLDocument &doc) const override
Definition: legdata.cpp:856
+ Here is the call graph for this function:

◆ extractTimeGridDefaultCurve()

vector< Time > extractTimeGridDefaultCurve ( const QuantLib::Handle< QuantLib::DefaultProbabilityTermStructure > &  dpts)
static

Definition at line 739 of file cdo.cpp.

739 {
740 if (auto c = QuantLib::ext::dynamic_pointer_cast<SpreadedSurvivalProbabilityTermStructure>(*dpts)) {
741 return SyntheticCDO::extractTimeGridDefaultCurve(c->referenceCurve());
742 } else if (auto c = QuantLib::ext::dynamic_pointer_cast<InterpolatedSurvivalProbabilityCurve<LogLinear>>(*dpts)) {
743 return c->times();
744 } else if (auto c = QuantLib::ext::dynamic_pointer_cast<InterpolatedHazardRateCurve<BackwardFlat>>(*dpts)) {
745 return c->times();
746 } else if (auto c = QuantLib::ext::dynamic_pointer_cast<SurvivalProbabilityCurve<LogLinear>>(*dpts)) {
747 return c->times();
748 } else {
749 WLOG("Extraction of time points failed, unsupported default probability termstructure");
750 return vector<double>();
751 }
752}
+ Here is the caller graph for this function:

◆ buildCalibratedConstiuentCurve()

QuantLib::Handle< QuantLib::DefaultProbabilityTermStructure > buildCalibratedConstiuentCurve ( const QuantLib::Handle< QuantLib::DefaultProbabilityTermStructure > &  curve,
const QuantLib::ext::shared_ptr< SimpleQuote > &  calibrationFactor 
)
static

Definition at line 755 of file cdo.cpp.

756 {
757 if (!calibrationFactor) {
758 return curve;
759 }
760 auto curveTimes = extractTimeGridDefaultCurve(curve);
761 vector<Handle<Quote>> spreads;
762 for (size_t timeIdx = 0; timeIdx < curveTimes.size(); ++timeIdx) {
763 auto sp = curve->survivalProbability(curveTimes[timeIdx]);
764 auto compQuote = QuantLib::ext::make_shared<CompositeQuote<std::function<double(double, double)>>>(
765 Handle<Quote>(calibrationFactor),
766 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(sp)),
767 [](const double q1, const double q2) -> double { return std::exp(-(1 - q1) * std::log(q2)); });
768 spreads.push_back(Handle<Quote>(compQuote));
769 }
770 Handle<DefaultProbabilityTermStructure> targetCurve = Handle<DefaultProbabilityTermStructure>(
771 QuantLib::ext::make_shared<SpreadedSurvivalProbabilityTermStructure>(curve, curveTimes, spreads));
772 if (curve->allowsExtrapolation()) {
773 targetCurve->enableExtrapolation();
774 }
775 return targetCurve;
776}
static vector< Time > extractTimeGridDefaultCurve(const QuantLib::Handle< QuantLib::DefaultProbabilityTermStructure > &dpts)
Definition: cdo.cpp:739
+ Here is the caller graph for this function:

◆ creditCurveIdWithTerm()

std::string creditCurveIdWithTerm ( ) const

Get credit curve id with term suffix "_5Y". If the creditCurveId contains such a suffix already this is used. Otherwise we try to imply it from the schedule. If that is not possible, the creditCurveId without tenor is returned.

Definition at line 778 of file cdo.cpp.

778 {
780 if (p.second != 0 * Days || !isIndexTranche())
781 return qualifier();
782
783 QuantLib::Schedule s = makeSchedule(leg().schedule());
784 if (s.dates().empty())
785 return p.first;
786 QuantLib::Period t = QuantExt::implyIndexTerm(
787 indexStartDateHint_ == Date() ? s.dates().front() : indexStartDateHint_, s.dates().back());
788 if (t != 0 * Days)
789 return p.first + "_" + ore::data::to_string(t);
790 return p.first;
791}
QuantLib::Period implyIndexTerm(const Date &startDate, const Date &endDate)
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
std::pair< std::string, QuantLib::Period > splitCurveIdWithTenor(const std::string &creditCurveId)
Definition: marketdata.cpp:231
+ Here is the call graph for this function:

◆ setIndexStartDateHint()

void setIndexStartDateHint ( const QuantLib::Date &  d) const

If set this is used to derive the term instead of the schedule start date. A concession to bad trade setups really, where the start date is not set to the index effective date

Definition at line 96 of file cdo.hpp.

QuantLib::Date indexStartDateHint_
Definition: cdo.hpp:122

◆ indexStartDateHint()

const QuantLib::Date & indexStartDateHint ( ) const

Get the index start date hint, or null if it was never set

Definition at line 99 of file cdo.hpp.

99{ return indexStartDateHint_; }

◆ isIndexTranche()

bool isIndexTranche ( ) const
private

Definition at line 103 of file cdo.hpp.

103{ return qualifier_.size() == 13 && qualifier_.substr(0, 3) == "RED"; }

Member Data Documentation

◆ qualifier_

string qualifier_
private

Definition at line 105 of file cdo.hpp.

◆ legData_

LegData legData_
private

Definition at line 106 of file cdo.hpp.

◆ basketData_

BasketData basketData_
private

Definition at line 107 of file cdo.hpp.

◆ attachmentPoint_

double attachmentPoint_
private

Definition at line 108 of file cdo.hpp.

◆ detachmentPoint_

double detachmentPoint_
private

Definition at line 109 of file cdo.hpp.

◆ settlesAccrual_

bool settlesAccrual_
private

Definition at line 110 of file cdo.hpp.

◆ protectionPaymentTime_

QuantExt::CreditDefaultSwap::ProtectionPaymentTime protectionPaymentTime_
private

Definition at line 111 of file cdo.hpp.

◆ protectionStart_

string protectionStart_
private

Definition at line 112 of file cdo.hpp.

◆ upfrontDate_

string upfrontDate_
private

Definition at line 113 of file cdo.hpp.

◆ upfrontFee_

double upfrontFee_
private

Definition at line 114 of file cdo.hpp.

◆ rebatesAccrual_

bool rebatesAccrual_
private

Definition at line 115 of file cdo.hpp.

◆ recoveryRate_

Real recoveryRate_
private

Definition at line 116 of file cdo.hpp.

◆ basketConstituents_

std::map<std::string, double> basketConstituents_
private

Definition at line 120 of file cdo.hpp.

◆ useSensitivitySimplification_

bool useSensitivitySimplification_
private

Definition at line 121 of file cdo.hpp.

◆ indexStartDateHint_

QuantLib::Date indexStartDateHint_
mutableprivate

Definition at line 122 of file cdo.hpp.