24#include <boost/accumulators/statistics/tail_quantile.hpp>
25#include <boost/range/adaptor/indexed.hpp>
29using namespace boost::accumulators;
33using TradeSensiCache = map<Size, map<Size, pair<Real, Real>>>;
37 const set<SensitivityRecord>& srs,
38 const vector<string>& tradeIds) {
44 while (SensitivityRecord sr = ss.
next()) {
47 auto itTrade = find(tradeIds.begin(), tradeIds.end(), sr.tradeId);
48 if (itTrade == tradeIds.end())
52 auto itSr = find_if(srs.begin(), srs.end(), [&sr](
const SensitivityRecord& other) {
53 return sr.key_1 == other.key_1 && sr.key_2 == other.key_2;
57 auto posTrade = distance(tradeIds.begin(), itTrade);
58 auto posSr = distance(srs.begin(), itSr);
61 auto itCacheSr = cache.find(posSr);
62 if (itCacheSr == cache.end()) {
63 cache[posSr][posTrade] = make_pair(sr.delta, sr.gamma);
65 auto itCacheTrade = itCacheSr->second.find(posTrade);
66 if (itCacheTrade == itCacheSr->second.end()) {
67 itCacheSr->second[posTrade] = make_pair(sr.delta, sr.gamma);
69 auto& p = itCacheTrade->second;
89 for (
auto ito = keys.begin(); ito != keys.end(); ito++) {
91 for (
auto iti = keys.begin(); iti != ito; iti++) {
98 TLOG(
"Updating Covariance accumlators for sensitivity record " << index);
103 Real oShift = shiftCube->get(it->first.first, 0, index);
104 if (it->first.first == it->first.second) {
105 it->second(oShift, covariate1 = oShift);
107 Real iShift = shiftCube->get(it->first.second, 0, index);
108 it->second(oShift, covariate1 = iShift);
115 LOG(
"Populate the covariance matrix with the calculated covariances");
118 for (
auto ito = keys.begin(); ito != keys.end(); ito++) {
119 covariance_[i][i] = boost::accumulators::covariance(
accCov_.at(make_pair(ito->second, ito->second)));
121 for (
auto iti = keys.begin(); iti != ito; iti++) {
123 boost::accumulators::covariance(
accCov_.at(make_pair(iti->second, ito->second)));
131 const std::vector<Real>& allFoPnls,
132 const std::vector<Date>& startDates,
133 const std::vector<Date>& endDates) {
134 QL_REQUIRE(allPnls.size() == allFoPnls.size(),
"PNLs and first order PNLs must be the same size");
136 pnls_.reserve(allPnls.size());
137 foPnls_.reserve(allPnls.size());
139 for (Size i = 0; i < allPnls.size(); i++) {
142 pnls_.push_back(allPnls[i]);
143 foPnls_.push_back(allFoPnls[i]);
146 pnls_.shrink_to_fit();
160 ext::shared_ptr<ScenarioShiftCalculator> shiftCalculator) {
163 QuantLib::ext::shared_ptr<Scenario> baseScenario =
hisScenGen_->baseScenario();
165 set<string> keyNames;
166 std::map<std::string, RiskFactorKey> keyNameMapping;
167 for (
auto k : keys) {
172 cube = QuantLib::ext::make_shared<DoublePrecisionInMemoryCube>(
173 baseScenario->asof(), keyNames, vector<Date>(1, baseScenario->asof()),
hisScenGen_->numScenarios());
177 for (Size i = 0; i <
hisScenGen_->numScenarios(); i++) {
178 QuantLib::ext::shared_ptr<Scenario> scenario =
hisScenGen_->next(baseScenario->asof());
181 for (
const auto& [_, key] : keyNameMapping) {
183 Real shift = shiftCalculator->shift(key, *baseScenario, *scenario);
184 cube->set(shift, j, 0, i);
185 }
catch (
const std::exception& e) {
187 "HistocialSensiPnlCalculator",
188 "Shift calcuation failed. Check consistency of simulation and sensi config.",
189 "Error retrieving sensi key '" +
ore::data::to_string(key) +
"' from ssm scenario: '" + e.what())
198 const set<SensitivityRecord>& srs,
const vector<RiskFactorKey>& rfKeys, ext::shared_ptr<NPVCube>& shiftCube,
199 const vector<ext::shared_ptr<PNLCalculator>>& pnlCalculators,
200 const ext::shared_ptr<CovarianceCalculator>& covarianceCalculator,
201 const vector<string>& tradeIds,
const bool includeGammaMargin,
202 const bool includeDeltaMargin,
const bool tradeLevel) {
206 set<pair<RiskFactorKey, Size>> keys;
207 for (
auto it = rfKeys.begin(); it != rfKeys.end(); it++) {
209 QL_REQUIRE(it1 != shiftCube->idsAndIndexes().end(),
210 "Could not find key " << *it <<
" in sensi shift cube keys");
211 keys.insert(make_pair(*it, it1->second));
216 vector<pair<Size, Size>> srsIndex;
217 for (
auto it = srs.begin(); it != srs.end(); it++) {
219 QL_REQUIRE(it1 != shiftCube->idsAndIndexes().end(),
220 "Could not find key " << it->key_1 <<
" in sensi shift cube keys");
221 Size ind_1 = it1->second;
223 if (it->isCrossGamma()) {
225 QL_REQUIRE(it2 != shiftCube->idsAndIndexes().end(),
226 "Could not find key " << it->key_2 <<
" in sensi shift cube keys");
229 srsIndex.push_back(make_pair(ind_1, ind_2));
232 if (covarianceCalculator)
233 covarianceCalculator->initialise(keys);
240 Size nCalculators = pnlCalculators.size();
241 vector<Real> allPnls(
hisScenGen_->numScenarios(), 0.0);
242 vector<Real> allFoPnls(
hisScenGen_->numScenarios(), 0.0);
245 using TradePnLStore = std::vector<std::vector<QuantLib::Real>>;
246 std::vector<TradePnLStore> tradePnls, foTradePnls;
251 tradePnls.reserve(nCalculators);
253 foTradePnls.reserve(nCalculators);
254 for (Size i = 0; i < nCalculators; i++) {
255 tradePnls.push_back(std::vector<std::vector<QuantLib::Real>>());
256 tradePnls.at(i).reserve(nScenarios);
257 foTradePnls.push_back(std::vector<std::vector<QuantLib::Real>>());
258 foTradePnls.at(i).reserve(nScenarios);
263 QuantLib::ext::shared_ptr<Scenario> baseScenario =
hisScenGen_->baseScenario();
267 TradeSensiCache tradeSensiCache;
273 for (Size i = 0; i <
hisScenGen_->numScenarios(); i++) {
277 for (Size j = 0; j < pnlCalculators.size(); j++) {
281 tradePnls.at(j).push_back(vector<Real>(tradeIds.size(), 0.0));
282 foTradePnls.at(j).push_back(vector<Real>(tradeIds.size(), 0.0));
287 for (
const auto elem : srs | boost::adaptors::indexed(0)) {
289 const auto& sr = elem.value();
290 auto j = elem.index();
292 if (!sr.isCrossGamma()) {
294 Real shift = shiftCube->get(srsIndex[j].first, 0, i);
295 Real deltaPnl = shift * sr.delta;
296 Real gammaPnl = 0.5 * shift * shift * sr.gamma;
298 allFoPnls[i] += deltaPnl;
300 if (includeDeltaMargin)
301 allPnls[i] += deltaPnl;
303 if (includeGammaMargin)
304 allPnls[i] += gammaPnl;
306 for (Size k = 0; k < pnlCalculators.size(); k++) {
307 if (pnlCalculators.at(k)->isInTimePeriod(
hisScenGen_->startDates()[i],
309 pnlCalculators.at(k)->writePNL(i,
true, sr.key_1, shift, sr.delta, sr.gamma, deltaPnl,
311 if (!tradeSensiCache.empty()) {
312 auto itSr = tradeSensiCache.find(j);
313 if (itSr != tradeSensiCache.end()) {
314 for (
const auto& kv : itSr->second) {
315 const auto& tradeId = tradeIds[kv.first];
316 Real tradeDelta = kv.second.first;
317 Real tradeDeltaPnl = shift * tradeDelta;
318 Real tradeGamma = kv.second.second;
319 Real tradeGammaPnl = 0.5 * shift * shift * tradeGamma;
321 pnlCalculators.at(k)->writePNL(i,
true, sr.key_1, shift, tradeDelta, tradeGamma,
326 foTradePnls.at(k).back()[kv.first] += tradeDeltaPnl;
327 if (includeDeltaMargin)
328 tradePnls.at(k).back()[kv.first] += tradeDeltaPnl;
329 if (includeGammaMargin)
330 tradePnls.at(k).back()[kv.first] += tradeGammaPnl;
338 Real shift_1 = shiftCube->get(srsIndex[j].first, 0, i);
339 Real shift_2 = shiftCube->get(srsIndex[j].second, 0, i);
340 Real gammaPnl = shift_1 * shift_2 * sr.gamma;
343 if (includeGammaMargin)
344 allPnls[i] += gammaPnl;
346 for (Size j = 0; j < pnlCalculators.size(); j++) {
347 const auto& c = pnlCalculators[j];
349 c->writePNL(i,
true, sr.key_1, shift_1, sr.delta, sr.gamma, 0.0, gammaPnl, sr.key_2, shift_2);
350 if (!tradeSensiCache.empty()) {
351 auto itSr = tradeSensiCache.find(j);
352 if (itSr != tradeSensiCache.end()) {
353 for (
const auto& kv : itSr->second) {
354 const auto& tradeId = tradeIds[kv.first];
355 Real tradeGamma = kv.second.second;
356 Real tradeGammaPnl = shift_1 * shift_2 * tradeGamma;
358 c->writePNL(i,
true, sr.key_1, shift_1, 0.0, tradeGamma, 0.0, tradeGammaPnl,
359 sr.key_2, shift_2, tradeId);
361 if (runTradeLevel && includeGammaMargin) {
362 tradePnls.at(j).back()[kv.first] += tradeGammaPnl;
372 if (covarianceCalculator)
373 covarianceCalculator->updateAccumulators(shiftCube,
hisScenGen_->startDates()[i],
hisScenGen_->endDates()[i], i);
375 if (covarianceCalculator)
376 covarianceCalculator->populateCovariance(keys);
378 LOG(
"Populate the sensitivity backtesting P&L vectors");
379 for (Size j = 0; j < pnlCalculators.size(); j++) {
380 pnlCalculators.at(j)->populatePNLs(allPnls, allFoPnls,
hisScenGen_->startDates(),
hisScenGen_->endDates());
382 pnlCalculators.at(j)->populateTradePNLs(tradePnls.at(j), foTradePnls.at(j));
boost::accumulators::accumulator_set< QuantLib::Real, boost::accumulators::stats< boost::accumulators::tag::covariance< QuantLib::Real, boost::accumulators::tag::covariate1 > > > accumulator
void initialise(const std::set< std::pair< RiskFactorKey, QuantLib::Size > > &keys)
void updateAccumulators(const QuantLib::ext::shared_ptr< NPVCube > &shiftCube, QuantLib::Date startDate, QuantLib::Date endDate, QuantLib::Size index)
QuantLib::Matrix covariance_
void populateCovariance(const std::set< std::pair< RiskFactorKey, QuantLib::Size > > &keys)
std::map< std::pair< QuantLib::Size, QuantLib::Size >, accumulator > accCov_
ore::data::TimePeriod covariancePeriod_
QuantLib::ext::shared_ptr< HistoricalScenarioGenerator > hisScenGen_
QuantLib::ext::shared_ptr< SensitivityStream > sensitivityStream_
Stream of sensitivity records used for the sensitivity based backtest.
void populateSensiShifts(QuantLib::ext::shared_ptr< NPVCube > &cube, const vector< RiskFactorKey > &keys, QuantLib::ext::shared_ptr< ScenarioShiftCalculator > shiftCalculator)
void calculateSensiPnl(const std::set< SensitivityRecord > &srs, const std::vector< RiskFactorKey > &rfKeys, QuantLib::ext::shared_ptr< NPVCube > &shiftCube, const std::vector< QuantLib::ext::shared_ptr< PNLCalculator > > &pnlCalculators, const QuantLib::ext::shared_ptr< CovarianceCalculator > &covarianceCalculator, const std::vector< std::string > &tradeIds={}, const bool includeGammaMargin=true, const bool includeDeltaMargin=true, const bool tradeLevel=false)
const std::vector< QuantLib::Real > & foPnls()
void populateTradePNLs(const TradePnLStore &allPnls, const TradePnLStore &foPnls)
const bool isInTimePeriod(QuantLib::Date startDate, QuantLib::Date endDate)
std::vector< QuantLib::Real > pnls_
ore::data::TimePeriod pnlPeriod_
TradePnLStore foTradePnls_
void populatePNLs(const std::vector< QuantLib::Real > &allPnls, const std::vector< QuantLib::Real > &foPnls, const std::vector< QuantLib::Date > &startDates, const std::vector< QuantLib::Date > &endDates)
std::vector< QuantLib::Real > foPnls_
std::vector< std::vector< QuantLib::Real > > TradePnLStore
Data types stored in the scenario class.
Base Class for streaming SensitivityRecords.
virtual void reset()=0
Resets the stream so that SensitivityRecord objects can be streamed again.
virtual SensitivityRecord next()=0
Returns the next SensitivityRecord in the stream.
bool contains(const Date &d) const
Class for generating sensi pnl.
A cube implementation that stores the cube in memory.
std::vector< std::vector< QuantLib::Real > > TradePnLStore
std::string to_string(const LocationInfo &l)
Structured analytics error.