Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
forwardbond.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2019 Quaternion Risk Management Ltd
3
4 All rights reserved.
5
6 This file is part of ORE, a free-software/open-source library
7 for transparent pricing and risk analysis - http://opensourcerisk.org
8
9 ORE is free software: you can redistribute it and/or modify it
10 under the terms of the Modified BSD License. You should have received a
11 copy of the license along with this program.
12 The license is also available online at <http://opensourcerisk.org>
13
14 This program is distributed on the basis that it will form a useful
15 contribution to risk analytics and model standardisation, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
18*/
19
29
31
32#include <ql/cashflows/simplecashflow.hpp>
33#include <ql/instruments/bond.hpp>
34#include <ql/instruments/bonds/zerocouponbond.hpp>
35#include <ql/quotes/simplequote.hpp>
36#include <ql/time/calendars/weekendsonly.hpp>
37#include <ql/time/daycounters/actual360.hpp>
38
39#include <boost/lexical_cast.hpp>
40#include <boost/make_shared.hpp>
41
42using namespace QuantLib;
43using namespace QuantExt;
44
45namespace ore {
46namespace data {
47
48void ForwardBond::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
49 DLOG("ForwardBond::build() called for trade " << id());
50
51 // ISDA taxonomy
52 additionalData_["isdaAssetClass"] = string("Interest Rate");
53 additionalData_["isdaBaseProduct"] = string("Forward");
54 additionalData_["isdaSubProduct"] = string("Debt");
55 additionalData_["isdaTransaction"] = string("");
56
57 additionalData_["currency"] = currency_;
58
59 const QuantLib::ext::shared_ptr<Market> market = engineFactory->market();
60
61 QuantLib::ext::shared_ptr<EngineBuilder> builder_fwd = engineFactory->builder("ForwardBond");
62 QuantLib::ext::shared_ptr<EngineBuilder> builder_bd = engineFactory->builder("Bond");
63
64 bondData_ = originalBondData_;
65 bondData_.populateFromBondReferenceData(engineFactory->referenceData());
66
67 npvCurrency_ = currency_ = bondData_.coupons().front().currency();
68 notionalCurrency_ = currency_;
69
70 QL_REQUIRE(!bondData_.referenceCurveId().empty(), "reference curve id required");
71 QL_REQUIRE(!bondData_.settlementDays().empty(), "settlement days required");
72
73 Date issueDate = parseDate(bondData_.issueDate());
74 Calendar calendar = parseCalendar(bondData_.calendar());
75 Natural settlementDays = boost::lexical_cast<Natural>(bondData_.settlementDays());
76
77 Date fwdMaturityDate = parseDate(fwdMaturityDate_);
78 Date fwdSettlementDate = fwdSettlementDate_.empty() ? fwdMaturityDate : parseDate(fwdSettlementDate_);
79 bool isPhysicallySettled;
80 if (settlement_ == "Physical" || settlement_.empty())
81 isPhysicallySettled = true;
82 else if (settlement_ == "Cash")
83 isPhysicallySettled = false;
84 else {
85 QL_FAIL("ForwardBond: invalid settlement '" << settlement_ << "', expected Cash or Physical");
86 }
87 Real amount = amount_.empty() ? Null<Real>() : parseReal(amount_);
88 Real lockRate = lockRate_.empty() ? Null<Real>() : parseReal(lockRate_);
89 Real dv01 = dv01_.empty() ? Null<Real>() : parseReal(dv01_);
90 DayCounter lockRateDayCounter = lockRateDayCounter_.empty() ? Actual360() : parseDayCounter(lockRateDayCounter_);
91 bool settlementDirty = settlementDirty_.empty() ? true : parseBool(settlementDirty_);
92 Real compensationPayment = parseReal(compensationPayment_);
93 Date compensationPaymentDate = parseDate(compensationPaymentDate_);
94 bool longInForward = parseBool(longInForward_);
95
96 QL_REQUIRE((amount == Null<Real>() && lockRate != Null<Real>()) ||
97 (amount != Null<Real>() && lockRate == Null<Real>()),
98 "ForwardBond: exactly one of Amount of LockRate must be given");
99 QL_REQUIRE(dv01 >= 0.0, "negative DV01 given");
100 QL_REQUIRE(compensationPaymentDate <= fwdMaturityDate, "Premium cannot be paid after forward contract maturity");
101
102 if (lockRate != Null<Real>())
103 isPhysicallySettled = false;
104
105 QL_REQUIRE(!bondData_.coupons().empty(), "ForwardBond: No LegData given. If you want to represent a zero bond, "
106 "set it up as a coupon bond with zero fixed rate");
107
108 bool firstLegIsPayer = bondData_.coupons()[0].isPayer();
109 QL_REQUIRE(firstLegIsPayer == false, "ForwardBond: The underlying bond must be entered with a receiver leg. Use "
110 "LongInBond to specify pay direction of forward payoff");
111 QL_REQUIRE(compensationPayment > 0.0 || close_enough(compensationPayment, 0.0),
112 "ForwardBond: Negative compensation payments ("
113 << compensationPayment
114 << ") are not allowed. Notice that we will ensure that a positive compensation amount will be paid "
115 "by the party being long in the forward contract.");
116
117 QuantLib::ext::shared_ptr<Payoff> payoff;
118 if (amount != Null<Real>()) {
119 payoff = longInForward ? QuantLib::ext::make_shared<QuantExt::ForwardBondTypePayoff>(Position::Long, amount)
120 : QuantLib::ext::make_shared<QuantExt::ForwardBondTypePayoff>(Position::Short, amount);
121 }
122 compensationPayment = longInForward ? compensationPayment : -compensationPayment;
123
124 std::vector<Leg> separateLegs;
125 for (Size i = 0; i < bondData_.coupons().size(); ++i) {
126 Leg leg;
127 auto configuration = builder_bd->configuration(MarketContext::pricing);
128 auto legBuilder = engineFactory->legBuilder(bondData_.coupons()[i].legType());
129 leg = legBuilder->buildLeg(bondData_.coupons()[i], engineFactory, requiredFixings_, configuration);
130 separateLegs.push_back(leg);
131 }
132 Leg leg = joinLegs(separateLegs);
133 auto bond = QuantLib::ext::make_shared<QuantLib::Bond>(settlementDays, calendar, issueDate, leg);
134
135 // cashflows will be generated as additional results in the pricing engine
136 legs_ = {};
137 legCurrencies_ = {npvCurrency_};
138 legPayers_ = {firstLegIsPayer};
139 Currency currency = parseCurrency(currency_);
140 maturity_ = bond->cashflows().back()->date();
141 notional_ = currentNotional(bond->cashflows()) * bondData_.bondNotional();
142
143 // first ctor is for vanilla fwd bonds, second for tlocks with a lock rate specifying the payoff
144 QuantLib::ext::shared_ptr<QuantLib::Instrument> fwdBond =
145 payoff ? QuantLib::ext::make_shared<QuantExt::ForwardBond>(bond, payoff, fwdMaturityDate, fwdSettlementDate,
146 isPhysicallySettled, settlementDirty, compensationPayment,
147 compensationPaymentDate, bondData_.bondNotional())
148 : QuantLib::ext::make_shared<QuantExt::ForwardBond>(bond, lockRate, lockRateDayCounter, longInForward,
149 fwdMaturityDate, fwdSettlementDate, isPhysicallySettled,
150 settlementDirty, compensationPayment,
151 compensationPaymentDate, bondData_.bondNotional(), dv01);
152
153 QuantLib::ext::shared_ptr<fwdBondEngineBuilder> fwdBondBuilder =
154 QuantLib::ext::dynamic_pointer_cast<fwdBondEngineBuilder>(builder_fwd);
155 QL_REQUIRE(fwdBondBuilder, "ForwardBond::build(): could not cast builder: " << id());
156
157 fwdBond->setPricingEngine(fwdBondBuilder->engine(id(), currency, bondData_.creditCurveId(),
158 bondData_.hasCreditRisk(), bondData_.securityId(),
159 bondData_.referenceCurveId(), bondData_.incomeCurveId()));
160 setSensitivityTemplate(*fwdBondBuilder);
161 instrument_.reset(new VanillaInstrument(fwdBond, 1.0));
162
163 additionalData_["currentNotional"] = currentNotional(bond->cashflows()) * bondData_.bondNotional();
164 additionalData_["originalNotional"] = originalNotional(bond->cashflows()) * bondData_.bondNotional();
165}
166
167void ForwardBond::fromXML(XMLNode* node) {
168 Trade::fromXML(node);
169 XMLNode* fwdBondNode = XMLUtils::getChildNode(node, "ForwardBondData");
170 QL_REQUIRE(fwdBondNode, "No ForwardBondData Node");
171 originalBondData_.fromXML(XMLUtils::getChildNode(fwdBondNode, "BondData"));
172 bondData_ = originalBondData_;
173
174 XMLNode* fwdSettlementNode = XMLUtils::getChildNode(fwdBondNode, "SettlementData");
175 QL_REQUIRE(fwdSettlementNode, "No fwdSettlementNode Node");
176
177 fwdMaturityDate_ = XMLUtils::getChildValue(fwdSettlementNode, "ForwardMaturityDate", true);
178 fwdSettlementDate_ = XMLUtils::getChildValue(fwdSettlementNode, "ForwardSettlementDate", false);
179 settlement_ = XMLUtils::getChildValue(fwdSettlementNode, "Settlement", false);
180 amount_ = XMLUtils::getChildValue(fwdSettlementNode, "Amount", false);
181 lockRate_ = XMLUtils::getChildValue(fwdSettlementNode, "LockRate", false);
182 lockRateDayCounter_ = XMLUtils::getChildValue(fwdSettlementNode, "LockRateDayCounter", false);
183 settlementDirty_ = XMLUtils::getChildValue(fwdSettlementNode, "SettlementDirty", false);
184 dv01_ = XMLUtils::getChildValue(fwdSettlementNode, "dv01", false);
185
186 XMLNode* fwdPremiumNode = XMLUtils::getChildNode(fwdBondNode, "PremiumData");
187 if (fwdPremiumNode) {
188 compensationPayment_ = XMLUtils::getChildValue(fwdPremiumNode, "Amount", true);
189 compensationPaymentDate_ = XMLUtils::getChildValue(fwdPremiumNode, "Date", true);
190 } else {
191 compensationPayment_ = "0.0";
193 }
194
195 longInForward_ = XMLUtils::getChildValue(fwdBondNode, "LongInForward", true);
196}
197
198XMLNode* ForwardBond::toXML(XMLDocument& doc) const {
199 XMLNode* node = Trade::toXML(doc);
200 XMLNode* fwdBondNode = doc.allocNode("ForwardBondData");
201 XMLUtils::appendNode(node, fwdBondNode);
202 XMLUtils::appendNode(fwdBondNode, originalBondData_.toXML(doc));
203
204 XMLNode* fwdSettlementNode = doc.allocNode("SettlementData");
205 XMLUtils::appendNode(fwdBondNode, fwdSettlementNode);
206 XMLUtils::addChild(doc, fwdSettlementNode, "ForwardMaturityDate", fwdMaturityDate_);
207 if (!fwdSettlementDate_.empty())
208 XMLUtils::addChild(doc, fwdSettlementNode, "ForwardSettlementDate", fwdSettlementDate_);
209 if (!settlement_.empty())
210 XMLUtils::addChild(doc, fwdSettlementNode, "Settlement", settlement_);
211 if (!amount_.empty())
212 XMLUtils::addChild(doc, fwdSettlementNode, "Amount", amount_);
213 if (!lockRate_.empty())
214 XMLUtils::addChild(doc, fwdSettlementNode, "LockRate", lockRate_);
215 if (!dv01_.empty())
216 XMLUtils::addChild(doc, fwdSettlementNode, "dv01", dv01_);
217 if (!lockRateDayCounter_.empty())
218 XMLUtils::addChild(doc, fwdSettlementNode, "LockRateDayCounter", lockRateDayCounter_);
219 if (!settlementDirty_.empty())
220 XMLUtils::addChild(doc, fwdSettlementNode, "SettlementDirty", settlementDirty_);
221
222 XMLNode* fwdPremiumNode = doc.allocNode("PremiumData");
223 XMLUtils::appendNode(fwdBondNode, fwdPremiumNode);
224 XMLUtils::addChild(doc, fwdPremiumNode, "Amount", compensationPayment_);
225 XMLUtils::addChild(doc, fwdPremiumNode, "Date", compensationPaymentDate_);
226
227 XMLUtils::addChild(doc, fwdBondNode, "LongInForward", longInForward_);
228
229 return node;
230}
231
232std::map<AssetClass, std::set<std::string>>
233ForwardBond::underlyingIndices(const QuantLib::ext::shared_ptr<ReferenceDataManager>& referenceDataManager) const {
234 std::map<AssetClass, std::set<std::string>> result;
235 result[AssetClass::BOND] = {bondData_.securityId()};
236 return result;
237}
238
239} // namespace data
240} // namespace ore
builder that returns an engine to price a bond instrument
Engine builder for forward bonds.
DayCounter lockRateDayCounter_
boost::optional< bool > longInForward_
virtual void build(const QuantLib::ext::shared_ptr< EngineFactory > &) override
Definition: forwardbond.cpp:48
Vanilla Instrument Wrapper.
Small XML Document wrapper class.
Definition: xmlutils.hpp:65
XMLNode * allocNode(const string &nodeName)
util functions that wrap rapidxml
Definition: xmlutils.cpp:132
Real amount_
Logic for calculating required fixing dates on legs.
Calendar parseCalendar(const string &s)
Convert text to QuantLib::Calendar.
Definition: parsers.cpp:157
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
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
DayCounter parseDayCounter(const string &s)
Convert text to QuantLib::DayCounter.
Definition: parsers.cpp:209
Map text representations to QuantLib/QuantExt types.
leg data model and serialization
Classes and functions for log message handling.
@ data
Definition: log.hpp:77
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
Calendar calendar
Definition: utilities.cpp:441
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
Real currentNotional(const Leg &leg)
Definition: legdata.cpp:2435
Size size(const ValueType &v)
Definition: value.cpp:145
Real originalNotional(const Leg &leg)
Definition: legdata.cpp:2449
Leg joinLegs(const std::vector< Leg > &legs)
Definition: legdata.cpp:2703
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Map text representations to QuantLib/QuantExt types.
Swap trade data model and serialization.