Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
scriptparser.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
19// clang-format off
20#include <boost/test/unit_test.hpp>
21#include <boost/test/data/test_case.hpp>
22// clang-format on
23#include <boost/timer/timer.hpp>
24
29#include <oret/toplevelfixture.hpp>
30
31#include <iostream>
32#include <iomanip>
33
34using namespace ore::data;
35
36namespace {
37struct TestDatum {
38 std::string label, script, expectedAST;
39};
40
41std::ostream& operator<<(std::ostream& os, const TestDatum& testDatum) { return os << "[" << testDatum.label << "]"; }
42
43TestDatum scriptData[] = {
44 {"assignment number", "x=1;",
45 "Sequence\n"
46 " Assignment\n"
47 " Variable(x)\n"
48 " -\n"
49 " ConstantNumber(1.000000)\n"},
50
51 {"assignment number", "x=1.2;",
52 "Sequence\n"
53 " Assignment\n"
54 " Variable(x)\n"
55 " -\n"
56 " ConstantNumber(1.200000)\n"},
57
58 {"european option",
59 "Option = Quantity * PAY(max( PutCall * (Underlying(Expiry) - Strike), 0 ),\n"
60 " Expiry, Settlement, PayCcy);",
61 "Sequence\n"
62 " Assignment\n"
63 " Variable(Option)\n"
64 " -\n"
65 " OperatorMultiply\n"
66 " Variable(Quantity)\n"
67 " -\n"
68 " FunctionPay\n"
69 " FunctionMax\n"
70 " OperatorMultiply\n"
71 " Variable(PutCall)\n"
72 " -\n"
73 " OperatorMinus\n"
74 " VarEvaluation\n"
75 " Variable(Underlying)\n"
76 " -\n"
77 " Variable(Expiry)\n"
78 " -\n"
79 " -\n"
80 " Variable(Strike)\n"
81 " -\n"
82 " ConstantNumber(0.000000)\n"
83 " Variable(Expiry)\n"
84 " -\n"
85 " Variable(Settlement)\n"
86 " -\n"
87 " Variable(PayCcy)\n"
88 " -\n"},
89
90 {"american window option",
91 "NUMBER Exercise, Continuation;\n"
92 "NUMBER ExerciseIndex;\n"
93 "FOR i IN (SIZE(Expiry), 1, -1) DO\n"
94 " Exercise = PAY( max(PutCall * (Underlying(Expiry[i]) - Strike), 0 ),\n"
95 " Expiry[i], Settlement[i], PayCcy );\n"
96 " IF exercise > NPV( continuation, Expiry[i] ) THEN\n"
97 " Continuation = Exercise;\n"
98 " ExerciseIndex = i;\n"
99 " END;\n"
100 "END;\n"
101 "Option = Quantity * PAY(Continuation,\n"
102 " Expiry[exerciseIndex], Settlement[exericseIndex], PayCcy);\n",
103 "Sequence\n"
104 " DeclarationNumber\n"
105 " Variable(Exercise)\n"
106 " -\n"
107 " Variable(Continuation)\n"
108 " -\n"
109 " DeclarationNumber\n"
110 " Variable(ExerciseIndex)\n"
111 " -\n"
112 " Loop(i)\n"
113 " Size(Expiry)\n"
114 " ConstantNumber(1.000000)\n"
115 " ConstantNumber(-1.000000)\n"
116 " Sequence\n"
117 " Assignment\n"
118 " Variable(Exercise)\n"
119 " -\n"
120 " FunctionPay\n"
121 " FunctionMax\n"
122 " OperatorMultiply\n"
123 " Variable(PutCall)\n"
124 " -\n"
125 " OperatorMinus\n"
126 " VarEvaluation\n"
127 " Variable(Underlying)\n"
128 " -\n"
129 " Variable(Expiry)\n"
130 " Variable(i)\n"
131 " -\n"
132 " -\n"
133 " Variable(Strike)\n"
134 " -\n"
135 " ConstantNumber(0.000000)\n"
136 " Variable(Expiry)\n"
137 " Variable(i)\n"
138 " -\n"
139 " Variable(Settlement)\n"
140 " Variable(i)\n"
141 " -\n"
142 " Variable(PayCcy)\n"
143 " -\n"
144 " IfThenElse\n"
145 " ConditionGt\n"
146 " Variable(exercise)\n"
147 " -\n"
148 " FunctionNpv\n"
149 " Variable(continuation)\n"
150 " -\n"
151 " Variable(Expiry)\n"
152 " Variable(i)\n"
153 " -\n"
154 " -\n"
155 " -\n"
156 " -\n"
157 " Sequence\n"
158 " Assignment\n"
159 " Variable(Continuation)\n"
160 " -\n"
161 " Variable(Exercise)\n"
162 " -\n"
163 " Assignment\n"
164 " Variable(ExerciseIndex)\n"
165 " -\n"
166 " Variable(i)\n"
167 " -\n"
168 " -\n"
169 " Assignment\n"
170 " Variable(Option)\n"
171 " -\n"
172 " OperatorMultiply\n"
173 " Variable(Quantity)\n"
174 " -\n"
175 " FunctionPay\n"
176 " Variable(Continuation)\n"
177 " -\n"
178 " Variable(Expiry)\n"
179 " Variable(exerciseIndex)\n"
180 " -\n"
181 " Variable(Settlement)\n"
182 " Variable(exericseIndex)\n"
183 " -\n"
184 " Variable(PayCcy)\n"
185 " -\n"},
186
187 {"volatility swap",
188 "NUMBER variance;\n"
189 "FOR i IN (2, SIZE(Observation), 1) DO\n"
190 "variance = variance + pow(\n"
191 " ln( Underlying(Observation[i]) / Underlying(Observation[i-1]) ), 2);\n"
192 "END;\n"
193 "Option = Notional * PAY( pow(variance / SIZE(Observation), 0.5) - Strike,\n"
194 " Observation[SIZE(Observation)], Payment, PayCcy);\n",
195 "Sequence\n"
196 " DeclarationNumber\n"
197 " Variable(variance)\n"
198 " -\n"
199 " Loop(i)\n"
200 " ConstantNumber(2.000000)\n"
201 " Size(Observation)\n"
202 " ConstantNumber(1.000000)\n"
203 " Sequence\n"
204 " Assignment\n"
205 " Variable(variance)\n"
206 " -\n"
207 " OperatorPlus\n"
208 " Variable(variance)\n"
209 " -\n"
210 " FunctionPow\n"
211 " FunctionLog\n"
212 " OperatorDivide\n"
213 " VarEvaluation\n"
214 " Variable(Underlying)\n"
215 " -\n"
216 " Variable(Observation)\n"
217 " Variable(i)\n"
218 " -\n"
219 " -\n"
220 " VarEvaluation\n"
221 " Variable(Underlying)\n"
222 " -\n"
223 " Variable(Observation)\n"
224 " OperatorMinus\n"
225 " Variable(i)\n"
226 " -\n"
227 " ConstantNumber(1.000000)\n"
228 " -\n"
229 " ConstantNumber(2.000000)\n"
230 " Assignment\n"
231 " Variable(Option)\n"
232 " -\n"
233 " OperatorMultiply\n"
234 " Variable(Notional)\n"
235 " -\n"
236 " FunctionPay\n"
237 " OperatorMinus\n"
238 " FunctionPow\n"
239 " OperatorDivide\n"
240 " Variable(variance)\n"
241 " -\n"
242 " Size(Observation)\n"
243 " ConstantNumber(0.500000)\n"
244 " Variable(Strike)\n"
245 " -\n"
246 " Variable(Observation)\n"
247 " Size(Observation)\n"
248 " Variable(Payment)\n"
249 " -\n"
250 " Variable(PayCcy)\n"
251 " -\n"},
252
253 {"cliquet option",
254 "NUMBER i;\n"
255 "FOR i IN (2, SIZE(Valuation), 1) DO\n"
256 "P = P + max( min( Underlying(Valuation[i]) / Underlying(Valuation[i-1]) - 1,\n"
257 " localCap),\n"
258 " localFloor);\n"
259 "END;\n"
260 "Option = PAY(Notional * max( min(P, globalCap) , globalFloor),\n"
261 " Valuation[SIZE(Valuation)], Settlement, PayCcy);\n",
262 "Sequence\n"
263 " DeclarationNumber\n"
264 " Variable(i)\n"
265 " -\n"
266 " Loop(i)\n"
267 " ConstantNumber(2.000000)\n"
268 " Size(Valuation)\n"
269 " ConstantNumber(1.000000)\n"
270 " Sequence\n"
271 " Assignment\n"
272 " Variable(P)\n"
273 " -\n"
274 " OperatorPlus\n"
275 " Variable(P)\n"
276 " -\n"
277 " FunctionMax\n"
278 " FunctionMin\n"
279 " OperatorMinus\n"
280 " OperatorDivide\n"
281 " VarEvaluation\n"
282 " Variable(Underlying)\n"
283 " -\n"
284 " Variable(Valuation)\n"
285 " Variable(i)\n"
286 " -\n"
287 " -\n"
288 " VarEvaluation\n"
289 " Variable(Underlying)\n"
290 " -\n"
291 " Variable(Valuation)\n"
292 " OperatorMinus\n"
293 " Variable(i)\n"
294 " -\n"
295 " ConstantNumber(1.000000)\n"
296 " -\n"
297 " ConstantNumber(1.000000)\n"
298 " Variable(localCap)\n"
299 " -\n"
300 " Variable(localFloor)\n"
301 " -\n"
302 " Assignment\n"
303 " Variable(Option)\n"
304 " -\n"
305 " FunctionPay\n"
306 " OperatorMultiply\n"
307 " Variable(Notional)\n"
308 " -\n"
309 " FunctionMax\n"
310 " FunctionMin\n"
311 " Variable(P)\n"
312 " -\n"
313 " Variable(globalCap)\n"
314 " -\n"
315 " Variable(globalFloor)\n"
316 " -\n"
317 " Variable(Valuation)\n"
318 " Size(Valuation)\n"
319 " Variable(Settlement)\n"
320 " -\n"
321 " Variable(PayCcy)\n"
322 " -\n"},
323
324 {"autocallable",
325 "NUMBER StrikePrice, KnockInPrice, Value;\n"
326 "NUMBER terminated, knockedIn, u, v;\n"
327 "FOR u IN (1, SIZE(Underlying), 1) DO\n"
328 " StrikePrice = StrikePrice + Underlying[u](StrikeDate);\n"
329 "END;\n"
330 "StrikePrice = StrikePrice / SIZE(Underlying);\n"
331 "KnockInPrice = KnockInRatio * StrikePrice;\n"
332 "FOR v IN (1, SIZE(Valuation), 1) DO\n"
333 " IF v == SIZE(Valuation) AND knockedIn == 1 AND terminated == 0 THEN\n"
334 " Option = PAY(Notional * ( 1 - Value / StrikePrice), Valuation[v], Settlement[v], PayCcy);\n"
335 " ELSE \n"
336 " IF v > 2 AND terminated == 0 THEN\n"
337 " Value = 0;\n"
338 " FOR u IN (1, SIZE(Underlying), 1) DO\n"
339 " Value = Value + Underlying[u](Valuation[v]);\n"
340 " END;\n"
341 " Value = Value / SIZE(Underlying);\n"
342 " IF Value > StrikePrice THEN\n"
343 " Option = PAY (Notional * v * 0.06, Valuation[v], Settlement[v], PayCcy);\n"
344 " terminated = 1;\n"
345 " ELSE\n"
346 " IF Value < KnockInPrice THEN\n"
347 " knockedIn = 1;\n"
348 " END;\n"
349 " END;\n"
350 " END;\n"
351 " END;\n"
352 "END;\n",
353 "Sequence\n"
354 " DeclarationNumber\n"
355 " Variable(StrikePrice)\n"
356 " -\n"
357 " Variable(KnockInPrice)\n"
358 " -\n"
359 " Variable(Value)\n"
360 " -\n"
361 " DeclarationNumber\n"
362 " Variable(terminated)\n"
363 " -\n"
364 " Variable(knockedIn)\n"
365 " -\n"
366 " Variable(u)\n"
367 " -\n"
368 " Variable(v)\n"
369 " -\n"
370 " Loop(u)\n"
371 " ConstantNumber(1.000000)\n"
372 " Size(Underlying)\n"
373 " ConstantNumber(1.000000)\n"
374 " Sequence\n"
375 " Assignment\n"
376 " Variable(StrikePrice)\n"
377 " -\n"
378 " OperatorPlus\n"
379 " Variable(StrikePrice)\n"
380 " -\n"
381 " VarEvaluation\n"
382 " Variable(Underlying)\n"
383 " Variable(u)\n"
384 " -\n"
385 " Variable(StrikeDate)\n"
386 " -\n"
387 " -\n"
388 " Assignment\n"
389 " Variable(StrikePrice)\n"
390 " -\n"
391 " OperatorDivide\n"
392 " Variable(StrikePrice)\n"
393 " -\n"
394 " Size(Underlying)\n"
395 " Assignment\n"
396 " Variable(KnockInPrice)\n"
397 " -\n"
398 " OperatorMultiply\n"
399 " Variable(KnockInRatio)\n"
400 " -\n"
401 " Variable(StrikePrice)\n"
402 " -\n"
403 " Loop(v)\n"
404 " ConstantNumber(1.000000)\n"
405 " Size(Valuation)\n"
406 " ConstantNumber(1.000000)\n"
407 " Sequence\n"
408 " IfThenElse\n"
409 " ConditionAnd\n"
410 " ConditionAnd\n"
411 " ConditionEq\n"
412 " Variable(v)\n"
413 " -\n"
414 " Size(Valuation)\n"
415 " ConditionEq\n"
416 " Variable(knockedIn)\n"
417 " -\n"
418 " ConstantNumber(1.000000)\n"
419 " ConditionEq\n"
420 " Variable(terminated)\n"
421 " -\n"
422 " ConstantNumber(0.000000)\n"
423 " Sequence\n"
424 " Assignment\n"
425 " Variable(Option)\n"
426 " -\n"
427 " FunctionPay\n"
428 " OperatorMultiply\n"
429 " Variable(Notional)\n"
430 " -\n"
431 " OperatorMinus\n"
432 " ConstantNumber(1.000000)\n"
433 " OperatorDivide\n"
434 " Variable(Value)\n"
435 " -\n"
436 " Variable(StrikePrice)\n"
437 " -\n"
438 " Variable(Valuation)\n"
439 " Variable(v)\n"
440 " -\n"
441 " Variable(Settlement)\n"
442 " Variable(v)\n"
443 " -\n"
444 " Variable(PayCcy)\n"
445 " -\n"
446 " Sequence\n"
447 " IfThenElse\n"
448 " ConditionAnd\n"
449 " ConditionGt\n"
450 " Variable(v)\n"
451 " -\n"
452 " ConstantNumber(2.000000)\n"
453 " ConditionEq\n"
454 " Variable(terminated)\n"
455 " -\n"
456 " ConstantNumber(0.000000)\n"
457 " Sequence\n"
458 " Assignment\n"
459 " Variable(Value)\n"
460 " -\n"
461 " ConstantNumber(0.000000)\n"
462 " Loop(u)\n"
463 " ConstantNumber(1.000000)\n"
464 " Size(Underlying)\n"
465 " ConstantNumber(1.000000)\n"
466 " Sequence\n"
467 " Assignment\n"
468 " Variable(Value)\n"
469 " -\n"
470 " OperatorPlus\n"
471 " Variable(Value)\n"
472 " -\n"
473 " VarEvaluation\n"
474 " Variable(Underlying)\n"
475 " Variable(u)\n"
476 " -\n"
477 " Variable(Valuation)\n"
478 " Variable(v)\n"
479 " -\n"
480 " -\n"
481 " Assignment\n"
482 " Variable(Value)\n"
483 " -\n"
484 " OperatorDivide\n"
485 " Variable(Value)\n"
486 " -\n"
487 " Size(Underlying)\n"
488 " IfThenElse\n"
489 " ConditionGt\n"
490 " Variable(Value)\n"
491 " -\n"
492 " Variable(StrikePrice)\n"
493 " -\n"
494 " Sequence\n"
495 " Assignment\n"
496 " Variable(Option)\n"
497 " -\n"
498 " FunctionPay\n"
499 " OperatorMultiply\n"
500 " OperatorMultiply\n"
501 " Variable(Notional)\n"
502 " -\n"
503 " Variable(v)\n"
504 " -\n"
505 " ConstantNumber(0.060000)\n"
506 " Variable(Valuation)\n"
507 " Variable(v)\n"
508 " -\n"
509 " Variable(Settlement)\n"
510 " Variable(v)\n"
511 " -\n"
512 " Variable(PayCcy)\n"
513 " -\n"
514 " Assignment\n"
515 " Variable(terminated)\n"
516 " -\n"
517 " ConstantNumber(1.000000)\n"
518 " Sequence\n"
519 " IfThenElse\n"
520 " ConditionLt\n"
521 " Variable(Value)\n"
522 " -\n"
523 " Variable(KnockInPrice)\n"
524 " -\n"
525 " Sequence\n"
526 " Assignment\n"
527 " Variable(knockedIn)\n"
528 " -\n"
529 " ConstantNumber(1.000000)\n"
530 " -\n"
531 " -\n"}};
532} // namespace
533
534BOOST_FIXTURE_TEST_SUITE(OREDataTestSuite, ore::test::TopLevelFixture)
535
536BOOST_AUTO_TEST_SUITE(ScriptParserTest)
537
538BOOST_DATA_TEST_CASE(testScriptParsing, boost::unit_test::data::make(scriptData), testScript) {
539
540 BOOST_TEST_MESSAGE("Testing Script Parser...");
541
542 ScriptParser parser(testScript.script);
543 if (parser.success()) {
544 BOOST_TEST_MESSAGE("Parsing succeeded\n" << to_string(parser.ast()));
545 } else {
546 BOOST_TEST_MESSAGE("Parsing failed\n" << parser.error());
547 }
548 BOOST_REQUIRE(parser.success());
549 BOOST_CHECK_EQUAL(to_string(parser.ast(), false), testScript.expectedAST);
550}
551
552BOOST_DATA_TEST_CASE(testRoundTrip, boost::unit_test::data::make(scriptData), testScript) {
553
554 BOOST_TEST_MESSAGE("Testing Script Parser AST->Script->AST Roundtrip...");
555
556 ScriptParser parser(testScript.script);
557 BOOST_REQUIRE(parser.success());
558 std::string script = to_script(parser.ast());
559 BOOST_TEST_MESSAGE("Generated script:\n<<<<<<<<<<\n" + script + "\n>>>>>>>>>>");
560 ScriptParser parser2(script);
561 BOOST_REQUIRE(parser2.success());
562 BOOST_CHECK_EQUAL(to_string(parser.ast(), false), to_string(parser2.ast(), false));
563}
564
565BOOST_AUTO_TEST_CASE(testRandomRoundTrip) {
566
567 BOOST_TEST_MESSAGE("Testing Script Parser Random AST->Script->AST Roundtrip...");
568
569 std::vector<std::tuple<Size, Size, Size>> testSizes;
570 testSizes.push_back(std::make_tuple(1, 5, 1000));
571 testSizes.push_back(std::make_tuple(5, 5, 1000));
572 testSizes.push_back(std::make_tuple(10, 5, 1000));
573 testSizes.push_back(std::make_tuple(1, 10, 1000));
574 testSizes.push_back(std::make_tuple(5, 10, 1000));
575 testSizes.push_back(std::make_tuple(10, 10, 100));
576
577 for (auto const& t : testSizes) {
578 BOOST_TEST_MESSAGE("Testing Script Parser Random AST->Script->AST RoundTrip (len="
579 << std::get<0>(t) << ", "
580 << "dep=" << std::get<1>(t) << ", n=" << std::get<2>(t) << ")");
581 Size maxLen = 0;
582 double avgLen = 0.0, avgTiming = 0.0, maxTiming = 0.0;
583 for (Size i = 0; i < std::get<2>(t); ++i) {
584 auto ast = generateRandomAST(std::get<0>(t), std::get<1>(t), 42 + i);
585 std::string script = to_script(ast);
586 maxLen = std::max(script.length(), maxLen);
587 avgLen += static_cast<double>(script.length()) / static_cast<double>(std::get<2>(t));
588 boost::timer::cpu_timer timer;
589 ScriptParser parser(script);
590 if (!parser.success()) {
591 BOOST_TEST_MESSAGE("Error while parsing script: " << parser.error());
592 }
593 BOOST_REQUIRE(parser.success());
594 double timing = timer.elapsed().wall * 1e-9;
595 maxTiming = std::max(timing, maxTiming);
596 avgTiming += timing / static_cast<double>(std::get<2>(t));
597 BOOST_CHECK_EQUAL(to_string(ast, false), to_string(parser.ast(), false));
598 }
599 BOOST_TEST_MESSAGE("Finished, script size avg = " << avgLen << ", max = " << maxLen
600 << ", timing avg = " << avgTiming * 1E3
601 << " ms, max = " << maxTiming * 1E3 << " ms");
602 }
603}
604
605BOOST_AUTO_TEST_CASE(generateRandomScript, *boost::unit_test::disabled()) {
606
607 // not a test, just for convenience, to be removed at some stage...
608
609 BOOST_TEST_MESSAGE("Creating randomg script based on LEN, DEP and SEED env variables");
610
611 if (getenv("LEN") && getenv("DEP") && getenv("SEED")) {
612 auto ast = generateRandomAST(atoi(getenv("LEN")), atoi(getenv("DEP")), atoi(getenv("SEED")));
613 std::string script = to_script(ast);
614 BOOST_TEST_MESSAGE("Generated script:\n<<<<<<<<<<\n" + script + "\n>>>>>>>>>>");
615 }
616}
617
618BOOST_AUTO_TEST_CASE(testInteractive, *boost::unit_test::disabled()) {
619
620 // not a test, just for convenience, to be removed at some stage...
621
622 BOOST_TEST_MESSAGE("Running Script Parser on INPUT env variable...");
623
624 std::string script = "IF x==2 THEN y=1; ELSE z=2; END;";
625 if (auto c = getenv("INPUT"))
626 script = std::string(c);
627
628 ScriptParser parser(script);
629 if (parser.success()) {
630 std::cout << "Parsing succeeded\n" << to_string(parser.ast()) << std::endl;
631 } else {
632 std::cout << "Parsing failed\n" << parser.error();
633 }
634}
635
636BOOST_AUTO_TEST_SUITE_END()
637
638BOOST_AUTO_TEST_SUITE_END()
ast printer
std::string script
ast to script converter
ASTNodePtr ast() const
const ParserError & error() const
@ data
Definition: log.hpp:77
std::ostream & operator<<(std::ostream &out, EquityReturnType t)
ASTNodePtr generateRandomAST(const Size maxSequenceLength, const Size maxDepth, const Size seed)
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
std::string to_script(const ASTNodePtr root)
random ast generator for testing purposes
script parser
BOOST_DATA_TEST_CASE(testScriptParsing, boost::unit_test::data::make(scriptData), testScript)
BOOST_AUTO_TEST_CASE(testRandomRoundTrip)