31#include <ql/time/daycounters/actual360.hpp>
40 DLOG(
"IndexCreditDefaultSwap::build() called for trade " <<
id());
43 additionalData_[
"isdaAssetClass"] = string(
"Credit");
44 additionalData_[
"isdaBaseProduct"] = string(
"Index");
45 string entity = swap_.creditCurveId();
46 QuantLib::ext::shared_ptr<ReferenceDataManager> refData = engineFactory->referenceData();
47 if (refData && refData->hasData(
"CreditIndex", entity)) {
48 auto refDatum = refData->getData(
"CreditIndex", entity);
49 QuantLib::ext::shared_ptr<CreditIndexReferenceDatum> creditIndexRefDatum =
50 QuantLib::ext::dynamic_pointer_cast<CreditIndexReferenceDatum>(refDatum);
51 additionalData_[
"isdaSubProduct"] = creditIndexRefDatum->indexFamily();
52 if (creditIndexRefDatum->indexFamily() ==
"") {
53 ALOG(
"IndexFamily is blank in credit index reference data for entity " << entity);
56 ALOG(
"Credit index reference data missing for entity " << entity <<
", isdaSubProduct left blank");
59 additionalData_[
"isdaTransaction"] = string(
"");
61 const QuantLib::ext::shared_ptr<Market> market = engineFactory->market();
62 QuantLib::ext::shared_ptr<EngineBuilder> builder = engineFactory->builder(
"IndexCreditDefaultSwap");
64 QL_REQUIRE(swap_.leg().legType() ==
"Fixed",
"IndexCreditDefaultSwap requires Fixed leg");
65 QuantLib::ext::shared_ptr<FixedLegData> fixedLegData =
66 QuantLib::ext::dynamic_pointer_cast<FixedLegData>(swap_.leg().concreteLegData());
68 auto configuration = builder->configuration(MarketContext::pricing);
71 legs_[0] = flb.
buildLeg(swap_.leg(), engineFactory, requiredFixings_, configuration);
73 Schedule schedule =
makeSchedule(swap_.leg().schedule());
75 Protection::Side prot = swap_.leg().isPayer() ? Protection::Side::Buyer : Protection::Side::Seller;
77 notional_ = notional();
82 Actual360 standardDayCounter;
83 DayCounter lastPeriodDayCounter = dc == standardDayCounter ? Actual360(
true) : dc;
85 QL_REQUIRE(fixedLegData->rates().size() == 1,
"IndexCreditDefaultSwap requires single rate");
87 Real indexFactor = 1.0;
90 vector<Real> basketNotionals;
91 vector<string> basketCreditCurves;
93 if (!swap_.basket().constituents().empty()) {
95 const auto& constituents = swap_.basket().constituents();
96 DLOG(
"Building constituents from basket data containing " << constituents.size() <<
" elements.");
99 for (
const auto& c : constituents) {
100 Real ntl = Null<Real>();
102 const auto& creditCurve = c.creditCurveId();
104 if (c.weightInsteadOfNotional()) {
105 ntl = c.weight() * notional_;
110 if (!close(0.0, ntl) && ntl > 0.0) {
111 if (std::find(basketCreditCurves.begin(), basketCreditCurves.end(), creditCurve) ==
112 basketCreditCurves.end()) {
113 DLOG(
"Adding underlying " << creditCurve <<
" with notional " << ntl);
114 constituents_[creditCurve] = ntl;
115 basketCreditCurves.push_back(creditCurve);
116 basketNotionals.push_back(ntl);
120 (
"Invalid Basket: found a duplicate credit curve " + creditCurve +
121 ", skip it. Check the basket data for possible errors.")
126 DLOG(
"Skipped adding underlying, " << creditCurve <<
", because its notional, " << ntl
127 <<
", was non-positive.");
130 QL_REQUIRE(basketCreditCurves.size() == basketNotionals.size(),
131 "numbers of defaults curves (" << basketCreditCurves.size() <<
") and notionals ("
132 << basketNotionals.size() <<
") doesnt match");
133 DLOG(
"All underlyings added, total notional = " << totalNtl);
135 if (totalNtl > notional_ * (1.0 + 1.0E-4)) {
137 (
"Sum of basket notionals (" + std::to_string(totalNtl) +
138 ") is greater than trade notional (" + std::to_string(notional_) +
139 "). Check the basket data for possible errors.")
144 indexFactor = totalNtl / notional_;
146 DLOG(
"Finished building constituents using basket data.");
151 DLOG(
"Getting CreditIndexReferenceDatum for id " <<
id);
152 QL_REQUIRE(engineFactory->referenceData(),
"No BasketData or ReferenceDataManager");
153 QL_REQUIRE(engineFactory->referenceData()->hasData(CreditIndexReferenceDatum::TYPE,
id),
154 "No CreditIndex reference data for " <<
id);
155 QuantLib::ext::shared_ptr<ReferenceDatum> refData =
156 engineFactory->referenceData()->getData(CreditIndexReferenceDatum::TYPE,
id);
157 QuantLib::ext::shared_ptr<CreditIndexReferenceDatum> creditRefData =
158 QuantLib::ext::dynamic_pointer_cast<CreditIndexReferenceDatum>(refData);
159 DLOG(
"Got CreditIndexReferenceDatum for id " <<
id);
161 Real totalWeight = 0.0;
162 for (
const auto& c : creditRefData->constituents()) {
164 const auto&
name = c.name();
165 auto weight = c.weight();
167 if (!close(0.0, weight) && weight > 0.0) {
168 DLOG(
"Adding underlying " <<
name <<
" with weight " << weight);
169 constituents_[
name] = notional_ * weight;
170 basketCreditCurves.push_back(
name);
171 basketNotionals.push_back(notional_ * weight);
172 totalWeight += weight;
174 DLOG(
"Skipped adding underlying, " <<
name <<
", because its weight, " << weight
175 <<
", was non-positive.");
179 indexFactor = totalWeight;
181 DLOG(
"All underlyings added, total weight = " << totalWeight);
183 if (!close(1.0, totalWeight) && totalWeight > 1.0) {
184 ALOG(
"Total weight is greater than 1, possible error in CreditIndexReferenceDatum");
188 QuantLib::ext::shared_ptr<QuantExt::IndexCreditDefaultSwap> cds;
189 if (swap_.upfrontFee() == Null<Real>()) {
190 cds = QuantLib::ext::make_shared<QuantExt::IndexCreditDefaultSwap>(
191 prot, indexFactor * notional_, basketNotionals, fixedLegData->rates().front(), schedule, payConvention, dc,
192 swap_.settlesAccrual(), swap_.protectionPaymentTime(), swap_.protectionStart(), QuantLib::ext::shared_ptr<Claim>(),
193 lastPeriodDayCounter,
true, swap_.tradeDate(), swap_.cashSettlementDays());
195 cds = QuantLib::ext::make_shared<QuantExt::IndexCreditDefaultSwap>(
196 prot, indexFactor * notional_, basketNotionals, swap_.upfrontFee(), fixedLegData->rates().front(), schedule,
197 payConvention, dc, swap_.settlesAccrual(), swap_.protectionPaymentTime(), swap_.protectionStart(),
198 swap_.upfrontDate(), QuantLib::ext::shared_ptr<Claim>(), lastPeriodDayCounter,
true, swap_.tradeDate(),
199 swap_.cashSettlementDays());
202 QuantLib::ext::shared_ptr<IndexCreditDefaultSwapEngineBuilder> cdsBuilder =
203 QuantLib::ext::dynamic_pointer_cast<IndexCreditDefaultSwapEngineBuilder>(builder);
205 npvCurrency_ = swap_.leg().currency();
206 notionalCurrency_ = swap_.leg().currency();
208 QL_REQUIRE(cdsBuilder,
"No Builder found for IndexCreditDefaultSwap: " <<
id());
209 std::string curveIdWithTerm = swap_.creditCurveIdWithTerm();
211 if (swap_.basket().constituents().empty() &&
splitCurveIdWithTenor(curveIdWithTerm).second == 0 * Days) {
213 id(), tradeType(),
"Could not imply Index CDS term.",
214 "Index CDS term could not be derived from start, end date, are these dates correct (credit curve id is '" +
215 swap_.creditCurveId() +
"')").
log();
218 maturity_ = cds->coupons().back()->date();
220 cds->setPricingEngine(cdsBuilder->engine(
parseCurrency(npvCurrency_), swap_.creditCurveIdWithTerm(),
221 basketCreditCurves, boost::none, swap_.recoveryRate(),
false));
222 setSensitivityTemplate(*cdsBuilder);
227 legs_ = {cds->coupons()};
228 legCurrencies_ = {npvCurrency_};
229 legPayers_ = {swap_.leg().isPayer()};
231 if (swap_.protectionStart() != Date())
232 additionalData_[
"startDate"] =
to_string(swap_.protectionStart());
234 additionalData_[
"startDate"] =
to_string(schedule.dates().front());
236 sensitivityDecomposition_ = cdsBuilder->sensitivityDecomposition();
239const std::map<std::string, boost::any>& IndexCreditDefaultSwap::additionalData()
const {
240 setLegBasedAdditionalData(0, 2);
241 additionalData_[
"legNPV[1]"] = instrument_->qlInstrument()->result<Real>(
"protectionLegNPV");
242 additionalData_[
"legNPV[2]"] = instrument_->qlInstrument()->result<Real>(
"premiumLegNPVDirty") +
243 instrument_->qlInstrument()->result<Real>(
"upfrontPremiumNPV") +
244 instrument_->qlInstrument()->result<Real>(
"accrualRebateNPV");
245 additionalData_[
"isPayer[1]"] = !swap_.leg().isPayer();
246 additionalData_[
"isPayer[2]"] = swap_.leg().isPayer();
247 additionalData_[
"legType[2]"] = swap_.leg().legType();
248 additionalData_[
"legType[1]"] = std::string(
"Protection");
249 additionalData_[
"currentNotional[1]"] = additionalData_[
"currentNotional[2]"];
250 additionalData_[
"originalNotional[1]"] = additionalData_[
"originalNotional[2]"];
251 additionalData_[
"notionalCurrency[1]"] = notionalCurrency_;
252 additionalData_[
"notionalCurrency[2]"] = notionalCurrency_;
253 return additionalData_;
257QuantLib::Real IndexCreditDefaultSwap::notional()
const {
258 Date asof = Settings::instance().evaluationDate();
260 for (Size i = 0; i < legs_[0].size(); ++i) {
261 QuantLib::ext::shared_ptr<Coupon> coupon = QuantLib::ext::dynamic_pointer_cast<Coupon>(legs_[0][i]);
262 if (coupon->date() > asof)
263 return coupon->nominal();
267 ALOG(
"Error retrieving current notional for index credit default swap " <<
id() <<
" as of " << io::iso_date(asof));
271void IndexCreditDefaultSwap::fromXML(
XMLNode* node) {
272 Trade::fromXML(node);
273 XMLNode* cdsNode = XMLUtils::getChildNode(node,
"IndexCreditDefaultSwapData");
274 QL_REQUIRE(cdsNode,
"No IndexCreditDefaultSwapData Node");
275 swap_.fromXML(cdsNode);
279 XMLNode* node = Trade::toXML(doc);
280 XMLUtils::appendNode(node, swap_.toXML(doc));
Leg buildLeg(const LegData &data, const QuantLib::ext::shared_ptr< EngineFactory > &engineFactory, RequiredFixings &requiredFixings, const string &configuration, const QuantLib::Date &openEndDateReplacement=Null< Date >(), const bool useXbsCurves=false) const override
virtual void build(const QuantLib::ext::shared_ptr< EngineFactory > &) override
void log() const
generate Boost log record to pass to corresponding sinks
Utility class for Structured Trade errors, contains the Trade ID and Type.
Utility classes for Structured warnings, contains the Trade ID and Type.
Vanilla Instrument Wrapper.
Small XML Document wrapper class.
Currency parseCurrency(const string &s)
Convert text to QuantLib::Currency.
BusinessDayConvention parseBusinessDayConvention(const string &s)
Convert text to QuantLib::BusinessDayConvention.
DayCounter parseDayCounter(const string &s)
Convert text to QuantLib::DayCounter.
Classes and functions for log message handling.
#define DLOG(text)
Logging Macro (Level = Debug)
#define ALOG(text)
Logging Macro (Level = Alert)
market data related utilties
std::string to_string(const LocationInfo &l)
std::pair< std::string, QuantLib::Period > splitCurveIdWithTenor(const std::string &creditCurveId)
Schedule makeSchedule(const ScheduleDates &data)
Serializable Credit Default Swap.
Map text representations to QuantLib/QuantExt types.
Structured Trade Error class.
Classes for structured trade warnings.
string conversion utilities