Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
cube_io.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2023 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 <orea/cube/cube_io.hpp>
21
23
24#include <boost/filesystem.hpp>
25#include <boost/iostreams/device/file_descriptor.hpp>
26#ifdef ORE_USE_ZLIB
27#include <boost/iostreams/filter/gzip.hpp>
28#endif
29#include <boost/iostreams/filtering_stream.hpp>
30
31#include <iomanip>
32#include <regex>
33
34namespace ore {
35namespace analytics {
36
37namespace {
38
39bool use_compression(const std::string& filename) {
40#ifdef ORE_USE_ZLIB
41 // assume compression for all filenames that do not end with csv or txt
42
43 std::string extension = boost::filesystem::path(filename).extension().string();
44 return extension != ".csv" && extension != ".txt";
45#else
46 return false;
47#endif
48}
49
50std::string getMetaData(const std::string& line, const std::string& tag, const bool mandatory = true) {
51
52 // assuming a fixed width format "# tag : <value>"
53
54 QL_REQUIRE(!mandatory || line.substr(0, 1) == "#",
55 "internal error: getMetaData(" << line << ", " << tag << "): line does not start with #");
56 QL_REQUIRE(!mandatory || line.substr(2, tag.size()) == tag,
57 "internal error: getMetaData(" << line << ", " << tag << ") failed, tag is not matched.");
58 return line.substr(0, 1) == "#" && line.substr(2, tag.size()) == tag ? line.substr(15) : std::string();
59}
60
61} // namespace
62
63NPVCubeWithMetaData loadCube(const std::string& filename, const bool doublePrecision) {
64
66
67 // open file
68
69 bool gzip = use_compression(filename);
70 std::ifstream in1(filename, gzip ? (std::ios::binary | std::ios::in) : std::ios::in);
71 boost::iostreams::filtering_stream<boost::iostreams::input> in;
72#ifdef ORE_USE_ZLIB
73 if (gzip)
74 in.push(boost::iostreams::gzip_decompressor());
75#endif
76 in.push(in1);
77
78 // read meta data
79
80 std::string line;
81
82 std::getline(in, line);
83 QuantLib::Date asof = ore::data::parseDate(getMetaData(line, "asof"));
84 std::getline(in, line);
85 Size numIds = ore::data::parseInteger(getMetaData(line, "numIds"));
86 std::getline(in, line);
87 Size numDates = ore::data::parseInteger(getMetaData(line, "numDates"));
88 std::getline(in, line);
89 Size samples = ore::data::parseInteger(getMetaData(line, "samples"));
90 std::getline(in, line);
91 Size depth = ore::data::parseInteger(getMetaData(line, "depth"));
92
93 std::getline(in, line);
94 getMetaData(line, "dates");
95 std::vector<QuantLib::Date> dates;
96 for (Size i = 0; i < numDates; ++i) {
97 std::getline(in, line);
98 dates.push_back(ore::data::parseDate(line.substr(2)));
99 }
100
101 std::getline(in, line);
102 getMetaData(line, "ids");
103 std::set<std::string> ids;
104 for (Size i = 0; i < numIds; ++i) {
105 std::getline(in, line);
106 ids.insert(line.substr(2));
107 }
108
109 std::getline(in, line);
110 if (std::string md = getMetaData(line, "scenGenDta", false); !md.empty()) {
111 result.scenarioGeneratorData = QuantLib::ext::make_shared<ScenarioGeneratorData>();
112 result.scenarioGeneratorData->fromXMLString(md);
113 std::getline(in, line);
114 DLOG("overwrite scenario generator data with meta data from cube: " << md);
115 }
116
117 if (std::string md = getMetaData(line, "storeFlows", false); !md.empty()) {
118 result.storeFlows = parseBool(md);
119 std::getline(in, line);
120 DLOG("overwrite storeFlows with meta data from cube: " << md);
121 }
122
123 if (std::string md = getMetaData(line, "storeCrSt", false); !md.empty()) {
125 std::getline(in, line);
126 DLOG("overwrite storeCreditStateNPVs with meta data from cube: " << md);
127 }
128
129 QuantLib::ext::shared_ptr<NPVCube> cube;
130 if (doublePrecision && depth <= 1) {
131 cube = QuantLib::ext::make_shared<DoublePrecisionInMemoryCube>(asof, ids, dates, samples, 0.0);
132 } else if (doublePrecision && depth > 1) {
133 cube = QuantLib::ext::make_shared<DoublePrecisionInMemoryCubeN>(asof, ids, dates, samples, depth, 0.0);
134 } else if (!doublePrecision && depth <= 1) {
135 cube = QuantLib::ext::make_shared<SinglePrecisionInMemoryCube>(asof, ids, dates, samples, 0.0f);
136 } else if (!doublePrecision && depth > 1) {
137 cube = QuantLib::ext::make_shared<SinglePrecisionInMemoryCubeN>(asof, ids, dates, samples, depth, 0.0f);
138 }
139 result.cube = cube;
140
141 vector<string> tokens;
142 Size nData = 0;
143 while (!in.eof()) {
144 std::getline(in, line);
145 if (line.empty())
146 continue;
147 boost::split(tokens, line, [](char c) { return c == ','; });
148 QL_REQUIRE(tokens.size() == 5, "loadCube(): invalid data line '" << line << "', expected 5 tokens");
149 Size id = ore::data::parseInteger(tokens[0]);
150 Size date = ore::data::parseInteger(tokens[1]);
151 Size sample = ore::data::parseInteger(tokens[2]);
152 Size depth = ore::data::parseInteger(tokens[3]);
153 double value = ore::data::parseReal(tokens[4]);
154 if (date == 0)
155 cube->setT0(value, id, depth);
156 else
157 cube->set(value, id, date - 1, sample, depth);
158 ++nData;
159 }
160
161 LOG("loaded cube from " << filename << ": asof = " << asof << ", dim = " << numIds << " x " << numDates << " x "
162 << samples << " x " << depth << ", " << nData << " data lines read.");
163
164 return result;
165}
166
167void saveCube(const std::string& filename, const NPVCubeWithMetaData& cube, const bool doublePrecision) {
168
169 // open file
170
171 bool gzip = use_compression(filename);
172 std::ofstream out1(filename, gzip ? (std::ios::binary | std::ios::out) : std::ios::out);
173 boost::iostreams::filtering_stream<boost::iostreams::output> out;
174#ifdef ORE_USE_ZLIB
175 if (gzip)
176 out.push(boost::iostreams::gzip_compressor(/*boost::iostreams::gzip_params(9)*/));
177#endif
178 out.push(out1);
179
180 // write meta data (tag width is hardcoded and used in getMetaData())
181
182 out << "# asof : " << ore::data::to_string(cube.cube->asof()) << "\n";
183 out << "# numIds : " << std::to_string(cube.cube->numIds()) << "\n";
184 out << "# numDates : " << std::to_string(cube.cube->numDates()) << "\n";
185 out << "# samples : " << ore::data::to_string(cube.cube->samples()) << "\n";
186 out << "# depth : " << ore::data::to_string(cube.cube->depth()) << "\n";
187 out << "# dates : \n";
188 for (auto const& d : cube.cube->dates())
189 out << "# " << ore::data::to_string(d) << "\n";
190
191 out << "# ids : \n";
192 std::map<Size, std::string> ids;
193 for (auto const& d : cube.cube->idsAndIndexes()) {
194 ids[d.second] = d.first;
195 }
196 for (auto const& d : ids) {
197 out << "# " << d.second << "\n";
198 }
199
200 if (cube.scenarioGeneratorData) {
201 std::string scenGenDataXml =
202 std::regex_replace(cube.scenarioGeneratorData->toXMLString(), std::regex("\\r\\n|\\r|\\n|\\t"), "");
203 out << "# scenGenDta : " << scenGenDataXml << "\n";
204 }
205 if (cube.storeFlows) {
206 out << "# storeFlows : " << std::boolalpha << *cube.storeFlows << "\n";
207 }
208 if (cube.storeCreditStateNPVs) {
209 out << "# storeCrSt : " << *cube.storeCreditStateNPVs << "\n";
210 }
211
212 // set precision
213
214 out << std::setprecision(doublePrecision ? std::numeric_limits<double>::max_digits10
215 : std::numeric_limits<float>::max_digits10);
216
217 // write cube data
218
219 out << "#id,date,sample,depth,value\n";
220 for (Size i = 0; i < cube.cube->numIds(); ++i) {
221 out << i << ",0,0,0," << cube.cube->getT0(i) << "\n";
222 for (Size j = 0; j < cube.cube->numDates(); ++j) {
223 for (Size k = 0; k < cube.cube->samples(); ++k) {
224 for (Size d = 0; d < cube.cube->depth(); ++d) {
225 double value = cube.cube->get(i, j, k, d);
226 if (value != 0.0)
227 out << i << "," << (j + 1) << "," << k << "," << d << "," << value << "\n";
228 }
229 }
230 }
231 }
232}
233
234QuantLib::ext::shared_ptr<AggregationScenarioData> loadAggregationScenarioData(const std::string& filename) {
235
236 // open file
237
238 bool gzip = use_compression(filename);
239 std::ifstream in1(filename, gzip ? (std::ios::binary | std::ios::in) : std::ios::in);
240 boost::iostreams::filtering_stream<boost::iostreams::input> in;
241#ifdef ORE_USE_ZLIB
242 if (gzip)
243 in.push(boost::iostreams::gzip_decompressor());
244#endif
245 in.push(in1);
246
247 // read meta data
248
249 std::string line;
250
251 std::getline(in, line);
252 Size dimDates = ore::data::parseInteger(getMetaData(line, "dimDates"));
253 std::getline(in, line);
254 Size dimSamples = ore::data::parseInteger(getMetaData(line, "dimSamples"));
255
256 vector<string> tokens;
257
258 std::getline(in, line);
259 Size numKeys = ore::data::parseInteger(getMetaData(line, "keys"));
260 std::vector<std::pair<AggregationScenarioDataType, std::string>> keys;
261 for (Size i = 0; i < numKeys; ++i) {
262 std::getline(in, line);
263 boost::split(tokens, line.substr(2), [](char c) { return c == ','; });
264 QL_REQUIRE(tokens.size() == 2,
265 "loadAggregationScenarioData(): invalid data line '" << line << "', expected 2 tokens");
266 keys.push_back(std::make_pair(AggregationScenarioDataType(ore::data::parseInteger(tokens[0])), tokens[1]));
267 }
268
269 std::getline(in, line); // header line for data
270
271 QuantLib::ext::shared_ptr<InMemoryAggregationScenarioData> result =
272 QuantLib::ext::make_shared<InMemoryAggregationScenarioData>(dimDates, dimSamples);
273
274 std::getline(in, line); // header line for data
275
276 Size nData = 0;
277 while (!in.eof()) {
278 std::getline(in, line);
279 if (line.empty())
280 continue;
281 boost::split(tokens, line, [](char c) { return c == ','; });
282 QL_REQUIRE(tokens.size() == 4,
283 "loadAggregationScenarioData(): invalid data line '" << line << "', expected 4 tokens");
284 Size date = ore::data::parseInteger(tokens[0]);
285 Size sample = ore::data::parseInteger(tokens[1]);
286 Size key = ore::data::parseInteger(tokens[2]);
287 double value = ore::data::parseReal(tokens[3]);
288 QL_REQUIRE(key < keys.size(), "loadAggregationScenarioData(): invalid data line '" << line << "', key (" << key
289 << ") is out of range 0..."
290 << (keys.size() - 1));
291 result->set(date - 1, sample, value, keys[key].first, keys[key].second);
292 ++nData;
293 }
294
295 LOG("loaded aggregation scenario data from " << filename << ": dimDates = " << dimDates
296 << ", dimSamples = " << dimSamples << ", keys = " << keys.size()
297 << ", " << nData << " data lines read.");
298
299 return result;
300}
301
302void saveAggregationScenarioData(const std::string& filename, const AggregationScenarioData& cube) {
303
304 // open file
305
306 bool gzip = use_compression(filename);
307 std::ofstream out1(filename, gzip ? (std::ios::binary | std::ios::out) : std::ios::out);
308 boost::iostreams::filtering_stream<boost::iostreams::output> out;
309#ifdef ORE_USE_ZLIB
310 if (gzip)
311 out.push(boost::iostreams::gzip_compressor(/*boost::iostreams::gzip_params(9)*/));
312#endif
313 out.push(out1);
314
315 // write meta data (tag width is hardcoded and used in getMetaData())
316
317 out << "# dimDates : " << std::to_string(cube.dimDates()) << "\n";
318 out << "# dimSamples : " << std::to_string(cube.dimSamples()) << "\n";
319
320 auto keys = cube.keys();
321
322 out << "# keys : " << std::to_string(keys.size()) << "\n";
323 for (auto const& k : keys) {
324 out << "# " << (unsigned int)k.first << "," << k.second << "\n";
325 }
326
327 // set precision
328
329 out << std::setprecision(std::numeric_limits<double>::max_digits10);
330
331 // write data
332
333 out << "#date,sample,key,value\n";
334 for (Size i = 0; i < cube.dimDates(); ++i) {
335 for (Size j = 0; j < cube.dimSamples(); ++j) {
336 for (Size k = 0; k < keys.size(); ++k) {
337 out << (i + 1) << "," << j << "," << k << "," << cube.get(i, j, keys[k].first, keys[k].second) << "\n";
338 }
339 }
340 }
341}
342
343} // namespace analytics
344} // namespace ore
Container for storing simulated market data.
virtual Real get(Size dateIndex, Size sampleIndex, const AggregationScenarioDataType &type, const string &qualifier="") const =0
Get a value from the cube.
virtual Size dimDates() const =0
Return the length of each dimension.
virtual Size dimSamples() const =0
virtual std::vector< std::pair< AggregationScenarioDataType, std::string > > keys() const =0
SafeStack< ValueType > value
load / save cubes and agg scen data from / to disk
Date parseDate(const string &s)
bool parseBool(const string &s)
Real parseReal(const string &s)
Integer parseInteger(const string &s)
A cube implementation that stores the cube in memory.
#define LOG(text)
#define DLOG(text)
QuantLib::ext::shared_ptr< AggregationScenarioData > loadAggregationScenarioData(const std::string &filename)
Definition: cube_io.cpp:234
void saveCube(const std::string &filename, const NPVCubeWithMetaData &cube, const bool doublePrecision)
Definition: cube_io.cpp:167
void saveAggregationScenarioData(const std::string &filename, const AggregationScenarioData &cube)
Definition: cube_io.cpp:302
NPVCubeWithMetaData loadCube(const std::string &filename, const bool doublePrecision)
Definition: cube_io.cpp:63
std::string to_string(const LocationInfo &l)
QuantLib::ext::shared_ptr< NPVCube > cube
Definition: cube_io.hpp:39
QuantLib::ext::shared_ptr< ScenarioGeneratorData > scenarioGeneratorData
Definition: cube_io.hpp:41
boost::optional< Size > storeCreditStateNPVs
Definition: cube_io.hpp:43
boost::optional< bool > storeFlows
Definition: cube_io.hpp:42
Date asof(14, Jun, 2018)