Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
shiftscenariogenerator.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2017 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/split.hpp>
24
25using namespace QuantLib;
26using namespace QuantExt;
27using namespace std;
28
30
31namespace ore {
32namespace analytics {
33
35 string keyName;
36 RiskFactorKey::KeyType keyType = key.keytype;
38 keyName = key.name;
39 else {
40 std::vector<string> tokens;
41 boost::split(tokens, key.name, boost::is_any_of("-"));
42 keyName = tokens[0];
43 }
44
45 boost::replace_all(keyName, "/", "\\/");
46
47 std::ostringstream o;
48 o << keyType << "/" << keyName;
49 return o.str();
50}
52
53 // Expect string to be possibly three pieces delimited by ":"
54 vector<string> tokens;
55 boost::split(tokens, description, boost::is_any_of(":"));
56
57 if (tokens.size() == 1 && tokens[0] == "Base") {
59
60 key1_ = RiskFactorKey();
61 indexDesc1_ = "";
62
63 key2_ = RiskFactorKey();
64 indexDesc2_ = "";
65
66 } else if (tokens.size() == 2 && (tokens[0] == "Up" || tokens[0] == "Down")) {
68
69 auto temp = deconstructFactor(tokens[1]);
70 key1_ = temp.first;
71 indexDesc1_ = temp.second;
72
73 key2_ = RiskFactorKey();
74 indexDesc2_ = "";
75
76 } else if (tokens.size() == 3 && tokens[0] == "Cross") {
78
79 auto temp = deconstructFactor(tokens[1]);
80 key1_ = temp.first;
81 indexDesc1_ = temp.second;
82
83 temp = deconstructFactor(tokens[2]);
84 key2_ = temp.first;
85 indexDesc2_ = temp.second;
86
87 } else {
88 QL_FAIL("Could not construct ScenarioDescription from string '" << description << "'");
89 }
90}
91
94 return "Base";
95 else if (type_ == ScenarioDescription::Type::Up)
96 return "Up";
97 else if (type_ == ScenarioDescription::Type::Down)
98 return "Down";
99 else if (type_ == ScenarioDescription::Type::Cross)
100 return "Cross";
101 else
102 QL_FAIL("ScenarioDescription::Type not covered");
103}
105 ostringstream o;
106 if (key1_ != RiskFactorKey()) {
107 o << key1_;
108 o << "/" << indexDesc1_;
109 return o.str();
110 }
111 return "";
112}
114 ostringstream o;
115 if (key2_ != RiskFactorKey()) {
116 o << key2_;
117 o << "/" << indexDesc2_;
118 return o.str();
119 }
120 return "";
121}
122
124 string result = factor1();
125 if (factor2() != "")
126 result += ":" + factor2();
127 return result;
128}
129
130ShiftScenarioGenerator::ShiftScenarioGenerator(const QuantLib::ext::shared_ptr<Scenario>& baseScenario,
131 const QuantLib::ext::shared_ptr<ScenarioSimMarketParameters>& simMarketData,
132 const QuantLib::ext::weak_ptr<ScenarioSimMarket>& simMarket)
133 : baseScenario_(baseScenario), simMarketData_(simMarketData), simMarket_(simMarket), counter_(0) {
134 QL_REQUIRE(baseScenario_ != NULL, "ShiftScenarioGenerator: baseScenario is null");
135 QL_REQUIRE(simMarketData_ != NULL, "ShiftScenarioGenerator: simMarketData is null");
136 scenarios_.push_back(baseScenario_);
138}
139
140QuantLib::ext::shared_ptr<Scenario> ShiftScenarioGenerator::next(const Date& d) {
141 QL_REQUIRE(counter_ < scenarios_.size(), "scenario vector size " << scenarios_.size() << " exceeded");
142 return scenarios_[counter_++];
143}
144
145ostream& operator<<(ostream& out, const ShiftScenarioGenerator::ScenarioDescription& scenarioDescription) {
146 out << scenarioDescription.typeString();
147 if (scenarioDescription.factor1() != "")
148 out << ":" << scenarioDescription.factor1();
149 if (scenarioDescription.factor2() != "")
150 out << ":" << scenarioDescription.factor2();
151 return out;
152}
153
154pair<RiskFactorKey, string> deconstructFactor(const string& factor) {
155
156 // If string is empty
157 if (factor.empty()) {
158 return make_pair(RiskFactorKey(), "");
159 }
160
161 boost::escaped_list_separator<char> sep('\\', '/', '\"');
162 boost::tokenizer<boost::escaped_list_separator<char> > tokenSplit(factor, sep);
163
164 vector<string> tokens(tokenSplit.begin(), tokenSplit.end());
165
166 // first 3 tokens are the risk factor key, the remainder are the description
167 ostringstream o;
168 if (tokens.size() > 3) {
169 o << tokens[3];
170 Size i = 4;
171 while (i < tokens.size()) {
172 o << "/" << tokens[i];
173 i++;
174 }
175 }
176
177 return make_pair(RiskFactorKey(parseRiskFactorKeyType(tokens[0]), tokens[1], parseInteger(tokens[2])), o.str());
178}
179
180string reconstructFactor(const RiskFactorKey& key, const string& desc) {
181 // If risk factor is empty
182 if (key == RiskFactorKey()) {
183 return "";
184 }
185
186 // If valid risk factor
187 return to_string(key) + "/" + desc;
188}
189
190QuantLib::ext::shared_ptr<RiskFactorKey> parseRiskFactorKey(const string& str, vector<string>& addTokens) {
191 // Use deconstructFactor to split in to pair: [key, additional token str]
192 auto p = deconstructFactor(str);
193
194 // The additional tokens
195 boost::escaped_list_separator<char> sep('\\', '/', '\"');
196 boost::tokenizer<boost::escaped_list_separator<char> > tokenSplit(p.second, sep);
197
198 vector<string> tokens(tokenSplit.begin(), tokenSplit.end());
199 addTokens = tokens;
200
201 // Return the key value
202 return QuantLib::ext::make_shared<RiskFactorKey>(p.first.keytype, p.first.name, p.first.index);
203}
204
205void ShiftScenarioGenerator::applyShift(Size j, Real shiftSize, bool up, ShiftType shiftType,
206 const vector<Time>& tenors, const vector<Real>& values,
207 const vector<Real>& times, vector<Real>& shiftedValues, bool initialise) {
208
209 QL_REQUIRE(j < tenors.size(), "index j out of range");
210 QL_REQUIRE(times.size() == values.size(), "vector size mismatch");
211 QL_REQUIRE(shiftedValues.size() == values.size(), "shifted values vector size does not match input");
212
213 Time t1 = tenors[j];
214
215 if (initialise) {
216 for (Size i = 0; i < values.size(); ++i)
217 shiftedValues[i] = values[i];
218 }
219
220 if (tenors.size() == 1) { // single shift tenor means parallel shift
221 Real w = up ? 1.0 : -1.0;
222 for (Size k = 0; k < times.size(); k++) {
223 if (shiftType == ShiftType::Absolute)
224 shiftedValues[k] += w * shiftSize;
225 else
226 shiftedValues[k] *= (1.0 + w * shiftSize);
227 }
228 } else if (j == 0) { // first shift tenor, flat extrapolation to the left
229 Time t2 = tenors[j + 1];
230 for (Size k = 0; k < times.size(); k++) {
231 Real w = 0.0;
232 if (times[k] <= t1) // full shift
233 w = 1.0;
234 else if (times[k] <= t2) // linear interpolation in t1 < times[k] < t2
235 w = (t2 - times[k]) / (t2 - t1);
236 if (!up)
237 w *= -1.0;
238 if (shiftType == ShiftType::Absolute)
239 shiftedValues[k] += w * shiftSize;
240 else
241 shiftedValues[k] *= (1.0 + w * shiftSize);
242 }
243 } else if (j == tenors.size() - 1) { // last shift tenor, flat extrapolation to the right
244 Time t0 = tenors[j - 1];
245 for (Size k = 0; k < times.size(); k++) {
246 Real w = 0.0;
247 if (times[k] >= t0 && times[k] <= t1) // linear interpolation in t0 < times[k] < t1
248 w = (times[k] - t0) / (t1 - t0);
249 else if (times[k] > t1) // full shift
250 w = 1.0;
251 if (!up)
252 w *= -1.0;
253 if (shiftType == ShiftType::Absolute)
254 shiftedValues[k] += w * shiftSize;
255 else
256 shiftedValues[k] *= (1.0 + w * shiftSize);
257 }
258 } else { // intermediate shift tenor
259 Time t0 = tenors[j - 1];
260 Time t2 = tenors[j + 1];
261 for (Size k = 0; k < times.size(); k++) {
262 Real w = 0.0;
263 if (times[k] >= t0 && times[k] <= t1) // linear interpolation in t0 < times[k] < t1
264 w = (times[k] - t0) / (t1 - t0);
265 else if (times[k] > t1 && times[k] <= t2) // linear interpolation in t1 < times[k] < t2
266 w = (t2 - times[k]) / (t2 - t1);
267 if (!up)
268 w *= -1.0;
269 if (shiftType == ShiftType::Absolute)
270 shiftedValues[k] += w * shiftSize;
271 else
272 shiftedValues[k] *= (1.0 + w * shiftSize);
273 }
274 }
275}
276
277void ShiftScenarioGenerator::applyShift(Size i, Size j, Real shiftSize, bool up, ShiftType shiftType,
278 const vector<Time>& shiftX, const vector<Time>& shiftY,
279 const vector<Time>& dataX, const vector<Time>& dataY,
280 const vector<vector<Real>>& data, vector<vector<Real>>& shiftedData,
281 bool initialise) {
282 QL_REQUIRE(shiftX.size() >= 1 && shiftY.size() >= 1, "shift vector size >= 1 required");
283 QL_REQUIRE(i < shiftX.size(), "index i out of range");
284 QL_REQUIRE(j < shiftY.size(), "index j out of range");
285
286 // initialise the shifted data
287 if (initialise) {
288 for (Size k = 0; k < dataX.size(); ++k) {
289 for (Size l = 0; l < dataY.size(); ++l)
290 shiftedData[k][l] = data[k][l];
291 }
292 }
293
294 // single shift point means parallel shift
295 if (shiftX.size() == 1 && shiftY.size() == 1) {
296 Real w = up ? 1.0 : -1.0;
297 for (Size k = 0; k < dataX.size(); ++k) {
298 for (Size l = 0; l < dataY.size(); ++l) {
299 if (shiftType == ShiftType::Absolute)
300 shiftedData[k][l] += w * shiftSize;
301 else
302 shiftedData[k][l] *= (1.0 + w * shiftSize);
303 }
304 }
305 return;
306 }
307
308 Size iMax = shiftX.size() - 1;
309 Size jMax = shiftY.size() - 1;
310 Real tx = shiftX[i];
311 Real ty = shiftY[j];
312 Real tx1 = i > 0 ? shiftX[i - 1] : QL_MAX_REAL;
313 Real ty1 = j > 0 ? shiftY[j - 1] : QL_MAX_REAL;
314 Real tx2 = i < iMax ? shiftX[i + 1] : -QL_MAX_REAL;
315 Real ty2 = j < jMax ? shiftY[j + 1] : -QL_MAX_REAL;
316
317 for (Size ix = 0; ix < dataX.size(); ++ix) {
318 Real x = dataX[ix];
319 for (Size iy = 0; iy < dataY.size(); ++iy) {
320 Real y = dataY[iy];
321 Real wx = 0.0;
322 Real wy = 0.0;
323 if (x >= tx && x <= tx2 && y >= ty && y <= ty2) {
324 wx = (tx2 - x) / (tx2 - tx);
325 wy = (ty2 - y) / (ty2 - ty);
326 } else if (x >= tx && x <= tx2 && y >= ty1 && y <= ty) {
327 wx = (tx2 - x) / (tx2 - tx);
328 wy = (y - ty1) / (ty - ty1);
329 } else if (x >= tx1 && x <= tx && y >= ty1 && y <= ty) {
330 wx = (x - tx1) / (tx - tx1);
331 wy = (y - ty1) / (ty - ty1);
332 } else if (x >= tx1 && x <= tx && y >= ty && y <= ty2) {
333 wx = (x - tx1) / (tx - tx1);
334 wy = (ty2 - y) / (ty2 - ty);
335 } else if ((x <= tx && i == 0 && y < ty && j == 0) || (x <= tx && i == 0 && y >= ty && j == jMax) ||
336 (x >= tx && i == iMax && y >= ty && j == jMax) || (x >= tx && i == iMax && y < ty && j == 0)) {
337 wx = 1.0;
338 wy = 1.0;
339 } else if (((x <= tx && i == 0) || (x >= tx && i == iMax)) && y >= ty1 && y <= ty) {
340 wx = 1.0;
341 wy = (y - ty1) / (ty - ty1);
342 } else if (((x <= tx && i == 0) || (x >= tx && i == iMax)) && y >= ty && y <= ty2) {
343 wx = 1.0;
344 wy = (ty2 - y) / (ty2 - ty);
345 } else if (x >= tx1 && x <= tx && ((y < ty && j == 0) || (y >= ty && j == jMax))) {
346 wx = (x - tx1) / (tx - tx1);
347 wy = 1.0;
348 } else if (x >= tx && x <= tx2 && ((y < ty && j == 0) || (y >= ty && j == jMax))) {
349 wx = (tx2 - x) / (tx2 - tx);
350 wy = 1.0;
351 }
352 QL_REQUIRE(wx >= 0.0 && wx <= 1.0, "wx out of range");
353 QL_REQUIRE(wy >= 0.0 && wy <= 1.0, "wy out of range");
354
355 Real w = up ? 1.0 : -1.0;
356 if (shiftType == ShiftType::Absolute)
357 shiftedData[ix][iy] += w * wx * wy * shiftSize;
358 else
359 shiftedData[ix][iy] *= (1.0 + w * wx * wy * shiftSize);
360 }
361 }
362}
363} // namespace analytics
364} // namespace ore
Data types stored in the scenario class.
Definition: scenario.hpp:48
KeyType keytype
Key type.
Definition: scenario.hpp:89
std::string name
Key name.
Definition: scenario.hpp:94
KeyType
Risk Factor types.
Definition: scenario.hpp:51
string factor2() const
Return key2 as string with text2 appended as key index description.
string factor1() const
Return key1 as string with text1 appended as key index description.
string factors() const
Return "factor1" and append ":factor2" if factor2 is not empty.
std::vector< ScenarioDescription > scenarioDescriptions_
ShiftScenarioGenerator(const QuantLib::ext::shared_ptr< Scenario > &baseScenario, const QuantLib::ext::shared_ptr< ScenarioSimMarketParameters > &simMarketData, const QuantLib::ext::weak_ptr< ScenarioSimMarket > &simMarket)
Constructor.
const QuantLib::ext::shared_ptr< Scenario > & baseScenario()
Return the base scenario, i.e. cached initial values of all relevant market points.
std::vector< QuantLib::ext::shared_ptr< Scenario > > scenarios_
QuantLib::ext::shared_ptr< Scenario > next(const Date &d) override
Scenario Generator interface.
const QuantLib::ext::shared_ptr< Scenario > baseScenario_
void applyShift(Size j, Real shiftSize, bool up, ShiftType type, const vector< Time > &shiftTimes, const vector< Real > &values, const vector< Time > &times, vector< Real > &shiftedValues, bool initialise)
Apply 1d triangular shift to 1d data such as yield curves, public to allow test suite access.
const QuantLib::ext::weak_ptr< ScenarioSimMarket > simMarket_
const QuantLib::ext::shared_ptr< ScenarioSimMarketParameters > simMarketData_
Integer parseInteger(const string &s)
data
std::ostream & operator<<(std::ostream &out, EquityReturnType t)
RiskFactorKey::KeyType parseRiskFactorKeyType(const string &str)
Definition: scenario.cpp:128
string reconstructFactor(const RiskFactorKey &key, const string &desc)
Reconstruct the string description from a risk factor key and its index description desc.
RiskFactorKey parseRiskFactorKey(const string &str)
Definition: scenario.cpp:183
pair< RiskFactorKey, string > deconstructFactor(const string &factor)
std::string to_string(const LocationInfo &l)
Shift scenario generation.
factory classes for simple scenarios