Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
scriptengine.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
24
28
29#include <ql/errors.hpp>
30#include <ql/indexes/indexmanager.hpp>
31
32#include <boost/timer/timer.hpp>
33
34#define TRACE(message, n) \
35 { \
36 if (interactive_) { \
37 std::cerr << "\nScriptEngine: " << message << " at " << to_string((n).locationInfo) \
38 << "\nexpr value = " << value.top() << "\ncurr filter = " << filter.top() << std::endl; \
39 std::cerr << printCodeContext(script_, &n); \
40 std::string c; \
41 do { \
42 std::cerr << "(c)ontext (q)uit "; \
43 std::getline(std::cin, c); \
44 if (c == "c") \
45 std::cerr << context_; \
46 else if (c == "q") \
47 interactive_ = false; \
48 } while (c == "c"); \
49 } \
50 }
51
52namespace ore {
53namespace data {
54
55namespace {
56class ASTRunner : public AcyclicVisitor,
57 public Visitor<ASTNode>,
58 public Visitor<OperatorPlusNode>,
59 public Visitor<OperatorMinusNode>,
60 public Visitor<OperatorMultiplyNode>,
61 public Visitor<OperatorDivideNode>,
62 public Visitor<NegateNode>,
63 public Visitor<FunctionAbsNode>,
64 public Visitor<FunctionExpNode>,
65 public Visitor<FunctionLogNode>,
66 public Visitor<FunctionSqrtNode>,
67 public Visitor<FunctionNormalCdfNode>,
68 public Visitor<FunctionNormalPdfNode>,
69 public Visitor<FunctionMinNode>,
70 public Visitor<FunctionMaxNode>,
71 public Visitor<FunctionPowNode>,
72 public Visitor<FunctionBlackNode>,
73 public Visitor<FunctionDcfNode>,
74 public Visitor<FunctionDaysNode>,
75 public Visitor<FunctionPayNode>,
76 public Visitor<FunctionLogPayNode>,
77 public Visitor<FunctionNpvNode>,
78 public Visitor<FunctionNpvMemNode>,
79 public Visitor<HistFixingNode>,
80 public Visitor<FunctionDiscountNode>,
81 public Visitor<FunctionFwdCompNode>,
82 public Visitor<FunctionFwdAvgNode>,
83 public Visitor<FunctionAboveProbNode>,
84 public Visitor<FunctionBelowProbNode>,
85 public Visitor<SortNode>,
86 public Visitor<PermuteNode>,
87 public Visitor<ConstantNumberNode>,
88 public Visitor<VariableNode>,
89 public Visitor<SizeOpNode>,
90 public Visitor<FunctionDateIndexNode>,
91 public Visitor<VarEvaluationNode>,
92 public Visitor<AssignmentNode>,
93 public Visitor<RequireNode>,
94 public Visitor<DeclarationNumberNode>,
95 public Visitor<SequenceNode>,
96 public Visitor<ConditionEqNode>,
97 public Visitor<ConditionNeqNode>,
98 public Visitor<ConditionLtNode>,
99 public Visitor<ConditionLeqNode>,
100 public Visitor<ConditionGtNode>,
101 public Visitor<ConditionGeqNode>,
102 public Visitor<ConditionNotNode>,
103 public Visitor<ConditionAndNode>,
104 public Visitor<ConditionOrNode>,
105 public Visitor<IfThenElseNode>,
106 public Visitor<LoopNode> {
107public:
108 ASTRunner(const QuantLib::ext::shared_ptr<Model> model, const std::string& script, bool& interactive, Context& context,
109 ASTNode*& lastVisitedNode, QuantLib::ext::shared_ptr<PayLog> paylog, bool includePastCashflows)
110 : model_(model), size_(model ? model->size() : 1), script_(script), interactive_(interactive), paylog_(paylog),
111 includePastCashflows_(includePastCashflows), context_(context), lastVisitedNode_(lastVisitedNode) {
112 filter.emplace(size_, true);
113 value.push(RandomVariable());
114 }
115
116 // helper functions to perform operations
117
118 template <typename R>
119 void binaryOp(ASTNode& n, const std::string& name, const std::function<R(ValueType, ValueType)>& op) {
120 n.args[0]->accept(*this);
121 n.args[1]->accept(*this);
122 checkpoint(n);
123 auto right = value.pop();
124 auto left = value.pop();
125 value.push(op(left, right));
126 TRACE(name << "( " << left << " , " << right << " )", n);
127 }
128
129 template <typename R> void unaryOp(ASTNode& n, const std::string& name, const std::function<R(ValueType)>& op) {
130 n.args[0]->accept(*this);
131 checkpoint(n);
132 auto arg = value.pop();
133 value.push(op(arg));
134 TRACE(name << "( " << arg << " )", n);
135 }
136
137 // get ref to context variable + index (0 for scalars, 0,1,2,... for arrays)
138
139 std::pair<ValueType&, long> getVariableRef(VariableNode& v) {
140 checkpoint(v);
141 if (v.isCached) {
142 if (v.isScalar) {
143 return std::make_pair(QuantLib::ext::ref(*v.cachedScalar), 0);
144 } else {
145 QL_REQUIRE(v.args[0], "array subscript required for variable '" << v.name << "'");
146 v.args[0]->accept(*this);
147 auto arg = value.pop();
148 QL_REQUIRE(arg.which() == ValueTypeWhich::Number,
149 "array subscript must be of type NUMBER, got " << valueTypeLabels.at(arg.which()));
150 RandomVariable i = QuantLib::ext::get<RandomVariable>(arg);
151 QL_REQUIRE(i.deterministic(), "array subscript must be deterministic");
152 long il = std::lround(i.at(0));
153 QL_REQUIRE(static_cast<long>(v.cachedVector->size()) >= il && il >= 1,
154 "array index " << il << " out of bounds 1..." << v.cachedVector->size());
155 return std::make_pair(QuantLib::ext::ref(v.cachedVector->operator[](il - 1)), il - 1);
156 }
157 } else {
158 auto scalar = context_.scalars.find(v.name);
159 if (scalar != context_.scalars.end()) {
160 QL_REQUIRE(!v.args[0], "no array subscript allowed for variable '" << v.name << "'");
161 v.isCached = true;
162 v.isScalar = true;
163 v.cachedScalar = &scalar->second;
164 return std::make_pair(QuantLib::ext::ref(scalar->second), 0);
165 }
166 auto array = context_.arrays.find(v.name);
167 if (array != context_.arrays.end()) {
168 v.isCached = true;
169 v.isScalar = false;
170 v.cachedVector = &array->second;
171 return getVariableRef(v);
172 }
173 QL_FAIL("variable '" << v.name << "' is not defined.");
174 }
175 }
176
177 // helepr to declare a new context variable
178
179 void declareVariable(const ASTNodePtr arg, const ValueType& val) {
180 checkpoint(*arg);
181 auto v = QuantLib::ext::dynamic_pointer_cast<VariableNode>(arg);
182 QL_REQUIRE(v, "invalid declaration");
183 if (context_.ignoreAssignments.find(v->name) != context_.ignoreAssignments.end()) {
184 TRACE("declare(" << v->name << " ignored, because listed in ignoreAssignment variables set", *arg);
185 return;
186 }
187 auto scalar = context_.scalars.find(v->name);
188 auto array = context_.arrays.find(v->name);
189 QL_REQUIRE(scalar == context_.scalars.end() && array == context_.arrays.end(),
190 "variable '" << v->name << "' already declared.");
191 if (v->args[0]) {
192 v->args[0]->accept(*this);
193 checkpoint(*arg);
194 auto size = value.pop();
195 QL_REQUIRE(size.which() == ValueTypeWhich::Number, "expected NUMBER for array size definition");
196 RandomVariable arraySize = QuantLib::ext::get<RandomVariable>(size);
197 QL_REQUIRE(arraySize.deterministic(), "array size definition requires deterministic argument");
198 long arraySizeL = std::lround(arraySize.at(0));
199 QL_REQUIRE(arraySizeL >= 0, "expected non-negative array size, got " << arraySizeL);
200 context_.arrays[v->name] = std::vector<ValueType>(arraySizeL, val);
201 TRACE("declare(" << v->name << "[" << arraySizeL << "], " << val << ")", *arg);
202 } else {
203 context_.scalars[v->name] = val;
204 TRACE("declare(" << v->name << ", " << val << ")", *arg);
205 }
206 }
207
208 // record last visited node for diagnostics
209
210 void checkpoint(ASTNode& n) { lastVisitedNode_ = &n; }
211
212 // if we end up here, a node type is not handled
213
214 void visit(ASTNode& n) override {
215 checkpoint(n);
216 QL_FAIL("unhandled node");
217 }
218
219 // operator / function node types
220
221 void visit(OperatorPlusNode& n) override { binaryOp<ValueType>(n, "plus", operator+); }
222 void visit(OperatorMinusNode& n) override {
223 binaryOp<ValueType>(n, "minus", [](const ValueType& x, const ValueType& y) { return x - y; });
224 }
225 void visit(OperatorMultiplyNode& n) override { binaryOp<ValueType>(n, "multiply", operator*); }
226 void visit(OperatorDivideNode& n) override { binaryOp<ValueType>(n, "divide", operator/); }
227 void visit(NegateNode& n) override {
228 unaryOp<ValueType>(n, "negate", [](const ValueType& x) { return -x; });
229 }
230 void visit(FunctionAbsNode& n) override { unaryOp<ValueType>(n, "abs", abs); }
231 void visit(FunctionExpNode& n) override { unaryOp<ValueType>(n, "exp", exp); }
232 void visit(FunctionLogNode& n) override { unaryOp<ValueType>(n, "log", log); }
233 void visit(FunctionSqrtNode& n) override { unaryOp<ValueType>(n, "sqrt", sqrt); }
234 void visit(FunctionNormalCdfNode& n) override { unaryOp<ValueType>(n, "normalCdf", normalCdf); }
235 void visit(FunctionNormalPdfNode& n) override { unaryOp<ValueType>(n, "normalPdf", normalPdf); }
236 void visit(FunctionMinNode& n) override { binaryOp<ValueType>(n, "min", min); }
237 void visit(FunctionMaxNode& n) override { binaryOp<ValueType>(n, "max", max); }
238 void visit(FunctionPowNode& n) override { binaryOp<ValueType>(n, "pow", pow); }
239
240 // condition nodes
241
242 void visit(ConditionEqNode& n) override { binaryOp<Filter>(n, "conditionEq", equal); }
243 void visit(ConditionNeqNode& n) override { binaryOp<Filter>(n, "conditionNeq", notequal); }
244 void visit(ConditionLtNode& n) override { binaryOp<Filter>(n, "conditionLt", lt); }
245 void visit(ConditionLeqNode& n) override { binaryOp<Filter>(n, "conditionLeq", leq); }
246 void visit(ConditionGeqNode& n) override { binaryOp<Filter>(n, "conditionGeq", geq); }
247 void visit(ConditionGtNode& n) override { binaryOp<Filter>(n, "conditionGt", gt); }
248 void visit(ConditionNotNode& n) override {
249 unaryOp<Filter>(n, "conditionNot", [](const ValueType& x) { return logicalNot(x); });
250 }
251
252 void visit(ConditionAndNode& n) override {
253 n.args[0]->accept(*this);
254 auto left = value.pop();
255 checkpoint(n);
256 QL_REQUIRE(left.which() == ValueTypeWhich::Filter, "expected condition");
257 Filter l = QuantLib::ext::get<Filter>(left);
258 if (l.deterministic() && !l[0]) {
259 // short cut if first expression is already false
260 value.push(Filter(l.size(), false));
261 TRACE("conditionAnd( false, ? )", n);
262 } else {
263 // no short cut possible
264 n.args[1]->accept(*this);
265 auto right = value.pop();
266 checkpoint(n);
267 value.push(logicalAnd(left, right));
268 TRACE("conditionAnd( " << left << " , " << right << " )", n);
269 }
270 }
271
272 void visit(ConditionOrNode& n) override {
273 n.args[0]->accept(*this);
274 auto left = value.pop();
275 checkpoint(n);
276 QL_REQUIRE(left.which() == ValueTypeWhich::Filter, "expected condition");
277 Filter l = QuantLib::ext::get<Filter>(left);
278 if (l.deterministic() && l[0]) {
279 // short cut if first expression is already true
280 value.push(Filter(l.size(), true));
281 TRACE("conditionOr( true, ? )", n);
282 } else {
283 // no short cut possible
284 n.args[1]->accept(*this);
285 auto right = value.pop();
286 checkpoint(n);
287 value.push(logicalOr(left, right));
288 TRACE("conditionOr( " << left << " , " << right << " )", n);
289 }
290 }
291
292 // constants / variable related nodes
293
294 void visit(ConstantNumberNode& n) override {
295 checkpoint(n);
296 value.push(RandomVariable(size_, n.value));
297 TRACE("constantNumber( " << n.value << " )", n);
298 }
299
300 void visit(VariableNode& n) override {
301 value.push(getVariableRef(n).first);
302 checkpoint(n);
303 TRACE("variable( " << n.name << " )", n);
304 }
305
306 void visit(DeclarationNumberNode& n) override {
307 for (auto const& arg : n.args) {
308 declareVariable(arg, RandomVariable(size_, 0.0));
309 checkpoint(n);
310 }
311 }
312
313 void visit(SizeOpNode& n) override {
314 checkpoint(n);
315 auto array = context_.arrays.find(n.name);
316 if (array == context_.arrays.end()) {
317 auto scalar = context_.scalars.find(n.name);
318 if (scalar == context_.scalars.end())
319 QL_FAIL("variable " << n.name << " is not defined");
320 else
321 QL_FAIL("SIZE can only be applied to array, " << n.name << " is a scalar");
322 }
323 value.push(RandomVariable(size_, static_cast<double>(array->second.size())));
324 TRACE("size( " << n.name << " )", n);
325 }
326
327 void visit(FunctionDateIndexNode& n) override {
328 checkpoint(n);
329 auto array = context_.arrays.find(n.name);
330 QL_REQUIRE(array != context_.arrays.end(),
331 "DATEINDEX: second argument event array '" << n.name << "' not found");
332 auto v = QuantLib::ext::dynamic_pointer_cast<VariableNode>(n.args[0]);
333 QL_REQUIRE(v, "DATEINDEX: first argument must be a variable expression");
334 auto ref = getVariableRef(*v);
335 checkpoint(n);
336 QL_REQUIRE(ref.first.which() == ValueTypeWhich::Event, "DATEINDEX: first argument must be of type event");
337 if (n.op == "EQ") {
338 auto pos = std::find_if(array->second.begin(), array->second.end(),
339 [&ref](const ValueType& v) { return ref.first == v; });
340 if (pos == array->second.end())
341 value.push(RandomVariable(size_, 0.0));
342 else
343 value.push(RandomVariable(size_, static_cast<double>(std::distance(array->second.begin(), pos) + 1)));
344 } else if (n.op == "GEQ") {
345 Size pos = std::lower_bound(array->second.begin(), array->second.end(), ref.first,
346 [](const ValueType& l, const ValueType& r) -> bool {
347 return QuantLib::ext::get<EventVec>(l).value < QuantLib::ext::get<EventVec>(r).value;
348 }) -
349 array->second.begin() + 1;
350 value.push(RandomVariable(size_, static_cast<double>(pos)));
351 } else if (n.op == "GT") {
352 Size pos = std::upper_bound(array->second.begin(), array->second.end(), ref.first,
353 [](const ValueType& l, const ValueType& r) -> bool {
354 return QuantLib::ext::get<EventVec>(l).value < QuantLib::ext::get<EventVec>(r).value;
355 }) -
356 array->second.begin() + 1;
357 value.push(RandomVariable(size_, static_cast<double>(pos)));
358 } else {
359 QL_FAIL("DATEINDEX: operation '" << n.op << "' not supported, expected EQ, GEQ, GT");
360 }
361 TRACE("dateindex( " << v->name << "[" << (ref.second + 1) << "] , " << n.name << " , " << n.op << " )", n);
362 }
363
364 void visit(AssignmentNode& n) override {
365 n.args[1]->accept(*this);
366 auto right = value.pop();
367 checkpoint(n);
368 auto v = QuantLib::ext::dynamic_pointer_cast<VariableNode>(n.args[0]);
369 QL_REQUIRE(v, "expected variable identifier on LHS of assignment");
370 if (context_.ignoreAssignments.find(v->name) != context_.ignoreAssignments.end()) {
371 TRACE("assign(" << v->name << ") ignored, because variable is listed in context's ignoreAssignment set",
372 n);
373 return;
374 }
375 QL_REQUIRE(std::find(context_.constants.begin(), context_.constants.end(), v->name) == context_.constants.end(),
376 "can not assign to const variable '" << v->name << "'");
377 auto ref = getVariableRef(*v);
378 checkpoint(n);
379 if (ref.first.which() == ValueTypeWhich::Event || ref.first.which() == ValueTypeWhich::Currency ||
380 ref.first.which() == ValueTypeWhich::Index) {
381 typeSafeAssign(ref.first, right);
382 } else {
383 QL_REQUIRE(ref.first.which() == ValueTypeWhich::Number,
384 "internal error: expected NUMBER, got " << valueTypeLabels.at(ref.first.which()));
385 QL_REQUIRE(right.which() == ValueTypeWhich::Number, "invalid assignment: type "
386 << valueTypeLabels.at(ref.first.which()) << " <- "
387 << valueTypeLabels.at(right.which()));
388 // TODO, better have a RESETTIME() function?
389 QuantLib::ext::get<RandomVariable>(ref.first).setTime(Null<Real>());
390 ref.first = conditionalResult(filter.top(), QuantLib::ext::get<RandomVariable>(right),
391 QuantLib::ext::get<RandomVariable>(ref.first));
392 QuantLib::ext::get<RandomVariable>(ref.first).updateDeterministic();
393 }
394 TRACE("assign( " << v->name << "[" << (ref.second + 1) << "] ) := " << ref.first << " ("
395 << valueTypeLabels.at(right.which()) << ") using filter " << filter.top(),
396 n);
397 }
398
399 // require node
400
401 void visit(RequireNode& n) override {
402 n.args[0]->accept(*this);
403 auto condition = value.pop();
404 checkpoint(n);
405 QL_REQUIRE(condition.which() == ValueTypeWhich::Filter, "expected condition");
406 // check implication filter true => condition true
407 auto c = !filter.top() || QuantLib::ext::get<Filter>(condition);
408 c.updateDeterministic();
409 QL_REQUIRE(c.deterministic() && c.at(0), "required condition is not (always) fulfilled");
410 TRACE("require( " << condition << " ) for filter " << filter.top(), n);
411 }
412
413 // control flow nodes
414
415 void visit(SequenceNode& n) override {
416 TRACE("instruction_sequence()", n);
417 for (auto const& arg : n.args) {
418 arg->accept(*this);
419 checkpoint(n);
420 }
421 }
422
423 void visit(IfThenElseNode& n) override {
424 n.args[0]->accept(*this);
425 auto if_ = value.pop();
426 checkpoint(n);
427 QL_REQUIRE(if_.which() == ValueTypeWhich::Filter,
428 "IF must be followed by a boolean, got " << valueTypeLabels.at(if_.which()));
429 Filter cond = QuantLib::ext::get<Filter>(if_);
430 TRACE("if( " << cond << " )", n);
431 Filter baseFilter = filter.top();
432 Filter currentFilter = baseFilter && cond;
433 currentFilter.updateDeterministic();
434 filter.push(currentFilter);
435 TRACE("then( filter = " << currentFilter << " )", n);
436 if (!currentFilter.deterministic() || currentFilter[0]) {
437 n.args[1]->accept(*this);
438 checkpoint(n);
439 }
440 filter.pop();
441 if (n.args[2]) {
442 currentFilter = baseFilter && !cond;
443 currentFilter.updateDeterministic();
444 filter.push(currentFilter);
445 TRACE("else( filter = " << currentFilter << ")", n);
446 if (!currentFilter.deterministic() || currentFilter[0]) {
447 n.args[2]->accept(*this);
448 checkpoint(n);
449 }
450 filter.pop();
451 }
452 }
453
454 void visit(LoopNode& n) override {
455 checkpoint(n);
456 auto var = context_.scalars.find(n.name);
457 QL_REQUIRE(var != context_.scalars.end(), "loop variable '" << n.name << "' not defined or not scalar");
458 QL_REQUIRE(std::find(context_.constants.begin(), context_.constants.end(), n.name) == context_.constants.end(),
459 "loop variable '" << n.name << "' is constant");
460 n.args[0]->accept(*this);
461 n.args[1]->accept(*this);
462 n.args[2]->accept(*this);
463 auto step = value.pop();
464 auto right = value.pop();
465 auto left = value.pop();
466 checkpoint(n);
467 QL_REQUIRE(left.which() == ValueTypeWhich::Number && right.which() == ValueTypeWhich::Number &&
468 step.which() == ValueTypeWhich::Number,
469 "loop bounds and step must be of type NUMBER, got " << valueTypeLabels.at(left.which()) << ", "
470 << valueTypeLabels.at(right.which()) << ", "
471 << valueTypeLabels.at(step.which()));
472 RandomVariable a = QuantLib::ext::get<RandomVariable>(left);
473 RandomVariable b = QuantLib::ext::get<RandomVariable>(right);
474 RandomVariable s = QuantLib::ext::get<RandomVariable>(step);
475 QL_REQUIRE(a.deterministic(), "first loop bound must be deterministic");
476 QL_REQUIRE(b.deterministic(), "second loop bound must be deterministic");
477 QL_REQUIRE(s.deterministic(), "loop step must be deterministic");
478 long al = std::lround(a.at(0));
479 long bl = std::lround(b.at(0));
480 long sl = std::lround(s.at(0));
481 QL_REQUIRE(sl != 0, "loop step must be non-zero");
482 long cl = al;
483 while ((sl > 0 && cl <= bl) || (sl < 0 && cl >= bl)) {
484 TRACE("for( " << n.name << " : " << cl << " (" << al << "," << bl << "))", n);
485 var->second = RandomVariable(size_, static_cast<double>(cl));
486 n.args[3]->accept(*this);
487 checkpoint(n);
488 QL_REQUIRE(var->second.which() == ValueTypeWhich::Number &&
489 close_enough_all(QuantLib::ext::get<RandomVariable>(var->second),
490 RandomVariable(size_, static_cast<double>(cl))),
491 "loop variable was modified in body from " << cl << " to " << var->second
492 << ", this is illegal.");
493 cl += sl;
494 }
495 }
496
497 // day counter functions
498
499 void dayCounterFunctionHelper(ASTNode& n, DayCounter& daycounter, Date& date1, Date& date2) {
500 n.args[0]->accept(*this);
501 n.args[1]->accept(*this);
502 n.args[2]->accept(*this);
503 checkpoint(n);
504
505 auto d2 = value.pop();
506 auto d1 = value.pop();
507 auto dc = value.pop();
508
509 QL_REQUIRE(dc.which() == ValueTypeWhich::Daycounter, "dc must be DAYCOUNTER");
510 QL_REQUIRE(d1.which() == ValueTypeWhich::Event, "d1 must be EVENT");
511 QL_REQUIRE(d2.which() == ValueTypeWhich::Event, "d2 must be EVENT");
512
513 date1 = QuantLib::ext::get<EventVec>(d1).value;
514 date2 = QuantLib::ext::get<EventVec>(d2).value;
515
516 daycounter = ore::data::parseDayCounter(QuantLib::ext::get<DaycounterVec>(dc).value);
517 }
518
519 void visit(FunctionDcfNode& n) override {
520 Date date1, date2;
521 DayCounter daycounter;
522 dayCounterFunctionHelper(n, daycounter, date1, date2);
523 QL_REQUIRE(model_, "model is null");
524 value.push(RandomVariable(model_->size(), daycounter.yearFraction(date1, date2)));
525 TRACE("dcf( " << date1 << " , " << date2 << " )", n);
526 }
527
528 void visit(FunctionDaysNode& n) override {
529 Date date1, date2;
530 DayCounter daycounter;
531 dayCounterFunctionHelper(n, daycounter, date1, date2);
532 QL_REQUIRE(model_, "model is null");
533 value.push(RandomVariable(model_->size(), static_cast<double>(daycounter.dayCount(date1, date2))));
534 TRACE("days( " << date1 << " , " << date2 << " )", n);
535 }
536
537 // SORT and PERMUTE instructions
538
539 void visit(SortNode& n) override {
540 checkpoint(n);
541
542 std::vector<RandomVariable*> x, y, p;
543
544 if (n.args[0]) {
545 auto xname = QuantLib::ext::dynamic_pointer_cast<VariableNode>(n.args[0]);
546 QL_REQUIRE(xname, "x must be a variable");
547 QL_REQUIRE(!xname->args[0], "x must not be indexed");
548 auto xv = context_.arrays.find(xname->name);
549 QL_REQUIRE(xv != context_.arrays.end(), "did not find array with name '" << xname->name << "'");
550 for (Size c = 0; c < xv->second.size(); ++c) {
551 QL_REQUIRE(xv->second[c].which() == ValueTypeWhich::Number, "x must be NUMBER");
552 x.push_back(&QuantLib::ext::get<RandomVariable>(xv->second[c]));
553 }
554 }
555
556 if (n.args[1]) {
557 auto yname = QuantLib::ext::dynamic_pointer_cast<VariableNode>(n.args[1]);
558 QL_REQUIRE(yname, "y must be a variable");
559 QL_REQUIRE(!yname->args[0], "y must not be indexed");
560 auto yv = context_.arrays.find(yname->name);
561 QL_REQUIRE(yv != context_.arrays.end(), "did not find array with name '" << yname->name << "'");
562 for (Size c = 0; c < yv->second.size(); ++c) {
563 QL_REQUIRE(yv->second[c].which() == ValueTypeWhich::Number, "y must be NUMBER");
564 y.push_back(&QuantLib::ext::get<RandomVariable>(yv->second[c]));
565 }
566 }
567
568 if (n.args[2]) {
569 auto pname = QuantLib::ext::dynamic_pointer_cast<VariableNode>(n.args[2]);
570 QL_REQUIRE(pname, "p must be a variable");
571 QL_REQUIRE(!pname->args[0], "p must not be indexed");
572 auto pv = context_.arrays.find(pname->name);
573 QL_REQUIRE(pv != context_.arrays.end(), "did not find array with name '" << pname->name << "'");
574 for (Size c = 0; c < pv->second.size(); ++c) {
575 QL_REQUIRE(pv->second[c].which() == ValueTypeWhich::Number, "p must be NUMBER");
576 p.push_back(&QuantLib::ext::get<RandomVariable>(pv->second[c]));
577 }
578 }
579
580 // set y to target (may be x itself)
581
582 if (y.empty())
583 y = x;
584
585 QL_REQUIRE(x.size() >= 1, "array size must be >= 1");
586 QL_REQUIRE(y.size() == x.size(),
587 "y array size (" << y.size() << ") must match x array size (" << x.size() << ")");
588 QL_REQUIRE(p.empty() || p.size() == x.size(),
589 "p array size (" << p.size() << ") must match x array size (" << p.size() << ")");
590
591 for (Size c = 0; c < x.size(); ++c) {
592 QL_REQUIRE(x[c]->size() == y[c]->size(), "x[" << c << "] size (" << x[c]->size() << ") must match y[" << c
593 << "] size (" << y[c]->size() << ")");
594 QL_REQUIRE(p.empty() || x[c]->size() == p[c]->size(), "x[" << c << "] size (" << x[c]->size()
595 << ") must match i[" << c << "] size ("
596 << p[c]->size() << ")");
597 }
598
599 const Filter& flt = filter.top();
600 QL_REQUIRE(flt.size() == x[0]->size(),
601 "filter has size " << flt.size() << ", but x[0] has size " << x[0]->size() << ")");
602 if (p.empty()) {
603 std::vector<Real> val(x.size());
604 for (Size k = 0; k < x[0]->size(); ++k) {
605 if (!flt[k])
606 continue;
607 for (Size c = 0; c < x.size(); ++c) {
608 val[c] = (*x[c])[k];
609 }
610 std::sort(val.begin(), val.end());
611 for (Size c = 0; c < x.size(); ++c) {
612 y[c]->set(k, val[c]);
613 }
614 }
615 } else {
616 std::vector<std::pair<Real, Size>> val(x.size());
617 for (Size k = 0; k < x[0]->size(); ++k) {
618 if (!flt[k])
619 continue;
620 for (Size c = 0; c < x.size(); ++c) {
621 val[c].first = (*x[c])[k];
622 val[c].second = c + 1; // permutation should start at 1
623 }
624 std::sort(val.begin(), val.end(), [](const std::pair<Real, Size>& x, const std::pair<Real, Size>& y) {
625 return x.first < y.first;
626 });
627 for (Size c = 0; c < x.size(); ++c) {
628 y[c]->set(k, val[c].first);
629 p[c]->set(k, static_cast<Real>(val[c].second));
630 }
631 }
632 }
633
634 TRACE("sort(...)", n); // TODO what would be a helpful output?
635 }
636
637 void visit(PermuteNode& n) override {
638 checkpoint(n);
639
640 std::vector<RandomVariable*> x, y, p;
641
642 if (n.args[0]) {
643 auto xname = QuantLib::ext::dynamic_pointer_cast<VariableNode>(n.args[0]);
644 QL_REQUIRE(xname, "x must be a variable");
645 QL_REQUIRE(!xname->args[0], "x must not be indexed");
646 auto xv = context_.arrays.find(xname->name);
647 QL_REQUIRE(xv != context_.arrays.end(), "did not find array with name '" << xname->name << "'");
648 for (Size c = 0; c < xv->second.size(); ++c) {
649 QL_REQUIRE(xv->second[c].which() == ValueTypeWhich::Number, "x must be NUMBER");
650 x.push_back(&QuantLib::ext::get<RandomVariable>(xv->second[c]));
651 }
652 }
653
654 if (n.args[1]) {
655 auto yname = QuantLib::ext::dynamic_pointer_cast<VariableNode>(n.args[1]);
656 QL_REQUIRE(yname, "y must be a variable");
657 QL_REQUIRE(!yname->args[0], "y must not be indexed");
658 auto yv = context_.arrays.find(yname->name);
659 QL_REQUIRE(yv != context_.arrays.end(), "did not find array with name '" << yname->name << "'");
660 for (Size c = 0; c < yv->second.size(); ++c) {
661 QL_REQUIRE(yv->second[c].which() == ValueTypeWhich::Number, "y must be NUMBER");
662 y.push_back(&QuantLib::ext::get<RandomVariable>(yv->second[c]));
663 }
664 }
665
666 if (n.args[2]) {
667 auto pname = QuantLib::ext::dynamic_pointer_cast<VariableNode>(n.args[2]);
668 QL_REQUIRE(pname, "p must be a variable");
669 QL_REQUIRE(!pname->args[0], "p must not be indexed");
670 auto pv = context_.arrays.find(pname->name);
671 QL_REQUIRE(pv != context_.arrays.end(), "did not find array with name '" << pname->name << "'");
672 for (Size c = 0; c < pv->second.size(); ++c) {
673 QL_REQUIRE(pv->second[c].which() == ValueTypeWhich::Number, "p must be NUMBER");
674 p.push_back(&QuantLib::ext::get<RandomVariable>(pv->second[c]));
675 }
676 }
677
678 // x should be source, y target and p permutation
679
680 if (p.empty()) {
681 p = y;
682 y = x;
683 }
684
685 Size N = x.size();
686
687 QL_REQUIRE(y.size() == N, "y array size (" << y.size() << ") must match x array size (" << N << ")");
688 QL_REQUIRE(p.size() == N, "p array size (" << p.size() << ") must match x array size (" << N << ")");
689 QL_REQUIRE(N >= 1, "array size must be >= 1");
690
691 for (Size c = 0; c < N; ++c) {
692 QL_REQUIRE(x[c]->size() == y[c]->size(), "x[" << c << "] size (" << x[c]->size() << ") must match y[" << c
693 << "] size (" << y[c]->size() << ")");
694 QL_REQUIRE(x[c]->size() == p[c]->size(), "x[" << c << "] size (" << x[c]->size() << ") must match p[" << c
695 << "] size (" << p[c]->size() << ")");
696 }
697
698 const Filter& flt = filter.top();
699 QL_REQUIRE(flt.size() == x[0]->size(),
700 "filter has size " << flt.size() << ", but x[0] has size " << x[0]->size() << ")");
701
702 std::vector<Real> val(N);
703 for (Size k = 0; k < x[0]->size(); ++k) {
704 if (!flt[k])
705 continue;
706 for (Size c = 0; c < N; ++c) {
707 Size permutedIndex = std::lround((*p[c])[k]);
708 QL_REQUIRE(permutedIndex >= 1 && permutedIndex <= N, "permuted index p[" << c << "] = " << permutedIndex
709 << " out of bounds 1..." << N
710 << " at component " << k);
711 val[c] = (*x[permutedIndex - 1])[k];
712 }
713 for (Size c = 0; c < N; ++c) {
714 y[c]->set(k, val[c]);
715 }
716 }
717
718 TRACE("permute(...)", n); // TODO what would be a helpful output?
719 }
720
721 // model dependent function nodes
722
723 void visit(FunctionBlackNode& n) override {
724 n.args[0]->accept(*this);
725 n.args[1]->accept(*this);
726 n.args[2]->accept(*this);
727 n.args[3]->accept(*this);
728 n.args[4]->accept(*this);
729 n.args[5]->accept(*this);
730 checkpoint(n);
731
732 auto impliedvol = value.pop();
733 auto forward = value.pop();
734 auto strike = value.pop();
735 auto expirydate = value.pop();
736 auto obsdate = value.pop();
737 auto callput = value.pop();
738
739 QL_REQUIRE(callput.which() == ValueTypeWhich::Number, "callput must be NUMBER");
740 QL_REQUIRE(obsdate.which() == ValueTypeWhich::Event, "obsdate must be EVENT");
741 QL_REQUIRE(expirydate.which() == ValueTypeWhich::Event, "expirydate must be EVENT");
742 QL_REQUIRE(strike.which() == ValueTypeWhich::Number, "strike must be NUMBER");
743 QL_REQUIRE(forward.which() == ValueTypeWhich::Number, "forward must be NUMBER");
744 QL_REQUIRE(forward.which() == ValueTypeWhich::Number, "impliedvol must be NUMBER");
745
746 RandomVariable omega = QuantLib::ext::get<RandomVariable>(callput);
747 Date obs = QuantLib::ext::get<EventVec>(obsdate).value;
748 Date expiry = QuantLib::ext::get<EventVec>(expirydate).value;
749 RandomVariable k = QuantLib::ext::get<RandomVariable>(strike);
750 RandomVariable f = QuantLib::ext::get<RandomVariable>(forward);
751 RandomVariable v = QuantLib::ext::get<RandomVariable>(impliedvol);
752
753 QL_REQUIRE(model_, "model is null");
754
755 QL_REQUIRE(obs <= expiry, "obsdate (" << obs << ") must be <= expirydate (" << expiry << ")");
756 RandomVariable t(model_->size(), model_->dt(obs, expiry));
757
758 value.push(black(omega, t, k, f, v));
759 TRACE("black( " << callput << " , " << obsdate << " , " << expirydate << " , " << strike << " , " << forward
760 << " , " << impliedvol << " ), t=" << t,
761 n);
762 }
763
764 void payHelper(ASTNode& n, const bool log) {
765 n.args[2]->accept(*this);
766 auto paydate = value.pop();
767 checkpoint(n);
768 QL_REQUIRE(paydate.which() == ValueTypeWhich::Event, "paydate must be EVENT");
769 QL_REQUIRE(model_, "model is null");
770 // handle case of past payments: do not evaluate the other parameters, since not needed (e.g. past fixings)
771 Date pay = boost::get<EventVec>(paydate).value;
772 if (pay <= model_->referenceDate() && (!log || !includePastCashflows_)) {
773 value.push(RandomVariable(size_, 0.0));
774 TRACE("pay() = 0, since paydate " << paydate << " <= " << model_->referenceDate(), n);
775 } else {
776 n.args[0]->accept(*this);
777 n.args[1]->accept(*this);
778 n.args[3]->accept(*this);
779 auto paycurr = value.pop();
780 auto obsdate = value.pop();
781 auto amount = value.pop();
782 checkpoint(n);
783 QL_REQUIRE(amount.which() == ValueTypeWhich::Number, "amount must be NUMBER");
784 QL_REQUIRE(obsdate.which() == ValueTypeWhich::Event, "obsdate must be EVENT");
785 QL_REQUIRE(paycurr.which() == ValueTypeWhich::Currency, "paycurr must be CURRENCY");
786 Date obs = QuantLib::ext::get<EventVec>(obsdate).value;
787 std::string pccy = QuantLib::ext::get<CurrencyVec>(paycurr).value;
788 QL_REQUIRE(obs <= pay, "observation date (" << obs << ") <= payment date (" << pay << ") required");
789 RandomVariable result = pay <= model_->referenceDate()
790 ? RandomVariable(model_->size(), 0.0)
791 : model_->pay(boost::get<RandomVariable>(amount), obs, pay, pccy);
792 RandomVariable cashflowResult =
793 pay <= model_->referenceDate() ? boost::get<RandomVariable>(amount) : result;
794 if (!log || paylog_ == nullptr) {
795 TRACE("pay( " << amount << " , " << obsdate << " , " << paydate << " , " << paycurr << " )", n);
796 } else {
797 // cashflow logging
798 FunctionLogPayNode& pn = dynamic_cast<FunctionLogPayNode&>(n); // throws bad_cast if n has wrong type
799 long legno = 0, slot = 0;
800 std::string cftype = "Unspecified";
801 if (pn.args[4]) {
802 pn.args[4]->accept(*this);
803 auto s = value.pop();
804 QL_REQUIRE(s.which() == ValueTypeWhich::Number, "legno must be NUMBER");
805 RandomVariable sv = QuantLib::ext::get<RandomVariable>(s);
806 sv.updateDeterministic();
807 QL_REQUIRE(sv.deterministic(), "legno must be deterministic");
808 legno = std::lround(sv.at(0));
809 QL_REQUIRE(slot >= 0, " legNo must be >= 0");
810 QL_REQUIRE(pn.args[5], "expected cashflow type argument when legno is given");
811 auto cftname = QuantLib::ext::dynamic_pointer_cast<VariableNode>(n.args[5]);
812 QL_REQUIRE(cftname, "cashflow type must be a variable name");
813 QL_REQUIRE(!cftname->args[0], "cashflow type must not be indexed");
814 cftype = cftname->name;
815 if (pn.args[6]) {
816 pn.args[6]->accept(*this);
817 auto s = value.pop();
818 QL_REQUIRE(s.which() == ValueTypeWhich::Number, "slot must be NUMBER");
819 RandomVariable sv = QuantLib::ext::get<RandomVariable>(s);
820 sv.updateDeterministic();
821 QL_REQUIRE(sv.deterministic(), "slot must be deterministic");
822 slot = std::lround(sv.at(0));
823 QL_REQUIRE(slot >= 1, " slot must be >= 1");
824 }
825 }
826 paylog_->write(cashflowResult, filter.top(), obs, pay, pccy, static_cast<Size>(legno), cftype,
827 static_cast<Size>(slot));
828 TRACE("logpay( " << amount << " , " << obsdate << " , " << paydate << " , " << paycurr << " , " << legno
829 << " , " << cftype << " , " << slot << ")",
830 n);
831 }
832 // push result
833 value.push(result);
834 }
835 }
836
837 void visit(FunctionPayNode& n) override { payHelper(n, false); }
838
839 void visit(FunctionLogPayNode& n) override { payHelper(n, true); }
840
841 void processNpvNode(ASTNode& n, bool hasMemSlot) {
842 n.args[0]->accept(*this);
843 n.args[1]->accept(*this);
844 ValueType memSlot;
845 if (hasMemSlot) {
846 n.args[2]->accept(*this);
847 memSlot = value.pop();
848 }
849 auto obsdate = value.pop();
850 auto amount = value.pop();
851 checkpoint(n);
852 Filter regFilter;
853 Size opt = hasMemSlot ? 3 : 2;
854 if (n.args[opt]) {
855 n.args[opt]->accept(*this);
856 auto val = value.pop();
857 checkpoint(n);
858 QL_REQUIRE(val.which() == ValueTypeWhich::Filter, "filter must be condition");
859 regFilter = QuantLib::ext::get<Filter>(val);
860 }
861 RandomVariable addRegressor1, addRegressor2;
862 if (n.args[opt + 1]) {
863 n.args[opt + 1]->accept(*this);
864 auto val = value.pop();
865 checkpoint(n);
866 QL_REQUIRE(val.which() == ValueTypeWhich::Number, "addRegressor1 must be NUMBER");
867 addRegressor1 = QuantLib::ext::get<RandomVariable>(val);
868 }
869 if (n.args[opt + 2]) {
870 n.args[opt + 2]->accept(*this);
871 auto val = value.pop();
872 checkpoint(n);
873 QL_REQUIRE(val.which() == ValueTypeWhich::Number, "addRegressor2 must be NUMBER");
874 addRegressor2 = QuantLib::ext::get<RandomVariable>(val);
875 }
876 QL_REQUIRE(amount.which() == ValueTypeWhich::Number, "amount must be NUMBER");
877 QL_REQUIRE(obsdate.which() == ValueTypeWhich::Event, "obsdate must be EVENT");
878 if (hasMemSlot) {
879 QL_REQUIRE(memSlot.which() == ValueTypeWhich::Number, "memorySlot must be NUMBER");
880 }
881 QL_REQUIRE(model_, "model is null");
882 Date obs = QuantLib::ext::get<EventVec>(obsdate).value;
883 // roll back to past dates is treated as roll back to TODAY for convenience
884 obs = std::max(obs, model_->referenceDate());
885 boost::optional<long> mem(boost::none);
886 if (hasMemSlot) {
887 RandomVariable v = QuantLib::ext::get<RandomVariable>(memSlot);
888 QL_REQUIRE(v.deterministic(), "memory slot must be deterministic");
889 mem = static_cast<long>(v.at(0));
890 }
891 value.push(model_->npv(QuantLib::ext::get<RandomVariable>(amount), obs, regFilter, mem, addRegressor1, addRegressor2));
892 if (hasMemSlot) {
893 TRACE("npvmem( " << amount << " , " << obsdate << " , " << memSlot << " , " << regFilter << " , "
894 << addRegressor1 << " , " << addRegressor2 << " )",
895 n);
896 } else {
897 TRACE("npv( " << amount << " , " << obsdate << " , " << regFilter << " , " << addRegressor1 << " , "
898 << addRegressor2 << " )",
899 n);
900 }
901 }
902
903 void visit(FunctionNpvNode& n) override { processNpvNode(n, false); }
904
905 void visit(FunctionNpvMemNode& n) override { processNpvNode(n, true); }
906
907 void visit(HistFixingNode& n) override {
908 checkpoint(n);
909 QL_REQUIRE(model_, "model is null");
910 n.args[0]->accept(*this);
911 n.args[1]->accept(*this);
912 auto obsdate = value.pop();
913 auto underlying = value.pop();
914 checkpoint(n);
915 QL_REQUIRE(underlying.which() == ValueTypeWhich::Index, "underlying must be INDEX");
916 QL_REQUIRE(obsdate.which() == ValueTypeWhich::Event, "obsdate must be EVENT");
917 Date obs = QuantLib::ext::get<EventVec>(obsdate).value;
918 std::string und = QuantLib::ext::get<IndexVec>(underlying).value;
919 // if observation date is in the future, the answer is always zero
920 if (obs > model_->referenceDate())
921 value.push(RandomVariable(model_->size(), 0.0));
922 else {
923 // otherwise check whether a fixing is present in the historical time series
924 TimeSeries<Real> series = IndexManager::instance().getHistory(IndexInfo(und).index()->name());
925 if (series[obs] == Null<Real>())
926 value.push(RandomVariable(model_->size(), 0.0));
927 else
928 value.push(RandomVariable(model_->size(), 1.0));
929 }
930 TRACE("histfixing( " << underlying << " , " << obsdate << " )", n);
931 }
932
933 void visit(FunctionDiscountNode& n) override {
934 checkpoint(n);
935 QL_REQUIRE(model_, "model is null");
936 n.args[0]->accept(*this);
937 n.args[1]->accept(*this);
938 n.args[2]->accept(*this);
939 auto paycurr = value.pop();
940 auto paydate = value.pop();
941 auto obsdate = value.pop();
942 checkpoint(n);
943 QL_REQUIRE(obsdate.which() == ValueTypeWhich::Event, "obsdate must be EVENT");
944 QL_REQUIRE(paydate.which() == ValueTypeWhich::Event, "paydate must be EVENT");
945 QL_REQUIRE(paycurr.which() == ValueTypeWhich::Currency, "paycurr must be CURRENCY");
946 Date obs = QuantLib::ext::get<EventVec>(obsdate).value;
947 Date pay = QuantLib::ext::get<EventVec>(paydate).value;
948 QL_REQUIRE(obs >= model_->referenceDate(),
949 "observation date (" << obs << ") >= reference date (" << model_->referenceDate() << ") required");
950 QL_REQUIRE(obs <= pay, "observation date (" << obs << ") <= payment date (" << pay << ") required");
951 value.push(model_->discount(obs, pay, QuantLib::ext::get<CurrencyVec>(paycurr).value));
952 TRACE("discount( " << obsdate << " , " << paydate << " , " << paycurr << " )", n);
953 }
954
955 void processFwdCompAvgNode(ASTNode& n, const bool isAvg) {
956 checkpoint(n);
957 QL_REQUIRE(model_, "model is null");
958 n.args[0]->accept(*this);
959 n.args[1]->accept(*this);
960 n.args[2]->accept(*this);
961 n.args[3]->accept(*this);
962 auto enddate = value.pop();
963 auto startdate = value.pop();
964 auto obsdate = value.pop();
965 auto underlying = value.pop();
966 checkpoint(n);
967 QL_REQUIRE(underlying.which() == ValueTypeWhich::Index, "underlying must be INDEX");
968 QL_REQUIRE(obsdate.which() == ValueTypeWhich::Event, "obsdate must be EVENT");
969 QL_REQUIRE(startdate.which() == ValueTypeWhich::Event, "start must be EVENT");
970 QL_REQUIRE(enddate.which() == ValueTypeWhich::Event, "end must be EVENT");
971 Date obs = QuantLib::ext::get<EventVec>(obsdate).value;
972 Date start = QuantLib::ext::get<EventVec>(startdate).value;
973 Date end = QuantLib::ext::get<EventVec>(enddate).value;
974 QL_REQUIRE(obs <= start, "observation date (" << obs << ") must be <= start date (" << start << ")");
975 QL_REQUIRE(start < end, "start date (" << start << ") must be < end date (" << end << ")");
976 RandomVariable spreadValue(model_->size(), 0.0);
977 RandomVariable gearingValue(model_->size(), 1.0);
978 RandomVariable lookbackValue(model_->size(), 0.0);
979 RandomVariable rateCutoffValue(model_->size(), 0.0);
980 RandomVariable fixingDaysValue(model_->size(), 0.0);
981 RandomVariable includeSpreadValue(model_->size(), -1.0);
982 RandomVariable capValue(model_->size(), 999999.0);
983 RandomVariable floorValue(model_->size(), -999999.0);
984 RandomVariable nakedOptionValue(model_->size(), -1.0);
985 RandomVariable localCapFloorValue(model_->size(), -1.0);
986 if (n.args[4]) {
987 QL_REQUIRE(n.args[5], "internal error: Fwd[Comp|Avg]: if spread is given, gearing must be given too");
988 n.args[4]->accept(*this);
989 auto spread = value.pop();
990 QL_REQUIRE(spread.which() == ValueTypeWhich::Number, "spread must be NUMBER");
991 spreadValue = QuantLib::ext::get<RandomVariable>(spread);
992 QL_REQUIRE(spreadValue.deterministic(), "spread must be deterministic");
993 n.args[5]->accept(*this);
994 auto gearing = value.pop();
995 QL_REQUIRE(gearing.which() == ValueTypeWhich::Number, "gearing must be NUMBER");
996 gearingValue = QuantLib::ext::get<RandomVariable>(gearing);
997 QL_REQUIRE(gearingValue.deterministic(), "gearing must be deterministic");
998 checkpoint(n);
999 }
1000 if (n.args[6]) {
1001 QL_REQUIRE(n.args[7] && n.args[8] && n.args[9],
1002 "internal error: Fwd[Comp|Avg]: if lookback is given, rateCutoff, fixingDays and includeSpread "
1003 "must be given too");
1004 n.args[6]->accept(*this);
1005 auto lookback = value.pop();
1006 QL_REQUIRE(lookback.which() == ValueTypeWhich::Number, "lookback must be NUMBER");
1007 lookbackValue = QuantLib::ext::get<RandomVariable>(lookback);
1008 QL_REQUIRE(lookbackValue.deterministic(), "lookback must be deterministic");
1009 n.args[7]->accept(*this);
1010 auto rateCutoff = value.pop();
1011 QL_REQUIRE(rateCutoff.which() == ValueTypeWhich::Number, "rateCutoff must be NUMBER");
1012 rateCutoffValue = QuantLib::ext::get<RandomVariable>(rateCutoff);
1013 QL_REQUIRE(rateCutoffValue.deterministic(), "rateCutoff must be deterministic");
1014 n.args[8]->accept(*this);
1015 auto fixingDays = value.pop();
1016 QL_REQUIRE(fixingDays.which() == ValueTypeWhich::Number, "fixingDays must be NUMBER");
1017 fixingDaysValue = QuantLib::ext::get<RandomVariable>(fixingDays);
1018 QL_REQUIRE(fixingDaysValue.deterministic(), "fixingDays must be deterministic");
1019 n.args[9]->accept(*this);
1020 auto includeSpread = value.pop();
1021 QL_REQUIRE(includeSpread.which() == ValueTypeWhich::Number, "lookback must be NUMBER");
1022 includeSpreadValue = QuantLib::ext::get<RandomVariable>(includeSpread);
1023 QL_REQUIRE(includeSpreadValue.deterministic() && (QuantLib::close_enough(includeSpreadValue.at(0), 1.0) ||
1024 QuantLib::close_enough(includeSpreadValue.at(0), -1.0)),
1025 "includeSpread must be deterministic and +1 or -1");
1026 checkpoint(n);
1027 }
1028 if (n.args[10]) {
1029 QL_REQUIRE(
1030 n.args[11] && n.args[12] && n.args[13],
1031 "internal error: Fwd[Comp|Avg]: if cap is given, floor, nakedOption, localCapFloor must be given too");
1032 n.args[10]->accept(*this);
1033 auto cap = value.pop();
1034 QL_REQUIRE(cap.which() == ValueTypeWhich::Number, "cap must be NUMBER");
1035 capValue = QuantLib::ext::get<RandomVariable>(cap);
1036 QL_REQUIRE(capValue.deterministic(), "cap must be deterministic");
1037 n.args[11]->accept(*this);
1038 auto floor = value.pop();
1039 QL_REQUIRE(floor.which() == ValueTypeWhich::Number, "floor must be NUMBER");
1040 floorValue = QuantLib::ext::get<RandomVariable>(floor);
1041 QL_REQUIRE(floorValue.deterministic(), "floor must be deterministic");
1042 n.args[12]->accept(*this);
1043 auto nakedOption = value.pop();
1044 QL_REQUIRE(nakedOption.which() == ValueTypeWhich::Number, "nakedOption must be NUMBER");
1045 nakedOptionValue = QuantLib::ext::get<RandomVariable>(nakedOption);
1046 QL_REQUIRE(nakedOptionValue.deterministic() && (QuantLib::close_enough(nakedOptionValue.at(0), 1.0) ||
1047 QuantLib::close_enough(nakedOptionValue.at(0), -1.0)),
1048 "nakedOption must be deterministic and +1 or -1");
1049 n.args[13]->accept(*this);
1050 auto localCapFloor = value.pop();
1051 QL_REQUIRE(localCapFloor.which() == ValueTypeWhich::Number, "localCapFloor must be NUMBER");
1052 localCapFloorValue = QuantLib::ext::get<RandomVariable>(localCapFloor);
1053 QL_REQUIRE(localCapFloorValue.deterministic() && (QuantLib::close_enough(localCapFloorValue.at(0), 1.0) ||
1054 QuantLib::close_enough(localCapFloorValue.at(0), -1.0)),
1055 "localCapFloor must be deterministic and +1 or -1");
1056 checkpoint(n);
1057 }
1058
1059 bool includeSpreadBool = QuantLib::close_enough(includeSpreadValue.at(0), 1.0);
1060 bool nakedOptionBool = QuantLib::close_enough(nakedOptionValue.at(0), 1.0);
1061 bool localCapFloorBool = QuantLib::close_enough(localCapFloorValue.at(0), 1.0);
1062
1063 value.push(model_->fwdCompAvg(isAvg, QuantLib::ext::get<IndexVec>(underlying).value, obs, start, end, spreadValue.at(0),
1064 gearingValue.at(0), static_cast<Integer>(lookbackValue.at(0)),
1065 static_cast<Natural>(rateCutoffValue.at(0)),
1066 static_cast<Natural>(fixingDaysValue.at(0)), includeSpreadBool, capValue.at(0),
1067 floorValue.at(0), nakedOptionBool, localCapFloorBool));
1068
1069 TRACE("fwdCompAvg(" << isAvg << " , " << underlying << " , " << obsdate << " , " << startdate << " , "
1070 << enddate << " , " << spreadValue.at(0) << " , " << gearingValue.at(0) << " , "
1071 << lookbackValue.at(0) << " , " << rateCutoffValue.at(0) << " , " << fixingDaysValue.at(0)
1072 << " , " << includeSpreadBool << " , " << capValue.at(0) << " , " << floorValue << " , "
1073 << nakedOptionBool << " , " << localCapFloorBool << ")",
1074 n);
1075 }
1076
1077 void visit(FunctionFwdCompNode& n) override { processFwdCompAvgNode(n, false); }
1078
1079 void visit(FunctionFwdAvgNode& n) override { processFwdCompAvgNode(n, true); }
1080
1081 void processProbNode(ASTNode& n, const bool above) {
1082 checkpoint(n);
1083 QL_REQUIRE(model_, "model is null");
1084 n.args[0]->accept(*this);
1085 n.args[1]->accept(*this);
1086 n.args[2]->accept(*this);
1087 n.args[3]->accept(*this);
1088 auto barrier = value.pop();
1089 auto obsdate2 = value.pop();
1090 auto obsdate1 = value.pop();
1091 auto underlying = value.pop();
1092 checkpoint(n);
1093 QL_REQUIRE(underlying.which() == ValueTypeWhich::Index, "underlying must be INDEX");
1094 QL_REQUIRE(obsdate1.which() == ValueTypeWhich::Event, "obsdate1 must be EVENT");
1095 QL_REQUIRE(obsdate2.which() == ValueTypeWhich::Event, "obsdate2 must be EVENT");
1096 QL_REQUIRE(barrier.which() == ValueTypeWhich::Number, "barrier must be NUMBER");
1097 std::string und = QuantLib::ext::get<IndexVec>(underlying).value;
1098 Date obs1 = QuantLib::ext::get<EventVec>(obsdate1).value;
1099 Date obs2 = QuantLib::ext::get<EventVec>(obsdate2).value;
1100 RandomVariable barrierValue = QuantLib::ext::get<RandomVariable>(barrier);
1101 if (obs1 > obs2)
1102 value.push(RandomVariable(model_->size(), 0.0));
1103 else
1104 value.push(model_->barrierProbability(und, obs1, obs2, barrierValue, above));
1105 TRACE((above ? "above" : "below")
1106 << "prob(" << underlying << " , " << obsdate1 << " , " << obsdate2 << " , " << barrier << ")",
1107 n);
1108 }
1109
1110 void visit(FunctionAboveProbNode& n) override { processProbNode(n, true); }
1111
1112 void visit(FunctionBelowProbNode& n) override { processProbNode(n, false); }
1113
1114 void visit(VarEvaluationNode& n) override {
1115 n.args[0]->accept(*this);
1116 checkpoint(n);
1117 n.args[1]->accept(*this);
1118 auto right = value.pop();
1119 auto left = value.pop();
1120 QL_REQUIRE(left.which() == ValueTypeWhich::Index,
1121 "evaluation operator () can only be applied to an INDEX, got " << valueTypeLabels.at(left.which()));
1122 QL_REQUIRE(right.which() == ValueTypeWhich::Event,
1123 "evaluation operator () argument obsDate must be EVENT, got " << valueTypeLabels.at(right.which()));
1124 checkpoint(n);
1125 Date obs = QuantLib::ext::get<EventVec>(right).value, fwd = Null<Date>();
1126 QL_REQUIRE(model_, "model is null");
1127 if (n.args[2]) {
1128 n.args[2]->accept(*this);
1129 auto fwdDate = value.pop();
1130 checkpoint(n);
1131 QL_REQUIRE(fwdDate.which() == ValueTypeWhich::Event,
1132 "evaluation operator () argument fwdDate must be EVENT, got "
1133 << valueTypeLabels.at(fwdDate.which()));
1134 fwd = QuantLib::ext::get<EventVec>(fwdDate).value;
1135 if (fwd == obs)
1136 fwd = Null<Date>();
1137 else {
1138 QL_REQUIRE(obs < fwd,
1139 "evaluation operator() requires obsDate (" << obs << ") < fwdDate (" << fwd << ")");
1140 }
1141 }
1142 value.push(model_->eval(QuantLib::ext::get<IndexVec>(left).value, obs, fwd));
1143 TRACE("indexEval( " << left << " , " << right << " , " << fwd << " )", n);
1144 }
1145
1146 // inputs
1147 const QuantLib::ext::shared_ptr<Model> model_;
1148 const Size size_;
1149 const std::string script_;
1151 QuantLib::ext::shared_ptr<PayLog> paylog_; // cashflow log
1153 // working variables
1154 Context& context_;
1156 // state of the runner
1157 SafeStack<Filter> filter;
1158 SafeStack<ValueType> value;
1159};
1160
1161} // namespace
1162
1163void ScriptEngine::run(const std::string& script, bool interactive, QuantLib::ext::shared_ptr<PayLog> paylog,
1164 bool includePastCashflows) {
1165
1166 ASTNode* loc;
1167 ASTRunner runner(model_, script, interactive, *context_, loc, paylog, paylog != nullptr && includePastCashflows);
1168
1170 if (model_ == nullptr || model_->type() == Model::Type::MC) {
1171 pattern = randomvariable_output_pattern(randomvariable_output_pattern::pattern::expectation);
1172 } else if (model_->type() == Model::Type::FD) {
1173 pattern = randomvariable_output_pattern(randomvariable_output_pattern::pattern::left_middle_right);
1174 } else {
1175 QL_FAIL("model type not handled when setting output pattern for random variables");
1176 }
1177
1178 DLOG("run script engine, context before run is:");
1179 DLOGGERSTREAM(pattern << *context_);
1180
1181 if (interactive) {
1182 std::cerr << pattern << "\nInitial Context: \n" << (*context_) << std::endl;
1183 }
1184
1185 boost::timer::cpu_timer timer;
1186 try {
1187 reset(root_);
1188 root_->accept(runner);
1189 timer.stop();
1190 QL_REQUIRE(runner.value.size() == 1,
1191 "ScriptEngine::run(): value stack has wrong size (" << runner.value.size() << "), should be 1");
1192 QL_REQUIRE(runner.filter.size() == 1,
1193 "ScriptEngine::run(): filter stack has wrong size (" << runner.filter.size() << "), should be 1");
1194 DLOG("script engine successfully finished, context after run is:");
1195
1196 if (interactive) {
1197 std::cerr << "\nScript engine finished without errors. Context after run:" << std::endl;
1198 }
1199
1200 } catch (const std::exception& e) {
1201 std::ostringstream errorMessage;
1202 errorMessage << "Error during script execution: " << e.what() << " at "
1203 << (loc ? to_string(loc->locationInfo) : "(last visited ast node not known)") << ": "
1204 << printCodeContext(script, loc, true);
1205
1206 std::ostringstream strippedErrorMsg;
1207 strippedErrorMsg << "Error during script execution: " << e.what() << " at "
1208 << (loc ? to_string(loc->locationInfo) : "(last visited ast node not known)");
1209
1210 DLOGGERSTREAM(strippedErrorMsg.str());
1212 DLOGGERSTREAM("Context when hitting the error:");
1213 DLOGGERSTREAM(pattern << *context_);
1214
1215 if (interactive) {
1216 std::cerr << strippedErrorMsg.str() << "\n";
1217 std::cerr << printCodeContext(script, loc);
1218 std::cerr << "Context when hitting the error:" << std::endl;
1219 std::cerr << (*context_) << std::endl;
1220 std::cin.get();
1221 }
1222
1223 QL_FAIL(errorMessage.str());
1224 }
1225
1226 DLOGGERSTREAM(pattern << *context_);
1227 DLOG("Script engine running time: " << boost::timer::format(timer.elapsed()));
1228
1229 if (interactive) {
1230 std::cerr << pattern << *context_ << std::endl;
1231 std::cin.get();
1232 }
1233}
1234
1235} // namespace data
1236} // namespace ore
resets cached values in ast
std::string script
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_
const ASTNodePtr root_
const QuantLib::ext::shared_ptr< Model > model_
#define TRACE(message, n)
ASTNode *& lastVisitedNode_
bool & interactive_
const std::string script_
SafeStack< ValueType > value
const Size size_
SafeStack< Filter > filter
Context & context_
const QuantLib::ext::shared_ptr< ModelCG > model_
const bool includePastCashflows_
DayCounter parseDayCounter(const string &s)
Convert text to QuantLib::DayCounter.
Definition: parsers.cpp:209
Classes and functions for log message handling.
@ data
Definition: log.hpp:77
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
#define DLOGGERSTREAM(text)
Definition: log.hpp:632
Date referenceDate
Definition: utilities.cpp:442
RandomVariable max(RandomVariable x, const RandomVariable &y)
RandomVariable exp(RandomVariable x)
RandomVariable sqrt(RandomVariable x)
RandomVariable log(RandomVariable x)
RandomVariable pow(RandomVariable x, const RandomVariable &y)
bool close_enough_all(const RandomVariable &x, const RandomVariable &y)
RandomVariable conditionalResult(const Filter &f, RandomVariable x, const RandomVariable &y)
RandomVariable black(const RandomVariable &omega, const RandomVariable &t, const RandomVariable &strike, const RandomVariable &forward, const RandomVariable &impliedVol)
RandomVariable normalCdf(RandomVariable x)
RandomVariable abs(RandomVariable x)
RandomVariable normalPdf(RandomVariable x)
Filter equal(Filter x, const Filter &y)
RandomVariable min(RandomVariable x, const RandomVariable &y)
Filter lt(const ValueType &x, const ValueType &y)
Definition: value.cpp:263
Filter notequal(const ValueType &x, const ValueType &y)
Definition: value.cpp:239
Filter gt(const ValueType &x, const ValueType &y)
Definition: value.cpp:282
ValueType typeSafeAssign(ValueType &x, const ValueType &y)
Definition: value.cpp:213
Filter logicalAnd(const ValueType &x, const ValueType &y)
Definition: value.cpp:352
std::string printCodeContext(std::string script, const ASTNode *loc, bool compact)
Size size(const ValueType &v)
Definition: value.cpp:145
void reset(const ASTNodePtr root)
Definition: astresetter.cpp:44
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
Filter logicalOr(const ValueType &x, const ValueType &y)
Definition: value.cpp:368
Filter leq(const ValueType &x, const ValueType &y)
Definition: value.cpp:301
Filter geq(const ValueType &x, const ValueType &y)
Definition: value.cpp:321
boost::variant< RandomVariable, EventVec, CurrencyVec, IndexVec, DaycounterVec, Filter > ValueType
Definition: value.hpp:60
Filter logicalNot(const ValueType &x)
Definition: value.cpp:341
QuantLib::ext::shared_ptr< ASTNode > ASTNodePtr
Definition: ast.hpp:46
Serializable Credit Default Swap.
Definition: namespaces.docs:23
QuantLib::ext::shared_ptr< PayLog > paylog_
Map text representations to QuantLib/QuantExt types.
stack with safety checks and pop() that returns rvalue reference of top element
scriptengine
some utility functions
script parser
LocationInfo locationInfo
Definition: ast.hpp:63
string conversion utilities
string name