@@ -184,78 +184,62 @@ void ValuationEngine::buildCube(const boost::shared_ptr<data::Portfolio>& portfo
184184
185185 // loop over Dates, increase cubeDateIndex for each valuation date we hit
186186 int cubeDateIndex = -1 ;
187- for (Size i = 0 ; i < dates.size (); ++i) {
188- Date d = dates[i];
189-
190- // Process auxiliary close-out dates first (may coincide with a valuation date, see below)
191- // Store result at same cubeDateIndex as the previous valuation date's result, but at different cube depth
192- // Differences to valuation date processing above:
193- // Update valuation date and fixings, trades exercisable depending on stickiness
194- bool scenarioUpdated = false ;
195- if (dg_->isCloseOutDate ()[i]) {
196- timer.start ();
197-
198- // update market
199- simMarket_->preUpdate ();
200- if (!mporStickyDate)
201- simMarket_->updateDate (d);
202- simMarket_->updateScenario (d);
203- scenarioUpdated = true ;
204- simMarket_->postUpdate (d, !mporStickyDate); // with fixings only if not sticky
205-
206- recalibrateModels ();
207-
208- timer.stop ();
209- updateTime += timer.elapsed ().wall * 1e-9 ;
210-
211- // loop over trades
212- timer.start ();
213- if (mporStickyDate) // switch off if sticky
214- tradeExercisable (false , trades);
215- QL_REQUIRE (cubeDateIndex >= 0 ,
216- " negative cube date index, ensure that the date grid starts with a valuation date" );
217- runCalculators (true , trades, tradeHasError, calculators, outputCube, outputCubeNettingSet, d,
218- cubeDateIndex, sample, simMarket_->label ());
219- if (mporStickyDate) // switch on again, if sticky
220- tradeExercisable (true , trades);
221- timer.stop ();
222- pricingTime += timer.elapsed ().wall * 1e-9 ;
187+ if (!dg_->closeOutDates ().empty () && mporStickyDate) {
188+ // loop over dates and always do value date and close out date in one run
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_->closeOutDates ()[i];
196+ std::tie (priceTime, upTime) = populateCube (
197+ valueDate, cubeDateIndex, sample, true , false , scenarioUpdated, trades, tradeHasError,
198+ calculators, outputCube, outputCubeNettingSet, counterparties, cptyCalculators, outputCptyCube);
199+ pricingTime += priceTime;
200+ updateTime += upTime;
201+ std::tie (priceTime, upTime) =
202+ populateCube (closeOutDate, cubeDateIndex, sample, false , mporStickyDate, scenarioUpdated,
203+ trades, tradeHasError, calculators, outputCube, outputCubeNettingSet, counterparties,
204+ cptyCalculators, outputCptyCube);
205+ pricingTime += priceTime;
206+ updateTime += upTime;
223207 }
224-
225- // process a valuation date as usual
226- if (dg_->isValuationDate ()[i]) {
227- timer.start ();
228-
229- cubeDateIndex++;
230-
231- // All the steps below from preUpdate() to updateAsd(d) are combined in update(d), but we decompose as
232- // follows simMarket_->update(d);
233- simMarket_->preUpdate ();
234- simMarket_->updateDate (d);
235- // We can skip this step, if we have done that above in the close-out date section
236- if (!scenarioUpdated)
237- simMarket_->updateScenario (d);
238- // Always with fixing update here, in contrast to the close-out date section
239- simMarket_->postUpdate (d, true );
240- // Aggregation scenario data update on valuation dates only
241- simMarket_->updateAsd (d);
242-
243- recalibrateModels ();
244-
245- timer.stop ();
246- updateTime += timer.elapsed ().wall * 1e-9 ;
247-
248- timer.start ();
249- // loop over trades
250- runCalculators (false , trades, tradeHasError, calculators, outputCube, outputCubeNettingSet, d,
251- cubeDateIndex, sample, simMarket_->label ());
252- // loop over counterparty names
253- runCalculators (false , counterparties, cptyCalculators, outputCptyCube, d, cubeDateIndex, sample);
254- timer.stop ();
255- pricingTime += timer.elapsed ().wall * 1e-9 ;
208+ } else {
209+ std::map<Date, size_t > valueDateIndexCache;
210+ for (Size i = 0 ; i < dates.size (); ++i) {
211+ Date d = dates[i];
212+ // Process auxiliary close-out dates first (may coincide with a valuation date, see below)
213+ // Store result at same cubeDateIndex as the corresponding valuation date's result, but at different cube
214+ // depth Differences to valuation date processing above: Update valuation date and fixings, trades
215+ // exercisable depending on stickiness
216+ bool scenarioUpdated = false ;
217+ if (dg_->isCloseOutDate ()[i]) {
218+ double priceTime = 0 ;
219+ double upTime = 0 ;
220+ Date valueDate = dg_->valuationDateFromCloseOutDate (d);
221+ size_t valueDateIndex = valueDateIndexCache[valueDate];
222+ std::tie (priceTime, upTime) = populateCube (
223+ d, valueDateIndex, sample, false , false , scenarioUpdated, trades, tradeHasError,
224+ calculators, outputCube, outputCubeNettingSet, counterparties, cptyCalculators, outputCptyCube);
225+ pricingTime += priceTime;
226+ updateTime += upTime;
227+ scenarioUpdated = true ;
228+ }
229+ if (dg_->isValuationDate ()[i]) {
230+ double priceTime = 0 ;
231+ double upTime = 0 ;
232+ ++cubeDateIndex;
233+ valueDateIndexCache[d] = cubeDateIndex;
234+ std::tie (priceTime, upTime) = populateCube (
235+ d, cubeDateIndex, sample, true , false , scenarioUpdated, trades, tradeHasError,
236+ calculators, outputCube, outputCubeNettingSet, counterparties, cptyCalculators, outputCptyCube);
237+ pricingTime += priceTime;
238+ updateTime += upTime;
239+ scenarioUpdated = true ;
240+ }
256241 }
257242 }
258-
259243 timer.start ();
260244 simMarket_->fixingManager ()->reset ();
261245 fixingTime += timer.elapsed ().wall * 1e-9 ;
@@ -352,5 +336,56 @@ void ValuationEngine::tradeExercisable(bool enable, const std::map<std::string,
352336 }
353337 }
354338}
339+
340+ std::pair<double , double > ValuationEngine::populateCube (
341+ const QuantLib::Date& d, size_t cubeDateIndex, size_t sample, bool isValueDate, bool isStickyDate,
342+ bool scenarioUpdated, const std::map<std::string, boost::shared_ptr<Trade>>& trades,
343+ std::vector<bool >& tradeHasError, const std::vector<boost::shared_ptr<ValuationCalculator>>& calculators,
344+ boost::shared_ptr<analytics::NPVCube>& outputCube, boost::shared_ptr<analytics::NPVCube>& outputCubeNettingSet,
345+ const std::map<string, Size>& counterparties,
346+ const vector<boost::shared_ptr<CounterpartyCalculator>>& cptyCalculators,
347+ boost::shared_ptr<analytics::NPVCube>& outputCptyCube) {
348+ double pricingTime = 0 ;
349+ double updateTime = 0 ;
350+ QL_REQUIRE (cubeDateIndex >= 0 , " first date should be a valuation date" );
351+ cpu_timer timer;
352+ timer.start ();
353+ // All the steps below from preUpdate() to updateAsd(d) are combined in update(d), but we decompose as
354+ // follows simMarket_->update(d);
355+ simMarket_->preUpdate ();
356+ if (isValueDate || !isStickyDate) {
357+ simMarket_->updateDate (d);
358+ }
359+ // We can skip this step, if we have done that above in the close-out date section
360+ if (!scenarioUpdated)
361+ simMarket_->updateScenario (d);
362+ // Always with fixing update here, in contrast to the close-out date section
363+ simMarket_->postUpdate (d, true );
364+ // Aggregation scenario data update on valuation dates only
365+ if (isValueDate) {
366+ simMarket_->updateAsd (d);
367+ }
368+ recalibrateModels ();
369+
370+ timer.stop ();
371+ updateTime += timer.elapsed ().wall * 1e-9 ;
372+
373+ timer.start ();
374+ if (isStickyDate) // switch on again, if sticky
375+ tradeExercisable (false , trades);
376+ // loop over trades
377+ runCalculators (!isValueDate, trades, tradeHasError, calculators, outputCube, outputCubeNettingSet, d, cubeDateIndex,
378+ sample, simMarket_->label ());
379+ if (isStickyDate) // switch on again, if sticky
380+ tradeExercisable (true , trades);
381+ // loop over counterparty names
382+ if (isValueDate) {
383+ runCalculators (false , counterparties, cptyCalculators, outputCptyCube, d, cubeDateIndex, sample);
384+ }
385+ timer.stop ();
386+ pricingTime += timer.elapsed ().wall * 1e-9 ;
387+ return std::make_pair (pricingTime, updateTime);
388+ }
389+
355390} // namespace analytics
356391} // namespace ore
0 commit comments