Skip to content

Commit 91d0080

Browse files
pcaspersjenkins
authored andcommitted
QPR-12014 generalize shift scheme
1 parent 129eeaa commit 91d0080

11 files changed

Lines changed: 321 additions & 337 deletions

OREAnalytics/orea/app/analytics/parconversionanalytic.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ void ParConversionAnalyticImpl::runAnalytic(const boost::shared_ptr<ore::data::I
9090

9191
auto scenarioGenerator = boost::make_shared<SensitivityScenarioGenerator>(
9292
configs.sensiScenarioData, simMarket->baseScenario(), configs.simMarketParams, simMarket,
93-
boost::make_shared<DeltaScenarioFactory>(simMarket->baseScenario()), true, true,
93+
boost::make_shared<DeltaScenarioFactory>(simMarket->baseScenario()), true, std::string(), true,
9494
simMarket->baseScenarioAbsolute());
9595

9696
simMarket->scenarioGenerator() = scenarioGenerator;

OREAnalytics/orea/cube/sensitivitycube.cpp

Lines changed: 65 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,16 @@ std::ostream& operator<<(std::ostream& out, const SensitivityCube::crossPair& cp
5757
SensitivityCube::SensitivityCube(const boost::shared_ptr<NPVSensiCube>& cube,
5858
const vector<ShiftScenarioDescription>& scenarioDescriptions,
5959
const map<RiskFactorKey, QuantLib::Real>& shiftsizes,
60-
const set<RiskFactorKey::KeyType>& twoSidedDeltas)
61-
: cube_(cube), scenarioDescriptions_(scenarioDescriptions), shiftSizes_(shiftsizes),
62-
twoSidedDeltas_(twoSidedDeltas) {
60+
const std::map<RiskFactorKey, std::string>& shiftSchemes)
61+
: cube_(cube), scenarioDescriptions_(scenarioDescriptions), shiftSizes_(shiftsizes), shiftSchemes_(shiftSchemes) {
6362
initialise();
6463
}
6564

6665
SensitivityCube::SensitivityCube(const boost::shared_ptr<NPVSensiCube>& cube,
6766
const vector<string>& scenarioDescriptions,
6867
const map<RiskFactorKey, QuantLib::Real>& shiftsizes,
69-
const set<RiskFactorKey::KeyType>& twoSidedDeltas)
70-
: cube_(cube), shiftSizes_(shiftsizes), twoSidedDeltas_(twoSidedDeltas) {
68+
const std::map<RiskFactorKey, std::string>& shiftSchemes)
69+
: cube_(cube), shiftSizes_(shiftsizes), shiftSchemes_(shiftSchemes) {
7170

7271
// Populate scenarioDescriptions_ from string descriptions
7372
scenarioDescriptions_.reserve(scenarioDescriptions.size());
@@ -110,6 +109,7 @@ void SensitivityCube::initialise() {
110109
QL_REQUIRE(downFactors_.count(des.key1()) == 0, "Cannot have multiple down factors with "
111110
"the same risk factor key ["
112111
<< des.key1() << "]");
112+
factors_.insert(des.key1());
113113
downFactors_[des.key1()] = fd;
114114
downIndexToKey_[fd.index] = des.key1();
115115
break;
@@ -135,34 +135,26 @@ void SensitivityCube::initialise() {
135135
}
136136

137137
// Log warnings if each factor does not have a shift size entry and that it is not a Null<Real>()
138-
if (upFactors_.size() != shiftSizes_.size()) {
139-
WLOG("The number of 'Up' shifts (" << upFactors_.size() << ") does not equal "
140-
<< "the number of shift sizes (" << shiftSizes_.size() << ") supplied");
138+
if (factors_.size() != shiftSizes_.size()) {
139+
WLOG("The number of factors from up / down shifts (" << factors_.size() << ") does not equal "
140+
<< "the number of shift sizes (" << shiftSizes_.size()
141+
<< ") supplied");
141142
}
142143

143-
for (auto const& kv : upFactors_) {
144-
auto it = shiftSizes_.find(kv.first);
144+
for (auto const& f : factors_) {
145+
auto it = shiftSizes_.find(f);
145146
if (it == shiftSizes_.end()) {
146-
WLOG("No entry for risk factor " << kv.first << " in shift sizes.");
147-
}
148-
if (it->second == Null<Real>()) {
149-
WLOG("The shift size for risk factor " << kv.first << " is not valid.")
147+
WLOG("No entry for risk factor " << f << " in shift sizes.");
150148
}
151149
}
152150
}
153151

154152
bool SensitivityCube::hasTrade(const string& tradeId) const { return tradeIdx_.count(tradeId) > 0; }
155153

156-
RiskFactorKey SensitivityCube::upFactor(const Size upIndex) const {
157-
if (auto k = upIndexToKey_.find(upIndex); k != upIndexToKey_.end()) {
154+
RiskFactorKey SensitivityCube::upDownFactor(const Size index) const {
155+
if (auto k = upIndexToKey_.find(index); k != upIndexToKey_.end()) {
158156
return k->second;
159-
} else {
160-
return RiskFactorKey();
161-
}
162-
}
163-
164-
RiskFactorKey SensitivityCube::downFactor(const Size downIndex) const {
165-
if (auto k = downIndexToKey_.find(downIndex); k != downIndexToKey_.end()) {
157+
} else if (auto k = downIndexToKey_.find(index); k != downIndexToKey_.end()) {
166158
return k->second;
167159
} else {
168160
return RiskFactorKey();
@@ -178,7 +170,7 @@ SensitivityCube::crossPair SensitivityCube::crossFactor(const Size crossIndex) c
178170
}
179171

180172
bool SensitivityCube::hasScenario(const ShiftScenarioDescription& scenarioDescription) const {
181-
return scenarioIdx_.count(scenarioDescription) > 0;
173+
return scenarioIdx_.find(scenarioDescription) != scenarioIdx_.end();
182174
}
183175

184176
std::string SensitivityCube::factorDescription(const RiskFactorKey& riskFactorKey) const {
@@ -199,6 +191,12 @@ Real SensitivityCube::shiftSize(const RiskFactorKey& riskFactorKey) const {
199191
return it->second;
200192
}
201193

194+
std::string SensitivityCube::shiftScheme(const RiskFactorKey& riskFactorKey) const {
195+
auto it = shiftSchemes_.find(riskFactorKey);
196+
QL_REQUIRE(it != shiftSchemes_.end(), "Risk factor, " << riskFactorKey << ", was not found in the shift schemes.");
197+
return it->second;
198+
}
199+
202200
Real SensitivityCube::npv(const string& tradeId) const { return cube_->getT0(tradeId, 0); }
203201

204202
Real SensitivityCube::npv(Size id) const { return cube_->getT0(id, 0); }
@@ -211,77 +209,76 @@ Real SensitivityCube::npv(const string& tradeId, const ShiftScenarioDescription&
211209
return npv(tradeIdx, scenarioIdx);
212210
}
213211

214-
Real SensitivityCube::delta(Size id, Size scenarioIdx) const {
215-
return cube_->get(id, scenarioIdx) - cube_->getT0(id, 0);
216-
}
217-
218-
Real SensitivityCube::delta(Size id, Size upIdx, Size downIdx) const {
219-
return (cube_->get(id, upIdx) - cube_->get(id, downIdx)) / 2.0;
220-
}
221-
222-
Real SensitivityCube::delta(const string& tradeId, const RiskFactorKey& riskFactorKey) const {
223-
Size scenarioIdx = index(riskFactorKey, upFactors_).index;
224-
Size tradeIdx = cube_->getTradeIndex(tradeId);
225-
if (!twoSidedDelta(riskFactorKey.keytype)) {
226-
return delta(tradeIdx, scenarioIdx);
227-
} else {
212+
Real SensitivityCube::delta(const Size tradeIdx, const RiskFactorKey& riskFactorKey) const {
213+
auto s = shiftSchemes_.find(riskFactorKey);
214+
QL_REQUIRE(s != shiftSchemes_.end(),
215+
"SensitivityCube::delta(" << tradeIdx << ", " << riskFactorKey << "): no shift scheme stored.");
216+
if (s->second == "Forward") {
217+
Size scenarioIdx = index(riskFactorKey, upFactors_).index;
218+
return cube_->get(tradeIdx, scenarioIdx) - cube_->getT0(tradeIdx, 0);
219+
} else if (s->second == "Backward") {
220+
Size scenarioIdx = index(riskFactorKey, downFactors_).index;
221+
return cube_->getT0(tradeIdx, 0) - cube_->get(tradeIdx, scenarioIdx);
222+
} else if (s->second == "Central") {
223+
Size upIdx = index(riskFactorKey, upFactors_).index;
228224
Size downIdx = index(riskFactorKey, downFactors_).index;
229-
return delta(tradeIdx, scenarioIdx, downIdx);
230-
}
225+
return (cube_->get(tradeIdx, upIdx) - cube_->get(tradeIdx, downIdx)) / 2.0;
226+
} else {
227+
QL_FAIL("SensitivityCube::delta(" << tradeIdx << ", " << riskFactorKey << "): unknown shift scheme '"
228+
<< s->second << "'");
229+
}
231230
}
232231

233-
Real SensitivityCube::gamma(Size id, Size upScenarioIdx, Size downScenarioIdx) const {
234-
235-
Real baseNpv = cube_->getT0(id, 0);
236-
Real upNpv = cube_->get(id, upScenarioIdx);
237-
Real downNpv = cube_->get(id, downScenarioIdx);
238-
239-
return upNpv - 2.0 * baseNpv + downNpv;
232+
Real SensitivityCube::delta(const string& tradeId, const RiskFactorKey& riskFactorKey) const {
233+
return delta(cube_->getTradeIndex(tradeId), riskFactorKey);
240234
}
241235

242-
Real SensitivityCube::gamma(const std::string& tradeId, const RiskFactorKey& riskFactorKey) const {
236+
Real SensitivityCube::gamma(const Size tradeIdx, const RiskFactorKey& riskFactorKey) const {
243237
Size upIdx = index(riskFactorKey, upFactors_).index;
244238
Size downIdx = index(riskFactorKey, downFactors_).index;
245-
Size tradeIdx = cube_->getTradeIndex(tradeId);
239+
Real baseNpv = cube_->getT0(tradeIdx, 0);
240+
Real upNpv = cube_->get(tradeIdx, upIdx);
241+
Real downNpv = cube_->get(tradeIdx, downIdx);
242+
return upNpv - 2.0 * baseNpv + downNpv;
243+
}
246244

247-
return gamma(tradeIdx, upIdx, downIdx);
245+
Real SensitivityCube::gamma(const string& tradeId, const RiskFactorKey& riskFactorKey) const {
246+
return gamma(cube_->getTradeIndex(tradeId), riskFactorKey);
248247
}
249248

250-
Real SensitivityCube::crossGamma(Size id, Size upIdx_1, Size upIdx_2, Size crossIdx) const {
249+
QuantLib::Real SensitivityCube::crossGamma(QuantLib::Size id, QuantLib::Size upIdx_1, QuantLib::Size upIdx_2,
250+
QuantLib::Size crossIdx) const {
251251
// Approximate f_{xy}|(x,y) by
252252
// ([f_{x}|(x,y + dy)] - [f_{x}|(x,y)]) / dy
253253
// ([f(x + dx,y + dy) - f(x, y + dy)] - [f(x + dx,y) - f(x,y)]) / (dx dy)
254254
Real baseNpv = cube_->getT0(id, 0);
255255
Real upNpv_1 = cube_->get(id, upIdx_1);
256256
Real upNpv_2 = cube_->get(id, upIdx_2);
257257
Real crossNpv = cube_->get(id, crossIdx);
258-
259258
return crossNpv - upNpv_1 - upNpv_2 + baseNpv;
260259
}
261260

262-
std::set<RiskFactorKey> SensitivityCube::relevantRiskFactors() {
263-
std::set<RiskFactorKey> result;
264-
for (auto const i : cube_->relevantScenarios()) {
265-
result.insert(scenarioDescriptions_[i].key1());
266-
if (scenarioDescriptions_[i].type() == ShiftScenarioDescription::Type::Cross)
267-
result.insert(scenarioDescriptions_[i].key2());
268-
}
269-
return result;
270-
}
271-
272-
Real SensitivityCube::crossGamma(const string& tradeId, const crossPair& riskFactorKeyPair) const {
261+
Real SensitivityCube::crossGamma(const Size tradeIdx, const crossPair& riskFactorKeyPair) const {
273262
FactorData upFd_1, upFd_2;
274-
Size upIdx_1, upIdx_2, crossIdx, tradeIdx;
263+
Size upIdx_1, upIdx_2, crossIdx;
275264
std::tie(upFd_1, upFd_2, crossIdx) = index(riskFactorKeyPair, crossFactors_);
276265
upIdx_1 = upFd_1.index;
277266
upIdx_2 = upFd_2.index;
278-
tradeIdx = cube_->getTradeIndex(tradeId);
279-
280267
return crossGamma(tradeIdx, upIdx_1, upIdx_2, crossIdx);
281268
}
282269

283-
bool SensitivityCube::twoSidedDelta(const RiskFactorKey::KeyType& keyType) const {
284-
return twoSidedDeltas_.count(keyType) == 1;
270+
Real SensitivityCube::crossGamma(const std::string& tradeId, const crossPair& riskFactorKeyPair) const {
271+
return crossGamma(cube_->getTradeIndex(tradeId), riskFactorKeyPair);
272+
}
273+
274+
std::set<RiskFactorKey> SensitivityCube::relevantRiskFactors() const {
275+
std::set<RiskFactorKey> result;
276+
for (auto const i : cube_->relevantScenarios()) {
277+
result.insert(scenarioDescriptions_[i].key1());
278+
if (scenarioDescriptions_[i].type() == ShiftScenarioDescription::Type::Cross)
279+
result.insert(scenarioDescriptions_[i].key2());
280+
}
281+
return result;
285282
}
286283

287284
} // namespace analytics

OREAnalytics/orea/cube/sensitivitycube.hpp

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -47,25 +47,22 @@ class SensitivityCube {
4747

4848
struct FactorData {
4949
FactorData() : index(0), shiftSize(0.0) {}
50-
5150
QuantLib::Size index;
5251
QuantLib::Real shiftSize;
5352
string factorDesc;
54-
5553
bool operator<(const FactorData& fd) const { return index < fd.index; }
5654
};
5755

5856
//! Constructor using a vector of scenario descriptions
5957
SensitivityCube(const boost::shared_ptr<NPVSensiCube>& cube,
6058
const std::vector<ShiftScenarioDescription>& scenarioDescriptions,
6159
const std::map<RiskFactorKey, QuantLib::Real>& shiftSizes,
62-
const std::set<RiskFactorKey::KeyType>& twoSidedDeltas = {});
60+
const std::map<RiskFactorKey, std::string>& shiftSchemes);
6361

6462
//! Constructor using a vector of scenario description strings
65-
SensitivityCube(const boost::shared_ptr<NPVSensiCube>& cube,
66-
const std::vector<std::string>& scenarioDescriptions,
63+
SensitivityCube(const boost::shared_ptr<NPVSensiCube>& cube, const std::vector<std::string>& scenarioDescriptions,
6764
const std::map<RiskFactorKey, QuantLib::Real>& shiftSizes,
68-
const std::set<RiskFactorKey::KeyType>& twoSidedDeltas = {});
65+
const std::map<RiskFactorKey, std::string>& shiftSchemes);
6966

7067
//! \name Inspectors
7168
//@{
@@ -79,11 +76,8 @@ class SensitivityCube {
7976
//! Return the map of up trade id's to index in cube
8077
const std::map<std::string, QuantLib::Size>& tradeIdx() const { return cube_->idsAndIndexes(); };
8178

82-
/*! Return factor for given up scenario index or None if given index is not an up scenario (to be reviewed) */
83-
RiskFactorKey upFactor(const Size upIndex) const;
84-
85-
/*! Return factor for given down scenario index or None if given index is not an down scenario (to be reviewed) */
86-
RiskFactorKey downFactor(const Size downIndex) const;
79+
/*! Return factor for given up or down scenario index or None if given index is not an up or down scenario (to be reviewed) */
80+
RiskFactorKey upDownFactor(const Size index) const;
8781

8882
/*! Return factor for given cross scenario index or None if given index is not a cross scenario (to be reviewed) */
8983
crossPair crossFactor(const Size crossIndex) const;
@@ -111,6 +105,9 @@ class SensitivityCube {
111105
//! Returns the absolute shift size for given risk factor \p key
112106
QuantLib::Real shiftSize(const RiskFactorKey& riskFactorKey) const;
113107

108+
//! Returns the shift scheme for given risk factor \p key
109+
std::string shiftScheme(const RiskFactorKey& riskFactorKey) const;
110+
114111
//! Get the base NPV for trade with ID \p tradeId
115112
QuantLib::Real npv(const std::string& tradeId) const;
116113

@@ -123,34 +120,32 @@ class SensitivityCube {
123120
//! Get the NPV for trade given the index of trade and scenario in the cube
124121
QuantLib::Real npv(QuantLib::Size id, QuantLib::Size scenarioIdx) const;
125122

123+
//! Get the trade delta for trade with index \p tradeIdx and for the given risk factor key \p riskFactorKey
124+
QuantLib::Real delta(const Size tradeIdx, const RiskFactorKey& riskFactorKey) const;
125+
126126
//! Get the trade delta for trade with ID \p tradeId and for the given risk factor key \p riskFactorKey
127127
QuantLib::Real delta(const std::string& tradeId, const RiskFactorKey& riskFactorKey) const;
128128

129-
//! Get the trade delta for trade given the index of trade and risk factors in the cube
130-
QuantLib::Real delta(QuantLib::Size id, QuantLib::Size scenarioIdx) const;
131-
132-
//! Get the two sided trade delta for trade given the index of trade and risk factors in the cube
133-
QuantLib::Real delta(QuantLib::Size id, QuantLib::Size upIdx, QuantLib::Size downIdx) const;
129+
//! Get the trade gamma for trade with index \p tradeIdx and for the given risk factor key \p riskFactorKey
130+
QuantLib::Real gamma(const Size tradeIdx, const RiskFactorKey& riskFactorKey) const;
134131

135132
//! Get the trade gamma for trade with ID \p tradeId and for the given risk factor key \p riskFactorKey
136133
QuantLib::Real gamma(const std::string& tradeId, const RiskFactorKey& riskFactorKey) const;
137134

138-
//! Get the trade gamma for trade given the index of trade and risk factors in the cube
139-
QuantLib::Real gamma(QuantLib::Size id, QuantLib::Size upScenarioIdx, QuantLib::Size downScenarioIdx) const;
135+
//! Get the trade cross gamma for trade with ID \p tradeId and for the given risk factor key pair \p
136+
//! riskFactorKeyPair
137+
QuantLib::Real crossGamma(const Size tradeIdx, const crossPair& riskFactorKeyPair) const;
140138

141139
//! Get the trade cross gamma for trade with ID \p tradeId and for the given risk factor key pair \p
142140
//! riskFactorKeyPair
143141
QuantLib::Real crossGamma(const std::string& tradeId, const crossPair& riskFactorKeyPair) const;
144142

145-
//! Get the relevant risk factors
146-
std::set<RiskFactorKey> relevantRiskFactors();
147-
148143
//! Get the trade cross gamma for trade given the index of trade and risk factors in the cube
149-
QuantLib::Real crossGamma(QuantLib::Size id, QuantLib::Size upIdx_1, QuantLib::Size upIdx_2,
144+
QuantLib::Real crossGamma(QuantLib::Size tradeIdx, QuantLib::Size upIdx_1, QuantLib::Size upIdx_2,
150145
QuantLib::Size crossIdx) const;
151146

152-
//! Check if a two sided delta has been configured for the given risk factor key type, \p keyType.
153-
bool twoSidedDelta(const RiskFactorKey::KeyType& keyType) const;
147+
//! Get the relevant risk factors
148+
std::set<RiskFactorKey> relevantRiskFactors() const;
154149

155150
private:
156151
//! Initialise method used by the constructors
@@ -159,6 +154,7 @@ class SensitivityCube {
159154
boost::shared_ptr<NPVSensiCube> cube_;
160155
std::vector<ShiftScenarioDescription> scenarioDescriptions_;
161156
std::map<RiskFactorKey, QuantLib::Real> shiftSizes_;
157+
std::map<RiskFactorKey, std::string> shiftSchemes_;
162158

163159
// Duplication between map keys below and these sets but trade-off
164160
// Means that we can return by reference in public inspector methods
@@ -174,9 +170,6 @@ class SensitivityCube {
174170
// crossFactor)
175171
std::map<crossPair, std::tuple<FactorData, FactorData, QuantLib::Size>> crossFactors_;
176172

177-
// Set of risk factor key types where we want a two-sided delta calculation.
178-
std::set<RiskFactorKey::KeyType> twoSidedDeltas_;
179-
180173
// map of up / down / cross factor index to risk factor key
181174
std::map<QuantLib::Size, RiskFactorKey> upIndexToKey_;
182175
std::map<QuantLib::Size, RiskFactorKey> downIndexToKey_;
@@ -188,4 +181,4 @@ std::ostream& operator<<(std::ostream& out, const SensitivityCube::crossPair& cp
188181

189182
} // namespace analytics
190183
} // namespace ore
191-
#
184+

0 commit comments

Comments
 (0)