35#include <ql/errors.hpp>
37#include <boost/timer/timer.hpp>
43using boost::timer::cpu_timer;
44using boost::timer::default_places;
50 const QuantLib::ext::shared_ptr<SimMarket>& simMarket,
51 const set<std::pair<
string, QuantLib::ext::shared_ptr<ModelBuilder>>>& modelBuilders)
52 : today_(today), dg_(dg), simMarket_(simMarket), modelBuilders_(modelBuilders) {
54 QL_REQUIRE(
dg_->size() > 0,
"Error, DateGrid size must be > 0");
55 QL_REQUIRE(today <= dg_->dates().front(),
"ValuationEngine: Error today ("
56 << today <<
") must not be later than first DateGrid date "
57 <<
dg_->dates().front());
58 QL_REQUIRE(
simMarket_,
"ValuationEngine: Error, Null SimMarket");
65 b.second->forceRecalculate();
66 b.second->recalibrate();
71 QuantLib::ext::shared_ptr<analytics::NPVCube> outputCube,
72 vector<QuantLib::ext::shared_ptr<ValuationCalculator>> calculators,
bool mporStickyDate,
73 QuantLib::ext::shared_ptr<analytics::NPVCube> outputCubeNettingSet,
74 QuantLib::ext::shared_ptr<analytics::NPVCube> outputCptyCube,
75 vector<QuantLib::ext::shared_ptr<CounterpartyCalculator>> cptyCalculators,
bool dryRun) {
77 struct SimMarketResetter {
78 SimMarketResetter(QuantLib::ext::shared_ptr<SimMarket> simMarket) : simMarket_(simMarket) {}
79 ~SimMarketResetter() { simMarket_->reset(); }
80 QuantLib::ext::shared_ptr<SimMarket> simMarket_;
83 LOG(
"Build cube with mporStickyDate=" << mporStickyDate <<
", dryRun=" << std::boolalpha << dryRun);
85 QL_REQUIRE(portfolio->size() > 0,
"ValuationEngine: Error portfolio is empty");
87 QL_REQUIRE(outputCube->numIds() == portfolio->trades().size(),
88 "cube x dimension (" << outputCube->numIds() <<
") "
89 <<
"different from portfolio size (" << portfolio->trades().size() <<
")");
91 QL_REQUIRE(outputCube->numDates() ==
dg_->valuationDates().size(),
92 "cube y dimension (" << outputCube->numDates() <<
") "
93 <<
"different from number of valuation dates (" <<
dg_->valuationDates().size()
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() <<
")");
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() <<
")");
108 LOG(
"Starting ValuationEngine for " << portfolio->size() <<
" trades, " << outputCube->samples() <<
" samples and "
109 <<
dg_->size() <<
" dates.");
112 Real updateTime = 0.0;
113 Real pricingTime = 0.0;
114 Real fixingTime = 0.0;
116 LOG(
"Initialise " << calculators.size() <<
" valuation calculators");
117 for (
auto const& c : calculators) {
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...");
130 for (
const auto& [tradeId, trade] : trades) {
131 QL_REQUIRE(!trade->npvCurrency().empty(),
"NPV currency not set for trade " << trade->id());
133 DLOG(
"Initialise wrapper for trade " << trade->id());
134 trade->instrument()->initialise(dates);
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;
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]);
153 frc->unregisterWith(frc->index());
154 trade->instrument()->qlInstrument()->unregisterWith(frc);
156 frc->unregisterWith(Settings::instance().evaluationDate());
157 frc->index()->unregisterWith(Settings::instance().evaluationDate());
158 trade->instrument()->qlInstrument()->unregisterWith(Settings::instance().evaluationDate());
165 LOG(
"Total number of trades = " << portfolio->size());
167 if (!dates.empty() && dates.front() >
simMarket_->asofDate()) {
174 Size nTrades = trades.size();
178 for (Size sample = 0; sample < (dryRun ? std::min<Size>(1, outputCube->samples()) : outputCube->samples());
180 TLOG(
"ValuationEngine: apply scenario sample #" << sample);
182 for (
auto& [tradeId, trade] : portfolio->trades())
183 trade->instrument()->reset();
186 int cubeDateIndex = -1;
187 if (!
dg_->closeOutDates().empty() && mporStickyDate) {
189 const bool scenarioUpdated =
false;
190 for (
size_t i = 0; i <
dg_->valuationDates().
size(); ++i) {
191 double priceTime = 0;
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;
210 std::map<Date, std::vector<size_t>> closeOutDateToValueDateIndex;
211 for (Size i = 0; i < dates.size(); ++i) {
217 bool scenarioUpdated =
false;
218 if (
dg_->isCloseOutDate()[i]) {
219 double priceTime = 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;
233 if (
dg_->isValuationDate()[i]) {
234 double priceTime = 0;
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;
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());
257 fixingTime += timer.elapsed().wall * 1e-9;
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) {
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);
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());
281 LOG(
"ValuationEngine completed: loop " << setprecision(2) << loopTimer.format(2,
"%w") <<
" sec, "
282 <<
"pricing " << pricingTime <<
" sec, "
283 <<
"update " << updateTime <<
" sec "
284 <<
"fixing " << fixingTime);
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);
299 std::vector<bool>& tradeHasError,
300 const std::vector<QuantLib::ext::shared_ptr<ValuationCalculator>>& calculators,
301 QuantLib::ext::shared_ptr<analytics::NPVCube>& outputCube,
302 QuantLib::ext::shared_ptr<analytics::NPVCube>& outputCubeNettingSet,
const Date& d,
303 const Size cubeDateIndex,
const Size sample,
const string& label) {
305 for (
auto& calc : calculators)
306 calc->initScenario();
309 for (
auto tradeIt = trades.begin(); tradeIt != trades.end(); ++tradeIt, ++j) {
310 auto trade = tradeIt->second;
311 if (tradeHasError[j]) {
317 trade->instrument()->updateQlInstruments();
319 for (
auto& calc : calculators)
320 calc->calculate(trade, j,
simMarket_, outputCube, outputCubeNettingSet, d, cubeDateIndex, sample,
322 }
catch (
const std::exception& e) {
326 tradeHasError[j] =
true;
332 const std::vector<QuantLib::ext::shared_ptr<CounterpartyCalculator>>& calculators,
333 QuantLib::ext::shared_ptr<analytics::NPVCube>& cptyCube,
const Date& d,
334 const Size cubeDateIndex,
const Size sample) {
336 for (
const auto& [counterparty, idx] : counterparties) {
337 for (
auto& calc : calculators) {
338 calc->calculate(counterparty, idx,
simMarket_, cptyCube, d, cubeDateIndex, sample, isCloseOutDate);
344 for (
const auto& [tradeId, trade] : trades) {
345 auto t = QuantLib::ext::dynamic_pointer_cast<OptionWrapper>(trade->instrument());
350 t->disableExercise();
356 const QuantLib::Date& d,
size_t cubeDateIndex,
size_t sample,
bool isValueDate,
bool isStickyDate,
357 bool scenarioUpdated,
const std::map<std::string, QuantLib::ext::shared_ptr<Trade>>& trades,
358 std::vector<bool>& tradeHasError,
const std::vector<QuantLib::ext::shared_ptr<ValuationCalculator>>& calculators,
359 QuantLib::ext::shared_ptr<analytics::NPVCube>& outputCube, QuantLib::ext::shared_ptr<analytics::NPVCube>& outputCubeNettingSet,
360 const std::map<string, Size>& counterparties,
361 const vector<QuantLib::ext::shared_ptr<CounterpartyCalculator>>& cptyCalculators,
362 QuantLib::ext::shared_ptr<analytics::NPVCube>& outputCptyCube) {
363 double pricingTime = 0;
364 double updateTime = 0;
365 QL_REQUIRE(cubeDateIndex >= 0,
"first date should be a valuation date");
369 if (isValueDate || !isStickyDate) {
373 if (!scenarioUpdated) {
377 simMarket_->postUpdate(d, !isStickyDate || isValueDate);
385 updateTime += timer.elapsed().wall * 1e-9;
388 if (isStickyDate && !isValueDate)
391 runCalculators(!isValueDate, trades, tradeHasError, calculators, outputCube, outputCubeNettingSet, d, cubeDateIndex,
393 if (isStickyDate && !isValueDate)
397 runCalculators(
false, counterparties, cptyCalculators, outputCptyCube, d, cubeDateIndex, sample);
400 pricingTime += timer.elapsed().wall * 1e-9;
401 return std::make_pair(pricingTime, updateTime);
QuantLib::ext::shared_ptr< ore::data::DateGrid > dg_
QuantLib::ext::shared_ptr< ore::analytics::SimMarket > simMarket_
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)
set< std::pair< std::string, QuantLib::ext::shared_ptr< QuantExt::ModelBuilder > > > modelBuilders_
void buildCube(const QuantLib::ext::shared_ptr< data::Portfolio > &portfolio, QuantLib::ext::shared_ptr< analytics::NPVCube > outputCube, std::vector< QuantLib::ext::shared_ptr< ValuationCalculator > > calculators, bool mporStickyDate=true, QuantLib::ext::shared_ptr< analytics::NPVCube > outputCubeNettingSet=nullptr, QuantLib::ext::shared_ptr< analytics::NPVCube > outputCptyCube=nullptr, std::vector< QuantLib::ext::shared_ptr< CounterpartyCalculator > > cptyCalculators={}, bool dryRun=false)
Build NPV cube.
ValuationEngine(const QuantLib::Date &today, const QuantLib::ext::shared_ptr< ore::data::DateGrid > &dg, const QuantLib::ext::shared_ptr< analytics::SimMarket > &simMarket, const set< std::pair< std::string, QuantLib::ext::shared_ptr< QuantExt::ModelBuilder > > > &modelBuilders=set< std::pair< std::string, QuantLib::ext::shared_ptr< QuantExt::ModelBuilder > > >())
Constructor.
void runCalculators(bool isCloseOutDate, 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 > &outputCubeSensis, const QuantLib::Date &d, const QuantLib::Size cubeDateIndex, const QuantLib::Size sample, const std::string &label="")
void tradeExercisable(bool enable, const std::map< std::string, QuantLib::ext::shared_ptr< ore::data::Trade > > &trades)
void updateProgress(const unsigned long progress, const unsigned long total, const std::string &detail="")
The counterparty cube calculator interface.
Size size(const ValueType &v)
std::string to_string(const LocationInfo &l)
Singleton class to hold global Observation Mode.
A Market class that can be Simulated.
The counterparty cube calculator interface.