Skip to content

Commit 15c42ed

Browse files
pcaspersjenkins
authored andcommitted
QPR-11641 optimize sensi cube stream
1 parent 0f7d093 commit 15c42ed

2 files changed

Lines changed: 77 additions & 81 deletions

File tree

OREAnalytics/orea/engine/sensitivitycubestream.cpp

Lines changed: 66 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
ORE is free software: you can redistribute it and/or modify it
99
under the terms of the Modified BSD License. You should have received a
1010
copy of the license along with this program.
11-
The license is also available online at <http://opensourcerisk.org>
11+
pr The license is also available online at <http://opensourcerisk.org>
1212
1313
This program is distributed on the basis that it will form a useful
1414
contribution to risk analytics and model standardisation, but WITHOUT
@@ -31,107 +31,100 @@ namespace analytics {
3131
using crossPair = SensitivityCube::crossPair;
3232

3333
SensitivityCubeStream::SensitivityCubeStream(const boost::shared_ptr<SensitivityCube>& cube, const string& currency)
34-
: cube_(cube), currency_(currency), upRiskFactor_(cube_->upFactors().begin()),
35-
downRiskFactor_(cube_->downFactors().begin()), itCrossPair_(cube_->crossFactors().begin()),
36-
tradeIdx_(cube_->tradeIdx().begin()), canComputeGamma_(false) {
34+
: cube_(cube), currency_(currency), canComputeGamma_(false) {
3735

3836
// Set the value of canComputeGamma_ based on up and down risk factors.
3937
const auto& upFactors = cube_->upFactors();
4038
const auto& downFactors = cube_->downFactors();
4139
if (upFactors.size() == downFactors.size()) {
42-
auto pred = [](decltype(*upFactors.left.begin()) a, pair<RiskFactorKey, SensitivityCube::FactorData> b) {
43-
return a.first == b.first;
44-
};
45-
canComputeGamma_ = equal(upFactors.left.begin(), upFactors.left.end(), downFactors.begin(), pred);
40+
canComputeGamma_ = equal(upFactors.begin(), upFactors.end(), downFactors.begin(),
41+
[](const auto& a, const auto& b) { return a.first == b.first; });
4642
}
43+
44+
reset();
4745
}
4846

4947
SensitivityRecord SensitivityCubeStream::next() {
5048

51-
SensitivityRecord sr;
49+
while (tradeIdx_ != cube_->tradeIdx().end() && currentDeltaKey_ == currentDeltaKeys_.end() &&
50+
currentCrossGammaKey_ == currentCrossGammaKeys_.end()) {
51+
++tradeIdx_;
52+
updateForNewTrade();
53+
}
5254

53-
const auto& upFactors = cube_->upFactors();
54-
const auto& downFactors = cube_->downFactors();
55+
if (tradeIdx_ == cube_->tradeIdx().end())
56+
return SensitivityRecord();
5557

56-
// If exhausted deltas, gammas AND cross gammas, update to next trade and reset iterators
57-
if (upRiskFactor_ == upFactors.end() && itCrossPair_ == cube_->crossFactors().end()) {
58-
tradeIdx_++;
59-
upRiskFactor_ = upFactors.begin();
60-
downRiskFactor_ = downFactors.begin();
61-
itCrossPair_ = cube_->crossFactors().begin();
58+
SensitivityRecord sr;
59+
Size tradeIdx = tradeIdx_->second;
60+
sr.tradeId = tradeIdx_->first;
61+
sr.isPar = false;
62+
sr.currency = currency_;
63+
sr.baseNpv = cube_->npv(tradeIdx);
64+
65+
if (currentDeltaKey_ != currentDeltaKeys_.end()) {
66+
auto fd = cube_->upFactors().at(*currentDeltaKey_);
67+
sr.key_1 = *currentDeltaKey_;
68+
sr.desc_1 = fd.factorDesc;
69+
sr.shift_1 = fd.shiftSize;
70+
sr.delta = cube_->delta(tradeIdx_->first, *currentDeltaKey_);
71+
if (canComputeGamma_)
72+
sr.gamma = cube_->gamma(tradeIdx_->first, *currentDeltaKey_);
73+
else
74+
sr.gamma = Null<Real>();
75+
++currentDeltaKey_;
76+
} else if (currentCrossGammaKey_ != currentCrossGammaKeys_.end()) {
77+
auto fd = cube_->crossFactors().at(*currentCrossGammaKey_);
78+
sr.key_1 = currentCrossGammaKey_->first;
79+
sr.desc_1 = std::get<0>(fd).factorDesc;
80+
sr.shift_1 = std::get<0>(fd).shiftSize;
81+
sr.key_2 = currentCrossGammaKey_->second;
82+
sr.desc_2 = std::get<1>(fd).factorDesc;
83+
sr.shift_2 = std::get<1>(fd).shiftSize;
84+
sr.gamma = cube_->crossGamma(tradeIdx_->first, *currentCrossGammaKey_);
85+
++currentCrossGammaKey_;
6286
}
6387

64-
// Give back next record if we have a valid trade index
65-
if (tradeIdx_ != cube_->tradeIdx().end()) {
66-
Size tradeIdx = tradeIdx_->second;
67-
sr.tradeId = tradeIdx_->first;
68-
sr.isPar = false;
69-
sr.currency = currency_;
70-
sr.baseNpv = cube_->npv(tradeIdx);
71-
72-
// Are there more deltas and gammas for current trade ID
73-
if (upRiskFactor_ != upFactors.end()) {
74-
Size usrx = upRiskFactor_->right.index;
75-
sr.key_1 = upRiskFactor_->left;
76-
sr.desc_1 = upRiskFactor_->right.factorDesc;
77-
sr.shift_1 = upRiskFactor_->right.shiftSize;
78-
79-
if (!cube_->twoSidedDelta(sr.key_1.keytype)) {
80-
sr.delta = cube_->delta(tradeIdx, usrx);
81-
} else {
82-
Size downIdx = downFactors.at(sr.key_1).index;
83-
sr.delta = cube_->delta(tradeIdx, usrx, downIdx);
84-
}
85-
86-
if (canComputeGamma_ && downRiskFactor_ != downFactors.end()) {
87-
Size dsrx = downRiskFactor_->second.index;
88-
sr.gamma = cube_->gamma(tradeIdx, usrx, dsrx);
89-
downRiskFactor_++;
90-
} else {
91-
sr.gamma = Null<Real>(); // marks na result
92-
}
88+
TLOG("Next record is: " << sr);
89+
return sr;
90+
}
9391

94-
upRiskFactor_++;
92+
void SensitivityCubeStream::updateForNewTrade() {
93+
currentDeltaKeys_.clear();
94+
currentCrossGammaKeys_.clear();
9595

96-
TLOG("Next record is: " << sr);
97-
return sr;
98-
}
96+
if (tradeIdx_ != cube_->tradeIdx().end()) {
9997

100-
// Are there more cross pairs for current trade ID
101-
if (itCrossPair_ != cube_->crossFactors().end()) {
102-
sr.key_1 = itCrossPair_->first.first;
103-
sr.desc_1 = std::get<0>(itCrossPair_->second).factorDesc;
104-
sr.shift_1 = std::get<0>(itCrossPair_->second).shiftSize;
98+
// add delta keys
10599

106-
sr.key_2 = itCrossPair_->first.second;
107-
sr.desc_2 = std::get<1>(itCrossPair_->second).factorDesc;
108-
sr.shift_2 = std::get<1>(itCrossPair_->second).shiftSize;
100+
for (auto const& [idx, _] : cube_->npvCube()->getTradeNPVs(tradeIdx_->second)) {
101+
if (auto k = cube_->upDownFactor(idx); k.keytype != RiskFactorKey::KeyType::None)
102+
currentDeltaKeys_.insert(k);
103+
}
109104

110-
Size id_1, id_2, id_x;
111-
id_1 = std::get<0>(itCrossPair_->second).index;
112-
id_2 = std::get<1>(itCrossPair_->second).index;
113-
id_x = std::get<2>(itCrossPair_->second);
105+
// add cross gamma keys
114106

115-
sr.gamma = cube_->crossGamma(tradeIdx, id_1, id_2, id_x);
107+
for (auto const& [crossPair, data] : cube_->crossFactors()) {
108+
if (!close_enough(cube_->crossGamma(tradeIdx_->second, std::get<0>(data).index, std::get<1>(data).index,
109+
std::get<2>(data)),
110+
0.0)) {
111+
currentCrossGammaKeys_.insert(crossPair);
116112

117-
itCrossPair_++;
113+
// make sure, delta keys contain both cross keys, that's a guarantee of the SensitivityCubeStream
118114

119-
TLOG("Next record is: " << sr);
120-
return sr;
115+
currentDeltaKeys_.insert(crossPair.first);
116+
currentDeltaKeys_.insert(crossPair.second);
117+
}
121118
}
122119
}
123120

124-
// If we get to here, no more cube sensitivities to process so return empty record
125-
TLOG("Next record is: " << sr);
126-
return sr;
121+
currentDeltaKey_ = currentDeltaKeys_.begin();
122+
currentCrossGammaKey_ = currentCrossGammaKeys_.begin();
127123
}
128124

129125
void SensitivityCubeStream::reset() {
130-
// Reset indices and iterators
131126
tradeIdx_ = cube_->tradeIdx().begin();
132-
upRiskFactor_ = cube_->upFactors().begin();
133-
downRiskFactor_ = cube_->downFactors().begin();
134-
itCrossPair_ = cube_->crossFactors().begin();
127+
updateForNewTrade();
135128
}
136129

137130
} // namespace analytics

OREAnalytics/orea/engine/sensitivitycubestream.hpp

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,20 +51,23 @@ class SensitivityCubeStream : public SensitivityStream {
5151
void reset() override;
5252

5353
private:
54+
void updateForNewTrade();
55+
5456
//! Handle on the SensitivityCube
5557
boost::shared_ptr<SensitivityCube> cube_;
5658
//! Currency of the sensitivities in the SensitivityCube
5759
std::string currency_;
5860

59-
//! Iterator to risk factor keys in the cube
60-
boost::bimap<RiskFactorKey, ore::analytics::SensitivityCube::FactorData>::const_iterator upRiskFactor_;
61-
std::map<RiskFactorKey, ore::analytics::SensitivityCube::FactorData>::const_iterator downRiskFactor_;
62-
//! Iterator to cross factors in the cube
63-
std::map<ore::analytics::SensitivityCube::crossPair,
64-
std::tuple<ore::analytics::SensitivityCube::FactorData, ore::analytics::SensitivityCube::FactorData,
65-
QuantLib::Size>>::const_iterator itCrossPair_;
66-
//! Index of current trade Id in the cube
61+
//! Current delta risk factor keys to process and iterators
62+
std::set<RiskFactorKey> currentDeltaKeys_;
63+
std::set<std::pair<RiskFactorKey,RiskFactorKey>> currentCrossGammaKeys_;
64+
65+
std::set<RiskFactorKey>::const_iterator currentDeltaKey_;
66+
std::set<std::pair<RiskFactorKey,RiskFactorKey>>::const_iterator currentCrossGammaKey_;
67+
68+
//! Current trade iterator
6769
std::map<std::string, QuantLib::Size>::const_iterator tradeIdx_;
70+
6871
//! Can only compute gamma if the up and down risk factors align
6972
bool canComputeGamma_;
7073
};

0 commit comments

Comments
 (0)