Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
scriptedinstrumentpricingengine.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2019 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
23
25
28
29namespace ore {
30namespace data {
31
32namespace {
33
34// helper that converts a context value to a ql additional result (i.e. boost::any)
35
36struct anyGetter : public boost::static_visitor<boost::any> {
37 explicit anyGetter(const QuantLib::ext::shared_ptr<Model>& model) : model_(model) {}
38 boost::any operator()(const RandomVariable& x) const { return model_->extractT0Result(x); }
39 boost::any operator()(const EventVec& x) const { return x.value; }
40 boost::any operator()(const IndexVec& x) const { return x.value; }
41 boost::any operator()(const CurrencyVec& x) const { return x.value; }
42 boost::any operator()(const DaycounterVec& x) const { return x.value; }
43 boost::any operator()(const Filter& x) const {
44 QL_FAIL("can not convert Filter to boost::any, unexpected call to anyGetter");
45 }
46 QuantLib::ext::shared_ptr<Model> model_;
47};
48
49boost::any valueToAny(const QuantLib::ext::shared_ptr<Model>& model, const ValueType& v) {
50 return boost::apply_visitor(anyGetter(model), v);
51}
52
53} // namespace
54
55Real ScriptedInstrumentPricingEngine::addMcErrorEstimate(const std::string& label, const ValueType& v) const {
56 if (model_->type() != Model::Type::MC)
57 return Null<Real>();
58 if (v.which() != ValueTypeWhich::Number)
59 return Null<Real>();
60 Real var = variance(QuantLib::ext::get<RandomVariable>(v)).at(0);
61 Real errEst = std::sqrt(var / static_cast<double>(model_->size()));
62 if(!label.empty())
63 results_.additionalResults[label] = errEst;
64 return errEst;
65}
66
68
70
71 // make sure we release the memory allocated by the model after the pricing
72 struct MemoryReleaser {
73 ~MemoryReleaser() { model->releaseMemory(); }
74 QuantLib::ext::shared_ptr<Model> model;
75 };
76 MemoryReleaser memoryReleaser{model_};
77
78 // set up copy of initial context to run the script engine on
79
80 auto workingContext = QuantLib::ext::make_shared<Context>(*context_);
81
82 // set TODAY in the context
83
84 checkDuplicateName(workingContext, "TODAY");
85 Date referenceDate = model_->referenceDate();
86 workingContext->scalars["TODAY"] = EventVec{model_->size(), referenceDate};
87 workingContext->constants.insert("TODAY");
88
89 // clear NPVMem() regression coefficients
90
91 model_->resetNPVMem();
92
93 // if the model uses a separate training phase for NPV(), run this
94
95 if (model_->trainingSamples() != Null<Size>()) {
96 auto trainingContext = QuantLib::ext::make_shared<Context>(*workingContext);
97 trainingContext->resetSize(model_->trainingSamples());
98 struct TrainingPathToggle {
99 TrainingPathToggle(QuantLib::ext::shared_ptr<Model> model) : model(model) { model->toggleTrainingPaths(); }
100 ~TrainingPathToggle() { model->toggleTrainingPaths(); }
101 QuantLib::ext::shared_ptr<Model> model;
102 } toggle(model_);
103 ScriptEngine trainingEngine(ast_, trainingContext, model_);
104 trainingEngine.run(script_, interactive_);
105 }
106
107 // set up script engine and run it
108
109 ScriptEngine engine(ast_, workingContext, model_);
110
111 QuantLib::ext::shared_ptr<PayLog> paylog;
113 paylog = QuantLib::ext::make_shared<PayLog>();
114
116
117 // extract npv result and set it
118
119 auto npv = workingContext->scalars.find(npv_);
120 QL_REQUIRE(npv != workingContext->scalars.end(),
121 "did not find npv result variable '" << npv_ << "' as scalar in context");
122 QL_REQUIRE(npv->second.which() == ValueTypeWhich::Number,
123 "result variable '" << npv_ << "' must be of type NUMBER, got " << npv->second.which());
124 results_.value = model_->extractT0Result(QuantLib::ext::get<RandomVariable>(npv->second));
125 DLOG("got NPV = " << results_.value << " " << model_->baseCcy());
126
127 // set additional results, if this feature is enabled
128
130 results_.errorEstimate = addMcErrorEstimate("NPV_MCErrEst", npv->second);
131 for (auto const& r : additionalResults_) {
132 auto s = workingContext->scalars.find(r.second);
133 bool resultSet = false;
134 if (s != workingContext->scalars.end()) {
135 boost::any t = valueToAny(model_, s->second);
136 results_.additionalResults[r.first] = t;
137 addMcErrorEstimate(r.first + "_MCErrEst", s->second);
138 DLOG("got additional result '" << r.first << "' referencing script variable '" << r.second << "'");
139 resultSet = true;
140 }
141 auto v = workingContext->arrays.find(r.second);
142 if (v != workingContext->arrays.end()) {
143 QL_REQUIRE(!resultSet, "result variable '"
144 << r.first << "' referencing script variable '" << r.second
145 << "' appears both as a scalar and an array, this is unexpected");
146 QL_REQUIRE(!v->second.empty(), "result variable '" << v->first << "' is an empty array.");
147 std::vector<double> tmpdouble;
148 std::vector<std::string> tmpstring;
149 std::vector<QuantLib::Date> tmpdate;
150 for (auto const& d : v->second) {
151 boost::any t = valueToAny(model_, d);
152 if (t.type() == typeid(double))
153 tmpdouble.push_back(boost::any_cast<double>(t));
154 else if (t.type() == typeid(std::string))
155 tmpstring.push_back(boost::any_cast<std::string>(t));
156 else if (t.type() == typeid(QuantLib::Date))
157 tmpdate.push_back(boost::any_cast<QuantLib::Date>(t));
158 else {
159 QL_FAIL("unexpected result type '" << t.type().name() << "' for result variable '" << r.first
160 << "' referencing script variable '" << r.second << "'");
161 }
162 }
163 QL_REQUIRE((int)!tmpdouble.empty() + (int)!tmpstring.empty() + (int)!tmpdate.empty() == 1,
164 "expected exactly one result type in result array '" << v->first << "'");
165 DLOG("got additional result '" << r.first << "' referencing script variable '" << r.second
166 << "' vector of size "
167 << tmpdouble.size() + tmpstring.size() + tmpdate.size());
168 if (!tmpdouble.empty())
169 results_.additionalResults[r.first] = tmpdouble;
170 else if (!tmpstring.empty())
171 results_.additionalResults[r.first] = tmpstring;
172 else if (!tmpdate.empty())
173 results_.additionalResults[r.first] = tmpdate;
174 else {
175 QL_FAIL("got empty result vector for result variable '"
176 << r.first << "' referencing script variable '" << r.second << "', this is unexpected");
177 }
178 std::vector<double> errEst;
179 for (auto const& d : v->second) {
180 errEst.push_back(addMcErrorEstimate(std::string(), d));
181 }
182 if (!errEst.empty() && errEst.front() != Null<Real>())
183 results_.additionalResults[r.first + "_MCErrEst"] = errEst;
184 resultSet = true;
185 }
186 QL_REQUIRE(resultSet, "could not set additional result '" << r.first << "' referencing script variable '"
187 << r.second << "'");
188 }
189
190 // set contents from paylog as additional results
191
192 paylog->consolidateAndSort();
193 std::vector<CashFlowResults> cashFlowResults(paylog->size());
194 std::map<Size, Size> cashflowNumber;
195 for (Size i = 0; i < paylog->size(); ++i) {
196 // cashflow is written as expectation of deflated base ccy amount at T0, converted to flow ccy
197 // with the T0 FX Spot and compounded back to the pay date on T0 curves
198 Real fx = 1.0;
199 Real discount = 1.0;
200 if (paylog->dates().at(i) > model_->referenceDate()) {
201 fx = model_->fxSpotT0(paylog->currencies().at(i), model_->baseCcy());
202 discount = model_->discount(referenceDate, paylog->dates().at(i), paylog->currencies().at(i)).at(0);
203 }
204 cashFlowResults[i].amount = model_->extractT0Result(paylog->amounts().at(i)) / fx / discount;
205 cashFlowResults[i].payDate = paylog->dates().at(i);
206 cashFlowResults[i].currency = paylog->currencies().at(i);
207 cashFlowResults[i].legNumber = paylog->legNos().at(i);
208 cashFlowResults[i].type = paylog->cashflowTypes().at(i);
209 DLOG("got cashflow " << QuantLib::io::iso_date(cashFlowResults[i].payDate) << " "
210 << cashFlowResults[i].currency << cashFlowResults[i].amount << " "
211 << cashFlowResults[i].currency << "-" << model_->baseCcy() << " " << fx << " discount("
212 << cashFlowResults[i].currency << ") " << discount);
213 if (paylog->dates().at(i) > model_->referenceDate()) {
214 addMcErrorEstimate("cashflow_" + std::to_string(paylog->legNos().at(i)) + "_" +
215 std::to_string(++cashflowNumber[paylog->legNos().at(i)]) + "_MCErrEst",
216 paylog->amounts().at(i) /
217 RandomVariable(paylog->amounts().at(i).size(), (fx * discount)));
218 }
219 }
220 results_.additionalResults["cashFlowResults"] = cashFlowResults;
221
222 // set additional results from the model
223
224 results_.additionalResults.insert(model_->additionalResults().begin(), model_->additionalResults().end());
225
226 } // if generate additional results
227
228 // if the engine is amc enabled, add an amc calculator to the additional results
229
230 if (amcEnabled_) {
231 DLOG("add amc calculator to results");
232 results_.additionalResults["amcCalculator"] =
233 QuantLib::ext::static_pointer_cast<AmcCalculator>(QuantLib::ext::make_shared<ScriptedInstrumentAmcCalculator>(
235 }
236
238}
239
240} // namespace data
241} // namespace ore
const Instrument::results * results_
void run(const std::string &script="", bool interactive=false, QuantLib::ext::shared_ptr< PayLog > paylog=nullptr, bool includePastCashflows=false)
const QuantLib::ext::shared_ptr< Context > context_
Real addMcErrorEstimate(const std::string &label, const ValueType &v) const
const std::vector< std::pair< std::string, std::string > > additionalResults_
const QuantLib::ext::shared_ptr< Model > model_
const QuantLib::ext::shared_ptr< ModelCG > model_
Classes and functions for log message handling.
@ data
Definition: log.hpp:77
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
Date referenceDate
Definition: utilities.cpp:442
RandomVariable variance(const RandomVariable &r)
void checkDuplicateName(const QuantLib::ext::shared_ptr< Context > context, const std::string &name)
Definition: utilities.cpp:156
boost::variant< RandomVariable, EventVec, CurrencyVec, IndexVec, DaycounterVec, Filter > ValueType
Definition: value.hpp:60
Serializable Credit Default Swap.
Definition: namespaces.docs:23
amc calculator for scripted trades
scripted instrument pricing engine
scriptengine
some utility functions
Real at(const Size i) const