31#include <ql/cashflows/fixedratecoupon.hpp>
32#include <ql/cashflows/floatingratecoupon.hpp>
39 LOG(
"RiskParticipationAgreement::build() for id \"" <<
id() <<
"\" called.");
42 additionalData_[
"isdaAssetClass"] = string(
"Interest Rate");
43 additionalData_[
"isdaBaseProduct"] = string(
"Exotic");
44 additionalData_[
"isdaSubProduct"] = string(
"");
45 additionalData_[
"isdaTransaction"] = string(
"");
49 QL_REQUIRE(!
protectionFee_.empty(),
"protection fees must not be empty");
51 QL_REQUIRE(
underlying_.empty() || tlockData_.empty(),
52 "RiskParticipationAgreement::build(): both LegData and TreasuryLockData given in Underlying node.");
55 buildWithSwapUnderlying(engineFactory);
56 else if (!tlockData_.empty())
57 buildWithTlockUnderlying(engineFactory);
59 QL_FAIL(
"RiskParticipationAgreement::build(): Underlying node must not be empty, LegData or TreasuryLockData "
60 "required as subnode");
67void RiskParticipationAgreement::buildWithSwapUnderlying(
const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
68 npvCurrency_ = notionalCurrency_ =
underlying_.front().currency();
72 isXccy = isXccy || u.currency() != npvCurrency_;
77 "the protection fee legs must all be pay or all receive");
95 std::set<std::string> legTypes;
96 std::set<bool> legPayers;
97 bool hasCapFloors =
false, hasIborInArrears =
false;
99 QL_REQUIRE(l.legType() ==
"Fixed" || l.legType() ==
"Floating" || l.legType() ==
"Cashflow",
100 "RiskParticipationAgreement: leg type " << l.legType()
101 <<
" not supported, expected Fixed, Floating, Cashflow");
102 legTypes.insert(l.legType());
103 legPayers.insert(l.isPayer());
104 if (
auto c = QuantLib::ext::dynamic_pointer_cast<FloatingLegData>(l.concreteLegData())) {
105 hasCapFloors = hasCapFloors || !c->caps().empty();
106 hasCapFloors = hasCapFloors || !c->floors().empty();
107 hasIborInArrears = hasIborInArrears || (c->isInArrears() && !
isOvernightIndex(c->index()));
111 std::string productVariant;
113 productVariant =
"RiskParticipationAgreement_Vanilla_XCcy";
114 QL_REQUIRE(!optionData_,
"XCcy Risk Participation Agreement does not allow for OptionData");
116 (legTypes == std::set<std::string>{
"Fixed",
"Floating"} ||
117 legTypes == std::set<std::string>{
"Cashflow",
"Floating"}) &&
118 legPayers == std::set<bool>{false, true} && !hasCapFloors && !hasIborInArrears && !optionData_) {
119 productVariant =
"RiskParticipationAgreement_Vanilla";
121 productVariant =
"RiskParticipationAgreement_Structured";
126 DLOG(
"get engine builder for product variant " << productVariant);
127 auto builder = QuantLib::ext::dynamic_pointer_cast<RiskParticipationAgreementEngineBuilderBase>(
128 engineFactory->builder(productVariant));
129 QL_REQUIRE(builder,
"wrong builder, expected RiskParticipationAgreementEngineBuilderBase");
130 auto configuration = builder->configuration(MarketContext::pricing);
134 std::vector<Leg> underlyingLegs, protectionFeeLegs;
138 auto legBuilder = engineFactory->legBuilder(l.legType());
139 underlyingLegs.push_back(legBuilder->buildLeg(l, engineFactory, requiredFixings_, configuration));
142 auto leg =
buildNotionalLeg(l, underlyingLegs.back(), requiredFixings_, engineFactory->market(), configuration);
144 underlyingLegs.push_back(leg);
150 auto legBuilder = engineFactory->legBuilder(l.legType());
151 protectionFeeLegs.push_back(legBuilder->buildLeg(l, engineFactory, requiredFixings_, configuration));
152 protectionPayer.push_back(l.isPayer());
153 protectionCcys.push_back(protectionFeeLegs.back().empty() ? npvCurrency_ : l.currency());
158 QuantLib::ext::shared_ptr<QuantLib::Exercise>
exercise;
159 bool exerciseIsLong =
true;
160 std::vector<QuantLib::ext::shared_ptr<CashFlow>> vectorPremium;
164 exerciseIsLong =
parsePositionType((*optionData_).longShort()) == QuantLib::Position::Long;
165 for (
const auto& premium : (*optionData_).premiumData().premiumData()) {
167 "premium currency must be the same than the swaption legs");
168 vectorPremium.push_back(QuantLib::ext::make_shared<SimpleCashFlow>(premium.amount, premium.payDate));
174 auto qleInstr = QuantLib::ext::make_shared<QuantExt::RiskParticipationAgreement>(
181 instrument_ = QuantLib::ext::make_shared<VanillaInstrument>(qleInstr);
186 for (Size i = 0; i < underlyingLegs.size(); ++i) {
188 engineFactory->market()
190 engineFactory->configuration(MarketContext::pricing))
193 legs_ = underlyingLegs;
196 legs_.insert(legs_.end(), protectionFeeLegs.begin(), protectionFeeLegs.end());
197 legCurrencies_.insert(legCurrencies_.end(), protectionCcys.begin(), protectionCcys.end());
198 legPayers_.insert(legPayers_.end(), protectionPayer.begin(), protectionPayer.end());
202 qleInstr->setPricingEngine(builder->engine(
id(),
this));
203 setSensitivityTemplate(*builder);
207DayCounter getDayCounter(
const Leg& l) {
208 for (
auto const& c : l) {
209 if (
auto cpn = QuantLib::ext::dynamic_pointer_cast<Coupon>(c))
210 return cpn->dayCounter();
212 QL_FAIL(
"RiskParticipationAgreement: could not deduce DayCounter from underlying bond, no coupons found in "
220void RiskParticipationAgreement::buildWithTlockUnderlying(
const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
221 std::string productVariant =
"RiskParticipationAgreement_TLock";
225 tlockData_.bondData() = tlockData_.originalBondData();
226 tlockData_.bondData().populateFromBondReferenceData(engineFactory->referenceData());
228 tmp.
build(engineFactory);
229 auto bond = QuantLib::ext::dynamic_pointer_cast<QuantLib::Bond>(tmp.
instrument()->qlInstrument());
230 QL_REQUIRE(bond !=
nullptr,
"RiskParticipationAgreement: could not build tlock underlying, cast failed (internal "
231 "error that dev needs to look at)");
235 npvCurrency_ = notionalCurrency_ = tlockData_.bondData().currency();
236 notional_ = tlockData_.bondData().bondNotional();
242 "the protection fee legs must all be pay or all receive");
246 DLOG(
"get engine builder for product variant " << productVariant);
247 auto builder = QuantLib::ext::dynamic_pointer_cast<RiskParticipationAgreementEngineBuilderBase>(
248 engineFactory->builder(productVariant));
249 QL_REQUIRE(builder,
"wrong builder, expected RiskParticipationAgreementEngineBuilderBase");
250 auto configuration = builder->configuration(MarketContext::pricing);
254 bool payer = tlockData_.payer();
255 Real referenceRate = tlockData_.referenceRate();
256 DayCounter dayCounter =
257 tlockData_.dayCounter().empty() ? getDayCounter(bond->cashflows()) :
parseDayCounter(tlockData_.dayCounter());
258 Date terminationDate =
parseDate(tlockData_.terminationDate());
259 Size paymentGap = tlockData_.paymentGap();
260 Calendar paymentCalendar =
parseCalendar(tlockData_.paymentCalendar());
262 std::vector<Leg> protectionFeeLegs;
263 std::vector<bool> protectionPayer;
264 std::vector<string> protectionCcys;
266 auto legBuilder = engineFactory->legBuilder(l.legType());
267 protectionFeeLegs.push_back(legBuilder->buildLeg(l, engineFactory, requiredFixings_, configuration));
268 protectionPayer.push_back(l.isPayer());
269 protectionCcys.push_back(protectionFeeLegs.back().empty() ? npvCurrency_ : l.currency());
272 Date paymentDate = paymentCalendar.advance(terminationDate, paymentGap * Days);
273 auto qleInstr = QuantLib::ext::make_shared<QuantExt::RiskParticipationAgreementTLock>(
274 bond, notional_, payer, referenceRate, dayCounter, terminationDate, paymentDate, protectionFeeLegs,
280 instrument_ = QuantLib::ext::make_shared<VanillaInstrument>(qleInstr);
284 legs_ = {bond->cashflows()};
285 legCurrencies_ = {npvCurrency_};
286 legPayers_ = {payer};
287 legs_.insert(legs_.end(), protectionFeeLegs.begin(), protectionFeeLegs.end());
288 legCurrencies_.insert(legCurrencies_.end(), protectionCcys.begin(), protectionCcys.end());
289 legPayers_.insert(legPayers_.end(), protectionPayer.begin(), protectionPayer.end());
294 qleInstr->setPricingEngine(builder->engine(
id(),
this));
295 setSensitivityTemplate(*builder);
298void RiskParticipationAgreement::fromXML(
XMLNode* node) {
299 Trade::fromXML(node);
300 XMLNode* n = XMLUtils::getChildNode(node,
"RiskParticipationAgreementData");
301 QL_REQUIRE(n,
"RiskParticipationAgreement::fromXML(): RiskParticipationAgreementData not found");
305 creditCurveId_ = XMLUtils::getChildValue(n,
"CreditCurveId",
true);
306 issuerId_ = XMLUtils::getChildValue(n,
"IssuerId",
false);
307 settlesAccrual_ = XMLUtils::getChildValueAsBool(n,
"SettlesAccrual",
false);
311 XMLNode* u = XMLUtils::getChildNode(n,
"Underlying");
312 QL_REQUIRE(u,
"RiskParticipationAgreement::fromXML(): Underlying not found");
314 vector<XMLNode*> nodes = XMLUtils::getChildrenNodes(u,
"LegData");
315 for (
auto const n : nodes) {
321 if (
auto tmp = XMLUtils::getChildNode(u,
"OptionData")) {
323 (*optionData_).fromXML(tmp);
326 nakedOption_ = XMLUtils::getChildValueAsBool(u,
"NakedOption",
false,
false);
328 if (
auto tmp = XMLUtils::getChildNode(u,
"TreasuryLockData")) {
329 tlockData_.fromXML(tmp);
333 XMLNode* p = XMLUtils::getChildNode(n,
"ProtectionFee");
334 QL_REQUIRE(p,
"RiskParticipationAgreement::fromXML(): ProtectionFee not found");
335 vector<XMLNode*> nodes2 = XMLUtils::getChildrenNodes(p,
"LegData");
336 for (
auto const n : nodes2) {
344 XMLNode* node = Trade::toXML(doc);
346 XMLUtils::appendNode(node, n);
350 XMLUtils::addChild(doc, n,
"CreditCurveId", creditCurveId_);
351 XMLUtils::addChild(doc, n,
"IssuerId", issuerId_);
357 XMLUtils::appendNode(n, p);
358 XMLUtils::appendNode(n, u);
360 XMLUtils::appendNode(u, (*optionData_).toXML(doc));
362 XMLUtils::addChild(doc, n,
"NakedOption",
nakedOption_);
364 XMLUtils::appendNode(p, l.toXML(doc));
366 XMLUtils::appendNode(u, l.toXML(doc));
367 if (!tlockData_.empty()) {
368 XMLUtils::appendNode(u, tlockData_.toXML(doc));
builder that returns an engine to price a bond instrument
const Real fixedRecoveryRate_
const boost::shared_ptr< Exercise > & exercise() const
const std::vector< Leg > underlying_
const std::vector< Leg > protectionFee_
const Date protectionStart_
const Real participationRate_
const bool settlesAccrual_
const std::vector< std::string > & underlyingCcys() const
const Date protectionEnd_
const std::vector< bool > & underlyingPayer() const
virtual void build(const QuantLib::ext::shared_ptr< EngineFactory > &) override
Trade interface.
Serializable object holding generic trade data, reporting dimensions.
QuantLib::ext::shared_ptr< QuantLib::Exercise > exercise() const
Serializable object holding leg data.
virtual void fromXML(XMLNode *node) override
Serializable object holding option data.
void build(const QuantLib::ext::shared_ptr< ore::data::EngineFactory > &) override
const QuantLib::ext::shared_ptr< InstrumentWrapper > & instrument() const
Small XML Document wrapper class.
XMLNode * allocNode(const string &nodeName)
util functions that wrap rapidxml
Logic for calculating required fixing dates on legs.
Calendar parseCalendar(const string &s)
Convert text to QuantLib::Calendar.
bool isOvernightIndex(const string &indexName)
Return true if the indexName is that of an overnight index, otherwise false.
bool tryParseReal(const string &s, QuantLib::Real &result)
Attempt to convert text to Real.
Date parseDate(const string &s)
Convert std::string to QuantLib::Date.
Position::Type parsePositionType(const std::string &s)
Convert text to QuantLib::Position::Type.
DayCounter parseDayCounter(const string &s)
Convert text to QuantLib::DayCounter.
#define LOG(text)
Logging Macro (Level = Notice)
#define DLOG(text)
Logging Macro (Level = Debug)
Real currentNotional(const Leg &leg)
Leg buildNotionalLeg(const LegData &data, const Leg &leg, RequiredFixings &requiredFixings, const QuantLib::ext::shared_ptr< Market > &market, const std::string &configuration)
std::string to_string(const LocationInfo &l)
Serializable Credit Default Swap.
risk participation agreement data model and serialization
string conversion utilities