Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
staticanalyser.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
22
24
26
27#include <ql/errors.hpp>
28
29#include <algorithm>
30
31#define TRACE(message, n) \
32 { DLOGGERSTREAM(message << " at " << to_string((n).locationInfo) << '\n'); }
33
34namespace ore {
35namespace data {
36
37namespace {
38class ASTIndexExtractor : public AcyclicVisitor,
39 public Visitor<ASTNode>,
40 public Visitor<FunctionPayNode>,
41 public Visitor<FunctionLogPayNode>,
42 public Visitor<FunctionNpvNode>,
43 public Visitor<FunctionNpvMemNode>,
44 public Visitor<FunctionDiscountNode>,
45 public Visitor<FunctionFwdCompNode>,
46 public Visitor<FunctionFwdAvgNode>,
47 public Visitor<FunctionAboveProbNode>,
48 public Visitor<FunctionBelowProbNode>,
49 public Visitor<VarEvaluationNode> {
50public:
51 ASTIndexExtractor(std::map<std::string, std::set<QuantLib::Date>>& indexEvalDates,
52 std::map<std::string, std::set<QuantLib::Date>>& indexFwdDates,
53 std::map<std::string, std::set<QuantLib::Date>>& payObsDates,
54 std::map<std::string, std::set<QuantLib::Date>>& payPayDates,
55 std::map<std::string, std::set<QuantLib::Date>>& discountObsDates,
56 std::map<std::string, std::set<QuantLib::Date>>& discountPayDates,
57 std::map<std::string, std::set<QuantLib::Date>>& fwdCompAvgFixingDates,
58 std::map<std::string, std::set<QuantLib::Date>>& fwdCompAvgEvalDates,
59 std::map<std::string, std::set<QuantLib::Date>>& fwdCompAvgStartEndDates,
60 std::map<std::string, std::set<QuantLib::Date>>& probFixingDates,
61 std::set<QuantLib::Date>& regressionDates, Context& context, ASTNode*& lastVisitedNode)
62 : indexEvalDates_(indexEvalDates), indexFwdDates_(indexFwdDates), payObsDates_(payObsDates),
63 payPayDates_(payPayDates), discountObsDates_(discountObsDates), discountPayDates_(discountPayDates),
64 fwdCompAvgFixingDates_(fwdCompAvgFixingDates), fwdCompAvgEvalDates_(fwdCompAvgEvalDates),
65 fwdCompAvgStartEndDates_(fwdCompAvgStartEndDates), probFixingDates_(probFixingDates),
66 regressionDates_(regressionDates), context_(context), lastVisitedNode_(lastVisitedNode) {}
67
68 void checkpoint(ASTNode& n) { lastVisitedNode_ = &n; }
69
70 void visit(ASTNode& n) override {
71 checkpoint(n);
72 for (auto p : n.args)
73 if (p)
74 p->accept(*this);
75 }
76
77 std::string getVariableName(const ASTNodePtr p) {
78 checkpoint(*p);
79 auto var = QuantLib::ext::dynamic_pointer_cast<VariableNode>(p);
80 if (var) {
81 TRACE("getVariableName(" << var->name << ")", *p);
82 return var->name;
83 }
84 QL_FAIL("not a variable identifier");
85 }
86
87 // returns Null<Real>() if not a constant number node
88 Real getConstantNumber(const ASTNodePtr p) {
89 checkpoint(*p);
90 auto cn = QuantLib::ext::dynamic_pointer_cast<ConstantNumberNode>(p);
91 if (cn) {
92 TRACE("getConstantNumber(" << cn->value << ")", *p);
93 return cn->value;
94 }
95 return Null<Real>();
96 }
97
98 std::vector<ValueType> getVariableValues(const std::string& name) {
99 std::vector<ValueType> result;
100 if (name.empty())
101 return result;
102 bool found = false;
103 // TODAY is allowed as an argument for a VarEvaluationNode, but actually not defined
104 // in the context necessarily when running the static analysis, and we don't need to add
105 // it to any results of the static analysis, so we just return an empty vector.
106 if (name == "TODAY") {
107 found = true;
108 }
109 auto scalar = context_.scalars.find(name);
110 if (scalar != context_.scalars.end()) {
111 result = std::vector<ValueType>(1, scalar->second);
112 found = true;
113 }
114 auto array = context_.arrays.find(name);
115 if (array != context_.arrays.end()) {
116 result = array->second;
117 found = true;
118 }
119 QL_REQUIRE(found, "variable '" << name << "' is not defined.");
120 // there might be null dates in the result, which we remove: Either they are not used
121 // in the script execution step or will throw an error there, but we can't deduce any
122 // useful information during the static analysis, so we just ignore them
123 result.erase(std::remove_if(result.begin(), result.end(),
124 [](const ValueType& v) {
125 return v.which() == ValueTypeWhich::Event &&
126 QuantLib::ext::get<EventVec>(v).value == Date();
127 }),
128 result.end());
129 return result;
130 }
131
132 void visit(VarEvaluationNode& n) override {
133 checkpoint(n);
134 std::string indexVariable = getVariableName(n.args[0]);
135 std::string datesVariable = getVariableName(n.args[1]);
136 std::string fwdDatesVariable = n.args[2] ? getVariableName(n.args[2]) : "";
137 checkpoint(n);
138 TRACE("varEvaluation(" << indexVariable << ", " << datesVariable << ", " << fwdDatesVariable << ")", n);
139 std::vector<ValueType> indexValues = getVariableValues(indexVariable);
140 std::vector<ValueType> datesValues = getVariableValues(datesVariable);
141 std::vector<ValueType> fwdDatesValues =
142 fwdDatesVariable.empty() ? std::vector<ValueType>() : getVariableValues(fwdDatesVariable);
143 for (auto const& v : indexValues)
144 TRACE("got index " << v, n);
145 for (auto const& v : datesValues)
146 TRACE("got date " << v, n);
147 for (auto const& v : fwdDatesValues)
148 TRACE("got fwd date " << v, n);
149 for (auto const& i : indexValues) {
150 for (auto const& d : datesValues) {
151 QL_REQUIRE(i.which() == ValueTypeWhich::Index, "index expected on lhs");
152 QL_REQUIRE(d.which() == ValueTypeWhich::Event, "event expected as obs date");
153 indexEvalDates_[QuantLib::ext::get<IndexVec>(i).value].insert(QuantLib::ext::get<EventVec>(d).value);
154 }
155 for (auto const& d : fwdDatesValues) {
156 QL_REQUIRE(i.which() == ValueTypeWhich::Index, "index expected on lhs");
157 QL_REQUIRE(d.which() == ValueTypeWhich::Event, "event expected as fwd date");
158 indexFwdDates_[QuantLib::ext::get<IndexVec>(i).value].insert(QuantLib::ext::get<EventVec>(d).value);
159 }
160 }
161 visit(static_cast<ASTNode&>(n));
162 }
163
164 void processPayOrDiscountNode(std::map<std::string, std::set<QuantLib::Date>>& obsDates,
165 std::map<std::string, std::set<QuantLib::Date>>& payDates, ASTNode& n,
166 const Size indexObs, const Size indexPay, const Size indexCurr) {
167 checkpoint(n);
168 std::string obsDateVariable = getVariableName(n.args[indexObs]);
169 std::vector<ValueType> obsDateValues = getVariableValues(obsDateVariable);
170 std::string payDateVariable = getVariableName(n.args[indexPay]);
171 std::vector<ValueType> payDateValues = getVariableValues(payDateVariable);
172 std::string currencyVariable = getVariableName(n.args[indexCurr]);
173 std::vector<ValueType> currencyValues = getVariableValues(currencyVariable);
174 checkpoint(n);
175 TRACE("pay(" << obsDateVariable << "," << payDateVariable << "," << currencyVariable << ")", n);
176 for (auto const& v : obsDateValues)
177 TRACE("got obs date " << v, n);
178 for (auto const& v : payDateValues)
179 TRACE("got pay date " << v, n);
180 for (auto const& v : currencyValues)
181 TRACE("got currency " << v, n);
182 for (auto const& v : obsDateValues) {
183 QL_REQUIRE(v.which() == ValueTypeWhich::Event, "date expected as arg #" << (indexObs + 1));
184 for (auto const& c : currencyValues) {
185 QL_REQUIRE(c.which() == ValueTypeWhich::Currency, "currency expected as arg #" << (indexCurr + 1));
186 obsDates[QuantLib::ext::get<CurrencyVec>(c).value].insert(QuantLib::ext::get<EventVec>(v).value);
187 }
188 }
189 for (auto const& v : payDateValues) {
190 QL_REQUIRE(v.which() == ValueTypeWhich::Event, "date expected as arg #" << (indexPay + 1));
191 for (auto const& c : currencyValues) {
192 QL_REQUIRE(c.which() == ValueTypeWhich::Currency, "currency expected as arg #" << (indexCurr + 1));
193 payDates[QuantLib::ext::get<CurrencyVec>(c).value].insert(QuantLib::ext::get<EventVec>(v).value);
194 }
195 }
196 visit(static_cast<ASTNode&>(n));
197 }
198
199 void visit(FunctionPayNode& n) override { processPayOrDiscountNode(payObsDates_, payPayDates_, n, 1, 2, 3); }
200 void visit(FunctionLogPayNode& n) override { processPayOrDiscountNode(payObsDates_, payPayDates_, n, 1, 2, 3); }
201
202 void visit(FunctionDiscountNode& n) override {
203 processPayOrDiscountNode(discountObsDates_, discountPayDates_, n, 0, 1, 2);
204 }
205
206 void processFwdCompAvg(ASTNode& n) {
207 checkpoint(n);
208 std::string indexVariable = getVariableName(n.args[0]);
209 std::vector<ValueType> indexValues = getVariableValues(indexVariable);
210 std::string obsDateVariable = getVariableName(n.args[1]);
211 std::vector<ValueType> obsDateValues = getVariableValues(obsDateVariable);
212 std::string startDateVariable = getVariableName(n.args[2]);
213 std::vector<ValueType> startDateValues = getVariableValues(startDateVariable);
214 std::string endDateVariable = getVariableName(n.args[3]);
215 std::vector<ValueType> endDateValues = getVariableValues(endDateVariable);
216 std::vector<ValueType> lookbackValues{RandomVariable(1, 0.0)};
217 std::vector<ValueType> fixingDaysValues{RandomVariable(1, 0.0)};
218 if (n.args[6]) {
219 auto value = getConstantNumber(n.args[6]);
220 if (value != Null<Real>())
221 lookbackValues = std::vector<ValueType>{RandomVariable(1, value)};
222 else {
223 std::string lookbackVariable = getVariableName(n.args[6]);
224 lookbackValues = getVariableValues(lookbackVariable);
225 }
226 }
227 if (n.args[8]) {
228 auto value = getConstantNumber(n.args[8]);
229 if (value != Null<Real>()) {
230 fixingDaysValues = std::vector<ValueType>{RandomVariable(1, value)};
231 } else {
232 std::string fixingDaysVariable = getVariableName(n.args[8]);
233 fixingDaysValues = getVariableValues(fixingDaysVariable);
234 }
235 }
236 checkpoint(n);
237 TRACE("fwd[comp|avg](" << indexVariable << "," << obsDateVariable << "," << startDateVariable << ","
238 << endDateVariable << ")",
239 n);
240 for (auto const& i : indexValues)
241 TRACE("got index " << i, n);
242 for (auto const& v : obsDateValues)
243 TRACE("got obs date " << v, n);
244 for (auto const& v : startDateValues)
245 TRACE("got start date " << v, n);
246 for (auto const& v : endDateValues)
247 TRACE("got end date " << v, n);
248 for (auto const& i : indexValues) {
249 QL_REQUIRE(i.which() == ValueTypeWhich::Index, "index expected as arg #1");
250 std::string indexName = QuantLib::ext::get<IndexVec>(i).value;
251 IndexInfo ind(indexName);
252 auto on = QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(ind.irIbor());
253 // ignore indices that are not on, those are not allowed in the end, but might still occur
254 // here, wenn an array contains both libor and on indices
255 if (!on) {
256 DLOG("skipping index " << i << "since it is not an overnight index.");
257 continue;
258 }
259 for (auto const& v : obsDateValues) {
260 QL_REQUIRE(v.which() == ValueTypeWhich::Event, "date expected as arg #2 (obsdate)");
261 fwdCompAvgEvalDates_[indexName].insert(QuantLib::ext::get<EventVec>(v).value);
262 }
263 Date minStart = Date::maxDate();
264 Date maxEnd = Date::minDate();
265 for (auto const& v : startDateValues) {
266 QL_REQUIRE(v.which() == ValueTypeWhich::Event, "date expected as arg #3 (startdate)");
267 minStart = std::min(minStart, QuantLib::ext::get<EventVec>(v).value);
268 }
269 for (auto const& w : endDateValues) {
270 QL_REQUIRE(w.which() == ValueTypeWhich::Event, "date expected as arg #4 (enddate)");
271 maxEnd = std::max(maxEnd, QuantLib::ext::get<EventVec>(w).value);
272 }
273 if (minStart >= maxEnd)
274 continue;
275 RandomVariable lookback, fixingDays;
276 for (auto const& l : lookbackValues) {
277 QL_REQUIRE(l.which() == ValueTypeWhich::Number, "number expected as arg #7 (lookback)");
278 lookback = QuantLib::ext::get<RandomVariable>(l);
279 QL_REQUIRE(lookback.deterministic(), "expected arg #7 (lookback) to be deterministic");
280 for (auto const& f : fixingDaysValues) {
281 QL_REQUIRE(f.which() == ValueTypeWhich::Number, "number expected as arg #9 (fixingDays)");
282 fixingDays = QuantLib::ext::get<RandomVariable>(f);
283 QL_REQUIRE(fixingDays.deterministic(), "expected arg #9 (fixingDays) to be deterministic");
284 // construct template coupon and extract fixing and value dates
286 maxEnd, 1.0, minStart, maxEnd, on, 1.0, 0.0, Date(), Date(), DayCounter(), false, false,
287 static_cast<Integer>(lookback.at(0)) * Days, 0, static_cast<Natural>(fixingDays.at(0)));
288 fwdCompAvgFixingDates_[indexName].insert(cpn.fixingDates().begin(), cpn.fixingDates().end());
289 DLOG("adding " << cpn.fixingDates().size() << " fixing dates for index " << indexName);
290 if (!cpn.valueDates().empty()) {
291 fwdCompAvgStartEndDates_[indexName].insert(cpn.valueDates().front());
292 fwdCompAvgStartEndDates_[indexName].insert(cpn.valueDates().back());
293 }
294 }
295 }
296 }
297 visit(static_cast<ASTNode&>(n));
298 }
299
300 void visit(FunctionFwdCompNode& n) override { processFwdCompAvg(n); }
301
302 void visit(FunctionFwdAvgNode& n) override { processFwdCompAvg(n); }
303
304 void processProbNode(ASTNode& n) {
305 checkpoint(n);
306 std::string indexVariable = getVariableName(n.args[0]);
307 std::vector<ValueType> indexValues = getVariableValues(indexVariable);
308 std::string obsDate1Variable = getVariableName(n.args[1]);
309 std::vector<ValueType> obsDate1Values = getVariableValues(obsDate1Variable);
310 std::string obsDate2Variable = getVariableName(n.args[2]);
311 std::vector<ValueType> obsDate2Values = getVariableValues(obsDate2Variable);
312 checkpoint(n);
313 TRACE("prob(" << indexVariable << "," << obsDate1Variable << "," << obsDate2Variable, n);
314 for (auto const& i : indexValues)
315 TRACE("got index " << i, n);
316 for (auto const& v : obsDate1Values)
317 TRACE("got obs date 1 " << v, n);
318 for (auto const& v : obsDate2Values)
319 TRACE("got obs date 2 " << v, n);
320 for (auto const& i : indexValues) {
321 QL_REQUIRE(i.which() == ValueTypeWhich::Index, "index expected as arg #1");
322 std::string indexName = QuantLib::ext::get<IndexVec>(i).value;
323 Date minDate = Date::maxDate(), maxDate = Date::minDate();
324 for (auto const& v : obsDate1Values) {
325 QL_REQUIRE(v.which() == ValueTypeWhich::Event, "date expected as arg #2");
326 Date d = QuantLib::ext::get<EventVec>(v).value;
327 indexEvalDates_[indexName].insert(d);
328 if (d < minDate)
329 minDate = d;
330 if (d > maxDate)
331 maxDate = d;
332 }
333 for (auto const& v : obsDate2Values) {
334 QL_REQUIRE(v.which() == ValueTypeWhich::Event, "date expected as arg #3");
335 Date d = QuantLib::ext::get<EventVec>(v).value;
336 indexEvalDates_[indexName].insert(d);
337 if (d < minDate)
338 minDate = d;
339 if (d > maxDate)
340 maxDate = d;
341 }
342 // determine the fixing calendar (assume that for commodity this is the same for different futures)
343 Calendar fixingCalendar;
344 IndexInfo ind(indexName);
345 if (ind.isComm()) {
346 Date today = Settings::instance().evaluationDate(); // any date will do
347 fixingCalendar = ind.comm(today)->fixingCalendar();
348 } else {
349 fixingCalendar = ind.index()->fixingCalendar();
350 }
351 DLOG("adding prob fixing dates from " << QuantLib::io::iso_date(minDate) << " to "
352 << QuantLib::io::iso_date(maxDate) << " for " << indexName);
353 for (Date d = minDate; d <= maxDate; ++d) {
354 if (fixingCalendar.isBusinessDay(d)) {
355 probFixingDates_[indexName].insert(d);
356 }
357 }
358 }
359 }
360
361 void visit(FunctionAboveProbNode& n) override {
362 processProbNode(n);
363 visit(static_cast<ASTNode&>(n));
364 }
365
366 void visit(FunctionBelowProbNode& n) override {
367 processProbNode(n);
368 visit(static_cast<ASTNode&>(n));
369 }
370
371 void processNpvNode(ASTNode& n) {
372 checkpoint(n);
373 std::string obsDateVariable = getVariableName(n.args[1]);
374 std::vector<ValueType> obsDateValues = getVariableValues(obsDateVariable);
375 checkpoint(n);
376 TRACE("npv(" << obsDateVariable << ")", n);
377 for (auto const& v : obsDateValues) {
378 QL_REQUIRE(v.which() == ValueTypeWhich::Event, "date expected and 2nd argument");
379 regressionDates_.insert(QuantLib::ext::get<EventVec>(v).value);
380 }
381 visit(static_cast<ASTNode&>(n));
382 }
383
384 void visit(FunctionNpvNode& n) override { processNpvNode(n); }
385
386 void visit(FunctionNpvMemNode& n) override { processNpvNode(n); }
387
388private:
389 std::map<std::string, std::set<QuantLib::Date>>&indexEvalDates_, &indexFwdDates_, &payObsDates_, &payPayDates_,
392 std::set<QuantLib::Date>& regressionDates_;
393 Context& context_;
395};
396} // namespace
397
398void StaticAnalyser::run(const std::string& script) {
399 indexEvalDates_.clear();
400 indexFwdDates_.clear();
401 payObsDates_.clear();
402 payPayDates_.clear();
403 discountObsDates_.clear();
404 discountPayDates_.clear();
406 fwdCompAvgEvalDates_.clear();
408 probFixingDates_.clear();
409 regressionDates_.clear();
410
411 ASTNode* loc;
415
416 try {
417 root_->accept(runner);
418 } catch (const std::exception& e) {
419 std::ostringstream errorMessage;
420 errorMessage << "Error during static script analysis: " << e.what() << " at "
421 << (loc ? to_string(loc->locationInfo) : "(last visited ast node not known)")
422 << " - see log for more details.";
423
425 ALOG(errorMessage.str());
426
427 QL_FAIL(errorMessage.str());
428 }
429
430 DLOG("Static analyser finished without errors.");
431}
432
433} // namespace data
434} // namespace ore
std::string script
const std::vector< Date > & valueDates() const
const std::vector< Date > & fixingDates() const
std::map< std::string, std::set< QuantLib::Date > > discountPayDates_
const QuantLib::ext::shared_ptr< Context > context_
std::map< std::string, std::set< QuantLib::Date > > indexEvalDates_
std::map< std::string, std::set< QuantLib::Date > > payObsDates_
std::map< std::string, std::set< QuantLib::Date > > fwdCompAvgStartEndDates_
std::set< QuantLib::Date > regressionDates_
std::map< std::string, std::set< QuantLib::Date > > probFixingDates_
std::map< std::string, std::set< QuantLib::Date > > payPayDates_
std::map< std::string, std::set< QuantLib::Date > > discountObsDates_
std::map< std::string, std::set< QuantLib::Date > > fwdCompAvgFixingDates_
std::map< std::string, std::set< QuantLib::Date > > indexFwdDates_
std::map< std::string, std::set< QuantLib::Date > > fwdCompAvgEvalDates_
void run(const std::string &script="")
#define TRACE(message, n)
ASTNode *& lastVisitedNode_
SafeStack< ValueType > value
Context & context_
Classes and functions for log message handling.
@ data
Definition: log.hpp:77
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
#define ALOG(text)
Logging Macro (Level = Alert)
Definition: log.hpp:544
#define LOGGERSTREAM(text)
Definition: log.hpp:631
std::string printCodeContext(std::string script, const ASTNode *loc, bool compact)
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
boost::variant< RandomVariable, EventVec, CurrencyVec, IndexVec, DaycounterVec, Filter > ValueType
Definition: value.hpp:60
QuantLib::ext::shared_ptr< ASTNode > ASTNodePtr
Definition: ast.hpp:46
Serializable Credit Default Swap.
Definition: namespaces.docs:23
some utility functions
script parser
std::map< std::string, std::set< QuantLib::Date > > & payPayDates_
std::map< std::string, std::set< QuantLib::Date > > & indexFwdDates_
std::map< std::string, std::set< QuantLib::Date > > & discountObsDates_
std::map< std::string, std::set< QuantLib::Date > > & payObsDates_
std::map< std::string, std::set< QuantLib::Date > > & fwdCompAvgEvalDates_
std::map< std::string, std::set< QuantLib::Date > > & indexEvalDates_
std::map< std::string, std::set< QuantLib::Date > > & probFixingDates_
std::map< std::string, std::set< QuantLib::Date > > & fwdCompAvgStartEndDates_
std::map< std::string, std::set< QuantLib::Date > > & discountPayDates_
std::map< std::string, std::set< QuantLib::Date > > & fwdCompAvgFixingDates_
std::set< QuantLib::Date > & regressionDates_
static script analyser
LocationInfo locationInfo
Definition: ast.hpp:63
string name