Build NPV cube.
75 {
76
77 struct SimMarketResetter {
78 SimMarketResetter(QuantLib::ext::shared_ptr<SimMarket> simMarket) : simMarket_(simMarket) {}
79 ~SimMarketResetter() { simMarket_->reset(); }
80 QuantLib::ext::shared_ptr<SimMarket> simMarket_;
82
83 LOG(
"Build cube with mporStickyDate=" << mporStickyDate <<
", dryRun=" << std::boolalpha << dryRun);
84
85 QL_REQUIRE(portfolio->size() > 0, "ValuationEngine: Error portfolio is empty");
86
87 QL_REQUIRE(outputCube->numIds() == portfolio->trades().size(),
88 "cube x dimension (" << outputCube->numIds() << ") "
89 << "different from portfolio size (" << portfolio->trades().size() << ")");
90
91 QL_REQUIRE(outputCube->numDates() ==
dg_->valuationDates().size(),
92 "cube y dimension (" << outputCube->numDates() << ") "
93 <<
"different from number of valuation dates (" <<
dg_->valuationDates().size()
94 << ")");
95
96 if (outputCptyCube) {
97 QL_REQUIRE(outputCptyCube->numIds() == portfolio->counterparties().size() + 1,
98 "cptyCube x dimension (" << outputCptyCube->numIds() << "minus 1) "
99 << "different from portfolio counterparty size ("
100 << portfolio->counterparties().size() << ")");
101
102 QL_REQUIRE(outputCptyCube->numDates() ==
dg_->dates().size(),
"outputCptyCube y dimension ("
103 << outputCptyCube->numDates() << ") "
104 << "different from number of time steps ("
105 <<
dg_->dates().size() <<
")");
106 }
107
108 LOG(
"Starting ValuationEngine for " << portfolio->size() <<
" trades, " << outputCube->samples() <<
" samples and "
109 <<
dg_->size() <<
" dates.");
110
112 Real updateTime = 0.0;
113 Real pricingTime = 0.0;
114 Real fixingTime = 0.0;
115
116 LOG(
"Initialise " << calculators.size() <<
" valuation calculators");
117 for (auto const& c : calculators) {
119 c->initScenario();
120 }
121
122
123 const auto& dates =
dg_->dates();
124 const auto& trades = portfolio->trades();
125 auto& counterparties = outputCptyCube ? outputCptyCube->idsAndIndexes() : std::map<string, Size>();
126 std::vector<bool> tradeHasError(portfolio->size(), false);
127 LOG(
"Initialise state objects...");
128
129 size_t i = 0;
130 for (const auto& [tradeId, trade] : trades) {
131 QL_REQUIRE(!trade->npvCurrency().empty(), "NPV currency not set for trade " << trade->id());
132
133 DLOG(
"Initialise wrapper for trade " << trade->id());
134 trade->instrument()->initialise(dates);
135
137
138
139 try {
140 for (auto& calc : calculators)
141 calc->calculateT0(trade, i,
simMarket_, outputCube, outputCubeNettingSet);
142 } catch (const std::exception& e) {
143 string expMsg = string("T0 valuation error: ") + e.what();
145 tradeHasError[i] = true;
146 }
147
149 for (const Leg& leg : trade->legs()) {
150 for (Size n = 0; n < leg.size(); n++) {
151 QuantLib::ext::shared_ptr<FloatingRateCoupon> frc = QuantLib::ext::dynamic_pointer_cast<FloatingRateCoupon>(leg[n]);
152 if (frc) {
153 frc->unregisterWith(frc->index());
154 trade->instrument()->qlInstrument()->unregisterWith(frc);
155
156 frc->unregisterWith(Settings::instance().evaluationDate());
157 frc->index()->unregisterWith(Settings::instance().evaluationDate());
158 trade->instrument()->qlInstrument()->unregisterWith(Settings::instance().evaluationDate());
159 }
160 }
161 }
162 }
163 ++i;
164 }
165 LOG(
"Total number of trades = " << portfolio->size());
166
167 if (!dates.empty() && dates.front() >
simMarket_->asofDate()) {
168
170 }
171
172 cpu_timer timer;
173 cpu_timer loopTimer;
174 Size nTrades = trades.size();
175
176
177
178 for (Size sample = 0; sample < (dryRun ? std::min<Size>(1, outputCube->samples()) : outputCube->samples());
179 ++sample) {
180 TLOG(
"ValuationEngine: apply scenario sample #" << sample);
181
182 for (auto& [tradeId, trade] : portfolio->trades())
183 trade->instrument()->reset();
184
185
186 int cubeDateIndex = -1;
187 if (!
dg_->closeOutDates().empty() && mporStickyDate) {
188
189 const bool scenarioUpdated = false;
190 for (
size_t i = 0; i <
dg_->valuationDates().
size(); ++i) {
191 double priceTime = 0;
192 double upTime = 0;
193 ++cubeDateIndex;
194 Date valueDate =
dg_->valuationDates()[i];
195 Date closeOutDate =
dg_->closeOutDateFromValuationDate(valueDate);
197 valueDate, cubeDateIndex, sample, true, false, scenarioUpdated, trades, tradeHasError, calculators,
198 outputCube, outputCubeNettingSet, counterparties, cptyCalculators, outputCptyCube);
199 pricingTime += priceTime;
200 updateTime += upTime;
201 if(closeOutDate != Date()){
203 closeOutDate, cubeDateIndex, sample, false, mporStickyDate, scenarioUpdated, trades, tradeHasError,
204 calculators, outputCube, outputCubeNettingSet, counterparties, cptyCalculators, outputCptyCube);
205 pricingTime += priceTime;
206 updateTime += upTime;
207 }
208 }
209 } else {
210 std::map<Date, std::vector<size_t>> closeOutDateToValueDateIndex;
211 for (Size i = 0; i < dates.size(); ++i) {
212 Date d = dates[i];
213
214
215
216
217 bool scenarioUpdated = false;
218 if (
dg_->isCloseOutDate()[i]) {
219 double priceTime = 0;
220 double upTime = 0;
221 QL_REQUIRE(closeOutDateToValueDateIndex.count(d) == 1 && !closeOutDateToValueDateIndex[d].empty(),
222 "Need to calculate valuation date before close out date");
223 for (size_t& valueDateIndex : closeOutDateToValueDateIndex[d]) {
224 std::tie(priceTime, upTime) =
225 populateCube(d, valueDateIndex, sample,
false, mporStickyDate, scenarioUpdated, trades,
226 tradeHasError, calculators, outputCube, outputCubeNettingSet, counterparties,
227 cptyCalculators, outputCptyCube);
228 pricingTime += priceTime;
229 updateTime += upTime;
230 scenarioUpdated = true;
231 }
232 }
233 if (
dg_->isValuationDate()[i]) {
234 double priceTime = 0;
235 double upTime = 0;
236 ++cubeDateIndex;
237 Date closeOutDate =
dg_->closeOutDateFromValuationDate(d);
238 if(closeOutDate != Date())
239 closeOutDateToValueDateIndex[closeOutDate].push_back(cubeDateIndex);
241 d, cubeDateIndex, sample, true, false, scenarioUpdated, trades, tradeHasError, calculators,
242 outputCube, outputCubeNettingSet, counterparties, cptyCalculators, outputCptyCube);
243 pricingTime += priceTime;
244 updateTime += upTime;
245 scenarioUpdated = true;
246 }
247 }
248 }
249
250 std::ostringstream detail;
251 detail << nTrades << " trade" << (nTrades == 1 ? "" : "s") << ", " << outputCube->samples() << " sample"
252 << (outputCube->samples() == 1 ? "" : "s");
253 updateProgress(sample * nTrades, outputCube->samples() * nTrades, detail.str());
254
255 timer.start();
257 fixingTime += timer.elapsed().wall * 1e-9;
258 }
259
260 if (dryRun) {
261 LOG(
"Doing a dry run - fill remaining cube with random values.");
262 for (Size sample = 1; sample < outputCube->samples(); ++sample) {
263 for (Size i = 0; i < dates.size(); ++i) {
264 for (Size j = 0; j < trades.size(); ++j) {
265 for (Size d = 0; d < outputCube->depth(); ++d) {
266
267
268 Real noise = sample < 10 ? static_cast<Real>(i + j + d + sample) : 0.0;
269 outputCube->set(outputCube->getT0(j, d) + noise, j, i, sample, d);
270 }
271 }
272 }
273 }
274 }
275
276 std::ostringstream detail;
277 detail << nTrades << " trade" << (nTrades == 1 ? "" : "s") << ", " << outputCube->samples() << " sample"
278 << (outputCube->samples() == 1 ? "" : "s");
279 updateProgress(outputCube->samples() * nTrades, outputCube->samples() * nTrades, detail.str());
280 loopTimer.stop();
281 LOG(
"ValuationEngine completed: loop " << setprecision(2) << loopTimer.format(2,
"%w") <<
" sec, "
282 << "pricing " << pricingTime << " sec, "
283 << "update " << updateTime << " sec "
284 << "fixing " << fixingTime);
285
286
287 i = 0;
288 for (auto& [tradeId, trade] : trades) {
289 if (tradeHasError[i]) {
290 ALOG(
"setting all results in output cube to zero for trade '"
291 << tradeId << "' since there was at least one error during simulation");
292 outputCube->remove(i);
293 }
294 i++;
295 }
296}
std::pair< double, double > populateCube(const QuantLib::Date &d, size_t cubeDateIndex, size_t sample, bool isValueDate, bool isStickyDate, bool scenarioUpdated, const std::map< std::string, QuantLib::ext::shared_ptr< ore::data::Trade > > &trades, std::vector< bool > &tradeHasError, const std::vector< QuantLib::ext::shared_ptr< ValuationCalculator > > &calculators, QuantLib::ext::shared_ptr< analytics::NPVCube > &outputCube, QuantLib::ext::shared_ptr< analytics::NPVCube > &outputCubeNettingSet, const std::map< std::string, size_t > &counterparties, const std::vector< QuantLib::ext::shared_ptr< CounterpartyCalculator > > &cptyCalculators, QuantLib::ext::shared_ptr< analytics::NPVCube > &outputCptyCube)
void updateProgress(const unsigned long progress, const unsigned long total, const std::string &detail="")
Size size(const ValueType &v)