Skip to content

Commit 50e0259

Browse files
author
Shen
committed
QPR-13609: Switch to scenario.csv input
1 parent 2ef6c35 commit 50e0259

7 files changed

Lines changed: 89 additions & 235 deletions

File tree

OREAnalytics/orea/app/analytics/calibrationanalytic.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,7 @@ void CalibrationAnalyticImpl::buildHwHistoricalCalibrationModelData() {
9898
hwHistoricalModelData_->setUseForwardRate(inputs_->useForwardRate());
9999

100100
if (inputs_->pcaCalibration()) {
101-
loader.loadHistoricalCurveDataFromCsv(inputs_->curveInputFile());
102-
loader.loadFixings(inputs_->fixingDataFile());
103-
loader.cleanData();
101+
loader.loadFromScenarioFile(inputs_->scenarioInputFile());
104102
hwHistoricalModelData_->setLambda(inputs_->lambda());
105103
hwHistoricalModelData_->setVarianceRetained(inputs_->varianceRetained());
106104
hwHistoricalModelData_->setIrCurves(loader.moveIrCurves());

OREAnalytics/orea/app/hwhistoricalcalibrationdataloader.cpp

Lines changed: 72 additions & 208 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@
1717
*/
1818

1919
#include <orea/app/hwhistoricalcalibrationdataloader.hpp>
20+
#include <orea/scenario/scenariofilereader.hpp>
21+
#include <orea/scenario/simplescenariofactory.hpp>
22+
#include <orea/scenario/scenarioloader.hpp>
2023

2124
#include <ored/utilities/csvfilereader.hpp>
2225
#include <ored/utilities/parsers.hpp>
26+
#include <ored/utilities/indexparser.hpp>
2327
#include <ored/utilities/log.hpp>
2428

2529
#include <sstream>
@@ -41,35 +45,46 @@ HwHistoricalCalibrationDataLoader::HwHistoricalCalibrationDataLoader(const std::
4145
: baseCurrency_(baseCurrency), foreignCurrency_(foreignCurrency), tenors_(curveTenors), startDate_(startDate),
4246
endDate_(endDate) {}
4347

44-
void HwHistoricalCalibrationDataLoader::loadHistoricalCurveDataFromCsv(const std::string& fileName) {
45-
LOG("Load Historical time series data from file " << fileName);
46-
CSVFileReader dataReader(fileName, true);
47-
QL_REQUIRE(dataReader.hasField("date"), "Date column is not found in the time series data file.");
48-
QL_REQUIRE(dataReader.hasField("curve"), "Curve column is not found in the time series data file.");
49-
QL_REQUIRE(dataReader.hasField("data"), "Data column is not found in the time series data file.");
50-
Size lines = 0;
51-
std::vector<std::string> remove_chars = {"\"", "[", "]", " "};
52-
while (dataReader.next()) {
53-
++lines;
54-
Date d = parseDate(dataReader.get("date"));
55-
string curveId = dataReader.get("curve");
56-
string dataStr = dataReader.get("data");
57-
// Remove all irrelavent chars in the data string
58-
for (Size i = 0; i < remove_chars.size(); i++) {
59-
boost::erase_all(dataStr, remove_chars[i]);
60-
}
61-
std::vector<std::string> tokens;
62-
boost::split(tokens, dataStr, boost::is_any_of(",;\t"), boost::token_compress_off);
63-
if (tokens.size() != tenors_.size()) {
64-
ALOG("Number of discount rates in line " << lines << " is not the same as tenors given.");
65-
continue;
66-
}
67-
std::vector<Real> discountFactor;
68-
for (Size i = 0; i < tenors_.size(); i++) {
69-
discountFactor.push_back(parseReal(tokens[i]));
48+
void HwHistoricalCalibrationDataLoader::loadFromScenarioFile(const std::string& fileName) {
49+
LOG("Load Historical time series data from scenario file " << fileName);
50+
51+
ext::shared_ptr<SimpleScenarioFactory> scenarioFactory = ext::make_shared<SimpleScenarioFactory>(false);
52+
ext::shared_ptr<ScenarioFileReader> scenarioReader =
53+
ext::make_shared<ScenarioFileReader>(fileName, scenarioFactory);
54+
ext::shared_ptr<HistoricalScenarioLoader> historicalScenarioLoader =
55+
ext::make_shared<HistoricalScenarioLoader>(scenarioReader, startDate_, endDate_, NullCalendar());
56+
57+
QL_REQUIRE(historicalScenarioLoader->scenarios().size() == 1,
58+
"Only one scenario allowed for HW historical calibration.");
59+
std::map<Date, ext::shared_ptr<Scenario>> scenarioMap = historicalScenarioLoader->scenarios()[0];
60+
QL_REQUIRE(scenarioMap.size() > 0, "Scenario file is empty.");
61+
const auto& keys = scenarioMap.begin()->second->keys();
62+
for (const auto& [date, scenario] : scenarioMap) {
63+
for (const auto& key : keys) {
64+
if (key.keytype == RiskFactorKey::KeyType::IndexCurve) {
65+
std::string ccy = parseCurrency(key.name);
66+
if (ccy == baseCurrency_ ||
67+
std::find(foreignCurrency_.begin(), foreignCurrency_.end(), ccy) != foreignCurrency_.end()) {
68+
loadIr(key.name, key.index, date, scenario->get(key));
69+
}
70+
} else if (key.keytype == RiskFactorKey::KeyType::FXSpot) {
71+
for (const auto& foreignCcy : foreignCurrency_) {
72+
std::string pair1 = foreignCcy + baseCurrency_;
73+
std::string pair2 = baseCurrency_ + foreignCcy;
74+
Real fxRate;
75+
if (key.name == pair1)
76+
fxRate = scenario->get(key);
77+
else if (key.name == pair2)
78+
fxRate = 1.0 / scenario->get(key);
79+
else
80+
continue;
81+
loadFx(pair1, date, fxRate);
82+
break;
83+
}
84+
}
7085
}
71-
loadIr(curveId, d, discountFactor);
7286
}
87+
cleanData();
7388
}
7489

7590
void HwHistoricalCalibrationDataLoader::loadPCAFromCsv(const std::vector<std::string>& fileNames) {
@@ -109,44 +124,20 @@ void HwHistoricalCalibrationDataLoader::loadPCAFromCsv(const std::vector<std::st
109124
}
110125
}
111126

112-
void HwHistoricalCalibrationDataLoader::loadFixings(const std::string& fileName) {
113-
LOG("Load fixings from file " << fileName);
114-
CSVFileReader dataReader(fileName, false);
115-
dataReader.next();
116-
QL_REQUIRE(dataReader.numberOfColumns() == 3, "Number of columns in fixings file must be 3.");
117-
Size lines = 0;
118-
// std::vector<std::string> remove_chars = { "\"", "[", "]", " " };
119-
while (dataReader.next()) {
120-
++lines;
121-
Date d = parseDate(dataReader.get(0));
122-
string curveId = dataReader.get(1);
123-
Real dataReal = parseReal(dataReader.get(2));
124-
std::vector<std::string> tokens;
125-
boost::split(tokens, curveId, boost::is_any_of("-"), boost::token_compress_off);
126-
if (tokens[0] == "FX")
127-
loadFx(curveId, d, dataReal);
128-
}
129-
}
130-
131-
void HwHistoricalCalibrationDataLoader::loadIr(const std::string& curveId, const Date& d, const std::vector<Real>& df) {
132-
// Check if the date is within the start and end date specified in ore.xml
133-
if (!(d >= startDate_ && d <= endDate_)) {
134-
return;
135-
}
136-
if (irCurves_.find(curveId) != irCurves_.end()) {
137-
if (irCurves_[curveId].find(d) != irCurves_[curveId].end()) {
138-
ALOG("Encounter duplicated records for curveId " << curveId << ", date " << d << " in the input file.");
139-
return;
140-
}
127+
void HwHistoricalCalibrationDataLoader::loadIr(const std::string& curveId, const Size& index, const Date& d,
128+
const Real& df) {
129+
std::map<Date, std::vector<Real>>& dateMap = irCurves_[curveId];
130+
std::vector<Real>& discountFactors = dateMap[d];
131+
if (discountFactors.empty()) {
132+
discountFactors.resize(tenors_.size(), 0.0);
141133
}
142-
irCurves_[curveId][d] = df;
134+
QL_REQUIRE(index < tenors_.size(), "Tenor index " << index << " out of range for curve " << curveId << " on date "
135+
<< d << " (max: " << tenors_.size() << ")");
136+
discountFactors[index] = df;
143137
}
144138

145139
void HwHistoricalCalibrationDataLoader::loadFx(const std::string& curveId, const Date& d, const Real& fxSpot) {
146140
// Check if the date is within the start and end date specified in ore.xml
147-
if (!(d >= startDate_ && d <= endDate_)) {
148-
return;
149-
}
150141
if (fxSpots_.find(curveId) != fxSpots_.end()) {
151142
if (fxSpots_[curveId].find(d) != fxSpots_[curveId].end()) {
152143
ALOG("Encounter duplicated records for curveId " << curveId << ", date " << d << " in the input file.");
@@ -169,167 +160,40 @@ void HwHistoricalCalibrationDataLoader::cleanData() {
169160
// Check if all required currency exist in irCurves_
170161
std::vector<std::string> requiredCcy = foreignCurrency_;
171162
requiredCcy.push_back(baseCurrency_);
172-
std::vector<string> erase_list;
173-
for (auto const& outer : irCurves_) {
174-
std::vector<std::string> tokens;
175-
boost::split(tokens, outer.first, boost::is_any_of("/-"), boost::token_compress_off);
176-
QL_REQUIRE(tokens.size() >= 2, "Curve should be in the format of IndexCurve/CCY-NAME-TENOR or IndexCurve/CCY");
177-
auto position = std::find(requiredCcy.begin(), requiredCcy.end(), tokens[1]);
178-
if (position == requiredCcy.end()) {
179-
erase_list.push_back(outer.first);
180-
} else {
163+
164+
for (const auto& [curveId, dataMap] : irCurves_) {
165+
auto position = std::find(requiredCcy.begin(), requiredCcy.end(), parseCurrency(curveId));
166+
if (position != requiredCcy.end()) {
181167
requiredCcy.erase(position);
182-
// Change map key to currency code
183-
auto handler = irCurves_.extract(outer.first);
184-
handler.key() = tokens[1];
185-
irCurves_.insert(std::move(handler));
186168
}
187169
}
188-
// Remove all the currencies that are not needed
189-
for (auto const& index : erase_list) {
190-
irCurves_.erase(index);
191-
}
170+
192171
std::string missingCcy;
193172
for (auto& c : requiredCcy)
194173
missingCcy += c + " ";
195174
QL_REQUIRE(requiredCcy.size() == 0, "Discount factor for " << missingCcy << "are not found in input file.");
196175

197176
// Check if all required fx spot exist in fxSpots
198-
requiredCcy = foreignCurrency_;
199-
erase_list.clear();
200-
for (auto const& outer : fxSpots_) {
201-
// Extract currency pair name
202-
std::vector<std::string> tokens;
203-
boost::split(tokens, outer.first, boost::is_any_of("/-"), boost::token_compress_off);
204-
QL_REQUIRE(tokens.size() >= 4, "Curve should be in the format of FX-NAME-CCY1-CCY2");
205-
string ccyPair;
206-
if (tokens[2] == baseCurrency_) {
207-
auto position = std::find(requiredCcy.begin(), requiredCcy.end(), tokens[3]);
208-
if (position != requiredCcy.end()) {
209-
ccyPair = tokens[3] + tokens[2];
210-
// Inverse the fx spot rates
211-
for (const auto& inner : outer.second)
212-
fxSpots_[outer.first][inner.first] = 1.0 / inner.second;
213-
requiredCcy.erase(position);
214-
} else {
215-
erase_list.push_back(outer.first);
216-
continue;
217-
}
218-
} else if (tokens[3] == baseCurrency_) {
219-
auto position = std::find(requiredCcy.begin(), requiredCcy.end(), tokens[2]);
220-
if (position != requiredCcy.end()) {
221-
ccyPair = tokens[2] + tokens[3];
222-
requiredCcy.erase(position);
223-
} else {
224-
erase_list.push_back(outer.first);
225-
continue;
226-
}
227-
} else {
228-
erase_list.push_back(outer.first);
229-
continue;
230-
}
231-
// Change map key to currency pair code
232-
auto handler = fxSpots_.extract(outer.first);
233-
handler.key() = ccyPair;
234-
fxSpots_.insert(std::move(handler));
235-
}
236-
// Remove all the currencies that are not needed
237-
for (auto const& index : erase_list) {
238-
fxSpots_.erase(index);
239-
}
240-
missingCcy = "";
241-
for (auto& c : requiredCcy)
242-
missingCcy += c + "-" + baseCurrency_ + " ";
243-
QL_REQUIRE(requiredCcy.size() == 0, "FX spot for " << missingCcy << "are not found in input file.");
244-
245-
// Master date set
246-
std::set<Date> masterDates;
247-
for (auto const& outer : fxSpots_)
248-
for (auto const& inner : outer.second)
249-
masterDates.insert(inner.first);
250-
for (auto const& outer : irCurves_)
251-
for (auto const& inner : outer.second)
252-
masterDates.insert(inner.first);
177+
std::vector<std::string> missingFxPairs;
178+
for (const auto& foreignCcy : foreignCurrency_) {
179+
std::string expectedPair = foreignCcy + baseCurrency_;
253180

254-
// Forward-fill FX and IR data
255-
for (auto& outer : fxSpots_) {
256-
if (outer.second.empty())
257-
continue;
258-
Real first = outer.second.begin()->second;
259-
Real last = first;
260-
261-
// Collect missing dates first, then insert
262-
std::vector<std::pair<Date, Real>> missingDates;
263-
missingDates.reserve(masterDates.size());
264-
265-
auto masterIt = masterDates.begin();
266-
auto dataIt = outer.second.begin();
267-
268-
while (masterIt != masterDates.end()) {
269-
const Date& masterDate = *masterIt;
270-
while (dataIt != outer.second.end() && dataIt->first < masterDate) {
271-
last = dataIt->second;
272-
++dataIt;
273-
}
274-
if (dataIt == outer.second.end() || dataIt->first != masterDate) {
275-
Real fillValue = (masterDate < outer.second.begin()->first ? first : last);
276-
missingDates.emplace_back(masterDate, fillValue);
277-
} else {
278-
last = dataIt->second;
279-
++dataIt;
280-
}
281-
++masterIt;
282-
}
283-
284-
for (const auto& [date, value] : missingDates) {
285-
outer.second[date] = value;
286-
LOG("Add missing fx spot rate on date " << date << ", Currency: " << outer.first << ", Value: " << value);
181+
if (fxSpots_.find(expectedPair) == fxSpots_.end()) {
182+
missingFxPairs.push_back(expectedPair);
287183
}
288184
}
289-
for (auto& outer : irCurves_) {
290-
if (outer.second.empty())
291-
continue;
292-
std::vector<Real> first = outer.second.begin()->second;
293-
std::vector<Real> last = first;
294-
295-
// Collect missing dates first, then insert
296-
std::vector<std::pair<Date, std::vector<Real>>> missingDates;
297-
missingDates.reserve(masterDates.size());
298-
299-
auto masterIt = masterDates.begin();
300-
auto dataIt = outer.second.begin();
301-
302-
while (masterIt != masterDates.end()) {
303-
const Date& masterDate = *masterIt;
304-
305-
while (dataIt != outer.second.end() && dataIt->first < masterDate) {
306-
last = dataIt->second;
307-
++dataIt;
308-
}
309-
310-
if (dataIt == outer.second.end() || dataIt->first != masterDate) {
311-
std::vector<Real> fillValue = (masterDate < outer.second.begin()->first ? first : last);
312-
missingDates.emplace_back(masterDate, fillValue);
313-
} else {
314-
last = dataIt->second;
315-
++dataIt;
316-
}
317-
318-
++masterIt;
319-
}
185+
missingCcy = "";
186+
for (auto& c : missingFxPairs)
187+
missingCcy += c + " ";
188+
QL_REQUIRE(missingFxPairs.size() == 0, "FX spot for " << missingCcy << "are not found in input file.");
189+
}
320190

321-
for (const auto& [date, value] : missingDates) {
322-
outer.second[date] = value;
323-
std::ostringstream oss;
324-
for (Size k = 0; k < value.size(); ++k) {
325-
if (k > 0)
326-
oss << ", ";
327-
oss << value[k];
328-
}
329-
LOG("Add missing IR curve rate on date " << date << ", Currency: " << outer.first
330-
<< ", Value: " << oss.str());
331-
}
332-
}
191+
std::string HwHistoricalCalibrationDataLoader::parseCurrency(const std::string& curveId) {
192+
vector<string> tokens;
193+
split(tokens, curveId, boost::is_any_of("-"));
194+
QL_REQUIRE(tokens.size() == 2 || tokens.size() == 3,
195+
"Two or three tokens required in " << curveId << ": CCY-INDEX or CCY-INDEX-TERM");
196+
return tokens[0];
333197
}
334198

335199
} // namespace analytics

OREAnalytics/orea/app/hwhistoricalcalibrationdataloader.hpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,8 @@ class HwHistoricalCalibrationDataLoader {
4040
const QuantLib::Date& startDate = QuantLib::Date(),
4141
const QuantLib::Date& endDate = QuantLib::Date());
4242

43-
44-
void loadHistoricalCurveDataFromCsv(const std::string& fileName);
43+
void loadFromScenarioFile(const std::string& fileName);
4544
void loadPCAFromCsv(const std::vector<std::string>& fileNames);
46-
void loadFixings(const std::string& fileName);
47-
void cleanData();
4845

4946
// Getters
5047
const std::map<std::string, std::map<Date, std::vector<Real>>>& getIrCurves() const { return irCurves_; }
@@ -60,10 +57,14 @@ class HwHistoricalCalibrationDataLoader {
6057
std::map<std::string, Matrix> moveEigenVector() { return std::move(eigenVector_); }
6158

6259
private:
63-
void loadIr(const std::string& curveId, const Date& d, const std::vector<Real>& df);
60+
void loadIr(const std::string& curveId, const Size& index, const Date& d, const Real& df);
6461
void loadFx(const std::string& curveId, const Date& d, const Real& fxSpot);
6562
void loadEigenValue(const std::string& ccy, const Array& eigenValue);
6663
void loadEigenVector(const std::string& ccy, const Matrix& eigenVector);
64+
void cleanData();
65+
66+
// Helper
67+
std::string parseCurrency(const std::string& curveId);
6768

6869
std::string baseCurrency_;
6970
std::vector<std::string> foreignCurrency_;

OREAnalytics/orea/app/inputparameters.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -913,12 +913,10 @@ void InputParameters::setForeignCurrencies(const std::string& s) { foreignCurren
913913

914914
void InputParameters::setCurveTenors(const std::string& s) { curveTenors_ = parseListOfValues<Period>(s, &parsePeriod); }
915915

916-
void InputParameters::setCurveInputFile(const std::string& s) {
917-
curveInputFile_ = s;
916+
void InputParameters::setScenarioInputFile(const std::string& s) {
917+
scenarioInputFile_ = s;
918918
}
919919

920-
void InputParameters::setFixingDataFile(const std::string& s) { fixingDataFile_ = s; }
921-
922920
void InputParameters::setStartDate(const Date& d) { startDate_ = d; }
923921

924922
void InputParameters::setEndDate(const Date& d) { endDate_ = d; }

0 commit comments

Comments
 (0)