Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
correlationmatrix.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
19#include <boost/algorithm/string.hpp>
22#include <ql/quotes/derivedquote.hpp>
23#include <ql/quotes/simplequote.hpp>
24
25using namespace QuantLib;
26using namespace std;
27
28namespace {
29
30string invertFx(const string& ccyPair) {
31 QL_REQUIRE(ccyPair.size() == 6, "invertFx: Expected currency pair to be 6 characters but got: " << ccyPair);
32 return ccyPair.substr(3, 3) + ccyPair.substr(0, 3);
33}
34
35}
36
37namespace ore {
38namespace data {
39
41
42bool operator<(const CorrelationFactor& lhs, const CorrelationFactor& rhs) {
43 return tie(lhs.type, lhs.name, lhs.index) < tie(rhs.type, rhs.name, rhs.index);
44}
45
46bool operator==(const CorrelationFactor& lhs, const CorrelationFactor& rhs) {
47 return lhs.type == rhs.type && lhs.name == rhs.name && lhs.index == rhs.index;
48}
49
50bool operator!=(const CorrelationFactor& lhs, const CorrelationFactor& rhs) {
51 return !(lhs == rhs);
52}
53
54ostream& operator<<(ostream& out, const CorrelationFactor& f) {
55 return out << f.type << ":" << f.name << ":" << f.index;
56}
57
58CorrelationFactor parseCorrelationFactor(const string& name, const char separator) {
59
60 std::string sep(1, separator);
61 vector<string> tokens;
62 boost::split(tokens, name, boost::is_any_of(sep));
63
64 QL_REQUIRE(tokens.size() == 2 || tokens.size() == 3,
65 "parseCorrelationFactor(" << name << "): expected 2 or 3 tokens separated by separator ('" << sep
66 << "'), e.g. 'IR" << sep << "USD' or 'INF" << sep << "UKRPI" << sep << "0'");
67
68 return {parseCamAssetType(tokens[0]), tokens[1],
69 static_cast<Size>(tokens.size() == 3 ? parseInteger(tokens[3]) : 0)};
70}
71
73 corrs_.clear();
74}
75
76void CorrelationMatrixBuilder::addCorrelation(const string& factor1, const string& factor2, Real correlation) {
79 addCorrelation(f_1, f_2, Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(correlation)));
80}
81
82void CorrelationMatrixBuilder::addCorrelation(const string& factor1, const string& factor2,
83 const Handle<Quote>& correlation) {
86 addCorrelation(f_1, f_2, correlation);
87}
88
89void CorrelationMatrixBuilder::addCorrelation(const CorrelationFactor& f_1,
90 const CorrelationFactor& f_2, Real correlation) {
91 addCorrelation(f_1, f_2, Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(correlation)));
92}
93
94void CorrelationMatrixBuilder::addCorrelation(const CorrelationFactor& f_1, const CorrelationFactor& f_2,
95 const Handle<Quote>& correlation) {
96
97 // Check the factors
98 checkFactor(f_1);
99 checkFactor(f_2);
100
101 // Store the correlation.
102 CorrelationKey ck = createKey(f_1, f_2);
103 QL_REQUIRE(correlation->value() >= -1.0 && correlation->value() <= 1.0, "Correlation value, " <<
104 correlation->value() << ", for key [" << ck.first << "," << ck.second << "] should be in [-1.0,1.0]");
105 corrs_[ck] = correlation;
106 DLOG("Added correlation: (" << f_1 << "," << f_2 << ") = " << correlation->value() << ".");
107}
108
109Matrix CorrelationMatrixBuilder::correlationMatrix(const vector<string>& ccys) {
111 return correlationMatrix(pi);
112}
113
114Matrix CorrelationMatrixBuilder::correlationMatrix(const vector<string>& ccys,
115 const vector<string>& infIndices) {
116 ProcessInfo pi = createProcessInfo(ccys, infIndices);
117 return correlationMatrix(pi);
118}
119
120Matrix CorrelationMatrixBuilder::correlationMatrix(const vector<string>& ccys,
121 const vector<string>& infIndices, const vector<string>& names) {
122 ProcessInfo pi = createProcessInfo(ccys, infIndices, names);
123 return correlationMatrix(pi);
124}
125
126Matrix CorrelationMatrixBuilder::correlationMatrix(const vector<string>& ccys,
127 const vector<string>& infIndices, const vector<string>& names, const vector<string>& equities) {
128 ProcessInfo pi = createProcessInfo(ccys, infIndices, names, equities);
129 return correlationMatrix(pi);
130}
131
133
134 // Get the dimension of the matrix to create and create a list of factors.
135 Size dim = 0;
136 vector<CorrelationFactor> factors;
137 for (const auto& kv : processInfo) {
138 for (const auto& p : kv.second) {
139
140 // Don't allow multiple factors for FX for now. Need to check later the FX inversion in the lookup below
141 // if we want to extend the builder to multiple factors for each FX process.
142 if (kv.first == CrossAssetModel::AssetType::FX) {
143 QL_REQUIRE(p.second == 1, "CorrelationMatrixBuilder does not support multiple factors for FX. " <<
144 p.first << " is set up with " << p.second << " factors.");
145 }
146
147 dim += p.second;
148 for (Size i = 0; i < p.second; ++i) {
149 factors.push_back({ kv.first, p.first, i });
150 }
151 }
152 }
153
154 // Start with the identity matrix
155 Matrix corr(dim, dim, 0.0);
156 for (Size i = 0; i < dim; i++)
157 corr[i][i] = 1.0;
158
159 // Populate all of the off-diagonal elements
160 for (Size i = 0; i < dim; ++i) {
161 for (Size j = 0; j < i; ++j) {
162 corr[i][j] = corr[j][i] = getCorrelation(factors[i], factors[j])->value();
163 }
164 }
165
166 return corr;
167}
168
170 const vector<string>& ccys,
171 const std::vector<std::string>& inflationIndices,
172 const std::vector<std::string>& creditNames,
173 const std::vector<std::string>& equityNames) {
174
175 // Check the currencies.
176 QL_REQUIRE(!ccys.empty(), "At least one currency required to build correlation matrix");
177 for (const string& ccy : ccys) {
178 QL_REQUIRE(ccy.size() == 3, "Invalid currency code " << ccy);
179 }
180
181 // Hold the resulting process information.
182 // Supporting a legacy method, assumed that there is 1 factor per process.
183 ProcessInfo result;
184
185 // Add process information for each currency.
186 for (const string& ccy : ccys) {
187 result[CrossAssetModel::AssetType::IR].emplace_back(ccy, 1);
188 }
189
190 // Add process information for each FX pair.
191 for (Size i = 1; i < ccys.size(); ++i) {
192 string ccyPair = ccys[i] + ccys[0];
193 result[CrossAssetModel::AssetType::FX].emplace_back(ccyPair, 1);
194 }
195
196 // Add process information for inflation indices.
197 for (const string& inflationIndex : inflationIndices) {
198 result[CrossAssetModel::AssetType::INF].emplace_back(inflationIndex, 1);
199 }
200
201 // Add process information for credit names.
202 for (const string& creditName : creditNames) {
203 result[CrossAssetModel::AssetType::CR].emplace_back(creditName, 1);
204 }
205
206 // Add process information for equity names.
207 for (const string& equityName : equityNames) {
208 result[CrossAssetModel::AssetType::EQ].emplace_back(equityName, 1);
209 }
210
211 return result;
212}
213
215 switch (f.type) {
216 case CrossAssetModel::AssetType::IR:
217 QL_REQUIRE(f.name.size() == 3, "Expected IR factor name to be 3 character currency code but got: " << f.name);
218 break;
219 case CrossAssetModel::AssetType::FX:
220 QL_REQUIRE(f.name.size() == 6, "Expected FX factor name to be 6 character currency pair but got: " << f.name);
221 break;
222 case CrossAssetModel::AssetType::INF:
223 case CrossAssetModel::AssetType::CR:
224 case CrossAssetModel::AssetType::EQ:
225 case CrossAssetModel::AssetType::COM:
226 case CrossAssetModel::AssetType::CrState:
227 QL_REQUIRE(!f.name.empty(), "Expected non-empty factor name for factor type " << f.type);
228 break;
229 default:
230 QL_FAIL("Did not recognise factor type " << static_cast<int>(f.type) << ".");
231 }
232}
233
235 const CorrelationFactor& f_2) const {
236
237 QL_REQUIRE(f_1 != f_2, "Correlation factors must be unique: " << f_1 << ".");
238
239 if (f_1 < f_2)
240 return make_pair(f_1, f_2);
241 else
242 return make_pair(f_2, f_1);
243}
244
245Handle<Quote> CorrelationMatrixBuilder::lookup(const string& f1, const string& f2) {
248 return getCorrelation(f_1, f_2);
249}
250
251//! Get the correlation between the factor \p f_1 and \p f_2.
253 const CorrelationFactor& f_2) const {
254
255 // Create key with the correct order for lookup
256 CorrelationKey ck = createKey(f_1, f_2);
257
258 // If we have the correlation via direct lookup, return it.
259 if (corrs_.find(ck) != corrs_.end())
260 return corrs_.at(ck);
261
262 // If one or both of the factors are FX, we may still be able to generate a correlation by using the inverse of
263 // a provided FX quote. Note that we have FX restricted to 1 factor in this class above.
264
265 typedef DerivedQuote<negate<Real>> InvQuote;
266
267 // If factor 1 is FX
268 if (f_1.type == CrossAssetModel::AssetType::FX) {
269 CorrelationFactor if_1{ CrossAssetModel::AssetType::FX, invertFx(f_1.name), f_1.index };
270 ck = createKey(if_1, f_2);
271 auto it = corrs_.find(ck);
272 if (it != corrs_.end())
273 return Handle<Quote>(QuantLib::ext::make_shared<InvQuote>(it->second, negate<Real>()));
274 }
275
276 // If factor 2 is FX
277 if (f_2.type == CrossAssetModel::AssetType::FX) {
278 CorrelationFactor if_2{ CrossAssetModel::AssetType::FX, invertFx(f_2.name), f_2.index };
279 ck = createKey(f_1, if_2);
280 auto it = corrs_.find(ck);
281 if (it != corrs_.end())
282 return Handle<Quote>(QuantLib::ext::make_shared<InvQuote>(it->second, negate<Real>()));
283 }
284
285 // If factor 1 and factor 2 are both FX
286 if (f_1.type == CrossAssetModel::AssetType::FX && f_2.type == CrossAssetModel::AssetType::FX) {
287 CorrelationFactor if_1{ CrossAssetModel::AssetType::FX, invertFx(f_1.name), f_1.index };
288 CorrelationFactor if_2{ CrossAssetModel::AssetType::FX, invertFx(f_2.name), f_2.index };
289 ck = createKey(if_1, if_2);
290 auto it = corrs_.find(ck);
291 if (it != corrs_.end())
292 return it->second;
293 }
294
295 // If we still haven't found anything, return a correlation of 0.
296 return Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.0));
297}
298
299const map<CorrelationKey, Handle<Quote>>& CorrelationMatrixBuilder::correlations() {
300 return corrs_;
301}
302
303
304} // namespace data
305} // namespace ore
QuantLib::Matrix correlationMatrix(const std::vector< std::string > &ccys)
std::map< CorrelationKey, QuantLib::Handle< QuantLib::Quote > > corrs_
Store the correlation between two factors.
void addCorrelation(const std::string &factor1, const std::string &factor2, QuantLib::Real correlation)
QuantLib::Handle< QuantLib::Quote > lookup(const std::string &f1, const std::string &f2)
Get the correlation between two factors.
QuantLib::Handle< QuantLib::Quote > getCorrelation(const CorrelationFactor &f_1, const CorrelationFactor &f_2) const
Get the correlation between the factor f_1 and f_2.
CorrelationKey createKey(const CorrelationFactor &f_1, const CorrelationFactor &f_2) const
ProcessInfo createProcessInfo(const std::vector< std::string > &ccys, const std::vector< std::string > &inflationIndices={}, const std::vector< std::string > &creditNames={}, const std::vector< std::string > &equityNames={})
void checkFactor(const CorrelationFactor &f) const
Perform some basic checks on the factor names.
QuantLib::Matrix correlationMatrix(const std::vector< std::string > &ccys, const std::vector< std::string > &infIndices, const std::vector< std::string > &names, const std::vector< std::string > &equities)
std::map< QuantExt::CrossAssetModel::AssetType, std::vector< std::pair< std::string, QuantLib::Size > > > ProcessInfo
const std::map< CorrelationKey, QuantLib::Handle< QuantLib::Quote > > & correlations()
Get the raw correlation data.
configuration class for building correlation matrices
QuantExt::CrossAssetModel::AssetType parseCamAssetType(const string &s)
Definition: parsers.cpp:839
Integer parseInteger(const string &s)
Convert text to QuantLib::Integer.
Definition: parsers.cpp:136
@ data
Definition: log.hpp:77
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
bool operator<(const Dividend &d1, const Dividend &d2)
bool operator!=(const Filter &a, const Filter &b)
std::ostream & operator<<(std::ostream &out, EquityReturnType t)
bool operator==(const Dividend &d1, const Dividend &d)
std::pair< CorrelationFactor, CorrelationFactor > CorrelationKey
CorrelationFactor parseCorrelationFactor(const string &name, const char separator)
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Map text representations to QuantLib/QuantExt types.
QuantExt::CrossAssetModel::AssetType type
string name