Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
convertiblebond.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2020 Quaternion Risk Management Ltd
3
4 This file is part of ORE, a free-software/open-source library
5 for transparent pricing and risk analysis - http://opensourcerisk.org
6
7 ORE is free software: you can redistribute it and/or modify it
8 under the terms of the Modified BSD License. You should have received a
9 copy of the license along with this program.
10 The license is also available online at <http://opensourcerisk.org>
11
12 This program is distributed on the basis that it will form a useful
13 contribution to risk analytics and model standardisation, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
16*/
17
21
25
32
33#include <ql/math/functional.hpp>
34#include <ql/quotes/derivedquote.hpp>
35#include <ql/termstructures/credit/flathazardrate.hpp>
36#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
37#include <ql/termstructures/yield/flatforward.hpp>
38
39namespace ore {
40namespace data {
41
42using namespace ore::data;
43using namespace QuantLib;
44using namespace QuantExt;
45
47 const std::string& id, const std::string& ccy, const std::string& creditCurveId, const bool hasCreditRisk,
48 const std::string& securityId, const std::string& referenceCurveId, const bool isExchangeable,
49 QuantLib::ext::shared_ptr<QuantExt::EquityIndex2> equity, const QuantLib::ext::shared_ptr<QuantExt::FxIndex>& fx,
50 const std::string& equityCreditCurveId, const QuantLib::Date& startDate, const QuantLib::Date& maturityDate) {
51 return id;
52}
53
54QuantLib::ext::shared_ptr<QuantLib::PricingEngine> ConvertibleBondFDDefaultableEquityJumpDiffusionEngineBuilder::engineImpl(
55 const std::string& id, const std::string& ccy, const std::string& creditCurveId, const bool hasCreditRisk,
56 const std::string& securityId, const std::string& referenceCurveId, const bool isExchangeable,
57 QuantLib::ext::shared_ptr<QuantExt::EquityIndex2> equity, const QuantLib::ext::shared_ptr<QuantExt::FxIndex>& fx,
58 const std::string& equityCreditCurveId, const QuantLib::Date& startDate, const QuantLib::Date& maturityDate) {
59
60 std::string config = this->configuration(ore::data::MarketContext::pricing);
61
62 // get pricing engine config
63
64 Real p = parseReal(modelParameter("p", {}, true));
65 Real eta = parseReal(modelParameter("eta", {}, true));
66 bool staticMesher = parseBool(engineParameter("MesherIsStatic", {}, true));
67 Size modelTimeStepsPerYear = parseInteger(engineParameter("Bootstrap.TimeStepsPerYear", {}, true));
68 Size modelStateGridPoints = parseInteger(engineParameter("Bootstrap.StateGridPoints", {}, true));
69 Real modelMesherEpsilon = parseReal(engineParameter("Bootstrap.MesherEpsilon", {}, true));
70 Real modelMesherScaling = parseReal(engineParameter("Bootstrap.MesherScaling", {}, true));
71 auto modelMesherConcentrationStr = engineParameter("Bootstrap.MesherConcentration", {}, false, "");
72 Real modelMesherConcentration = Null<Real>();
73 if (!modelMesherConcentrationStr.empty())
74 modelMesherConcentration = parseReal(modelMesherConcentrationStr);
75 auto bootstrapModeStr = engineParameter("Bootstrap.Mode", {}, true);
77 if (bootstrapModeStr == "Alternating") {
78 bootstrapMode = DefaultableEquityJumpDiffusionModelBuilder::BootstrapMode::Alternating;
79 } else if (bootstrapModeStr == "Simultaneously") {
80 bootstrapMode = DefaultableEquityJumpDiffusionModelBuilder::BootstrapMode::Simultaneously;
81 } else {
82 QL_FAIL("invalid Bootstrap.Mode, expected Alternating or Simultaneously");
83 }
84 Size engineTimeStepsPerYear = parseInteger(engineParameter("Pricing.TimeStepsPerYear", {}, true));
85 Size engineStateGridPoints = parseInteger(engineParameter("Pricing.StateGridPoints", {}, true));
86 Real engineMesherEpsilon = parseReal(engineParameter("Pricing.MesherEpsilon", {}, true));
87 Real engineMesherScaling = parseReal(engineParameter("Pricing.MesherScaling", {}, true));
88 std::vector<Real> conversionRatioDiscretisationGrid =
89 parseListOfValues<Real>(engineParameter("Pricing.ConversionRatioDiscretisationGrid", {}, true), &parseReal);
90
91 // technical parameters, we might want to review these an replace by less technical settings
92
93 bool adjustDiscounting = parseBool(modelParameter("AdjustDiscounting", {}, false, "true"));
94 bool adjustEquityVolatility = parseBool(modelParameter("AdjustEquityVolatility", {}, false, "true"));
95 bool adjustEquityForward = parseBool(modelParameter("AdjustEquityForward", {}, false, "true"));
96 bool adjustCreditSpreadToRR = parseBool(modelParameter("AdjustCreditSpreadToRR", {}, false, "true"));
97 bool zeroRecoveryOverwrite = parseBool(modelParameter("ZeroRecoveryOverwrite", {}, false, "false"));
98 bool treatSecuritySpreadAsCreditSpread =
99 parseBool(modelParameter("TreatSecuritySpreadAsCreditSpread", {}, false, "false"));
100
101 // get equity curve and volatility
102
103 std::string equityName;
104 Handle<BlackVolTermStructure> volatility;
105
106 if (equity != nullptr) {
107 equityName = equity->name();
108 volatility = market_->equityVol(equityName, config);
109 } else {
110 // create dummy equity and zero volatility if either of these are left empty (this is used for fixed amount
111 // conversion)
112 equity = QuantLib::ext::make_shared<QuantExt::EquityIndex2>(
113 "dummyFamily", NullCalendar(), fx == nullptr ? parseCurrency(ccy) : fx->sourceCurrency(),
114 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(1.0)),
115 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.0, Actual365Fixed())),
116 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.0, Actual365Fixed())));
117
118 volatility = Handle<BlackVolTermStructure>(QuantLib::ext::make_shared<BlackConstantVol>(
119 0, NullCalendar(), Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.0)), Actual365Fixed()));
120 }
121
122 // get bond related curves
123
124 Handle<YieldTermStructure> referenceCurve;
125 if (adjustDiscounting)
126 referenceCurve = market_->yieldCurve(referenceCurveId, config);
127
128 Handle<DefaultProbabilityTermStructure> creditCurve;
129 Real creditCurveRecovery = 0.0;
130 if (!creditCurveId.empty() && hasCreditRisk) {
131 creditCurve = securitySpecificCreditCurve(market_, securityId, creditCurveId, config)->curve();
132 creditCurveRecovery = market_->recoveryRate(creditCurveId, config)->value();
133 } else {
134 if (!creditCurveId.empty()) {
135 securitySpecificCreditCurve(market_, securityId, creditCurveId, config);
136 market_->recoveryRate(creditCurveId, config)->value();
137 }
138 creditCurve = Handle<DefaultProbabilityTermStructure>(
139 QuantLib::ext::make_shared<FlatHazardRate>(0, NullCalendar(), 0.0, Actual365Fixed()));
140 }
141
142 // get (bond) recovery rate, fallback is the recovery rate of the credit curve
143
144 Handle<Quote> recovery;
145 if (!zeroRecoveryOverwrite) {
146 try {
147 recovery = market_->recoveryRate(securityId, config);
148 } catch (...) {
149 if (!creditCurveId.empty() && hasCreditRisk) {
150 recovery = market_->recoveryRate(creditCurveId, config);
151 } else if (!creditCurveId.empty()) {
152 market_->recoveryRate(creditCurveId, config);
153 }
154 }
155 }
156
157 // get security spread
158
159 Handle<Quote> spread;
160 if (adjustDiscounting || treatSecuritySpreadAsCreditSpread) {
161 try {
162 spread = market_->securitySpread(securityId, config);
163 } catch (...) {
164 spread = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.0));
165 }
166 }
167
168 // for exchangeables get equity credit curve
169
170 Handle<DefaultProbabilityTermStructure> equityCreditCurve;
171 Real equityCreditCurveRecovery = 0.0;
172 if (isExchangeable) {
173 if (!equityCreditCurveId.empty() && hasCreditRisk) {
174 equityCreditCurve = market_->defaultCurve(equityCreditCurveId, config)->curve();
175 equityCreditCurveRecovery = market_->recoveryRate(equityCreditCurveId, config)->value();
176 } else {
177 if (!equityCreditCurveId.empty()) {
178 market_->defaultCurve(equityCreditCurveId, config);
179 market_->recoveryRate(equityCreditCurveId, config)->value();
180 }
181 equityCreditCurve = Handle<DefaultProbabilityTermStructure>(
182 QuantLib::ext::make_shared<FlatHazardRate>(0, NullCalendar(), 0.0, Actual365Fixed()));
183 }
184 }
185
186 // if adjust credit spread to recovery rate is enabled, build the adjusted curves
187
188 if (adjustCreditSpreadToRR) {
189 Real rr = recovery.empty() ? 0.0 : recovery->value();
190 creditCurve = Handle<DefaultProbabilityTermStructure>(QuantLib::ext::make_shared<AdjustedDefaultCurve>(
191 creditCurve, Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>((1.0 - creditCurveRecovery) / (1.0 - rr)))));
192 if (!equityCreditCurve.empty()) {
193 equityCreditCurve = Handle<DefaultProbabilityTermStructure>(QuantLib::ext::make_shared<AdjustedDefaultCurve>(
194 equityCreditCurve,
195 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>((1.0 - equityCreditCurveRecovery) / (1.0 - rr)))));
196 }
197 }
198
199 // if treat security spread as credit spread is active, add the (scaled) security spread to the curves
200
201 if (treatSecuritySpreadAsCreditSpread) {
202 Real rr = recovery.empty() ? 0.0 : recovery->value();
203 auto m = [rr](Real x) { return x / ( 1.0 -rr); };
204 Handle<Quote> scaledSecuritySpread(QuantLib::ext::make_shared<DerivedQuote<decltype(m)>>(spread, m));
205 creditCurve = Handle<DefaultProbabilityTermStructure>(
206 QuantLib::ext::make_shared<HazardSpreadedDefaultTermStructure>(creditCurve, scaledSecuritySpread));
207 if (!equityCreditCurve.empty()) {
208 equityCreditCurve = Handle<DefaultProbabilityTermStructure>(
209 QuantLib::ext::make_shared<HazardSpreadedDefaultTermStructure>(equityCreditCurve, scaledSecuritySpread));
210 }
211 }
212
213 // for cross currency, set up converted equity index and eq vol
214
215 if (fx != nullptr) {
216 auto fxVol = market_->fxVol(equity->currency().code() + ccy, config);
217 QuantLib::Handle<QuantExt::CorrelationTermStructure> corrCurve(
218 QuantLib::ext::make_shared<FlatCorrelation>(0, NullCalendar(), 0.0, Actual365Fixed()));
219 try {
220 corrCurve = market_->correlationCurve("FX-GENERIC-" + equity->currency().code() + "-" + ccy,
221 "EQ-" + equity->name());
222 } catch (...) {
223 WLOG("correlation curve for FX-GENERIC-" + equity->currency().code()
224 << ", EQ-" << equity->name() << " not found, fall back to zero correlation.");
225 }
226 equity = QuantLib::ext::make_shared<CompoEquityIndex>(equity, fx, startDate);
227 volatility = Handle<BlackVolTermStructure>(QuantLib::ext::make_shared<BlackTriangulationATMVolTermStructure>(
228 volatility, fxVol, corrCurve, parseBool(engineParameter("FxVolIsStatic", {}, false, "false"))));
229 }
230
231 // set up calibration grid
232
233 std::vector<Date> calibrationDates = DateGrid(engineParameter("Bootstrap.CalibrationGrid", {}, true)).dates();
234 std::vector<Real> calibrationTimes;
235 Date today = Settings::instance().evaluationDate();
236 for (Size i = 0; i < calibrationDates.size(); ++i) {
237 if (calibrationDates[i] < maturityDate) {
238 calibrationTimes.push_back(!volatility.empty() ? volatility->timeFromReference(calibrationDates[i])
239 : Actual365Fixed().yearFraction(today, calibrationDates[i]));
240 }
241 }
242 calibrationTimes.push_back(!volatility.empty() ? volatility->timeFromReference(maturityDate)
243 : Actual365Fixed().yearFraction(today, maturityDate));
244
245 // read global parameters
246
247 bool calibrate = true;
248 auto calParam = globalParameters_.find("Calibrate");
249 if (calParam != globalParameters_.end()) {
250 calibrate = parseBool(calParam->second);
251 }
252
253 bool generateAdditionalResults = false;
254 auto genAddParam = globalParameters_.find("GenerateAdditionalResults");
255 if (genAddParam != globalParameters_.end()) {
256 generateAdditionalResults = parseBool(genAddParam->second);
257 }
258
259 // set up model and pricing engine
260
261 auto modelBuilder = QuantLib::ext::make_shared<DefaultableEquityJumpDiffusionModelBuilder>(
262 calibrationTimes, equity, volatility, isExchangeable ? equityCreditCurve : creditCurve, p, eta, staticMesher,
263 modelTimeStepsPerYear, modelStateGridPoints, modelMesherEpsilon, modelMesherScaling, modelMesherConcentration,
264 bootstrapMode, false, calibrate, adjustEquityVolatility, adjustEquityForward);
265
266 modelBuilders_.insert(std::make_pair(id, modelBuilder));
267
268 return QuantLib::ext::make_shared<FdDefaultableEquityJumpDiffusionConvertibleBondEngine>(
269 modelBuilder->model(), referenceCurve, treatSecuritySpreadAsCreditSpread ? Handle<Quote>() : spread,
270 isExchangeable ? creditCurve : Handle<DefaultProbabilityTermStructure>(), recovery, Handle<FxIndex>(fx),
271 staticMesher, engineTimeStepsPerYear, engineStateGridPoints, engineMesherEpsilon, engineMesherScaling,
272 conversionRatioDiscretisationGrid, generateAdditionalResults);
273}
274
275} // namespace data
276} // namespace ore
std::string keyImpl(const std::string &id, const std::string &ccy, const std::string &creditCurveId, const bool hasCreditRisk, const std::string &securityId, const std::string &referenceCurveId, const bool isExchangeable, QuantLib::ext::shared_ptr< QuantExt::EquityIndex2 > equity, const QuantLib::ext::shared_ptr< QuantExt::FxIndex > &fx, const std::string &equityCreditCurveId, const QuantLib::Date &startDate, const QuantLib::Date &maturityDate) override
QuantLib::ext::shared_ptr< QuantExt::PricingEngine > engineImpl(const std::string &id, const std::string &ccy, const std::string &creditCurveId, const bool hasCreditRisk, const std::string &securityId, const std::string &referenceCurveId, const bool isExchangeable, QuantLib::ext::shared_ptr< QuantExt::EquityIndex2 > equity, const QuantLib::ext::shared_ptr< QuantExt::FxIndex > &fx, const std::string &equityCreditCurveId, const QuantLib::Date &startDate, const QuantLib::Date &maturityDate) override
Simulation Date Grid.
Definition: dategrid.hpp:38
const std::vector< QuantLib::Date > & dates() const
Definition: dategrid.hpp:81
QuantLib::ext::shared_ptr< Market > market_
std::string modelParameter(const std::string &p, const std::vector< std::string > &qualifiers={}, const bool mandatory=true, const std::string &defaultValue="") const
std::string engineParameter(const std::string &p, const std::vector< std::string > &qualifiers={}, const bool mandatory=true, const std::string &defaultValue="") const
const string & configuration(const MarketContext &key)
Return a configuration (or the default one if key not found)
set< std::pair< string, QuantLib::ext::shared_ptr< QuantExt::ModelBuilder > > > modelBuilders_
std::map< std::string, std::string > globalParameters_
The date grid class.
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
Integer parseInteger(const string &s)
Convert text to QuantLib::Integer.
Definition: parsers.cpp:136
leg data model and serialization
@ data
Definition: log.hpp:77
#define WLOG(text)
Logging Macro (Level = Warning)
Definition: log.hpp:550
market data related utilties
QuantLib::Handle< QuantExt::CreditCurve > securitySpecificCreditCurve(const QuantLib::ext::shared_ptr< Market > &market, const std::string &securityId, const std::string &creditCurveId, const std::string &configuration)
Definition: marketdata.cpp:98
Serializable Credit Default Swap.
Definition: namespaces.docs:23