Skip to content

Commit e096718

Browse files
damienbarkerGitlab CI
authored andcommitted
Merge branch 'feature/QPR-13626' into 'master'
QPR-13626 Base RiskFactor PnL report by tradId Closes QPR-13626 See merge request qs/oreplus!3133
1 parent c7bf462 commit e096718

4 files changed

Lines changed: 34 additions & 20 deletions

File tree

OREAnalytics/orea/engine/historicalpnlgenerator.cpp

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,6 @@ RiskFactorPnLSeries HistoricalPnlGenerator::riskFactorLevelPnlSeries(const ore::
257257
return series;
258258
}
259259

260-
// Build per-scenario maps for scenarios within the period
261260
// Determine how many scenarios we have from any cube in the map
262261
Size samples = mapCube_.begin()->second->samples();
263262
series.resize(samples);
@@ -269,29 +268,36 @@ RiskFactorPnLSeries HistoricalPnlGenerator::riskFactorLevelPnlSeries(const ore::
269268
if (!(period.contains(start) && period.contains(end))) {
270269
continue; // leave this scenario's map empty
271270
}
272-
// Aggregate PnL across trades and collapse duplicates by risk factor name
273-
std::unordered_map<std::string, std::pair<RiskFactorKey, Real>> byName;
271+
// Aggregate PnL per trade and collapse duplicates by risk factor name
272+
// Map: rf name -> (representative key, per-trade pnl vector)
273+
std::unordered_map<std::string, std::pair<RiskFactorKey, std::vector<Real>>> byName;
274274
for (const auto& kv : mapCube_) {
275275
const RiskFactorKey& rfKey = kv.first;
276276
const ext::shared_ptr<NPVCube>& rfCube = kv.second;
277-
Real pnl = 0.0;
278277
Size tradeCount = rfCube->numIds();
278+
std::vector<Real> pnlVec(tradeCount, 0.0);
279+
bool anyNonZero = false;
279280
for (Size i = 0; i < tradeCount; ++i) {
280-
pnl += rfCube->get(i, dateIdx, s) - rfCube->getT0(i);
281+
Real v = rfCube->get(i, dateIdx, s) - rfCube->getT0(i);
282+
pnlVec[i] = v;
283+
anyNonZero = anyNonZero || (v != 0.0);
281284
}
282-
if (pnl == 0.0)
285+
if (!anyNonZero)
283286
continue;
284287
auto it = byName.find(rfKey.name);
285288
if (it == byName.end()) {
286-
byName.emplace(rfKey.name, std::make_pair(rfKey, pnl));
289+
byName.emplace(rfKey.name, std::make_pair(rfKey, std::move(pnlVec)));
287290
} else {
288-
it->second.second += pnl;
291+
auto& aggVec = it->second.second;
292+
if (aggVec.size() < pnlVec.size())
293+
aggVec.resize(pnlVec.size(), 0.0);
294+
for (Size i = 0; i < pnlVec.size(); ++i)
295+
aggVec[i] += pnlVec[i];
289296
}
290297
}
291-
// Write one entry per risk factor name into the scenario map
292-
for (const auto& e : byName) {
293-
series[s].emplace(e.second.first, e.second.second);
294-
}
298+
for (auto& e : byName) {
299+
series[s].emplace(e.second.first, std::move(e.second.second));
300+
}
295301
}
296302

297303
return series;

OREAnalytics/orea/engine/historicalpnlgenerator.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,9 @@ class HistoricalPnlGenerator : public ore::data::ProgressReporter {
138138
TradePnlStore tradeLevelPnl() const;
139139

140140
// Series of per-scenario portfolio PnL by risk factor (aligned with scenarios)
141-
using RiskFactorPnLSeries = std::vector<std::map<RiskFactorKey,QuantLib::Real>>;
141+
// For each scenario s, we map RiskFactorKey -> vector<Real> with one entry per trade
142+
// aligned to the order in tradeIdIndexPairs().
143+
using RiskFactorPnLSeries = std::vector<std::map<RiskFactorKey,std::vector<QuantLib::Real>>>;
142144
RiskFactorPnLSeries riskFactorLevelPnlSeries(const ore::data::TimePeriod& period) const;
143145

144146
/*! Return the last cube generated by generateCube.

OREAnalytics/orea/engine/historicalsensipnlcalculator.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ void HistoricalSensiPnlCalculator::calculateSensiPnl(
258258
vector<Real> allPnls(hisScenGen_->numScenarios(), 0.0);
259259
vector<Real> allFoPnls(hisScenGen_->numScenarios(), 0.0);
260260

261-
// calculators,scenarios, trades
261+
// calculators,scenarios, trades
262262
using TradePnLStore = std::vector<std::vector<QuantLib::Real>>;
263263
std::vector<TradePnLStore> tradePnls, foTradePnls;
264264

OREAnalytics/orea/engine/historicalsimulationvar.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ void HistoricalSimulationVarReport::createAdditionalReports(
6767
// prepare report
6868
QuantLib::ext::shared_ptr<Report> report2 = reports->reports().at(2);
6969
report2->addColumn("RiskFactor", string())
70+
.addColumn("TradeId", string())
7071
.addColumn("PLDate1", Date())
7172
.addColumn("PLDate2", Date())
7273
.addColumn("PLAmount", double(), 6);
@@ -129,13 +130,18 @@ void HistoricalSimulationVarReport::writeAdditionalReports(
129130
QuantLib::ext::shared_ptr<Report> report2 = reports->reports().at(2);
130131
if (s < riskFactorPnls_.size() && countRF_ < 1) {
131132
for (const auto& r : riskFactorPnls_[s]) {
132-
report2->next();
133133
const auto& key = r.first;
134-
const Real value = r.second;
135-
report2->add(ore::data::to_string(key));
136-
report2->add(hisScenGen_->startDates()[s]);
137-
report2->add(hisScenGen_->endDates()[s]);
138-
report2->add(value);
134+
const std::vector<Real>& vals = r.second;
135+
Size idx = 0;
136+
for (const auto& t : tradeIdIdxPairs_) {
137+
report2->next();
138+
report2->add(ore::data::to_string(key));
139+
report2->add(t.first);
140+
report2->add(hisScenGen_->startDates()[s]);
141+
report2->add(hisScenGen_->endDates()[s]);
142+
report2->add(idx < vals.size() ? vals[idx] : 0.0);
143+
++idx;
144+
}
139145
}
140146
}
141147
}

0 commit comments

Comments
 (0)