Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
creditdefaultswapoption.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
26#include <ql/time/daycounters/actual360.hpp>
27
28using namespace QuantLib;
29using namespace QuantExt;
30
31namespace ore {
32namespace data {
33
35 : auctionFinalPrice_(Null<Real>()) {}
36
38 const Date& auctionSettlementDate, Real& auctionFinalPrice)
39 : auctionSettlementDate_(auctionSettlementDate), auctionFinalPrice_(auctionFinalPrice) {}
40
42 return auctionSettlementDate_;
43}
44
46 return auctionFinalPrice_;
47}
48
50 XMLUtils::checkNode(node, "AuctionSettlementInformation");
51 auctionSettlementDate_ = parseDate(XMLUtils::getChildValue(node, "AuctionSettlementDate", true));
52 auctionFinalPrice_ = XMLUtils::getChildValueAsDouble(node, "AuctionFinalPrice", true);
53}
54
56 XMLNode* node = doc.allocNode("AuctionSettlementInformation");
57 XMLUtils::addChild(doc, node, "AuctionSettlementDate", to_string(auctionSettlementDate_));
58 XMLUtils::addChild(doc, node, "AuctionFinalPrice", auctionFinalPrice_);
59 return node;
60}
61
63 : Trade("CreditDefaultSwapOption"), strike_(Null<Real>()), knockOut_(true) {}
64
66 const OptionData& option,
67 const CreditDefaultSwapData& swap,
68 Real strike,
69 const string& strikeType,
70 bool knockOut,
71 const string& term,
72 const boost::optional<AuctionSettlementInformation>& asi)
73 : Trade("CreditDefaultSwapOption", env), option_(option), swap_(swap), strike_(strike),
74 strikeType_(strikeType), knockOut_(knockOut), term_(term), asi_(asi) {}
75
76void CreditDefaultSwapOption::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
77
78 DLOG("CreditDefaultSwapOption::build() called for trade " << id());
79
80 // ISDA taxonomy
81 additionalData_["isdaAssetClass"] = string("Credit");
82 additionalData_["isdaBaseProduct"] = string("Swaptions");
83 // set isdaSubProduct to entityType in credit reference data
84 additionalData_["isdaSubProduct"] = string("");
85 string entity =
87 QuantLib::ext::shared_ptr<ReferenceDataManager> refData = engineFactory->referenceData();
88 if (refData && refData->hasData("Credit", entity)) {
89 auto refDatum = refData->getData("Credit", entity);
90 QuantLib::ext::shared_ptr<CreditReferenceDatum> creditRefDatum =
91 QuantLib::ext::dynamic_pointer_cast<CreditReferenceDatum>(refDatum);
92 additionalData_["isdaSubProduct"] = creditRefDatum->creditData().entityType;
93 if (creditRefDatum->creditData().entityType == "") {
94 ALOG("EntityType is blank in credit reference data for entity " << entity);
95 }
96 } else {
97 ALOG("Credit reference data missing for entity " << entity << ", isdaSubProduct left blank");
98 }
99 // Skip the transaction level mapping for now
100 additionalData_["isdaTransaction"] = string("");
101
102 // Notionals
103 const auto& legData = swap_.leg();
104 const auto& ntls = legData.notionals();
105 QL_REQUIRE(ntls.size() == 1, "CreditDefaultSwapOption requires a single notional.");
106 notional_ = ntls.front();
107 notionalCurrency_ = legData.currency();
108
109 // Type of instrument we build depends on whether the reference entity has already defaulted.
110 if (asi_) {
111 buildDefaulted(engineFactory);
112 } else {
113 buildNoDefault(engineFactory);
114 }
115}
116
118 return option_;
119}
120
122 return swap_;
123}
124
126 return strike_;
127}
128
130 return strikeType_;
131}
132
134 return knockOut_;
135}
136
137const string& CreditDefaultSwapOption::term() const {
138 return term_;
139}
140
142const boost::optional<ASI>& CreditDefaultSwapOption::auctionSettlementInformation() const {
143 return asi_;
144}
145
147
148 Trade::fromXML(node);
149
150 XMLNode* cdsOptionData = XMLUtils::getChildNode(node, "CreditDefaultSwapOptionData");
151 QL_REQUIRE(cdsOptionData, "Expected CreditDefaultSwapOptionData node on trade " << id() << ".");
152 strike_ = XMLUtils::getChildValueAsDouble(cdsOptionData, "Strike", false, Null<Real>());
153 strikeType_ = "Spread";
154 if (auto n = XMLUtils::getChildNode(cdsOptionData, "StrikeType")) {
156 }
157 knockOut_ = XMLUtils::getChildValueAsBool(cdsOptionData, "KnockOut", false, true);
158 term_ = XMLUtils::getChildValue(cdsOptionData, "Term", false);
159
160 if (XMLNode* asiNode = XMLUtils::getChildNode(cdsOptionData, "AuctionSettlementInformation"))
161 asi_->fromXML(asiNode);
162
163 XMLNode* cdsData = XMLUtils::getChildNode(cdsOptionData, "CreditDefaultSwapData");
164 QL_REQUIRE(cdsData, "Expected CreditDefaultSwapData node on trade " << id() << ".");
165 swap_.fromXML(cdsData);
166
167 XMLNode* optionData = XMLUtils::getChildNode(cdsOptionData, "OptionData");
168 QL_REQUIRE(optionData, "Expected OptionData node on trade " << id() << ".");
169 option_.fromXML(optionData);
170}
171
173
174 // Trade node
175 XMLNode* node = Trade::toXML(doc);
176
177 // CreditDefaultSwapOptionData node
178 XMLNode* cdsOptionDataNode = doc.allocNode("CreditDefaultSwapOptionData");
179 if (strike_ != Null<Real>())
180 XMLUtils::addChild(doc, cdsOptionDataNode, "Strike", strike_);
181 if (strikeType_ != "")
182 XMLUtils::addChild(doc, cdsOptionDataNode, "StrikeType", strikeType_);
183 XMLUtils::addChild(doc, cdsOptionDataNode, "KnockOut", knockOut_);
184 if (!term_.empty())
185 XMLUtils::addChild(doc, cdsOptionDataNode, "Term", term_);
186
187 if (asi_)
188 XMLUtils::appendNode(cdsOptionDataNode, asi_->toXML(doc));
189
190 XMLUtils::appendNode(cdsOptionDataNode, swap_.toXML(doc));
191 XMLUtils::appendNode(cdsOptionDataNode, option_.toXML(doc));
192
193 // Add CreditDefaultSwapOptionData node to Trade node
194 XMLUtils::appendNode(node, cdsOptionDataNode);
195
196 return node;
197}
198
199void CreditDefaultSwapOption::buildNoDefault(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
200
201 DLOG("CreditDefaultSwapOption: building CDS option trade " << id() << " given no default.");
202
203 // Need fixed leg data with one rate. This should be the standard running coupon on the CDS e.g.
204 // generally 100bp for IG CDS and 500bp for HY CDS. For single name CDS options, one can use this field to give
205 // the strike spread. It may matter for the resulting valuation depending on the engine that is used - see
206 // "A CDS Option Miscellany, Richard J. Martin, 2019, Section 2.4".
207 const auto& legData = swap_.leg();
208 QL_REQUIRE(legData.legType() == "Fixed", "CDS option " << id() << " requires fixed leg.");
209 auto fixedLegData = QuantLib::ext::dynamic_pointer_cast<FixedLegData>(legData.concreteLegData());
210 QL_REQUIRE(fixedLegData->rates().size() == 1, "Index CDS option " << id() << " requires single fixed rate.");
211 auto runningCoupon = fixedLegData->rates().front();
212
213 // Payer (Receiver) swaption if the leg is paying (receiving).
214 auto side = legData.isPayer() ? Protection::Side::Buyer : Protection::Side::Seller;
215
216 // Day counter. In general for CDS, the standard day counter is Actual/360 and the final
217 // period coupon accrual includes the maturity date.
218 Actual360 standardDayCounter;
219 const std::string& strDc = legData.dayCounter();
220 DayCounter dc = strDc.empty() ? standardDayCounter : parseDayCounter(strDc);
221 DayCounter lastPeriodDayCounter = dc == standardDayCounter ? Actual360(true) : dc;
222
223 // Schedule
224 Schedule schedule = makeSchedule(legData.schedule());
225 BusinessDayConvention payConvention = legData.paymentConvention().empty() ? Following :
226 parseBusinessDayConvention(legData.paymentConvention());
227
228 // Don't support upfront fee on the underlying CDS for the moment.
229 QL_REQUIRE(swap_.upfrontFee() == Null<Real>() || close(swap_.upfrontFee(), 0.0),
230 "Upfront fee on the CDS underlying a CDS option is not supported.");
231
232 // The underlying CDS trade
233 auto cds = QuantLib::ext::make_shared<QuantLib::CreditDefaultSwap>(side, notional_, runningCoupon, schedule,
235 QuantLib::ext::shared_ptr<Claim>(), lastPeriodDayCounter, true, swap_.tradeDate(), swap_.cashSettlementDays());
236
237 // Copying here what is done for the index CDS option. The comment there is:
238 // Align option product maturities with ISDA AANA/GRID guidance as of November 2020.
239 maturity_ = std::max(cds->coupons().back()->date(), option_.premiumData().latestPremiumDate());
240
241 // Set engine on the underlying CDS.
242 auto cdsBuilder = QuantLib::ext::dynamic_pointer_cast<CreditDefaultSwapEngineBuilder>(
243 engineFactory->builder("CreditDefaultSwap"));
244 QL_REQUIRE(cdsBuilder, "CreditDefaultSwapOption expected CDS engine " <<
245 " builder for underlying while building trade " << id() << ".");
246 npvCurrency_ = legData.currency();
247 auto ccy = parseCurrency(npvCurrency_);
248 cds->setPricingEngine(cdsBuilder->engine(ccy, swap_.creditCurveId(), swap_.recoveryRate()));
249 setSensitivityTemplate(*cdsBuilder);
250
251 // Check option data
252 QL_REQUIRE(option_.style() == "European", "CreditDefaultSwapOption option style must" <<
253 " be European but got " << option_.style() << ".");
254 QL_REQUIRE(!option_.payoffAtExpiry(), "CreditDefaultSwapOption payoff must be at exercise.");
255 QL_REQUIRE(option_.exerciseFees().empty(), "CreditDefaultSwapOption cannot handle exercise fees.");
256
257 // Exercise must be European
258 const auto& exerciseDates = option_.exerciseDates();
259 QL_REQUIRE(exerciseDates.size() == 1, "CreditDefaultSwapOption expects one exercise date" <<
260 " but got " << exerciseDates.size() << " exercise dates.");
261 Date exerciseDate = parseDate(exerciseDates.front());
262 QuantLib::ext::shared_ptr<Exercise> exercise = QuantLib::ext::make_shared<EuropeanExercise>(exerciseDate);
263
264 // Limit strike type to Spread for now.
266 QL_REQUIRE(strikeType == QuantExt::CdsOption::Spread, "CreditDefaultSwapOption strike type must be Spread.");
267
268 // If the strike_ is Null, the strike is taken as the running coupon.
269 Real strike = strike_ == Null<Real>() ? runningCoupon : strike_;
270
271 // Build the option instrument
272 auto cdsOption = QuantLib::ext::make_shared<QuantExt::CdsOption>(cds, exercise, knockOut_, strike, strikeType);
273
274 // Set the option engine
275 auto cdsOptionEngineBuilder = QuantLib::ext::dynamic_pointer_cast<CreditDefaultSwapOptionEngineBuilder>(
276 engineFactory->builder("CreditDefaultSwapOption"));
277 QL_REQUIRE(cdsOptionEngineBuilder, "CreditDefaultSwapOption expected CDS option engine " <<
278 " builder for underlying while building trade " << id() << ".");
279 cdsOption->setPricingEngine(cdsOptionEngineBuilder->engine(ccy, swap_.creditCurveId(), term_));
280 setSensitivityTemplate(*cdsOptionEngineBuilder);
281
282 // Set Trade members.
283 legs_ = { cds->coupons() };
285 legPayers_ = { legData.isPayer() };
286
287 // Include premium if enough information is provided
288 vector<QuantLib::ext::shared_ptr<Instrument>> additionalInstruments;
289 vector<Real> additionalMultipliers;
290 string marketConfig = cdsOptionEngineBuilder->configuration(MarketContext::pricing);
291 addPremium(engineFactory, ccy, marketConfig, additionalInstruments, additionalMultipliers);
292
293 // Instrument wrapper depends on the settlement type.
294 Position::Type positionType = parsePositionType(option_.longShort());
295 Settlement::Type settleType = parseSettlementType(option_.settlement());
296 // The instrument build should be indpednent of the evaluation date. However, the general behavior
297 // in ORE (e.g. IR swaptions) for normal pricing runs is that the option is considered expired on
298 // the expiry date with no assumptions on an (automatic) exercise. Therefore we build a vanilla
299 // instrument if the exercise date is <= the eval date at build time.
300 if (settleType == Settlement::Cash || exerciseDate <= Settings::instance().evaluationDate()) {
301 Real indicatorLongShort = positionType == Position::Long ? 1.0 : -1.0;
302 instrument_ = QuantLib::ext::make_shared<VanillaInstrument>(cdsOption, indicatorLongShort,
303 additionalInstruments, additionalMultipliers);
304 } else {
305 bool isLong = positionType == Position::Long;
306 bool isPhysical = settleType == Settlement::Physical;
307 instrument_ = QuantLib::ext::make_shared<EuropeanOptionWrapper>(cdsOption, isLong, exerciseDate,
308 isPhysical, cds, 1.0, 1.0, additionalInstruments, additionalMultipliers);
309 }
310}
311
312void CreditDefaultSwapOption::buildDefaulted(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
313
314 DLOG("CreditDefaultSwapOption: building CDS option trade " << id() << " given default occurred.");
315
316 // We add a simple payment for CDS options where the reference entity has already defaulted.
317 // If it is a knock-out CDS option, we add a dummy payment of 0.0 with date today instead of throwing.
318 const auto& legData = swap_.leg();
319 Date paymentDate = engineFactory->market()->asofDate();
320 Real amount = 0.0;
321 if (!knockOut_) {
322 paymentDate = asi_->auctionSettlementDate();
323 amount = notional_ * (1 - asi_->auctionFinalPrice());
324 // If it is a receiver option, i.e. selling protection, the FEP is paid out.
325 if (!legData.isPayer())
326 amount *= -1.0;
327 }
328
329 Position::Type positionType = parsePositionType(option_.longShort());
330 Real indicatorLongShort = positionType == Position::Long ? 1.0 : -1.0;
331
332 // Use the add premiums method to add the payment.
333 string marketConfig = Market::defaultConfiguration;
335 vector<QuantLib::ext::shared_ptr<Instrument>> additionalInstruments;
336 vector<Real> additionalMultipliers;
337 Date premiumPayDate =
338 addPremiums(additionalInstruments, additionalMultipliers, indicatorLongShort,
339 PremiumData(amount, notionalCurrency_, paymentDate), 1.0, ccy, engineFactory, marketConfig);
340 DLOG("FEP payment (date = " << paymentDate << ", amount = " << amount << ") added for CDS option " << id() << ".");
341
342 // Use the instrument added as the main instrument and clear the vectors
343 auto qlInst = additionalInstruments.back();
344 QL_REQUIRE(qlInst, "Expected a FEP payment to have been added for CDS option " << id() << ".");
345 maturity_ = std::max(paymentDate, premiumPayDate);
346 additionalInstruments.clear();
347 additionalMultipliers.clear();
348
349 // Include premium if enough information is provided
350 addPremium(engineFactory, ccy, marketConfig, additionalInstruments, additionalMultipliers);
351
352 // Instrument wrapper.
353 instrument_ = QuantLib::ext::make_shared<VanillaInstrument>(qlInst, indicatorLongShort,
354 additionalInstruments, additionalMultipliers);
355}
356
357Date CreditDefaultSwapOption::addPremium(const QuantLib::ext::shared_ptr<EngineFactory>& ef,
358 const Currency& tradeCurrency,
359 const string& marketConfig,
360 vector<QuantLib::ext::shared_ptr<Instrument>>& additionalInstruments,
361 vector<Real>& additionalMultipliers) {
362 // The premium amount is always provided as a non-negative amount. Assign the correct sign here i.e.
363 // pay the premium if long the option and receive the premium if short the option.
364 Position::Type positionType = parsePositionType(option_.longShort());
365 Real indicatorLongShort = positionType == Position::Long ? 1.0 : -1.0;
366 return addPremiums(additionalInstruments, additionalMultipliers, indicatorLongShort, option_.premiumData(),
367 indicatorLongShort, tradeCurrency, ef, marketConfig);
368}
369
370}
371}
Builder that returns an engine to price a credit default swap option.
const boost::optional< CdsReferenceInformation > & referenceInformation() const
QuantLib::Natural cashSettlementDays() const
void fromXML(XMLNode *node) override
XMLNode * toXML(XMLDocument &doc) const override
const QuantLib::Date & tradeDate() const
void buildDefaulted(const QuantLib::ext::shared_ptr< EngineFactory > &ef)
Build instrument given that default has occurred.
void build(const QuantLib::ext::shared_ptr< EngineFactory > &ef) override
const CreditDefaultSwapData & swap() const
XMLNode * toXML(XMLDocument &doc) const override
const boost::optional< AuctionSettlementInformation > & auctionSettlementInformation() const
boost::optional< AuctionSettlementInformation > asi_
Date addPremium(const QuantLib::ext::shared_ptr< EngineFactory > &ef, const QuantLib::Currency &tradeCurrency, const std::string &marketConfig, std::vector< QuantLib::ext::shared_ptr< QuantLib::Instrument > > &additionalInstruments, std::vector< QuantLib::Real > &additionalMultipliers)
Add the premium payment.
void buildNoDefault(const QuantLib::ext::shared_ptr< EngineFactory > &ef)
Build CDS option given that no default.
Serializable object holding generic trade data, reporting dimensions.
Definition: envelope.hpp:51
const vector< double > & notionals() const
Definition: legdata.hpp:875
static const string defaultConfiguration
Default configuration label.
Definition: market.hpp:296
Serializable object holding option data.
Definition: optiondata.hpp:42
const string & longShort() const
Definition: optiondata.hpp:70
const string & style() const
Definition: optiondata.hpp:74
const string & settlement() const
Definition: optiondata.hpp:81
virtual void fromXML(XMLNode *node) override
Definition: optiondata.cpp:32
virtual XMLNode * toXML(XMLDocument &doc) const override
Definition: optiondata.cpp:86
const bool & payoffAtExpiry() const
Definition: optiondata.hpp:75
const PremiumData & premiumData() const
Definition: optiondata.hpp:83
const vector< double > & exerciseFees() const
Definition: optiondata.hpp:84
const vector< string > & exerciseDates() const
Definition: optiondata.hpp:76
Serializable object holding premium data.
Definition: premiumdata.hpp:37
QuantLib::Date latestPremiumDate() const
Definition: premiumdata.cpp:28
Trade base class.
Definition: trade.hpp:55
string npvCurrency_
Definition: trade.hpp:201
std::vector< bool > legPayers_
Definition: trade.hpp:200
std::vector< string > legCurrencies_
Definition: trade.hpp:199
std::vector< QuantLib::Leg > legs_
Definition: trade.hpp:198
QuantLib::Real notional_
Definition: trade.hpp:202
virtual void fromXML(XMLNode *node) override
Definition: trade.cpp:34
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 XMLNode * toXML(XMLDocument &doc) const override
Definition: trade.cpp:46
QuantLib::ext::shared_ptr< InstrumentWrapper > instrument_
Definition: trade.hpp:197
string notionalCurrency_
Definition: trade.hpp:203
std::map< std::string, boost::any > additionalData_
Definition: trade.hpp:224
Small XML Document wrapper class.
Definition: xmlutils.hpp:65
XMLNode * allocNode(const string &nodeName)
util functions that wrap rapidxml
Definition: xmlutils.cpp:132
static void checkNode(XMLNode *n, const string &expectedName)
Definition: xmlutils.cpp:175
static Real getChildValueAsDouble(XMLNode *node, const string &name, bool mandatory=false, double defaultValue=0.0)
Definition: xmlutils.cpp:286
static string getChildValue(XMLNode *node, const string &name, bool mandatory=false, const string &defaultValue=string())
Definition: xmlutils.cpp:277
static bool getChildValueAsBool(XMLNode *node, const string &name, bool mandatory=false, bool defaultValue=true)
Definition: xmlutils.cpp:296
static XMLNode * getChildNode(XMLNode *n, const string &name="")
Definition: xmlutils.cpp:387
static string getNodeValue(XMLNode *node)
Get a node's value.
Definition: xmlutils.cpp:489
static XMLNode * addChild(XMLDocument &doc, XMLNode *n, const string &name)
Definition: xmlutils.cpp:181
static void appendNode(XMLNode *parent, XMLNode *child)
Definition: xmlutils.cpp:406
credit default swap option trade data model and serialization
CdsOption::StrikeType parseCdsOptionStrikeType(const string &s)
Definition: parsers.cpp:1240
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
Position::Type parsePositionType(const std::string &s)
Convert text to QuantLib::Position::Type.
Definition: parsers.cpp:404
BusinessDayConvention parseBusinessDayConvention(const string &s)
Convert text to QuantLib::BusinessDayConvention.
Definition: parsers.cpp:173
Settlement::Type parseSettlementType(const std::string &s)
Convert text to QuantLib::Settlement::Type.
Definition: parsers.cpp:434
DayCounter parseDayCounter(const string &s)
Convert text to QuantLib::DayCounter.
Definition: parsers.cpp:209
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
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
Schedule makeSchedule(const ScheduleDates &data)
Definition: schedule.cpp:263
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Wrapper for option instruments, tracks whether option has been exercised or not.
Map text representations to QuantLib/QuantExt types.
Reference data model and serialization.
string conversion utilities