29#include <ql/errors.hpp>
30#include <ql/indexes/indexmanager.hpp>
32#include <boost/timer/timer.hpp>
34#define TRACE(message, n) \
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); \
42 std::cerr << "(c)ontext (q)uit "; \
43 std::getline(std::cin, c); \
45 std::cerr << context_; \
47 interactive_ = false; \
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> {
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)
113 value.push(RandomVariable());
118 template <
typename R>
120 n.args[0]->accept(*
this);
121 n.args[1]->accept(*
this);
123 auto right =
value.pop();
124 auto left =
value.pop();
125 value.push(op(left, right));
126 TRACE(
name <<
"( " << left <<
" , " << right <<
" )", n);
129 template <
typename R>
void unaryOp(ASTNode& n,
const std::string&
name,
const std::function<R(
ValueType)>& op) {
130 n.args[0]->accept(*
this);
132 auto arg =
value.pop();
139 std::pair<ValueType&, long> getVariableRef(VariableNode& v) {
143 return std::make_pair(QuantLib::ext::ref(*v.cachedScalar), 0);
145 QL_REQUIRE(v.args[0],
"array subscript required for variable '" << v.name <<
"'");
146 v.args[0]->accept(*
this);
147 auto arg =
value.pop();
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);
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 <<
"'");
163 v.cachedScalar = &scalar->second;
164 return std::make_pair(QuantLib::ext::ref(scalar->second), 0);
166 auto array =
context_.arrays.find(v.name);
167 if (array !=
context_.arrays.end()) {
170 v.cachedVector = &array->second;
171 return getVariableRef(v);
173 QL_FAIL(
"variable '" << v.name <<
"' is not defined.");
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);
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.");
192 v->args[0]->accept(*
this);
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);
204 TRACE(
"declare(" << v->name <<
", " << val <<
")", *arg);
214 void visit(ASTNode& n)
override {
216 QL_FAIL(
"unhandled node");
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; });
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; });
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); }
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 {
252 void visit(ConditionAndNode& n)
override {
253 n.args[0]->accept(*
this);
254 auto left =
value.pop();
257 Filter l = QuantLib::ext::get<Filter>(left);
258 if (l.deterministic() && !l[0]) {
260 value.push(Filter(l.size(),
false));
261 TRACE(
"conditionAnd( false, ? )", n);
264 n.args[1]->accept(*
this);
265 auto right =
value.pop();
268 TRACE(
"conditionAnd( " << left <<
" , " << right <<
" )", n);
272 void visit(ConditionOrNode& n)
override {
273 n.args[0]->accept(*
this);
274 auto left =
value.pop();
277 Filter l = QuantLib::ext::get<Filter>(left);
278 if (l.deterministic() && l[0]) {
280 value.push(Filter(l.size(),
true));
281 TRACE(
"conditionOr( true, ? )", n);
284 n.args[1]->accept(*
this);
285 auto right =
value.pop();
288 TRACE(
"conditionOr( " << left <<
" , " << right <<
" )", n);
294 void visit(ConstantNumberNode& n)
override {
297 TRACE(
"constantNumber( " << n.value <<
" )", n);
300 void visit(VariableNode& n)
override {
301 value.push(getVariableRef(n).first);
303 TRACE(
"variable( " << n.name <<
" )", n);
306 void visit(DeclarationNumberNode& n)
override {
307 for (
auto const& arg : n.args) {
308 declareVariable(arg, RandomVariable(
size_, 0.0));
313 void visit(SizeOpNode& n)
override {
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");
321 QL_FAIL(
"SIZE can only be applied to array, " << n.name <<
" is a scalar");
323 value.push(RandomVariable(
size_,
static_cast<double>(array->second.size())));
324 TRACE(
"size( " << n.name <<
" )", n);
327 void visit(FunctionDateIndexNode& n)
override {
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);
336 QL_REQUIRE(ref.first.which() ==
ValueTypeWhich::Event,
"DATEINDEX: first argument must be of type event");
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())
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,
347 return QuantLib::ext::get<EventVec>(l).value < QuantLib::ext::get<EventVec>(r).value;
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,
354 return QuantLib::ext::get<EventVec>(l).value < QuantLib::ext::get<EventVec>(r).value;
356 array->second.begin() + 1;
357 value.push(RandomVariable(
size_,
static_cast<double>(pos)));
359 QL_FAIL(
"DATEINDEX: operation '" << n.op <<
"' not supported, expected EQ, GEQ, GT");
361 TRACE(
"dateindex( " << v->name <<
"[" << (ref.second + 1) <<
"] , " << n.name <<
" , " << n.op <<
" )", n);
364 void visit(AssignmentNode& n)
override {
365 n.args[1]->accept(*
this);
366 auto right =
value.pop();
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",
376 "can not assign to const variable '" << v->name <<
"'");
377 auto ref = getVariableRef(*v);
384 "internal error: expected NUMBER, got " << valueTypeLabels.at(ref.first.which()));
386 << valueTypeLabels.at(ref.first.which()) <<
" <- "
387 << valueTypeLabels.at(right.which()));
389 QuantLib::ext::get<RandomVariable>(ref.first).setTime(Null<Real>());
391 QuantLib::ext::get<RandomVariable>(ref.first));
392 QuantLib::ext::get<RandomVariable>(ref.first).updateDeterministic();
394 TRACE(
"assign( " << v->name <<
"[" << (ref.second + 1) <<
"] ) := " << ref.first <<
" ("
395 << valueTypeLabels.at(right.which()) <<
") using filter " <<
filter.top(),
401 void visit(RequireNode& n)
override {
402 n.args[0]->accept(*
this);
403 auto condition =
value.pop();
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);
415 void visit(SequenceNode& n)
override {
416 TRACE(
"instruction_sequence()", n);
417 for (
auto const& arg : n.args) {
423 void visit(IfThenElseNode& n)
override {
424 n.args[0]->accept(*
this);
425 auto if_ =
value.pop();
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);
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);
454 void visit(LoopNode& n)
override {
456 auto var =
context_.scalars.find(n.name);
457 QL_REQUIRE(var !=
context_.scalars.end(),
"loop variable '" << n.name <<
"' not defined or not scalar");
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();
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");
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);
490 RandomVariable(
size_,
static_cast<double>(cl))),
491 "loop variable was modified in body from " << cl <<
" to " << var->second
492 <<
", this is illegal.");
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);
505 auto d2 =
value.pop();
506 auto d1 =
value.pop();
507 auto dc =
value.pop();
513 date1 = QuantLib::ext::get<EventVec>(d1).value;
514 date2 = QuantLib::ext::get<EventVec>(d2).value;
519 void visit(FunctionDcfNode& n)
override {
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);
528 void visit(FunctionDaysNode& n)
override {
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);
539 void visit(SortNode& n)
override {
542 std::vector<RandomVariable*> x, y, p;
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) {
552 x.push_back(&QuantLib::ext::get<RandomVariable>(xv->second[c]));
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) {
564 y.push_back(&QuantLib::ext::get<RandomVariable>(yv->second[c]));
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) {
576 p.push_back(&QuantLib::ext::get<RandomVariable>(pv->second[c]));
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() <<
")");
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() <<
")");
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() <<
")");
603 std::vector<Real> val(x.size());
604 for (Size k = 0; k < x[0]->size(); ++k) {
607 for (Size c = 0; c < x.size(); ++c) {
610 std::sort(val.begin(), val.end());
611 for (Size c = 0; c < x.size(); ++c) {
612 y[c]->set(k, val[c]);
616 std::vector<std::pair<Real, Size>> val(x.size());
617 for (Size k = 0; k < x[0]->size(); ++k) {
620 for (Size c = 0; c < x.size(); ++c) {
621 val[c].first = (*x[c])[k];
622 val[c].second = c + 1;
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;
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));
634 TRACE(
"sort(...)", n);
637 void visit(PermuteNode& n)
override {
640 std::vector<RandomVariable*> x, y, p;
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) {
650 x.push_back(&QuantLib::ext::get<RandomVariable>(xv->second[c]));
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) {
662 y.push_back(&QuantLib::ext::get<RandomVariable>(yv->second[c]));
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) {
674 p.push_back(&QuantLib::ext::get<RandomVariable>(pv->second[c]));
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");
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() <<
")");
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() <<
")");
702 std::vector<Real> val(N);
703 for (Size k = 0; k < x[0]->size(); ++k) {
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];
713 for (Size c = 0; c < N; ++c) {
714 y[c]->set(k, val[c]);
718 TRACE(
"permute(...)", n);
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);
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();
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);
753 QL_REQUIRE(
model_,
"model is null");
755 QL_REQUIRE(obs <= expiry,
"obsdate (" << obs <<
") must be <= expirydate (" << expiry <<
")");
756 RandomVariable t(
model_->size(),
model_->dt(obs, expiry));
759 TRACE(
"black( " << callput <<
" , " << obsdate <<
" , " << expirydate <<
" , " << strike <<
" , " << forward
760 <<
" , " << impliedvol <<
" ), t=" << t,
764 void payHelper(ASTNode& n,
const bool log) {
765 n.args[2]->accept(*
this);
766 auto paydate =
value.pop();
769 QL_REQUIRE(
model_,
"model is null");
771 Date pay = boost::get<EventVec>(paydate).value;
774 TRACE(
"pay() = 0, since paydate " << paydate <<
" <= " <<
model_->referenceDate(), n);
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();
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;
795 TRACE(
"pay( " << amount <<
" , " << obsdate <<
" , " << paydate <<
" , " << paycurr <<
" )", n);
798 FunctionLogPayNode& pn =
dynamic_cast<FunctionLogPayNode&
>(n);
799 long legno = 0, slot = 0;
800 std::string cftype =
"Unspecified";
802 pn.args[4]->accept(*
this);
803 auto s =
value.pop();
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;
816 pn.args[6]->accept(*
this);
817 auto s =
value.pop();
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");
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 <<
")",
837 void visit(FunctionPayNode& n)
override { payHelper(n,
false); }
839 void visit(FunctionLogPayNode& n)
override { payHelper(n,
true); }
841 void processNpvNode(ASTNode& n,
bool hasMemSlot) {
842 n.args[0]->accept(*
this);
843 n.args[1]->accept(*
this);
846 n.args[2]->accept(*
this);
847 memSlot =
value.pop();
849 auto obsdate =
value.pop();
850 auto amount =
value.pop();
853 Size opt = hasMemSlot ? 3 : 2;
855 n.args[opt]->accept(*
this);
856 auto val =
value.pop();
859 regFilter = QuantLib::ext::get<Filter>(val);
861 RandomVariable addRegressor1, addRegressor2;
862 if (n.args[opt + 1]) {
863 n.args[opt + 1]->accept(*
this);
864 auto val =
value.pop();
867 addRegressor1 = QuantLib::ext::get<RandomVariable>(val);
869 if (n.args[opt + 2]) {
870 n.args[opt + 2]->accept(*
this);
871 auto val =
value.pop();
874 addRegressor2 = QuantLib::ext::get<RandomVariable>(val);
881 QL_REQUIRE(
model_,
"model is null");
882 Date obs = QuantLib::ext::get<EventVec>(obsdate).value;
884 obs = std::max(obs,
model_->referenceDate());
885 boost::optional<long> mem(boost::none);
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));
891 value.push(
model_->npv(QuantLib::ext::get<RandomVariable>(amount), obs, regFilter, mem, addRegressor1, addRegressor2));
893 TRACE(
"npvmem( " << amount <<
" , " << obsdate <<
" , " << memSlot <<
" , " << regFilter <<
" , "
894 << addRegressor1 <<
" , " << addRegressor2 <<
" )",
897 TRACE(
"npv( " << amount <<
" , " << obsdate <<
" , " << regFilter <<
" , " << addRegressor1 <<
" , "
898 << addRegressor2 <<
" )",
903 void visit(FunctionNpvNode& n)
override { processNpvNode(n,
false); }
905 void visit(FunctionNpvMemNode& n)
override { processNpvNode(n,
true); }
907 void visit(HistFixingNode& n)
override {
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();
917 Date obs = QuantLib::ext::get<EventVec>(obsdate).value;
918 std::string und = QuantLib::ext::get<IndexVec>(underlying).value;
920 if (obs >
model_->referenceDate())
924 TimeSeries<Real> series = IndexManager::instance().getHistory(IndexInfo(und).index()->
name());
925 if (series[obs] == Null<Real>())
930 TRACE(
"histfixing( " << underlying <<
" , " << obsdate <<
" )", n);
933 void visit(FunctionDiscountNode& n)
override {
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();
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);
955 void processFwdCompAvgNode(ASTNode& n,
const bool isAvg) {
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();
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);
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();
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();
996 gearingValue = QuantLib::ext::get<RandomVariable>(gearing);
997 QL_REQUIRE(gearingValue.deterministic(),
"gearing must be deterministic");
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();
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();
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();
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();
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");
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();
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();
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();
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();
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");
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);
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));
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 <<
")",
1077 void visit(FunctionFwdCompNode& n)
override { processFwdCompAvgNode(n,
false); }
1079 void visit(FunctionFwdAvgNode& n)
override { processFwdCompAvgNode(n,
true); }
1081 void processProbNode(ASTNode& n,
const bool above) {
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();
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);
1104 value.push(
model_->barrierProbability(und, obs1, obs2, barrierValue, above));
1105 TRACE((above ?
"above" :
"below")
1106 <<
"prob(" << underlying <<
" , " << obsdate1 <<
" , " << obsdate2 <<
" , " << barrier <<
")",
1110 void visit(FunctionAboveProbNode& n)
override { processProbNode(n,
true); }
1112 void visit(FunctionBelowProbNode& n)
override { processProbNode(n,
false); }
1114 void visit(VarEvaluationNode& n)
override {
1115 n.args[0]->accept(*
this);
1117 n.args[1]->accept(*
this);
1118 auto right =
value.pop();
1119 auto left =
value.pop();
1121 "evaluation operator () can only be applied to an INDEX, got " << valueTypeLabels.at(left.which()));
1123 "evaluation operator () argument obsDate must be EVENT, got " << valueTypeLabels.at(right.which()));
1125 Date obs = QuantLib::ext::get<EventVec>(right).value, fwd = Null<Date>();
1126 QL_REQUIRE(
model_,
"model is null");
1128 n.args[2]->accept(*
this);
1129 auto fwdDate =
value.pop();
1132 "evaluation operator () argument fwdDate must be EVENT, got "
1133 << valueTypeLabels.at(fwdDate.which()));
1134 fwd = QuantLib::ext::get<EventVec>(fwdDate).value;
1138 QL_REQUIRE(obs < fwd,
1139 "evaluation operator() requires obsDate (" << obs <<
") < fwdDate (" << fwd <<
")");
1142 value.push(
model_->eval(QuantLib::ext::get<IndexVec>(left).value, obs, fwd));
1143 TRACE(
"indexEval( " << left <<
" , " << right <<
" , " << fwd <<
" )", n);
1147 const QuantLib::ext::shared_ptr<Model>
model_;
1164 bool includePastCashflows) {
1167 ASTRunner runner(
model_,
script, interactive, *
context_, loc, paylog, paylog !=
nullptr && includePastCashflows);
1175 QL_FAIL(
"model type not handled when setting output pattern for random variables");
1178 DLOG(
"run script engine, context before run is:");
1182 std::cerr << pattern <<
"\nInitial Context: \n" << (*context_) << std::endl;
1185 boost::timer::cpu_timer timer;
1188 root_->accept(runner);
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:");
1197 std::cerr <<
"\nScript engine finished without errors. Context after run:" << std::endl;
1200 }
catch (
const std::exception& e) {
1201 std::ostringstream errorMessage;
1202 errorMessage <<
"Error during script execution: " << e.what() <<
" at "
1206 std::ostringstream strippedErrorMsg;
1207 strippedErrorMsg <<
"Error during script execution: " << e.what() <<
" at "
1216 std::cerr << strippedErrorMsg.str() <<
"\n";
1218 std::cerr <<
"Context when hitting the error:" << std::endl;
1219 std::cerr << (*context_) << std::endl;
1223 QL_FAIL(errorMessage.str());
1227 DLOG(
"Script engine running time: " << boost::timer::format(timer.elapsed()));
1230 std::cerr << pattern << *
context_ << std::endl;
resets cached values in ast
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 QuantLib::ext::shared_ptr< Model > model_
#define TRACE(message, n)
ASTNode *& lastVisitedNode_
const std::string script_
SafeStack< ValueType > value
SafeStack< Filter > filter
const QuantLib::ext::shared_ptr< ModelCG > model_
const bool includePastCashflows_
DayCounter parseDayCounter(const string &s)
Convert text to QuantLib::DayCounter.
Classes and functions for log message handling.
#define DLOG(text)
Logging Macro (Level = Debug)
#define DLOGGERSTREAM(text)
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)
Filter notequal(const ValueType &x, const ValueType &y)
Filter gt(const ValueType &x, const ValueType &y)
ValueType typeSafeAssign(ValueType &x, const ValueType &y)
Filter logicalAnd(const ValueType &x, const ValueType &y)
std::string printCodeContext(std::string script, const ASTNode *loc, bool compact)
Size size(const ValueType &v)
void reset(const ASTNodePtr root)
std::string to_string(const LocationInfo &l)
Filter logicalOr(const ValueType &x, const ValueType &y)
Filter leq(const ValueType &x, const ValueType &y)
Filter geq(const ValueType &x, const ValueType &y)
boost::variant< RandomVariable, EventVec, CurrencyVec, IndexVec, DaycounterVec, Filter > ValueType
Filter logicalNot(const ValueType &x)
QuantLib::ext::shared_ptr< ASTNode > ASTNodePtr
Serializable Credit Default Swap.
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
LocationInfo locationInfo
string conversion utilities