Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
sensitivitycube.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2017 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
20
23
24#include <boost/range/adaptor/indexed.hpp>
25
26using namespace QuantLib;
27using namespace std;
28
29using std::make_pair;
30
31namespace ore {
32namespace analytics {
33
34namespace {
35
36// Utility method for lookup in the various maps
37template <class KeyType, class ValueType> ValueType index(const KeyType& k, const map<KeyType, ValueType>& m) {
38
39 auto it = m.find(k);
40 QL_REQUIRE(it != m.end(), "Key, " << k << ", was not found in the sensitivity cube.");
41 return it->second;
42}
43
46 const std::map<ore::analytics::RiskFactorKey, SensitivityCube::FactorData>& m) {
47
48 auto it = m.find(k);
49 QL_REQUIRE(it != m.end(), "Key, " << k << ", was not found in the sensitivity cube.");
50 return it->second;
51}
52
53} // namespace
54
55std::ostream& operator<<(std::ostream& out, const SensitivityCube::crossPair& cp) {
56 return out << cp.first << "-" << cp.second;
57}
58
59SensitivityCube::SensitivityCube(const QuantLib::ext::shared_ptr<NPVSensiCube>& cube,
60 const vector<ShiftScenarioDescription>& scenarioDescriptions,
61 const map<RiskFactorKey, QuantLib::Real>& targetShiftSizes,
62 const map<RiskFactorKey, QuantLib::Real>& actualShiftSizes,
63 const std::map<RiskFactorKey, ShiftScheme>& shiftSchemes)
64 : cube_(cube), scenarioDescriptions_(scenarioDescriptions), targetShiftSizes_(targetShiftSizes),
65 actualShiftSizes_(actualShiftSizes), shiftSchemes_(shiftSchemes) {
66 initialise();
67}
68
69SensitivityCube::SensitivityCube(const QuantLib::ext::shared_ptr<NPVSensiCube>& cube,
70 const vector<string>& scenarioDescriptions,
71 const map<RiskFactorKey, QuantLib::Real>& targetShiftSizes,
72 const map<RiskFactorKey, QuantLib::Real>& actualShiftSizes,
73 const std::map<RiskFactorKey, ShiftScheme>& shiftSchemes)
74 : cube_(cube), targetShiftSizes_(targetShiftSizes), actualShiftSizes_(actualShiftSizes),
75 shiftSchemes_(shiftSchemes) {
76
77 // Populate scenarioDescriptions_ from string descriptions
79 for (const auto& des : scenarioDescriptions) {
81 }
82
83 initialise();
84}
85
87
89 "Expected the first scenario in the sensitivity cube to be of type 'Base'");
90
91 // Populate the scenario lookup map
92 crossPair factorPair;
93 std::map<crossPair, Size> crossFactors;
94 for (Size i = 0; i < scenarioDescriptions_.size(); i++) {
95 auto des = scenarioDescriptions_[i];
96 FactorData fd;
97 // Don't info add for base - missing from maps
98 if (des.type() != ShiftScenarioDescription::Type::Base) {
99 fd.index = i;
100 fd.targetShiftSize = targetShiftSize(des.key1());
101 fd.actualShiftSize = actualShiftSize(des.key1());
102 fd.rfkey = des.key1();
103 fd.factorDesc = deconstructFactor(des.factor1()).second;
104 }
105 scenarioIdx_[des] = i;
106
107 // Populate factors_ = list of factors for which we can calculate a delta/gamma
108 switch (des.type()) {
110 QL_REQUIRE(upFactors_.count(des.key1()) == 0, "Cannot have multiple up factors with "
111 "the same risk factor key["
112 << des.key1() << "]");
113 factors_.insert(des.key1());
114 upFactors_[des.key1()] = fd;
115 upIndexToKey_[fd.index] = des.key1();
116 break;
118 QL_REQUIRE(downFactors_.count(des.key1()) == 0, "Cannot have multiple down factors with "
119 "the same risk factor key ["
120 << des.key1() << "]");
121 factors_.insert(des.key1());
122 downFactors_[des.key1()] = fd;
123 downIndexToKey_[fd.index] = des.key1();
124 break;
126 factorPair = make_pair(des.key1(), des.key2());
127 QL_REQUIRE(crossFactors.count(factorPair) == 0, "Cannot have multiple cross factors with "
128 "the same risk factor key pair ["
129 << des.key1() << ", " << des.key2() << "]");
130 crossFactors[factorPair] = i;
131 crossIndexToKey_[i] = factorPair;
132 break;
133 default:
134 // Do nothing
135 break;
136 }
137 }
138
139 // Add each cross factor to crossFactors with index of the two contributing factors
140 for (auto cf : crossFactors) {
141 FactorData id_1 = index(cf.first.first, upFactors_);
142 FactorData id_2 = index(cf.first.second, upFactors_);
143 crossFactors_[cf.first] = make_tuple(id_1, id_2, cf.second);
144 }
145}
146
147bool SensitivityCube::hasTrade(const string& tradeId) const { return tradeIdx_.count(tradeId) > 0; }
148
150 if (auto k = upIndexToKey_.find(index); k != upIndexToKey_.end()) {
151 return k->second;
152 } else if (auto k = downIndexToKey_.find(index); k != downIndexToKey_.end()) {
153 return k->second;
154 } else {
155 return RiskFactorKey();
156 }
157}
158
160 if (auto k = crossIndexToKey_.find(crossIndex); k != crossIndexToKey_.end()) {
161 return k->second;
162 } else {
163 return std::make_pair(RiskFactorKey(), RiskFactorKey());
164 }
165}
166
167bool SensitivityCube::hasScenario(const ShiftScenarioDescription& scenarioDescription) const {
168 return scenarioIdx_.find(scenarioDescription) != scenarioIdx_.end();
169}
170
171std::string SensitivityCube::factorDescription(const RiskFactorKey& riskFactorKey) const {
172 Size scenarioIdx = index(riskFactorKey, upFactors_).index;
173 return scenarioDescriptions_[scenarioIdx].factor1();
174}
175
176const set<RiskFactorKey>& SensitivityCube::factors() const { return factors_; }
177
178const std::map<SensitivityCube::crossPair, tuple<SensitivityCube::FactorData, SensitivityCube::FactorData, Size>>&
180 return crossFactors_;
181}
182
183Real SensitivityCube::targetShiftSize(const RiskFactorKey& riskFactorKey) const {
184 auto it = targetShiftSizes_.find(riskFactorKey);
185 QL_REQUIRE(it != targetShiftSizes_.end(),
186 "Risk factor, " << riskFactorKey << ", was not found in the target shift sizes.");
187 return it->second;
188}
189
190Real SensitivityCube::actualShiftSize(const RiskFactorKey& riskFactorKey) const {
191 auto it = actualShiftSizes_.find(riskFactorKey);
192 QL_REQUIRE(it != actualShiftSizes_.end(),
193 "Risk factor, " << riskFactorKey << ", was not found in the actual shift sizes.");
194 return it->second;
195}
196
198 auto it = shiftSchemes_.find(riskFactorKey);
199 QL_REQUIRE(it != shiftSchemes_.end(), "Risk factor, " << riskFactorKey << ", was not found in the shift schemes.");
200 return it->second;
201}
202
203Real SensitivityCube::npv(const string& tradeId) const { return cube_->getT0(tradeId, 0); }
204
205Real SensitivityCube::npv(Size id) const { return cube_->getT0(id, 0); }
206
207namespace {
208Real scaling(const SensitivityCube::FactorData& fd) {
209 if (fd.targetShiftSize == 0.0 || fd.actualShiftSize == 0) {
210 WLOG("Sensitivity Calculation: Scaling from different shift size is not possible, if that is configured. No "
211 "shift sizes available for '"
212 << fd.rfkey << "', check consistency of simulation and sensitivity config.");
213 return 1.0;
214 }
215 return fd.targetShiftSize / fd.actualShiftSize;
216}
217} // namespace
218
219Real SensitivityCube::delta(const Size tradeIdx, const RiskFactorKey& riskFactorKey) const {
220 auto s = shiftSchemes_.find(riskFactorKey);
221 QL_REQUIRE(s != shiftSchemes_.end(),
222 "SensitivityCube::delta(" << tradeIdx << ", " << riskFactorKey << "): no shift scheme stored.");
223 if (s->second == ShiftScheme::Forward) {
224 auto fd = index(riskFactorKey, upFactors_);
225 return (cube_->get(tradeIdx, fd.index) - cube_->getT0(tradeIdx, 0)) * scaling(fd);
226 } else if (s->second == ShiftScheme::Backward) {
227 auto fd = index(riskFactorKey, downFactors_);
228 return (cube_->getT0(tradeIdx, 0) - cube_->get(tradeIdx, fd.index)) * scaling(fd);
229 } else if (s->second == ShiftScheme::Central) {
230 auto fdup = index(riskFactorKey, upFactors_);
231 auto fddown = index(riskFactorKey, downFactors_);
232 return (cube_->get(tradeIdx, fdup.index) - cube_->get(tradeIdx, fddown.index)) / 2.0 * scaling(fdup);
233 } else {
234 QL_FAIL("SensitivityCube::delta(" << tradeIdx << ", " << riskFactorKey << "): unknown shift scheme '"
235 << s->second << "'");
236 }
237}
238
239Real SensitivityCube::delta(const string& tradeId, const RiskFactorKey& riskFactorKey) const {
240 return delta(cube_->getTradeIndex(tradeId), riskFactorKey);
241}
242
243Real SensitivityCube::gamma(const Size tradeIdx, const RiskFactorKey& riskFactorKey) const {
244 auto fdup = index(riskFactorKey, upFactors_);
245 auto fddown = index(riskFactorKey, downFactors_);
246 Real baseNpv = cube_->getT0(tradeIdx, 0);
247 Real upNpv = cube_->get(tradeIdx, fdup.index);
248 Real downNpv = cube_->get(tradeIdx, fddown.index);
249 return (upNpv - 2.0 * baseNpv + downNpv) * std::pow(scaling(fdup), 2);
250}
251
252Real SensitivityCube::gamma(const string& tradeId, const RiskFactorKey& riskFactorKey) const {
253 return gamma(cube_->getTradeIndex(tradeId), riskFactorKey);
254}
255
256QuantLib::Real SensitivityCube::crossGamma(QuantLib::Size id, QuantLib::Size upIdx_1, QuantLib::Size upIdx_2,
257 QuantLib::Size crossIdx, QuantLib::Real scaling1,
258 QuantLib::Real scaling2) const {
259 // Approximate f_{xy}|(x,y) by
260 // ([f_{x}|(x,y + dy)] - [f_{x}|(x,y)]) / dy
261 // ([f(x + dx,y + dy) - f(x, y + dy)] - [f(x + dx,y) - f(x,y)]) / (dx dy)
262 Real baseNpv = cube_->getT0(id, 0);
263 Real upNpv_1 = cube_->get(id, upIdx_1);
264 Real upNpv_2 = cube_->get(id, upIdx_2);
265 Real crossNpv = cube_->get(id, crossIdx);
266 return (crossNpv - upNpv_1 - upNpv_2 + baseNpv) * scaling1 * scaling2;
267}
268
269Real SensitivityCube::crossGamma(const Size tradeIdx, const crossPair& riskFactorKeyPair) const {
270 FactorData upFd_1, upFd_2;
271 Size crossIdx;
272 std::tie(upFd_1, upFd_2, crossIdx) = index(riskFactorKeyPair, crossFactors_);
273 return crossGamma(tradeIdx, upFd_1.index, upFd_2.index, crossIdx, scaling(upFd_1), scaling(upFd_2));
274}
275
276Real SensitivityCube::crossGamma(const std::string& tradeId, const crossPair& riskFactorKeyPair) const {
277 return crossGamma(cube_->getTradeIndex(tradeId), riskFactorKeyPair);
278}
279
280std::set<RiskFactorKey> SensitivityCube::relevantRiskFactors() const {
281 std::set<RiskFactorKey> result;
282 for (auto const i : cube_->relevantScenarios()) {
283 result.insert(scenarioDescriptions_[i].key1());
285 result.insert(scenarioDescriptions_[i].key2());
286 }
287 return result;
288}
289
290} // namespace analytics
291} // namespace ore
Data types stored in the scenario class.
Definition: scenario.hpp:48
std::map< RiskFactorKey, ShiftScheme > shiftSchemes_
std::string factorDescription(const RiskFactorKey &riskFactorKey) const
void initialise()
Initialise method used by the constructors.
std::vector< ShiftScenarioDescription > scenarioDescriptions_
std::map< std::string, QuantLib::Size > tradeIdx_
std::map< QuantLib::Size, RiskFactorKey > downIndexToKey_
bool hasTrade(const std::string &tradeId) const
Check if the cube has scenario NPVs for trade with ID tradeId.
std::map< RiskFactorKey, QuantLib::Real > targetShiftSizes_
SensitivityCube(const QuantLib::ext::shared_ptr< NPVSensiCube > &cube, const std::vector< ShiftScenarioDescription > &scenarioDescriptions, const std::map< RiskFactorKey, QuantLib::Real > &targetShiftSizes, const std::map< RiskFactorKey, QuantLib::Real > &actualShiftSizes, const std::map< RiskFactorKey, ShiftScheme > &shiftSchemes)
Constructor using a vector of scenario descriptions.
QuantLib::Real actualShiftSize(const RiskFactorKey &riskFactorKey) const
Returns the absolute actual shift size for given risk factor key.
const std::map< std::string, QuantLib::Size > & tradeIdx() const
Return the map of up trade id's to index in cube.
std::map< ShiftScenarioDescription, QuantLib::Size > scenarioIdx_
QuantLib::Real targetShiftSize(const RiskFactorKey &riskFactorKey) const
Returns the absolute target shift size for given risk factor key.
std::map< RiskFactorKey, FactorData > upFactors_
std::map< crossPair, std::tuple< FactorData, FactorData, QuantLib::Size > > crossFactors_
QuantLib::ext::shared_ptr< NPVSensiCube > cube_
QuantLib::Real gamma(const Size tradeIdx, const RiskFactorKey &riskFactorKey) const
Get the trade gamma for trade with index tradeIdx and for the given risk factor key riskFactorKey.
bool hasScenario(const ShiftScenarioDescription &scenarioDescription) const
Check if the cube has scenario NPVs for scenario with description scenarioDescription.
std::map< QuantLib::Size, crossPair > crossIndexToKey_
std::map< QuantLib::Size, RiskFactorKey > upIndexToKey_
QuantLib::Real delta(const Size tradeIdx, const RiskFactorKey &riskFactorKey) const
Get the trade delta for trade with index tradeIdx and for the given risk factor key riskFactorKey.
std::set< RiskFactorKey > factors_
RiskFactorKey upDownFactor(const Size index) const
const std::vector< ShiftScenarioDescription > & scenarioDescriptions() const
QuantLib::Real crossGamma(const Size tradeIdx, const crossPair &riskFactorKeyPair) const
std::map< RiskFactorKey, QuantLib::Real > actualShiftSizes_
const std::set< RiskFactorKey > & factors() const
Returns the set of risk factor keys for which a delta and gamma can be calculated.
const std::map< crossPair, std::tuple< SensitivityCube::FactorData, SensitivityCube::FactorData, QuantLib::Size > > & crossFactors() const
Returns the set of pairs of risk factor keys for which a cross gamma is available.
ShiftScheme shiftScheme(const RiskFactorKey &riskFactorKey) const
Returns the shift scheme for given risk factor key.
crossPair crossFactor(const Size crossIndex) const
QuantLib::Real npv(const std::string &tradeId) const
Get the base NPV for trade with ID tradeId.
std::map< RiskFactorKey, FactorData > downFactors_
std::pair< RiskFactorKey, RiskFactorKey > crossPair
std::set< RiskFactorKey > relevantRiskFactors() const
Get the relevant risk factors.
ShiftScenarioGenerator::ScenarioDescription ShiftScenarioDescription
#define WLOG(text)
std::ostream & operator<<(std::ostream &out, EquityReturnType t)
pair< RiskFactorKey, string > deconstructFactor(const string &factor)
boost::variant< RandomVariable, EventVec, CurrencyVec, IndexVec, DaycounterVec, Filter > ValueType
holds a grid of NPVs for a list of trades under various scenarios