Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
historicalpnlgenerator.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2018 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
26
27#include <boost/range/adaptor/indexed.hpp>
28
35using QuantLib::Real;
36using QuantLib::io::iso_date;
37using std::map;
38using std::set;
39using std::string;
40using std::vector;
41
42namespace ore {
43namespace analytics {
44
46 const string& baseCurrency, const QuantLib::ext::shared_ptr<Portfolio>& portfolio,
47 const QuantLib::ext::shared_ptr<ScenarioSimMarket>& simMarket,
48 const QuantLib::ext::shared_ptr<HistoricalScenarioGenerator>& hisScenGen, const QuantLib::ext::shared_ptr<NPVCube>& cube,
49 const set<std::pair<string, QuantLib::ext::shared_ptr<QuantExt::ModelBuilder>>>& modelBuilders, bool dryRun)
50 : useSingleThreadedEngine_(true), portfolio_(portfolio), simMarket_(simMarket), hisScenGen_(hisScenGen),
51 cube_(cube), dryRun_(dryRun),
52 npvCalculator_([&baseCurrency]() -> std::vector<QuantLib::ext::shared_ptr<ValuationCalculator>> {
53 return {QuantLib::ext::make_shared<NPVCalculator>(baseCurrency)};
54 }) {
55
56 // Check the cube's dimensions
57 // Possibly easier to create the cube here but then need to enforce a cube type or make a templated class.
58 QL_REQUIRE(cube_->asof() == simMarket_->asofDate(),
59 "The cube's as of date (" << iso_date(cube_->asof()) << ") should equal that of the simulation market ("
60 << iso_date(simMarket_->asofDate()) << ")");
61
62 std::set<std::string> cubeIds;
63 for (const auto& [id, pos] : cube->idsAndIndexes()) {
64 cubeIds.insert(id);
65 }
66 QL_REQUIRE(cubeIds == portfolio_->ids(), "The cube ids should equal the portfolio ids");
67 QL_REQUIRE(cube_->samples() == hisScenGen_->numScenarios(),
68 "The cube sample size (" << cube_->samples() << ") should equal the number of historical scenarios ("
69 << hisScenGen_->numScenarios() << ")");
70 QL_REQUIRE(cube_->numDates() == 1, "The cube should have exactly one date");
71 QL_REQUIRE(cube_->depth() == 1, "The cube should have a depth of one");
72
73 simMarket_->scenarioGenerator() = hisScenGen_;
74
75 auto grid = QuantLib::ext::make_shared<DateGrid>();
76 valuationEngine_ = QuantLib::ext::make_shared<ValuationEngine>(simMarket_->asofDate(), grid, simMarket_, modelBuilders);
77}
78
79HistoricalPnlGenerator::HistoricalPnlGenerator(
80 const string& baseCurrency, const QuantLib::ext::shared_ptr<Portfolio>& portfolio,
81 const QuantLib::ext::shared_ptr<HistoricalScenarioGenerator>& hisScenGen, const QuantLib::ext::shared_ptr<EngineData>& engineData,
82 const Size nThreads, const Date& today, const QuantLib::ext::shared_ptr<ore::data::Loader>& loader,
83 const QuantLib::ext::shared_ptr<ore::data::CurveConfigurations>& curveConfigs,
84 const QuantLib::ext::shared_ptr<ore::data::TodaysMarketParameters>& todaysMarketParams, const std::string& configuration,
85 const QuantLib::ext::shared_ptr<ore::analytics::ScenarioSimMarketParameters>& simMarketData,
86 const QuantLib::ext::shared_ptr<ReferenceDataManager>& referenceData, const IborFallbackConfig& iborFallbackConfig,
87 bool dryRun, const std::string& context)
88 : useSingleThreadedEngine_(false), portfolio_(portfolio), hisScenGen_(hisScenGen), engineData_(engineData),
89 nThreads_(nThreads), today_(today), loader_(loader), curveConfigs_(curveConfigs),
90 todaysMarketParams_(todaysMarketParams), configuration_(configuration), simMarketData_(simMarketData),
91 referenceData_(referenceData), iborFallbackConfig_(iborFallbackConfig), dryRun_(dryRun), context_(context),
92 npvCalculator_([&baseCurrency]() -> std::vector<QuantLib::ext::shared_ptr<ValuationCalculator>> {
93 return {QuantLib::ext::make_shared<NPVCalculator>(baseCurrency)};
94 }) {}
95
96void HistoricalPnlGenerator::generateCube(const QuantLib::ext::shared_ptr<ScenarioFilter>& filter) {
97
98 DLOG("Filling historical P&L cube for " << portfolio_->size() << " trades and " << hisScenGen_->numScenarios()
99 << " scenarios.");
100
101 if (useSingleThreadedEngine_) {
102
103 valuationEngine_->unregisterAllProgressIndicators();
104 for (auto const& i : this->progressIndicators()) {
105 i->reset();
106 valuationEngine_->registerProgressIndicator(i);
107 }
108
109 hisScenGen_->reset();
110 simMarket_->filter() = filter;
111 simMarket_->reset();
112 simMarket_->scenarioGenerator() = hisScenGen_;
113 hisScenGen_->baseScenario() = simMarket_->baseScenario();
114 valuationEngine_->buildCube(portfolio_, cube_, npvCalculator_(), true, nullptr, nullptr, {}, dryRun_);
115
116 } else {
118 nThreads_, today_, QuantLib::ext::make_shared<ore::analytics::DateGrid>(), hisScenGen_->numScenarios(), loader_,
119 hisScenGen_, engineData_, curveConfigs_, todaysMarketParams_, configuration_, simMarketData_, false, false,
120 filter, referenceData_, iborFallbackConfig_, true, true, true, {}, {}, {}, context_);
121 for (auto const& i : this->progressIndicators()) {
122 i->reset();
124 }
125 engine.buildCube(portfolio_, npvCalculator_, {}, true, dryRun_);
126 cube_ = QuantLib::ext::make_shared<JointNPVCube>(engine.outputCubes(), portfolio_->ids(), true);
127 }
128
129 DLOG("Historical P&L cube generated");
130}
131
132vector<Real> HistoricalPnlGenerator::pnl(const TimePeriod& period, const set<pair<string, Size>>& tradeIds) const {
133
134 // Create result with enough space
135 vector<Real> pnls;
136 pnls.reserve(cube_->samples());
137
138 // Look up the date index once
139 Size dateIdx = indexAsof();
140
141 for (Size s = 0; s < cube_->samples(); ++s) {
142 // Start and end of period relating to current sample
143 Date start = hisScenGen_->startDates()[s];
144 Date end = hisScenGen_->endDates()[s];
145
146 if (period.contains(start) && period.contains(end)) {
147 Real pnl = 0.0;
148 for (const auto& tradeId : tradeIds) {
149 pnl -= cube_->getT0(tradeId.second);
150 pnl += cube_->get(tradeId.second, dateIdx, s);
151 }
152 pnls.push_back(pnl);
153 }
154 }
155
156 pnls.shrink_to_fit();
157
158 return pnls;
159}
160
161vector<Real> HistoricalPnlGenerator::pnl(const TimePeriod& period) const { return pnl(period, tradeIdIndexPairs()); }
162
163vector<Real> HistoricalPnlGenerator::pnl(const set<pair<string, Size>>& tradeIds) const {
164 return pnl(timePeriod(), tradeIds);
165}
166
167vector<Real> HistoricalPnlGenerator::pnl() const { return pnl(timePeriod()); }
168
170
171TradePnlStore HistoricalPnlGenerator::tradeLevelPnl(const TimePeriod& period,
172 const set<pair<string, Size>>& tradeIds) const {
173
174 // Create result with enough space
175 TradePnlStore pnls;
176 pnls.reserve(cube_->samples());
177
178 // Look up the date index once
179 Size dateIdx = indexAsof();
180
181 // Store t0 npvs on first pass
182 vector<Real> t0Npvs;
183 t0Npvs.reserve(tradeIds.size());
184
185 for (Size s = 0; s < cube_->samples(); ++s) {
186
187 // Start and end of period relating to current sample
188 Date start = hisScenGen_->startDates()[s];
189 Date end = hisScenGen_->endDates()[s];
190
191 if (period.contains(start) && period.contains(end)) {
192
193 // Store the t0 NPVs on first pass.
194 if (t0Npvs.empty()) {
195 for (const auto& p : tradeIds) {
196 t0Npvs.push_back(cube_->getT0(p.second));
197 }
198 }
199
200 // Add vector to hold the trade level P&Ls
201 pnls.push_back(vector<Real>(tradeIds.size(), 0.0));
202
203 // Populate the trade level P&L vector
204 for (const auto elem : tradeIds | boost::adaptors::indexed(0)) {
205 auto idx = elem.index();
206 pnls.back()[idx] = cube_->get(elem.value().second, dateIdx, s) - t0Npvs[idx];
207 }
208 }
209 }
210
211 pnls.shrink_to_fit();
212
213 return pnls;
214}
215
216TradePnlStore HistoricalPnlGenerator::tradeLevelPnl(const ore::data::TimePeriod& period) const {
217 return tradeLevelPnl(period, tradeIdIndexPairs());
218}
219
220TradePnlStore HistoricalPnlGenerator::tradeLevelPnl(const set<pair<string, Size>>& tradeIds) const {
221 return tradeLevelPnl(timePeriod(), tradeIds);
222}
223
224TradePnlStore HistoricalPnlGenerator::tradeLevelPnl() const { return tradeLevelPnl(timePeriod()); }
225
226const QuantLib::ext::shared_ptr<NPVCube>& HistoricalPnlGenerator::cube() const { return cube_; }
227
228set<pair<string, Size>> HistoricalPnlGenerator::tradeIdIndexPairs() const {
229 set<pair<string, Size>> res;
230 Size i = 0;
231 for (const auto& [id, trade]: portfolio_->trades())
232 res.insert(std::make_pair(id, i++));
233 return res;
234}
235
236TimePeriod HistoricalPnlGenerator::timePeriod() const {
237 vector<Date> dates{hisScenGen_->startDates().front(), hisScenGen_->endDates().back()};
238 return TimePeriod(dates);
239}
240
241Size HistoricalPnlGenerator::indexAsof() const {
242 Date asof = useSingleThreadedEngine_ ? simMarket_->asofDate() : today_;
243 const auto& dates = cube_->dates();
244 auto it = std::find(dates.begin(), dates.end(), asof);
245 QL_REQUIRE(it != dates.end(), "Can't find an index for asof date " << asof << " in cube");
246 return std::distance(dates.begin(), it);
247}
248
249} // namespace analytics
250} // namespace ore
std::vector< std::vector< QuantLib::Real > > TradePnlStore
HistoricalPnlGenerator(const std::string &baseCurrency, const QuantLib::ext::shared_ptr< ore::data::Portfolio > &portfolio, const QuantLib::ext::shared_ptr< ScenarioSimMarket > &simMarket, const QuantLib::ext::shared_ptr< HistoricalScenarioGenerator > &hisScenGen, const QuantLib::ext::shared_ptr< NPVCube > &cube, const set< std::pair< string, QuantLib::ext::shared_ptr< QuantExt::ModelBuilder > > > &modelBuilders={}, bool dryRun=false)
std::vector< QuantLib::ext::shared_ptr< ore::analytics::NPVCube > > outputCubes() const
void buildCube(const QuantLib::ext::shared_ptr< ore::data::Portfolio > &portfolio, const std::function< std::vector< QuantLib::ext::shared_ptr< ore::analytics::ValuationCalculator > >()> &calculators, const std::function< std::vector< QuantLib::ext::shared_ptr< ore::analytics::CounterpartyCalculator > >()> &cptyCalculators={}, bool mporStickyDate=true, bool dryRun=false)
void registerProgressIndicator(const QuantLib::ext::shared_ptr< ProgressIndicator > &indicator)
const std::vector< Date > & startDates() const
bool contains(const Date &d) const
SafeStack< Filter > filter
Context & context_
Class for generating portfolio P&Ls based on historical scenarios.
A cube implementation that stores the cube in memory.
join n cubes in terms of stored ids
#define DLOG(text)
multi-threaded valuation engine
HistoricalPnlGenerator::TradePnlStore TradePnlStore
vector< string > curveConfigs
Date asof(14, Jun, 2018)
The counterparty cube calculator interface.