Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
market.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2016 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
25
31
32#include <ql/quotes/compositequote.hpp>
33#include <ql/time/daycounters/actualactual.hpp>
34
35namespace ore {
36namespace data {
37
38const string Market::defaultConfiguration = "default";
39const string Market::inCcyConfiguration = "inccy";
40
41namespace {
42bool hasPseudoCurrencyConfig(const string& code) {
43 QL_REQUIRE(code.size() == 3, "Invalid currency code \"" << code << "\" for hasPseudoCurrencyConfig()");
44 const PseudoCurrencyMarketParameters& params = GlobalPseudoCurrencyMarketParameters::instance().get();
45 return params.curves.find(code) != params.curves.end();
46}
47
48bool hasPseudoCurrencyConfigPair(const string& pair) {
49 QL_REQUIRE(pair.size() == 6, "Invalid currency pair \"" << pair << "\" for isPseudoCurrencyConfigPair()");
50 return hasPseudoCurrencyConfig(pair.substr(0, 3)) || hasPseudoCurrencyConfig(pair.substr(3));
51}
52} // namespace
53
56
57 // default
58 params.treatAsFX = true;
59
60 auto it = pegp.find("PseudoCurrency.TreatAsFX");
61 if (it != pegp.end()) {
62 DLOG("Building PseudoCurrencyMarketParameters from PricingEngine GlobalParameters");
63 try {
64 // Build the params
65 params.treatAsFX = parseBool(it->second);
66 it = pegp.find("PseudoCurrency.BaseCurrency");
67 QL_REQUIRE(it != pegp.end(), "No BaseCurrency field");
68 params.baseCurrency = it->second;
69 parseCurrency(params.baseCurrency); // to check it is valid
70
71 // Now just search for the 4 precious metals and 7 crypto currencies
72 for (const string& pm : CurrencyParser::instance().pseudoCurrencyCodes()) {
73 it = pegp.find("PseudoCurrency.Curve." + pm);
74 if (it != pegp.end())
75 params.curves[pm] = it->second;
76 }
77 QL_REQUIRE(!params.curves.empty(), "At least one PM Curve required");
78
79 // Look for fxIndexTag
80 it = pegp.find("PseudoCurrency.FXIndexTag");
81 QL_REQUIRE(it != pegp.end(), "No FXIndexTag field");
82 params.fxIndexTag = it->second;
83
84 // Look for Optional default correlation
85 it = pegp.find("PseudoCurrency.DefaultCorrelation");
86 if (it == pegp.end()) {
87 LOG("No Default Correlation present");
88 params.defaultCorrelation = Null<Real>();
89 } else {
90 LOG("Default Correlation is \"" << it->second << "\"");
91 params.defaultCorrelation = parseReal(it->second);
92 QL_REQUIRE(-1.0 <= params.defaultCorrelation && params.defaultCorrelation <= 1.0,
93 "Invalid DefaultCorrelation value " << it->second);
94 }
95 } catch (std::exception& e) {
96 QL_FAIL("Failed to build PseudoCurrencyMarketParameters : " << e.what());
97 }
98 } else {
99 DLOG("Building default PseudoCurrencyMarketParameters");
100 }
101 DLOG(params);
102
103 return params;
104}
105
106std::ostream& operator<<(std::ostream& os, const struct PseudoCurrencyMarketParameters& p) {
107 // we don't need to write everything out, this is enough for debugging most things
108 return os << "PseudoCurrencyMarketParameters { "
109 << "TreatAsFX:" << (p.treatAsFX ? "True" : "False") << ", BaseCurrency:" << p.baseCurrency << "}";
110}
111
112string Market::commodityCurveLookup(const string& pm) const {
113 QL_REQUIRE(handlePseudoCurrencies_, "Market::commodityCurveLookup() disabled - this is an internal error.");
114 auto it = GlobalPseudoCurrencyMarketParameters::instance().get().curves.find(pm);
115 QL_REQUIRE(it != GlobalPseudoCurrencyMarketParameters::instance().get().curves.end(),
116 "Unable to find a commodity curve for pseudo currency " << pm << " in Market");
117 return it->second;
118}
119
120Handle<Quote> Market::getFxBaseQuote(const string& ccy, const string& config) const {
121 QL_REQUIRE(handlePseudoCurrencies_, "Market::commodityCurveLookup() disabled - this is an internal error.");
122 if (hasPseudoCurrencyConfig(ccy)) {
123 auto priceCurve = commodityPriceCurve(commodityCurveLookup(ccy), config);
124 QL_REQUIRE(!priceCurve.empty(),
125 "Failed to get Commodity Price curve for " << ccy << " using " << commodityCurveLookup(ccy));
126 TLOG("PseudoCurrencyMarket building DerivedPriceQuote for "
127 << ccy << "/" << GlobalPseudoCurrencyMarketParameters::instance().get().baseCurrency
128 << " with curve that has minTime of " << priceCurve->minTime());
129 return Handle<Quote>(QuantLib::ext::make_shared<QuantExt::DerivedPriceQuote>(priceCurve));
130 } else {
131 return fxRateImpl(ccy + GlobalPseudoCurrencyMarketParameters::instance().get().baseCurrency, config);
132 }
133}
134
135Handle<Quote> Market::getFxSpotBaseQuote(const string& ccy, const string& config) const {
136 QL_REQUIRE(handlePseudoCurrencies_, "Market::commodityCurveLookup() disabled - this is an internal error.");
137 if (hasPseudoCurrencyConfig(ccy)) {
138 // TODO: this gives back the commodity rate at t=0, should be at spot
139 auto priceCurve = commodityPriceCurve(commodityCurveLookup(ccy), config);
140 QL_REQUIRE(!priceCurve.empty(),
141 "Failed to get Commodity Price curve for " << ccy << " using " << commodityCurveLookup(ccy));
142 TLOG("PseudoCurrencyMarket building DerivedPriceQuote for "
143 << ccy << "/" << GlobalPseudoCurrencyMarketParameters::instance().get().baseCurrency
144 << " with curve that has minTime of " << priceCurve->minTime());
145 return Handle<Quote>(QuantLib::ext::make_shared<QuantExt::DerivedPriceQuote>(priceCurve));
146 } else {
147 return fxSpotImpl(ccy + GlobalPseudoCurrencyMarketParameters::instance().get().baseCurrency, config);
148 }
149}
150
151QuantLib::Handle<QuantExt::FxIndex> Market::fxIndex(const string& fxIndex, const string& configuration) const {
152 if (!handlePseudoCurrencies_ || GlobalPseudoCurrencyMarketParameters::instance().get().treatAsFX)
153 return fxIndexImpl(fxIndex, configuration);
154
155 std::string familyName;
156 std::string forCcy;
157 std::string domCcy;
158
159 if (isFxIndex(fxIndex)) {
160 auto ind = parseFxIndex(fxIndex);
161 familyName = ind->familyName();
162 forCcy = ind->sourceCurrency().code();
163 domCcy = ind->targetCurrency().code();
164 } else {
165 familyName = "GENERIC";
166 forCcy = fxIndex.substr(0, 3);
167 domCcy = fxIndex.substr(3);
168 }
169
170 if (hasPseudoCurrencyConfigPair(forCcy + domCcy)) {
171 DLOG("Market::fxIndex() requested for PM pair " << forCcy << domCcy);
172 string index = "FX-" + familyName + "-" + forCcy + "-" + domCcy;
173 Handle<QuantExt::FxIndex> fxInd;
174 auto it = fxIndicesCache_.find(make_pair(configuration, index));
175 // if no index found we build it
176 if (it == fxIndicesCache_.end()) {
177 // Parse the index we have with no term structures
178 QuantLib::ext::shared_ptr<QuantExt::FxIndex> fxIndexBase = parseFxIndex(index);
179
180 string source = fxIndexBase->sourceCurrency().code();
181 string target = fxIndexBase->targetCurrency().code();
182
183 // use todays rate here
184 Handle<Quote> spot = fxRate(source + target, configuration);
185
186 Handle<YieldTermStructure> sorTS = discountCurve(source, configuration);
187 Handle<YieldTermStructure> tarTS = discountCurve(target, configuration);
188
189 // spot is always zero here as we use the fxRates, which give rate today
190 Natural spotDays = 0;
191 Calendar calendar = NullCalendar();
192
193 if (source != target) {
194 try {
195 auto [sDays, cal, _] = getFxIndexConventions(fxIndex);
196 calendar = cal;
197 } catch (...) {
198 WLOG("Market::fxIndex Cannot find commodity conventions for " << fxIndex);
199 }
200 }
201 fxInd = Handle<QuantExt::FxIndex>(QuantLib::ext::make_shared<QuantExt::FxIndex>(
202 fxIndexBase->familyName(), spotDays, fxIndexBase->sourceCurrency(), fxIndexBase->targetCurrency(),
203 calendar, spot, sorTS, tarTS));
204 // add it to the cache
205 fxIndicesCache_[make_pair(configuration, index)] = fxInd;
206 } else {
207 fxInd = it->second;
208 }
209 return fxInd;
210 }
211 return fxIndexImpl(fxIndex, configuration);
212}
213
214// These calls are intercepted
215
216Handle<Quote> Market::fxRate(const string& pair, const string& config) const {
217 if (!handlePseudoCurrencies_ || GlobalPseudoCurrencyMarketParameters::instance().get().treatAsFX)
218 return fxRateImpl(pair, config);
219 if (hasPseudoCurrencyConfigPair(pair)) {
220 DLOG("Market::fxSpot() requested for PM pair " << pair);
221 if (fxRateCache_.find(pair) == fxRateCache_.end()) {
222 // Get the FX Spot rate. Rather than deal with all the combinations we just get the FX rate for each vs
223 // the baseCcy and create a ratio quote. This might mean we have combined a USD/USD spot quote below,
224 // but it all works fine and this is cleaner code.
225 //
226 // Note that we could just call market->fxSpot(forCode, domCode) and this would give us the correct
227 // quote from the markets FXTriangulation, however this would create a depedendacy on the pair
228 // and cause the configuration builder to go off building XAU-IN-USD and the like.
229 auto forBaseSpot = getFxBaseQuote(pair.substr(0, 3), config);
230 auto domBaseSpot = getFxBaseQuote(pair.substr(3), config);
231 auto fx = Handle<Quote>(QuantLib::ext::make_shared<CompositeQuote<std::function<Real(Real, Real)>>>(
232 forBaseSpot, domBaseSpot, [](Real a, Real b) { return b > 0.0 ? a / b : 0.0; }));
233 DLOG("Market returning " << fx->value() << " for " << pair << ".");
234 fxRateCache_[pair] = fx;
235 }
236 return fxRateCache_[pair];
237 }
238 return fxRateImpl(pair, config);
239}
240
241Handle<Quote> Market::fxSpot(const string& pair, const string& config) const {
242 if (!handlePseudoCurrencies_ || GlobalPseudoCurrencyMarketParameters::instance().get().treatAsFX)
243 return fxSpotImpl(pair, config);
244 if (hasPseudoCurrencyConfigPair(pair)) {
245 DLOG("Market::fxSpot() requested for PM pair " << pair);
246 if (spotCache_.find(pair) == spotCache_.end()) {
247 // Get the FX Spot rate. Rather than deal with all the combinations we just get the FX rate for each vs
248 // the baseCcy and create a ratio quote. This might mean we have combined a USD/USD spot quote below,
249 // but it all works fine and this is cleaner code.
250 //
251 // Note that we could just call market->fxSpot(forCode, domCode) and this would give us the correct
252 // quote from the markets FXTriangulation, however this would create a depedendacy on the pair
253 // and cause the configuration builder to go off building XAU-IN-USD and the like.
254 auto forBaseSpot = getFxSpotBaseQuote(pair.substr(0, 3), config);
255 auto domBaseSpot = getFxSpotBaseQuote(pair.substr(3), config);
256 auto fx = Handle<Quote>(QuantLib::ext::make_shared<CompositeQuote<std::function<Real(Real, Real)>>>(
257 forBaseSpot, domBaseSpot, [](Real a, Real b) { return b > 0.0 ? a / b : 0.0; }));
258 DLOG("Market returning " << fx->value() << " for " << pair << ".");
259 spotCache_[pair] = fx;
260 }
261 return spotCache_[pair];
262 }
263 return fxSpotImpl(pair, config);
264}
265
266Handle<BlackVolTermStructure> Market::getVolatility(const string& ccy, const string& config) const {
267 QL_REQUIRE(handlePseudoCurrencies_, "Market::getVolatility() disabled - this is an internal error.");
268 if (hasPseudoCurrencyConfig(ccy)) {
269 return commodityVolatility(commodityCurveLookup(ccy), config);
270 } else {
271 return fxVolImpl(ccy + GlobalPseudoCurrencyMarketParameters::instance().get().baseCurrency, config);
272 }
273}
274
275string Market::getCorrelationIndexName(const string& ccy) const {
276 QL_REQUIRE(handlePseudoCurrencies_, "Market::getCorrelationIndexName() disabled - this is an internal error.");
277 if (hasPseudoCurrencyConfig(ccy)) {
278 // e.g. COMM-PM:XAUUSD
279 return "COMM-" + commodityCurveLookup(ccy);
280 } else {
281 // e.g. FX-GENERIC-XAU-USD
282 return "FX-" + GlobalPseudoCurrencyMarketParameters::instance().get().fxIndexTag + "-" + ccy + "-" +
283 GlobalPseudoCurrencyMarketParameters::instance().get().baseCurrency;
284 }
285}
286
287Handle<BlackVolTermStructure> Market::fxVol(const string& pair, const string& config) const {
288 if (!handlePseudoCurrencies_ || GlobalPseudoCurrencyMarketParameters::instance().get().treatAsFX)
289 return fxVolImpl(pair, config);
290
291 if (hasPseudoCurrencyConfigPair(pair)) {
292 DLOG("Market::fxVol() requested for PM pair " << pair);
293 if (volCache_.find(pair) == volCache_.end()) {
294
295 Handle<BlackVolTermStructure> vol;
296
297 auto forCode = pair.substr(0, 3);
298 auto domCode = pair.substr(3);
299
300 // we handle the easy and common case first.
301 if (forCode == GlobalPseudoCurrencyMarketParameters::instance().get().baseCurrency ||
302 domCode == GlobalPseudoCurrencyMarketParameters::instance().get().baseCurrency) {
303 // this is a straight mapping
304 string pm =
305 forCode == GlobalPseudoCurrencyMarketParameters::instance().get().baseCurrency ? domCode : forCode;
306 auto comVol = commodityVolatility(commodityCurveLookup(pm), config);
307 if (domCode == GlobalPseudoCurrencyMarketParameters::instance().get().baseCurrency)
308 vol = comVol;
309 else
310 vol = Handle<BlackVolTermStructure>(
311 QuantLib::ext::make_shared<QuantExt::BlackInvertedVolTermStructure>(comVol));
312 } else {
313
314 // otherwise we must triangulate - we get both surfaces vs baseCcy
315 auto forBaseVol = getVolatility(forCode, config);
316 auto domBaseVol = getVolatility(domCode, config);
317
318 // get the correlation
319 string forIndex = getCorrelationIndexName(forCode);
320 string domIndex = getCorrelationIndexName(domCode);
321 Handle<QuantExt::CorrelationTermStructure> rho;
322 try {
323 rho = correlationCurve(forIndex, domIndex, config);
324 } catch (std::exception& e) {
325 // No correlation, if we have a default we use it
326 WLOG("No correlation found for " << forIndex << "/" << domIndex);
327 if (GlobalPseudoCurrencyMarketParameters::instance().get().defaultCorrelation != Null<Real>()) {
328 WLOG("Using default correlation value "
329 << GlobalPseudoCurrencyMarketParameters::instance().get().defaultCorrelation);
330 rho = Handle<QuantExt::CorrelationTermStructure>(QuantLib::ext::make_shared<QuantExt::FlatCorrelation>(
331 asofDate(), GlobalPseudoCurrencyMarketParameters::instance().get().defaultCorrelation,
332 ActualActual(ActualActual::ISDA)));
333 } else {
334 QL_FAIL("No Correlation which is needed for PseudoCurrency Volatility :" << e.what());
335 }
336 }
337
338 // build and return triangulation
339 vol = Handle<BlackVolTermStructure>(
340 QuantLib::ext::make_shared<QuantExt::BlackTriangulationATMVolTermStructure>(forBaseVol, domBaseVol, rho));
341 }
342
343 DLOG("Market returning vol surface for " << pair << ".");
344 volCache_[pair] = vol;
345 }
346 return volCache_[pair];
347 }
348 return fxVolImpl(pair, config);
349}
350
351Handle<YieldTermStructure> Market::discountCurve(const string& ccy, const string& config) const {
352 if (!handlePseudoCurrencies_ || GlobalPseudoCurrencyMarketParameters::instance().get().treatAsFX)
353 return discountCurveImpl(ccy, config);
354
355 string baseCcy = GlobalPseudoCurrencyMarketParameters::instance().get().baseCurrency;
356
357 if (hasPseudoCurrencyConfig(ccy)) {
358 DLOG("Market::discount() requested for PM " << ccy);
359 if (discountCurveCache_.find(ccy) == discountCurveCache_.end()) {
360 auto baseDiscount = discountCurveImpl(baseCcy, config);
361 auto priceCurve = commodityPriceCurve(commodityCurveLookup(ccy), config);
362 QL_REQUIRE(!priceCurve.empty(),
363 "Failed to get Commodity Price curve for " << ccy << " using " << commodityCurveLookup(ccy));
365 Handle<YieldTermStructure>(QuantLib::ext::make_shared<QuantExt::PriceTermStructureAdapter>(
366 priceCurve.currentLink(), baseDiscount.currentLink(), fxRate(ccy + baseCcy, config)));
367 discountCurveCache_[ccy]->enableExtrapolation();
368 }
369 return discountCurveCache_[ccy];
370 }
371
372 return discountCurveImpl(ccy, config);
373}
374
375} // namespace data
376} // namespace ore
Handle< Quote > fxSpot(const string &ccypair, const string &configuration=Market::defaultConfiguration) const
Definition: market.cpp:241
static const string inCcyConfiguration
InCcy configuration label.
Definition: market.hpp:299
virtual QuantLib::Handle< QuantLib::BlackVolTermStructure > commodityVolatility(const std::string &commodityName, const std::string &configuration=Market::defaultConfiguration) const =0
std::map< std::pair< string, string >, QuantLib::Handle< QuantExt::FxIndex > > fxIndicesCache_
Definition: market.hpp:351
std::map< string, Handle< BlackVolTermStructure > > volCache_
Definition: market.hpp:347
std::map< string, Handle< YieldTermStructure > > discountCurveCache_
Definition: market.hpp:348
Handle< Quote > fxRate(const string &ccypair, const string &configuration=Market::defaultConfiguration) const
Definition: market.cpp:216
bool handlePseudoCurrencies_
Definition: market.hpp:342
virtual QuantLib::Handle< QuantExt::PriceTermStructure > commodityPriceCurve(const std::string &commodityName, const std::string &configuration=Market::defaultConfiguration) const =0
std::map< string, Handle< Quote > > fxRateCache_
Definition: market.hpp:350
Handle< BlackVolTermStructure > fxVol(const string &ccypair, const string &configuration=Market::defaultConfiguration) const
Definition: market.cpp:287
Handle< BlackVolTermStructure > getVolatility(const string &ccy, const string &config) const
Definition: market.cpp:266
virtual Handle< YieldTermStructure > discountCurveImpl(const string &ccy, const string &configuration=Market::defaultConfiguration) const =0
virtual QuantLib::Handle< QuantExt::CorrelationTermStructure > correlationCurve(const std::string &index1, const std::string &index2, const std::string &configuration=Market::defaultConfiguration) const =0
string commodityCurveLookup(const string &pm) const
Definition: market.cpp:112
QuantLib::Handle< QuantExt::FxIndex > fxIndex(const string &fxIndex, const string &configuration=Market::defaultConfiguration) const
Definition: market.cpp:151
static const string defaultConfiguration
Default configuration label.
Definition: market.hpp:296
virtual Date asofDate() const =0
Get the asof Date.
virtual Handle< Quote > fxSpotImpl(const string &ccypair, const string &configuration=Market::defaultConfiguration) const =0
virtual QuantLib::Handle< QuantExt::FxIndex > fxIndexImpl(const string &fxIndex, const string &configuration=Market::defaultConfiguration) const =0
virtual Handle< Quote > fxRateImpl(const string &ccypair, const string &configuration=Market::defaultConfiguration) const =0
Handle< Quote > getFxSpotBaseQuote(const string &ccy, const string &config) const
Definition: market.cpp:135
string getCorrelationIndexName(const string &ccy) const
Definition: market.cpp:275
Handle< Quote > getFxBaseQuote(const string &ccy, const string &config) const
Definition: market.cpp:120
virtual Handle< BlackVolTermStructure > fxVolImpl(const string &ccypair, const string &configuration=Market::defaultConfiguration) const =0
std::map< string, Handle< Quote > > spotCache_
Definition: market.hpp:346
Handle< YieldTermStructure > discountCurve(const string &ccy, const string &configuration=Market::defaultConfiguration) const
Definition: market.cpp:351
currency parser singleton class
QuantLib::ext::shared_ptr< FxIndex > parseFxIndex(const string &s, const Handle< Quote > &fxSpot, const Handle< YieldTermStructure > &sourceYts, const Handle< YieldTermStructure > &targetYts, const bool useConventions)
Convert std::string to QuantExt::FxIndex.
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
Map text representations to QuantLib/QuantExt types.
Classes and functions for log message handling.
@ data
Definition: log.hpp:77
#define LOG(text)
Logging Macro (Level = Notice)
Definition: log.hpp:552
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
#define WLOG(text)
Logging Macro (Level = Warning)
Definition: log.hpp:550
#define TLOG(text)
Logging Macro (Level = Data)
Definition: log.hpp:556
Base Market class.
market data related utilties
Calendar calendar
Definition: utilities.cpp:441
std::ostream & operator<<(std::ostream &out, EquityReturnType t)
bool isFxIndex(const std::string &indexName)
PseudoCurrencyMarketParameters buildPseudoCurrencyMarketParameters(const std::map< string, string > &pegp)
Function to build parameters from PricingEngine GlobalParametrs.
Definition: market.cpp:54
std::tuple< Natural, Calendar, BusinessDayConvention > getFxIndexConventions(const string &index)
Definition: marketdata.cpp:160
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Map text representations to QuantLib/QuantExt types.
Struct to store parameters for commodities to be treatred as pseudo currencies.
Definition: market.hpp:91
std::map< string, string > curves
Definition: market.hpp:94