20#include <boost/test/unit_test.hpp>
21#include <boost/test/data/test_case.hpp>
23#include <boost/timer/timer.hpp>
32#include <oret/toplevelfixture.hpp>
40#include <ql/indexes/ibor/eonia.hpp>
41#include <ql/instruments/vanillaoption.hpp>
42#include <ql/pricingengines/blackformula.hpp>
43#include <ql/pricingengines/vanilla/fdblackscholesvanillaengine.hpp>
44#include <ql/processes/stochasticprocessarray.hpp>
45#include <ql/quotes/simplequote.hpp>
46#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
47#include <ql/termstructures/yield/flatforward.hpp>
48#include <ql/time/calendars/nullcalendar.hpp>
49#include <ql/time/daycounters/actualactual.hpp>
56using boost::timer::cpu_timer;
57using boost::timer::default_places;
59BOOST_FIXTURE_TEST_SUITE(OREDataTestSuite, ore::test::TopLevelFixture)
61BOOST_AUTO_TEST_SUITE(ScriptEngineTest)
64 BOOST_TEST_MESSAGE(
"Testing simple script...");
65 std::string
script =
"NUMBER x,i; FOR i IN (1,100,1) DO x = x + i; END;";
67 BOOST_REQUIRE(parser.
success());
68 BOOST_TEST_MESSAGE(
"Parsing successful, AST:\n" <<
to_string(parser.
ast()));
70 auto context = QuantLib::ext::make_shared<Context>();
72 BOOST_REQUIRE_NO_THROW(engine.
run());
73 BOOST_TEST_MESSAGE(
"Script Engine successfully run, context is:\n" << *context);
84RandomVariable executeScript(
const std::string&
script,
const QuantLib::ext::shared_ptr<Context> initialContext) {
86 BOOST_REQUIRE(parser.success());
87 auto context = QuantLib::ext::make_shared<Context>(*initialContext);
88 ScriptEngine engine(parser.ast(), context, QuantLib::ext::make_shared<DummyModel>(1));
89 BOOST_REQUIRE_NO_THROW(engine.run());
90 BOOST_REQUIRE(context->scalars.find(
"result") != context->scalars.end());
92 return QuantLib::ext::get<RandomVariable>(context->scalars.at(
"result"));
97 BOOST_TEST_MESSAGE(
"Testing functions...");
99 auto c = QuantLib::ext::make_shared<Context>();
106 c->scalars[
"ref"] =
EventVec{1, Date(6, Jun, 2019)};
107 c->scalars[
"expiry"] =
EventVec{1, Date(6, Jun, 2022)};
111 c->scalars[
"result"] = result;
130 BOOST_CHECK(
close_enough_all(executeScript(
"result=x+y-y+x/y*y-x;", c), x + y - y + x / y * y - x));
133 executeScript(
"result=black(omega,ref,expiry,strike,forward,vol);", c).at(0),
135 Option::Put, 98.0, 100.0,
136 0.2 * std::sqrt(ActualActual(ActualActual::ISDA).yearFraction(Date(6, June, 2019), Date(6, June, 2022)))),
141 BOOST_TEST_MESSAGE(
"Testing daycounter functions...");
143 Date d1(15, Sep, 2019), d2(8, Jan, 2033);
146 auto c = QuantLib::ext::make_shared<Context>();
151 c->scalars[
"date1"] = date1;
152 c->scalars[
"date2"] = date2;
153 c->scalars[
"daycounter"] = daycounter;
154 c->scalars[
"result"] = result;
156 BOOST_CHECK_CLOSE(executeScript(
"result=dcf(daycounter, date1, date2);", c).at(0), dc.yearFraction(d1, d2), 1E-10);
157 BOOST_CHECK_CLOSE(executeScript(
"result=days(daycounter, date1, date2);", c).at(0), dc.dayCount(d1, d2), 1E-12);
161 BOOST_TEST_MESSAGE(
"Testing sort function...");
163 constexpr Real tol = 1E-14;
175 std::vector<RandomVariable> x{x1, x2, x3, x4};
176 std::vector<ValueType> xv;
177 for (
auto const& c : x)
182 auto c0 = QuantLib::ext::make_shared<Context>();
183 c0->arrays[
"x"] = xv;
188 auto c = QuantLib::ext::make_shared<Context>(*c0);
190 BOOST_REQUIRE_NO_THROW(engine.
run());
191 std::vector<ValueType> result = c->arrays.at(
"x");
192 BOOST_REQUIRE(result.size() == x.size());
193 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(0)).size() == x.at(0).size());
194 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(1)).size() == x.at(1).size());
195 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(0), 1.0, tol);
196 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(0), 2.0, tol);
197 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(0), 3.0, tol);
198 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(0), 4.0, tol);
199 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(1), 1.0, tol);
200 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(1), 2.0, tol);
201 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(1), 3.0, tol);
202 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(1), 4.0, tol);
205 c = QuantLib::ext::make_shared<Context>(*c0);
207 BOOST_REQUIRE_NO_THROW(engine2.
run());
208 result = c->arrays.at(
"x");
209 BOOST_REQUIRE(result.size() == x.size());
210 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(0)).size() == x.at(0).size());
211 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(1)).size() == x.at(1).size());
212 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(0), x.at(0).at(0), tol);
213 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(0), x.at(1).at(0), tol);
214 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(0), x.at(2).at(0), tol);
215 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(0), x.at(3).at(0), tol);
216 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(1), x.at(0).at(1), tol);
217 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(1), x.at(1).at(1), tol);
218 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(1), x.at(2).at(1), tol);
219 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(1), x.at(3).at(1), tol);
220 result = c->arrays.at(
"y");
221 BOOST_REQUIRE(result.size() == x.size());
222 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(0)).size() == x.at(0).size());
223 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(1)).size() == x.at(1).size());
224 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(0), 1.0, tol);
225 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(0), 2.0, tol);
226 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(0), 3.0, tol);
227 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(0), 4.0, tol);
228 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(1), 1.0, tol);
229 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(1), 2.0, tol);
230 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(1), 3.0, tol);
231 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(1), 4.0, tol);
234 c = QuantLib::ext::make_shared<Context>(*c0);
236 BOOST_REQUIRE_NO_THROW(engine3.
run());
237 result = c->arrays.at(
"x");
238 BOOST_REQUIRE(result.size() == x.size());
239 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(0)).size() == x.at(0).size());
240 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(1)).size() == x.at(1).size());
241 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(0), x.at(0).at(0), tol);
242 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(0), x.at(1).at(0), tol);
243 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(0), x.at(2).at(0), tol);
244 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(0), x.at(3).at(0), tol);
245 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(1), x.at(0).at(1), tol);
246 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(1), x.at(1).at(1), tol);
247 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(1), x.at(2).at(1), tol);
248 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(1), x.at(3).at(1), tol);
249 result = c->arrays.at(
"y");
250 BOOST_REQUIRE(result.size() == x.size());
251 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(0)).size() == x.at(0).size());
252 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(1)).size() == x.at(1).size());
253 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(0), 1.0, tol);
254 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(0), 2.0, tol);
255 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(0), 3.0, tol);
256 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(0), 4.0, tol);
257 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(1), 1.0, tol);
258 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(1), 2.0, tol);
259 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(1), 3.0, tol);
260 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(1), 4.0, tol);
261 result = c->arrays.at(
"i");
262 BOOST_REQUIRE(result.size() == x.size());
263 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(0)).size() == x.at(0).size());
264 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(1)).size() == x.at(1).size());
265 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(0), 4.0, tol);
266 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(0), 3.0, tol);
267 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(0), 1.0, tol);
268 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(0), 2.0, tol);
269 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(1), 1.0, tol);
270 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(1), 2.0, tol);
271 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(1), 4.0, tol);
272 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(1), 3.0, tol);
275 c = QuantLib::ext::make_shared<Context>(*c0);
276 c->arrays[
"y"] = std::vector<ValueType>(3);
278 BOOST_REQUIRE_THROW(engine4.
run(), std::exception);
281 c = QuantLib::ext::make_shared<Context>(*c0);
282 c->arrays[
"i"] = std::vector<ValueType>(3);
284 BOOST_REQUIRE_THROW(engine5.
run(), std::exception);
288 BOOST_TEST_MESSAGE(
"Testing permute function...");
290 constexpr Real tol = 1E-14;
312 std::vector<RandomVariable> x{x1, x2, x3, x4};
313 std::vector<RandomVariable> p{p1, p2, p3, p4};
314 std::vector<ValueType> xv, pv;
315 for (
auto const& c : x)
317 for (
auto const& c : p)
322 auto c0 = QuantLib::ext::make_shared<Context>();
323 c0->arrays[
"x"] = xv;
324 c0->arrays[
"y"] = yv;
325 c0->arrays[
"p"] = pv;
328 auto c = QuantLib::ext::make_shared<Context>(*c0);
330 BOOST_REQUIRE_NO_THROW(engine.
run());
331 std::vector<ValueType> result = c->arrays.at(
"x");
332 BOOST_REQUIRE(result.size() == x.size());
333 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(0)).size() == x.at(0).size());
334 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(1)).size() == x.at(1).size());
335 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(0), 1.0, tol);
336 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(0), 2.0, tol);
337 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(0), 3.0, tol);
338 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(0), 4.0, tol);
339 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(1), 1.0, tol);
340 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(1), 2.0, tol);
341 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(1), 3.0, tol);
342 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(1), 4.0, tol);
345 c = QuantLib::ext::make_shared<Context>(*c0);
347 BOOST_REQUIRE_NO_THROW(engine2.
run());
348 result = c->arrays.at(
"x");
349 BOOST_REQUIRE(result.size() == x.size());
350 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(0)).size() == x.at(0).size());
351 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(1)).size() == x.at(1).size());
352 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(0), x.at(0).at(0), tol);
353 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(0), x.at(1).at(0), tol);
354 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(0), x.at(2).at(0), tol);
355 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(0), x.at(3).at(0), tol);
356 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(1), x.at(0).at(1), tol);
357 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(1), x.at(1).at(1), tol);
358 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(1), x.at(2).at(1), tol);
359 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(1), x.at(3).at(1), tol);
360 result = c->arrays.at(
"y");
361 BOOST_REQUIRE(result.size() == x.size());
362 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(0)).size() == x.at(0).size());
363 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(1)).size() == x.at(1).size());
364 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(0), 1.0, tol);
365 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(0), 2.0, tol);
366 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(0), 3.0, tol);
367 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(0), 4.0, tol);
368 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(1), 1.0, tol);
369 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(1), 2.0, tol);
370 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(1), 3.0, tol);
371 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(1), 4.0, tol);
374 c = QuantLib::ext::make_shared<Context>(*c0);
375 c->arrays[
"p"] = std::vector<ValueType>(5);
377 BOOST_REQUIRE_THROW(engine3.
run(), std::exception);
380 c = QuantLib::ext::make_shared<Context>(*c0);
381 c->arrays[
"y"] = std::vector<ValueType>(5);
383 BOOST_REQUIRE_THROW(engine4.
run(), std::exception);
386 c = QuantLib::ext::make_shared<Context>(*c0);
387 std::vector<ValueType> pv2 = pv;
388 QuantLib::ext::get<RandomVariable>(pv2[2]).set(1, 5.0);
389 c->arrays[
"p"] = pv2;
391 BOOST_REQUIRE_THROW(engine5.
run(), std::exception);
395 BOOST_TEST_MESSAGE(
"Testing sort and permute functions with filter...");
397 constexpr Real tol = 1E-14;
419 std::vector<RandomVariable> x{x1, x2, x3, x4};
420 std::vector<RandomVariable> p{p1, p2, p3, p4};
421 std::vector<ValueType> xv, pv;
422 for (
auto const& c : x)
424 for (
auto const& c : p)
428 indicator.
set(0, 0.0);
429 indicator.
set(1, 1.0);
431 auto c0 = QuantLib::ext::make_shared<Context>();
432 c0->arrays[
"x"] = xv;
433 c0->arrays[
"p"] = xv;
434 c0->scalars[
"indicator"] = indicator;
437 auto c = QuantLib::ext::make_shared<Context>(*c0);
439 QuantLib::ext::make_shared<DummyModel>(2));
440 BOOST_REQUIRE_NO_THROW(engine.
run());
441 std::vector<ValueType> result = c->arrays.at(
"x");
442 BOOST_REQUIRE(result.size() == x.size());
443 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(0)).size() == x.at(0).size());
444 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(1)).size() == x.at(1).size());
445 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(0), x.at(0).at(0), tol);
446 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(0), x.at(1).at(0), tol);
447 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(0), x.at(2).at(0), tol);
448 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(0), x.at(3).at(0), tol);
449 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(1), 1.0, tol);
450 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(1), 2.0, tol);
451 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(1), 3.0, tol);
452 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(1), 4.0, tol);
455 c = QuantLib::ext::make_shared<Context>(*c0);
457 QuantLib::ext::make_shared<DummyModel>(2));
458 BOOST_REQUIRE_NO_THROW(engine2.
run());
459 result = c->arrays.at(
"x");
460 BOOST_REQUIRE(result.size() == x.size());
461 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(0)).size() == x.at(0).size());
462 BOOST_REQUIRE(QuantLib::ext::get<RandomVariable>(result.at(1)).size() == x.at(1).size());
463 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(0), x.at(0).at(0), tol);
464 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(0), x.at(1).at(0), tol);
465 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(0), x.at(2).at(0), tol);
466 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(0), x.at(3).at(0), tol);
467 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(0)).at(1), 1.0, tol);
468 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(1)).at(1), 2.0, tol);
469 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(2)).at(1), 3.0, tol);
470 BOOST_CHECK_CLOSE(QuantLib::ext::get<RandomVariable>(result.at(3)).at(1), 4.0, tol);
474 BOOST_TEST_MESSAGE(
"Testing HISTFIXING() function...");
476 std::string
script =
"NUMBER hasFixing1, hasFixing2, hasFixing3;\n"
477 "hasFixing1 = HISTFIXING(Underlying, date1);\n"
478 "hasFixing2 = HISTFIXING(Underlying, date2);\n"
479 "hasFixing3 = HISTFIXING(Underlying, date3);\n";
481 BOOST_REQUIRE(parser.
success());
482 BOOST_TEST_MESSAGE(
"Parsing successful, AST:\n" <<
to_string(parser.
ast()));
484 constexpr Size nPaths = 50000;
486 auto context = QuantLib::ext::make_shared<Context>();
487 context->scalars[
"Underlying"] =
IndexVec{nPaths,
"EQ-SP5"};
488 context->scalars[
"date1"] =
EventVec{nPaths, Date(7, May, 2019)};
489 context->scalars[
"date2"] =
EventVec{nPaths, Date(8, May, 2019)};
490 context->scalars[
"date3"] =
EventVec{nPaths, Date(9, May, 2019)};
495 static Date date(8, May, 2019);
500 auto model = QuantLib::ext::make_shared<MyModel>(nPaths);
503 ind.addFixing(Date(8, May, 2019), 100.0);
504 ind.addFixing(Date(9, May, 2019), 100.0);
507 BOOST_REQUIRE_NO_THROW(engine.
run());
512 RandomVariable rv1 = QuantLib::ext::get<RandomVariable>(context->scalars[
"hasFixing1"]);
513 RandomVariable rv2 = QuantLib::ext::get<RandomVariable>(context->scalars[
"hasFixing2"]);
514 RandomVariable rv3 = QuantLib::ext::get<RandomVariable>(context->scalars[
"hasFixing3"]);
520 constexpr Real tol = 1E-10;
521 BOOST_CHECK_CLOSE(rv1.
at(0), 0.0, tol);
522 BOOST_CHECK_CLOSE(rv2.
at(0), 1.0, tol);
523 BOOST_CHECK_CLOSE(rv3.
at(0), 0.0, tol);
527 BOOST_TEST_MESSAGE(
"Testing DATEINDEX(...,...,EQ) function");
529 std::string
script =
"NUMBER i;\n"
530 "i = DATEINDEX(d, a, EQ);";
532 BOOST_REQUIRE(parser.
success());
533 BOOST_TEST_MESSAGE(
"Parsing successful, AST:\n" <<
to_string(parser.
ast()));
535 constexpr Size nPaths = 10;
536 constexpr Real tol = 1E-10;
538 auto model = QuantLib::ext::make_shared<DummyModel>(nPaths);
540 std::vector<ValueType> dates;
541 dates.push_back(
EventVec{nPaths, Date(7, May, 2019)});
542 dates.push_back(
EventVec{nPaths, Date(10, June, 2020)});
545 auto context1 = QuantLib::ext::make_shared<Context>();
546 context1->arrays[
"a"] = dates;
547 context1->scalars[
"d"] =
EventVec{nPaths, Date(7, May, 2019)};
549 BOOST_REQUIRE_NO_THROW(engine1.
run());
551 RandomVariable rv1 = QuantLib::ext::get<RandomVariable>(context1->scalars[
"i"]);
553 BOOST_CHECK_CLOSE(rv1.
at(0), 1.0, tol);
556 auto context2 = QuantLib::ext::make_shared<Context>();
557 context2->arrays[
"a"] = dates;
558 context2->scalars[
"d"] =
EventVec{nPaths, Date(10, June, 2020)};
560 BOOST_REQUIRE_NO_THROW(engine2.
run());
562 RandomVariable rv2 = QuantLib::ext::get<RandomVariable>(context2->scalars[
"i"]);
564 BOOST_CHECK_CLOSE(rv2.
at(0), 2.0, tol);
567 auto context3 = QuantLib::ext::make_shared<Context>();
568 context3->arrays[
"a"] = dates;
569 context3->scalars[
"d"] =
EventVec{nPaths, Date(15, June, 2020)};
571 BOOST_REQUIRE_NO_THROW(engine3.
run());
573 RandomVariable rv3 = QuantLib::ext::get<RandomVariable>(context3->scalars[
"i"]);
575 BOOST_CHECK_CLOSE(rv3.
at(0), 0.0, tol);
578 auto context4 = QuantLib::ext::make_shared<Context>();
580 context4->arrays[
"a"] = numbers;
581 context4->scalars[
"d"] =
EventVec{nPaths, Date(15, June, 2020)};
583 BOOST_REQUIRE_NO_THROW(engine4.
run());
585 RandomVariable rv4 = QuantLib::ext::get<RandomVariable>(context4->scalars[
"i"]);
587 BOOST_CHECK_CLOSE(rv4.
at(0), 0.0, tol);
590 auto context5 = QuantLib::ext::make_shared<Context>();
591 context5->arrays[
"a"] = dates;
594 BOOST_CHECK_THROW(engine5.
run(), QuantLib::Error);
597 auto context6 = QuantLib::ext::make_shared<Context>();
598 context6->scalars[
"a"] =
EventVec{nPaths, Date(15, June, 2020)};
599 context6->scalars[
"d"] =
EventVec{nPaths, Date(15, June, 2020)};
601 BOOST_CHECK_THROW(engine6.
run(), QuantLib::Error);
605 BOOST_TEST_MESSAGE(
"Testing DATEINDEX(...,...,GEQ) function");
607 std::string
script =
"NUMBER i;\n"
608 "i = DATEINDEX(d, a, GEQ);";
610 BOOST_REQUIRE(parser.
success());
611 BOOST_TEST_MESSAGE(
"Parsing successful, AST:\n" <<
to_string(parser.
ast()));
613 constexpr Size nPaths = 10;
614 constexpr Real tol = 1E-10;
616 auto model = QuantLib::ext::make_shared<DummyModel>(nPaths);
618 std::vector<ValueType> dates;
619 dates.push_back(
EventVec{nPaths, Date(7, May, 2019)});
620 dates.push_back(
EventVec{nPaths, Date(10, June, 2020)});
623 auto context1 = QuantLib::ext::make_shared<Context>();
624 context1->arrays[
"a"] = dates;
625 context1->scalars[
"d"] =
EventVec{nPaths, Date(7, May, 2019)};
627 BOOST_REQUIRE_NO_THROW(engine1.
run());
629 RandomVariable rv1 = QuantLib::ext::get<RandomVariable>(context1->scalars[
"i"]);
631 BOOST_CHECK_CLOSE(rv1.
at(0), 1.0, tol);
634 auto context2 = QuantLib::ext::make_shared<Context>();
635 context2->arrays[
"a"] = dates;
636 context2->scalars[
"d"] =
EventVec{nPaths, Date(10, June, 2020)};
638 BOOST_REQUIRE_NO_THROW(engine2.
run());
640 RandomVariable rv2 = QuantLib::ext::get<RandomVariable>(context2->scalars[
"i"]);
642 BOOST_CHECK_CLOSE(rv2.
at(0), 2.0, tol);
645 auto context3 = QuantLib::ext::make_shared<Context>();
646 context3->arrays[
"a"] = dates;
647 context3->scalars[
"d"] =
EventVec{nPaths, Date(15, June, 2020)};
649 BOOST_REQUIRE_NO_THROW(engine3.
run());
651 RandomVariable rv3 = QuantLib::ext::get<RandomVariable>(context3->scalars[
"i"]);
653 BOOST_CHECK_CLOSE(rv3.
at(0), 3.0, tol);
656 auto context4 = QuantLib::ext::make_shared<Context>();
657 context4->arrays[
"a"] = dates;
658 context4->scalars[
"d"] =
EventVec{nPaths, Date(2, May, 2019)};
660 BOOST_REQUIRE_NO_THROW(engine4.
run());
662 RandomVariable rv4 = QuantLib::ext::get<RandomVariable>(context4->scalars[
"i"]);
664 BOOST_CHECK_CLOSE(rv4.
at(0), 1.0, tol);
667 auto context5 = QuantLib::ext::make_shared<Context>();
668 context5->arrays[
"a"] = dates;
669 context5->scalars[
"d"] =
EventVec{nPaths, Date(2, June, 2020)};
671 BOOST_REQUIRE_NO_THROW(engine5.
run());
673 RandomVariable rv5 = QuantLib::ext::get<RandomVariable>(context5->scalars[
"i"]);
675 BOOST_CHECK_CLOSE(rv5.
at(0), 2.0, tol);
679 BOOST_TEST_MESSAGE(
"Testing FWDCOMP() function");
681 Date ref(7, May, 2019);
682 Settings::instance().evaluationDate() = ref;
684 std::string
script =
"NUMBER rate;\n"
685 "rate = FWDCOMP(underlying, obs, start, end, spread, gearing);\n";
687 BOOST_REQUIRE(parser.
success());
688 BOOST_TEST_MESSAGE(
"Parsing successful, AST:\n" <<
to_string(parser.
ast()));
690 constexpr Size nPaths = 10;
692 Handle<YieldTermStructure> yts(QuantLib::ext::make_shared<FlatForward>(ref, 0.02, ActualActual(ActualActual::ISDA)));
693 auto on = QuantLib::ext::make_shared<Eonia>(yts);
695 Date start = Date(10, October, 2018);
696 Date end = Date(10, October, 2019);
697 std::string indexName =
"EUR-EONIA";
701 std::vector<std::pair<std::string, QuantLib::ext::shared_ptr<InterestRateIndex>>> irIndices;
702 irIndices.push_back(std::make_pair(indexName, on));
705 auto model = QuantLib::ext::make_shared<BlackScholes>(
706 nPaths, std::vector<std::string>{
"EUR"}, std::vector<Handle<YieldTermStructure>>{yts},
707 std::vector<Handle<Quote>>(), irIndices,
708 std::vector<std::pair<std::string, QuantLib::ext::shared_ptr<ZeroInflationIndex>>>(), std::vector<std::string>(),
709 std::vector<std::string>(), Handle<BlackScholesModelWrapper>(QuantLib::ext::make_shared<BlackScholesModelWrapper>()),
710 std::map<std::pair<std::string, std::string>, Handle<QuantExt::CorrelationTermStructure>>(), mcParams,
713 auto context = QuantLib::ext::make_shared<Context>();
714 context->scalars[
"underlying"] =
IndexVec{nPaths, indexName};
715 context->scalars[
"obs"] =
EventVec{nPaths, start};
716 context->scalars[
"start"] =
EventVec{nPaths, start};
717 context->scalars[
"end"] =
EventVec{nPaths, end};
723 auto indexInfo = QuantLib::ext::make_shared<StaticAnalyser>(parser.
ast(), context);
724 BOOST_REQUIRE_NO_THROW(indexInfo->run(););
725 BOOST_REQUIRE_EQUAL(indexInfo->fwdCompAvgFixingDates().size(), 1);
726 BOOST_REQUIRE(indexInfo->fwdCompAvgFixingDates().at(indexName).size() == coupon.
fixingDates().size());
728 for (
auto const& f : indexInfo->fwdCompAvgFixingDates().at(indexName)) {
731 BOOST_REQUIRE_EQUAL(indexInfo->fwdCompAvgEvalDates().size(), 1);
732 BOOST_REQUIRE_EQUAL(indexInfo->fwdCompAvgStartEndDates().size(), 1);
733 BOOST_REQUIRE_EQUAL(indexInfo->fwdCompAvgEvalDates().at(indexName).size(), 1);
734 BOOST_CHECK_EQUAL(*indexInfo->fwdCompAvgEvalDates().at(indexName).begin(), start);
735 BOOST_REQUIRE_EQUAL(indexInfo->fwdCompAvgStartEndDates().size(), 1);
736 BOOST_REQUIRE_EQUAL(indexInfo->fwdCompAvgStartEndDates().at(indexName).size(), 2);
737 auto it = indexInfo->fwdCompAvgStartEndDates().at(indexName).begin();
738 BOOST_CHECK_EQUAL(*it++, start);
739 BOOST_CHECK_EQUAL(*it, end);
741 for (
auto const& d : indexInfo->fwdCompAvgFixingDates().at(indexName)) {
742 on->addFixing(d, 0.01);
746 BOOST_REQUIRE_NO_THROW(engine.
run());
748 RandomVariable rv = QuantLib::ext::get<RandomVariable>(context->scalars[
"rate"]);
749 BOOST_TEST_MESSAGE(
"rate from engine = " << rv.
at(0) <<
" rate from coupon = " << coupon.rate());
750 BOOST_CHECK_CLOSE(rv.
at(0), coupon.rate(), 1E-10);
754 BOOST_TEST_MESSAGE(
"Testing ABOVEPROB(), BELOWPROB() functions");
756 Date ref(7, May, 2019);
757 Settings::instance().evaluationDate() = ref;
759 std::string
script =
"AboveProb = ABOVEPROB(Underlying, Date1, Date2, BarrierUp);\n"
760 "BelowProb = BELOWPROB(Underlying, Date1, Date2, BarrierDown);\n";
762 BOOST_REQUIRE(parser.
success());
763 BOOST_TEST_MESSAGE(
"Parsing successful, AST:\n" <<
to_string(parser.
ast()));
769 Date date1(7, May, 2020);
770 Date date2(7, December, 2020);
771 Real barrierUp = 110.0;
772 Real barrierDown = 80.0;
774 auto context = QuantLib::ext::make_shared<Context>();
775 context->scalars[
"Underlying"] =
IndexVec{nPaths,
"EQ-Dummy"};
776 context->scalars[
"Date1"] =
EventVec{nPaths, date1};
777 context->scalars[
"Date2"] =
EventVec{nPaths, date2};
778 context->scalars[
"BarrierUp"] =
RandomVariable(nPaths, barrierUp);
779 context->scalars[
"BarrierDown"] =
RandomVariable(nPaths, barrierDown);
780 context->scalars[
"AboveProb"] =
RandomVariable(nPaths, barrierUp);
781 context->scalars[
"BelowProb"] =
RandomVariable(nPaths, barrierDown);
783 Handle<YieldTermStructure> yts0(QuantLib::ext::make_shared<FlatForward>(ref, 0.0, ActualActual(ActualActual::ISDA)));
784 Handle<BlackVolTermStructure> volts(
785 QuantLib::ext::make_shared<BlackConstantVol>(ref, NullCalendar(), vol, ActualActual(ActualActual::ISDA)));
786 auto process = QuantLib::ext::make_shared<GeneralizedBlackScholesProcess>(
787 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(s0)), yts0, yts0, volts);
788 std::set<Date> simulationDates = {date1, date2};
791 auto model = QuantLib::ext::make_shared<BlackScholes>(
792 nPaths,
"USD", yts0,
"EQ-Dummy",
"USD",
796 BOOST_REQUIRE_NO_THROW(engine.
run());
799 RandomVariable rvAbove = QuantLib::ext::get<RandomVariable>(context->scalars[
"AboveProb"]);
800 RandomVariable rvBelow = QuantLib::ext::get<RandomVariable>(context->scalars[
"BelowProb"]);
801 BOOST_REQUIRE(rvAbove.
size() == nPaths);
802 BOOST_REQUIRE(rvBelow.
size() == nPaths);
806 BOOST_TEST_MESSAGE(
"prob estimation using ABOVEPROB(), BELOWPROB(): " << avgAbove <<
" (above), " << avgBelow
810 Size timeSteps = 500;
811 std::vector<Real> times;
812 Real t0 = process->riskFreeRate()->timeFromReference(date1);
813 Real t1 = process->riskFreeRate()->timeFromReference(date2);
815 for (Size i = 0; i < timeSteps; ++i) {
816 times.push_back(t0 + (t1 - t0) *
static_cast<Real
>(i + 1) /
static_cast<Real
>(timeSteps));
818 TimeGrid timeGrid(times.begin(), times.end());
820 double avgAbove2 = 0.0, avgBelow2 = 0.0;
821 for (Size path = 0; path < nPaths; ++path) {
822 MultiPath p = pg->next().value;
824 bool hitAbove =
false, hitBelow =
false;
825 for (Size i = 1; i < timeGrid.size(); ++i) {
826 if (p[0][i] > barrierUp)
828 else if (p[0][i] < barrierDown)
832 avgAbove2 += 1.0 /
static_cast<Real
>(nPaths);
834 avgBelow2 += 1.0 /
static_cast<Real
>(nPaths);
836 BOOST_TEST_MESSAGE(
"prob estimation using MC (timeSteps=" << timeSteps <<
"): " << avgAbove2 <<
" (above), "
837 << avgBelow2 <<
" (below)");
838 BOOST_CHECK_CLOSE(avgAbove, avgAbove2, 5.0);
839 BOOST_CHECK_CLOSE(avgBelow, avgBelow2, 5.0);
842 std::vector<Real> times2 = {t0, t1};
843 TimeGrid timeGrid2(times2.begin(), times2.end());
845 double checkAvgAbove = 0.0, checkAvgBelow = 0.0;
846 for (Size path = 0; path < nPaths; ++path) {
847 MultiPath p = pg2->next().value;
848 Real v1 = p[0][1], v2 = p[0][2];
850 if (v1 > barrierUp || v2 > barrierUp)
853 pAbove = std::exp(-2.0 / (vol * vol * (t1 - t0)) * std::log(v1 / barrierUp) * std::log(v2 / barrierUp));
854 if (v1 < barrierDown || v2 < barrierDown)
857 pBelow = std::exp(-2.0 / (vol * vol * (t1 - t0)) * std::log(v1 / barrierDown) * std::log(v2 / barrierDown));
858 checkAvgAbove += pAbove /
static_cast<Real
>(nPaths);
859 checkAvgBelow += pBelow /
static_cast<Real
>(nPaths);
861 BOOST_TEST_MESSAGE(
"prob estimation using MC + analytical formula for endpoints: " << checkAvgAbove <<
" (above), "
862 << checkAvgBelow <<
" (below)");
863 BOOST_CHECK_CLOSE(avgAbove, checkAvgAbove, 1.0E-4);
864 BOOST_CHECK_CLOSE(avgBelow, checkAvgBelow, 1.0E-4);
868 BOOST_TEST_MESSAGE(
"Testing european option...");
870 Date ref(7, May, 2019);
871 Settings::instance().evaluationDate() = ref;
873 std::string
script =
"Option = Quantity * PAY(max( PutCall * (Underlying(Expiry) - Strike), 0 ),\n"
874 " Expiry, Settlement, PayCcy);";
876 BOOST_REQUIRE(parser.
success());
877 BOOST_TEST_MESSAGE(
"Parsing successful, AST:\n" <<
to_string(parser.
ast()));
882 Real quantity = 10.0;
885 Date expiry(7, May, 2020);
886 Date settlement(9, May, 2020);
888 constexpr Size nPaths = 50000;
890 auto context = QuantLib::ext::make_shared<Context>();
894 context->scalars[
"Underlying"] =
IndexVec{nPaths,
"EQ-SP5"};
895 context->scalars[
"Expiry"] =
EventVec{nPaths, expiry};
896 context->scalars[
"Settlement"] =
EventVec{nPaths, settlement};
897 context->scalars[
"PayCcy"] =
CurrencyVec{nPaths,
"USD"};
900 auto indexInfo = QuantLib::ext::make_shared<StaticAnalyser>(parser.
ast(), context);
901 BOOST_REQUIRE_NO_THROW(indexInfo->run());
902 BOOST_REQUIRE_EQUAL(indexInfo->indexEvalDates().size(), 1);
903 BOOST_CHECK_EQUAL(indexInfo->indexEvalDates().begin()->first,
"EQ-SP5");
904 BOOST_CHECK_EQUAL(indexInfo->indexEvalDates().begin()->second.size(), 1);
905 BOOST_CHECK_EQUAL(*indexInfo->indexEvalDates().begin()->second.begin(), expiry);
906 BOOST_REQUIRE_EQUAL(indexInfo->payObsDates().size(), 1);
907 BOOST_CHECK_EQUAL(indexInfo->payObsDates().begin()->first,
"USD");
908 BOOST_CHECK_EQUAL(indexInfo->payObsDates().begin()->second.size(), 1);
909 BOOST_CHECK_EQUAL(*indexInfo->payObsDates().begin()->second.begin(), expiry);
910 BOOST_REQUIRE_EQUAL(indexInfo->payPayDates().size(), 1);
911 BOOST_CHECK_EQUAL(indexInfo->payPayDates().begin()->first,
"USD");
912 BOOST_CHECK_EQUAL(indexInfo->payPayDates().begin()->second.size(), 1);
913 BOOST_CHECK_EQUAL(*indexInfo->payPayDates().begin()->second.begin(), settlement);
914 BOOST_CHECK(indexInfo->regressionDates().empty());
916 Handle<YieldTermStructure> yts(QuantLib::ext::make_shared<FlatForward>(ref, rate, ActualActual(ActualActual::ISDA)));
917 Handle<YieldTermStructure> yts0(QuantLib::ext::make_shared<FlatForward>(ref, 0.0, ActualActual(ActualActual::ISDA)));
918 Handle<BlackVolTermStructure> volts(
919 QuantLib::ext::make_shared<BlackConstantVol>(ref, NullCalendar(), vol, ActualActual(ActualActual::ISDA)));
920 auto process = QuantLib::ext::make_shared<GeneralizedBlackScholesProcess>(
921 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(s0)), yts0, yts, volts);
924 std::set<Date> simulationDates, payDates;
925 for (
auto const& s : indexInfo->indexEvalDates())
926 simulationDates.insert(s.second.begin(), s.second.end());
927 for (
auto const& s : indexInfo->payObsDates())
928 simulationDates.insert(s.second.begin(), s.second.end());
929 simulationDates.insert(indexInfo->regressionDates().begin(), indexInfo->regressionDates().end());
930 for (
auto const& s : indexInfo->payPayDates())
931 payDates.insert(s.second.begin(), s.second.end());
936 auto model = QuantLib::ext::make_shared<BlackScholes>(
937 nPaths,
"USD", yts,
"EQ-SP5",
"USD",
940 BOOST_REQUIRE_NO_THROW(engine.
run());
942 RandomVariable rv = QuantLib::ext::get<RandomVariable>(context->scalars[
"Option"]);
943 BOOST_REQUIRE(rv.
size() == nPaths);
946 BOOST_TEST_MESSAGE(
"option value estimation " << avg <<
" (timing " << timer.format(default_places,
"%w") <<
"s)");
950 std::vector<Real> times;
951 times.push_back(process->riskFreeRate()->timeFromReference(expiry));
953 SobolBrownianGenerator::Steps);
955 for (Size path = 0; path < nPaths; ++path) {
956 MultiPath p = pg->next().value;
957 Real v = quantity * std::max(putcall * (p[0][1] - strike), 0.0);
960 avg2 *= process->riskFreeRate()->discount(settlement) /
static_cast<double>(nPaths);
962 BOOST_TEST_MESSAGE(
"result with hardcoded script " << avg2 <<
" (timing " << timer.format(default_places,
"%w")
964 BOOST_CHECK_CLOSE(avg, avg2, 1E-10);
968 quantity * blackFormula(Option::Call, s0, s0 / yts->discount(expiry),
969 vol * std::sqrt(yts->timeFromReference(expiry)), yts->discount(settlement));
970 BOOST_TEST_MESSAGE(
"option value expected " << expected);
971 BOOST_CHECK_CLOSE(avg, expected, 0.1);
975 BOOST_TEST_MESSAGE(
"Testing american option...");
977 Date ref(7, May, 2019);
978 Settings::instance().evaluationDate() = ref;
980 std::string
script =
"NUMBER Exercise;\n"
982 "FOR i IN (SIZE(Expiry), 1, -11) DO\n"
983 " Exercise = PAY( PutCall * (Underlying(Expiry[i]) - Strike),\n"
984 " Expiry[i], Settlement[i], PayCcy );\n"
985 " IF Exercise > NPV( Option, Expiry[i], Exercise > 0 ) AND Exercise > 0 THEN\n"
986 " Option = Exercise;\n"
989 "Option = Quantity * Option;\n";
992 BOOST_REQUIRE(parser.
success());
993 BOOST_TEST_MESSAGE(
"Parsing successful, AST:\n" <<
to_string(parser.
ast()));
998 Real quantity = 10.0;
1000 Real strike = 100.0;
1002 constexpr Size nPaths = 100000;
1004 Schedule expirySchedule(Date(8, May, 2019), Date(9, May, 2020), 1 * Weeks, NullCalendar(), Unadjusted, Unadjusted,
1005 DateGeneration::Forward,
false);
1006 std::vector<ValueType> expiryDates, settlDates;
1007 for (
auto const& d : expirySchedule.dates()) {
1008 expiryDates.push_back(
EventVec{nPaths, d});
1009 settlDates.push_back(
EventVec{nPaths, d });
1012 auto context = QuantLib::ext::make_shared<Context>();
1016 context->scalars[
"Underlying"] =
IndexVec{nPaths,
"EQ-SP5"};
1017 context->arrays[
"Expiry"] = expiryDates;
1018 context->arrays[
"Settlement"] = settlDates;
1019 context->scalars[
"PayCcy"] =
CurrencyVec{nPaths,
"USD"};
1022 auto indexInfo = QuantLib::ext::make_shared<StaticAnalyser>(parser.
ast(), context);
1023 BOOST_REQUIRE_NO_THROW(indexInfo->run());
1026 std::set<Date> simulationDates, payDates;
1027 for (
auto const& s : indexInfo->indexEvalDates())
1028 simulationDates.insert(s.second.begin(), s.second.end());
1029 for (
auto const& s : indexInfo->payObsDates())
1030 simulationDates.insert(s.second.begin(), s.second.end());
1031 simulationDates.insert(indexInfo->regressionDates().begin(), indexInfo->regressionDates().end());
1032 for (
auto const& s : indexInfo->payPayDates())
1033 payDates.insert(s.second.begin(), s.second.end());
1035 BOOST_REQUIRE_EQUAL(simulationDates.size(), expiryDates.size());
1037 Handle<YieldTermStructure> yts(QuantLib::ext::make_shared<FlatForward>(ref, rate, ActualActual(ActualActual::ISDA)));
1038 Handle<YieldTermStructure> yts0(QuantLib::ext::make_shared<FlatForward>(ref, 0.0, ActualActual(ActualActual::ISDA)));
1039 Handle<BlackVolTermStructure> volts(
1040 QuantLib::ext::make_shared<BlackConstantVol>(ref, NullCalendar(), vol, ActualActual(ActualActual::ISDA)));
1041 auto process = QuantLib::ext::make_shared<GeneralizedBlackScholesProcess>(
1042 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(s0)), yts0, yts, volts);
1047 auto model = QuantLib::ext::make_shared<BlackScholes>(
1048 nPaths,
"USD", yts,
"EQ-SP5",
"USD",
1051 BOOST_REQUIRE_NO_THROW(engine.
run());
1052 BOOST_TEST_MESSAGE(*context);
1054 RandomVariable rv = QuantLib::ext::get<RandomVariable>(context->scalars[
"Option"]);
1055 BOOST_REQUIRE(rv.
size() == nPaths);
1058 BOOST_TEST_MESSAGE(
"option value estimation " << avg <<
" (timing " << timer.format(default_places,
"%w") <<
"s)");
1061 auto fdEngine = QuantLib::ext::make_shared<FdBlackScholesVanillaEngine>(process, 100, 100);
1062 VanillaOption option(QuantLib::ext::make_shared<PlainVanillaPayoff>(putcall > 0.0 ? Option::Call : Option::Put, strike),
1063 QuantLib::ext::make_shared<AmericanExercise>(ref, expirySchedule.dates().back()));
1064 option.setPricingEngine(fdEngine);
1066 Real fdNpv = option.NPV() * quantity;
1068 BOOST_TEST_MESSAGE(
"fd engine result " << fdNpv <<
" (timing " << timer.format(default_places,
"%w") <<
"s)");
1069 BOOST_CHECK_CLOSE(avg, fdNpv, 5.0);
1073 BOOST_TEST_MESSAGE(
"Testing asian option...");
1075 Date ref(7, May, 2019);
1076 Settings::instance().evaluationDate() = ref;
1078 std::string
script =
"NUMBER avg; NUMBER i;"
1079 "FOR i IN (1,SIZE(ObservationDates),1) DO"
1080 " avg = avg + Underlying(ObservationDates[i]);"
1082 "Option = Quantity * PAY( max( PutCall * (avg / SIZE(ObservationDates) - Strike), 0),"
1083 " Settlement, Settlement, PayCcy);";
1086 BOOST_REQUIRE(parser.
success());
1087 BOOST_TEST_MESSAGE(
"Parsing successful, AST:\n" <<
to_string(parser.
ast()));
1092 Real quantity = 10.0;
1094 Real strike = 100.0;
1096 constexpr Size nPaths = 10000;
1098 Schedule observationSchedule(Date(9, May, 2019), Date(9, May, 2020), 1 * Weeks, NullCalendar(), Unadjusted,
1099 Unadjusted, DateGeneration::Forward,
false);
1100 std::vector<ValueType> observationDates;
1101 for (
auto const& d : observationSchedule.dates())
1102 observationDates.push_back(
EventVec{nPaths, d});
1104 auto context = QuantLib::ext::make_shared<Context>();
1108 context->scalars[
"Underlying"] =
IndexVec{nPaths,
"EQ-SP5"};
1109 context->arrays[
"ObservationDates"] = observationDates;
1110 context->scalars[
"Settlement"] = observationDates.back();
1111 context->scalars[
"PayCcy"] =
CurrencyVec{nPaths,
"USD"};
1114 auto indexInfo = QuantLib::ext::make_shared<StaticAnalyser>(parser.
ast(), context);
1115 BOOST_REQUIRE_NO_THROW(indexInfo->run());
1118 std::set<Date> simulationDates, payDates;
1119 for (
auto const& s : indexInfo->indexEvalDates())
1120 simulationDates.insert(s.second.begin(), s.second.end());
1121 for (
auto const& s : indexInfo->payObsDates())
1122 simulationDates.insert(s.second.begin(), s.second.end());
1123 simulationDates.insert(indexInfo->regressionDates().begin(), indexInfo->regressionDates().end());
1124 for (
auto const& s : indexInfo->payPayDates())
1125 payDates.insert(s.second.begin(), s.second.end());
1127 BOOST_REQUIRE_EQUAL(indexInfo->indexEvalDates().size(), 1);
1128 BOOST_CHECK_EQUAL(indexInfo->indexEvalDates().begin()->first,
"EQ-SP5");
1129 BOOST_REQUIRE_EQUAL(simulationDates.size(), observationDates.size());
1131 for (
auto const& d : simulationDates)
1132 BOOST_CHECK_EQUAL(d, QuantLib::ext::get<EventVec>(observationDates[i++]).
value);
1134 Handle<YieldTermStructure> yts(QuantLib::ext::make_shared<FlatForward>(ref, rate, ActualActual(ActualActual::ISDA)));
1135 Handle<YieldTermStructure> yts0(QuantLib::ext::make_shared<FlatForward>(ref, 0.0, ActualActual(ActualActual::ISDA)));
1136 Handle<BlackVolTermStructure> volts(
1137 QuantLib::ext::make_shared<BlackConstantVol>(ref, NullCalendar(), vol, ActualActual(ActualActual::ISDA)));
1138 auto process = QuantLib::ext::make_shared<GeneralizedBlackScholesProcess>(
1139 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(s0)), yts0, yts, volts);
1144 auto model = QuantLib::ext::make_shared<BlackScholes>(
1145 nPaths,
"USD", yts,
"EQ-SP5",
"USD",
1148 BOOST_REQUIRE_NO_THROW(engine.
run());
1150 RandomVariable rv = QuantLib::ext::get<RandomVariable>(context->scalars[
"Option"]);
1151 BOOST_REQUIRE(rv.
size() == nPaths);
1154 BOOST_TEST_MESSAGE(
"option value estimation " << avg <<
" (timing " << timer.format(default_places,
"%w") <<
"s)");
1157 std::vector<Real> times;
1158 for (
auto const& d : observationDates)
1159 times.push_back(process->riskFreeRate()->timeFromReference(QuantLib::ext::get<EventVec>(d).value));
1161 SobolBrownianGenerator::Steps);
1164 for (Size path = 0; path < nPaths; ++path) {
1165 MultiPath p = pg->next().value;
1167 for (Size i = 1; i < p[0].length(); ++i)
1169 Real v = std::max(payoff /
static_cast<double>(observationDates.size()) - strike, 0.0);
1172 avg2 *= quantity * process->riskFreeRate()->discount(QuantLib::ext::get<EventVec>(observationDates.back()).value) /
1173 static_cast<double>(nPaths);
1175 BOOST_TEST_MESSAGE(
"result with hardcoded script " << avg2 <<
"(timing " << timer.format(default_places,
"%w")
1177 BOOST_CHECK_CLOSE(avg, avg2, 1E-10);
1181 BOOST_TEST_MESSAGE(
"Testing autocallable...");
1183 Date ref(7, May, 2019);
1184 Settings::instance().evaluationDate() = ref;
1187 "NUMBER StrikePrice, KnockInPrice, Value;\n"
1188 "NUMBER terminated, knockedIn, u, v;\n"
1189 "FOR u IN (1, SIZE(Underlying), 1) DO\n"
1190 " StrikePrice = StrikePrice + Underlying[u](StrikeDate);\n"
1192 "StrikePrice = StrikePrice / SIZE(Underlying);\n"
1193 "KnockInPrice = KnockInRatio * StrikePrice;\n"
1194 "FOR v IN (1, SIZE(Valuation), 1) DO\n"
1196 " FOR u IN (1, SIZE(Underlying), 1) DO\n"
1197 " Value = Value + Underlying[u](Valuation[v]);\n"
1199 " Value = Value / SIZE(Underlying);\n"
1200 " IF Value < KnockInPrice THEN\n"
1203 " IF v == SIZE(Valuation) THEN\n"
1204 " IF knockedIn == 1 AND terminated == 0 THEN\n"
1205 " Option = PAY(Notional * ( 1 - Value / StrikePrice), Valuation[v], Settlement[v], PayCcy);\n"
1208 " IF v > 1 AND terminated == 0 THEN\n"
1209 " IF Value > StrikePrice THEN\n"
1210 " Option = PAY(Notional * v * 0.06, Valuation[v], Settlement[v], PayCcy);\n"
1211 " terminated = 1;\n"
1218 BOOST_REQUIRE(parser.
success());
1219 BOOST_TEST_MESSAGE(
"Parsing successful, AST:\n" <<
to_string(parser.
ast()));
1224 Real notional = 1000.0;
1225 Real strike = 100.0;
1226 Real knockInRatio = 0.7;
1228 constexpr Size nPaths = 10000;
1230 Schedule observationSchedule(Date(9, May, 2019), Date(9, May, 2020), 1 * Months, NullCalendar(), Unadjusted,
1231 Unadjusted, DateGeneration::Forward,
false);
1232 std::vector<ValueType> observationDates, settlementDates;
1233 for (Size i = 1; i < observationSchedule.dates().
size(); ++i) {
1234 Date d = observationSchedule.date(i);
1235 observationDates.push_back(
EventVec{nPaths, d});
1236 settlementDates.push_back(
EventVec{nPaths, d + 5});
1238 std::vector<Date> expectedSimDates = observationSchedule.dates();
1239 std::vector<std::string> indicesStr = {
"EQ-1",
"EQ-2",
"EQ-3"};
1240 std::vector<ValueType> indices;
1241 for (
auto const& i : indicesStr)
1242 indices.push_back(
IndexVec{nPaths, i});
1244 auto context = QuantLib::ext::make_shared<Context>();
1247 context->scalars[
"StrikeDate"] =
EventVec{nPaths, observationSchedule.dates().front()};
1248 context->scalars[
"KnockInRatio"] =
RandomVariable(nPaths, knockInRatio);
1249 context->arrays[
"Underlying"] = indices;
1250 context->arrays[
"Valuation"] = observationDates;
1251 context->arrays[
"Settlement"] = settlementDates;
1252 context->scalars[
"PayCcy"] =
CurrencyVec{nPaths,
"USD"};
1255 auto indexInfo = QuantLib::ext::make_shared<StaticAnalyser>(parser.
ast(), context);
1256 BOOST_REQUIRE_NO_THROW(indexInfo->run());
1257 BOOST_REQUIRE_EQUAL(indexInfo->indexEvalDates().size(), 3);
1259 for (
auto const& k : indexInfo->indexEvalDates())
1260 BOOST_CHECK_EQUAL(k.first, indicesStr[i++]);
1261 for (
auto const& ind : indicesStr) {
1263 BOOST_CHECK_EQUAL(indexInfo->indexEvalDates().at(ind).size(), expectedSimDates.size());
1264 for (
auto const& d : indexInfo->indexEvalDates().at(ind))
1265 BOOST_CHECK_EQUAL(d, expectedSimDates[i++]);
1267 BOOST_REQUIRE_EQUAL(indexInfo->payObsDates().size(), 1);
1268 BOOST_CHECK_EQUAL(indexInfo->payObsDates().begin()->first,
"USD");
1270 BOOST_REQUIRE_EQUAL(indexInfo->payObsDates().begin()->second.size(), observationDates.size());
1271 for (
auto const& d : indexInfo->payObsDates().begin()->second) {
1272 BOOST_CHECK_EQUAL(d, QuantLib::ext::get<EventVec>(observationDates[i++]).
value);
1274 BOOST_REQUIRE_EQUAL(indexInfo->payPayDates().size(), 1);
1275 BOOST_CHECK_EQUAL(indexInfo->payPayDates().begin()->first,
"USD");
1277 BOOST_REQUIRE_EQUAL(indexInfo->payPayDates().begin()->second.size(), settlementDates.size());
1278 for (
auto const& d : indexInfo->payPayDates().begin()->second) {
1279 BOOST_CHECK_EQUAL(d, QuantLib::ext::get<EventVec>(settlementDates[i++]).
value);
1283 std::set<Date> simulationDates, payDates;
1284 for (
auto const& s : indexInfo->indexEvalDates())
1285 simulationDates.insert(s.second.begin(), s.second.end());
1286 for (
auto const& s : indexInfo->payObsDates())
1287 simulationDates.insert(s.second.begin(), s.second.end());
1288 simulationDates.insert(indexInfo->regressionDates().begin(), indexInfo->regressionDates().end());
1289 for (
auto const& s : indexInfo->payPayDates())
1290 payDates.insert(s.second.begin(), s.second.end());
1292 Handle<YieldTermStructure> yts(QuantLib::ext::make_shared<FlatForward>(ref, rate, ActualActual(ActualActual::ISDA)));
1293 Handle<YieldTermStructure> yts0(QuantLib::ext::make_shared<FlatForward>(ref, 0.0, ActualActual(ActualActual::ISDA)));
1294 Handle<BlackVolTermStructure> volts(
1295 QuantLib::ext::make_shared<BlackConstantVol>(ref, NullCalendar(), vol, ActualActual(ActualActual::ISDA)));
1296 auto process1 = QuantLib::ext::make_shared<GeneralizedBlackScholesProcess>(
1297 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(s0)), yts0, yts, volts);
1298 auto process2 = QuantLib::ext::make_shared<GeneralizedBlackScholesProcess>(
1299 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(s0)), yts0, yts, volts);
1300 auto process3 = QuantLib::ext::make_shared<GeneralizedBlackScholesProcess>(
1301 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(s0)), yts0, yts, volts);
1302 std::vector<QuantLib::ext::shared_ptr<QuantLib::StochasticProcess1D>> processes = {process1, process2, process3};
1303 std::vector<QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess>> processesBs = {process1, process2, process3};
1304 std::map<std::pair<std::string, std::string>, Handle<QuantExt::CorrelationTermStructure>> correlations;
1305 correlations[std::make_pair(
"EQ-1",
"EQ-2")] = Handle<QuantExt::CorrelationTermStructure>(
1306 QuantLib::ext::make_shared<QuantExt::FlatCorrelation>(0, NullCalendar(), 0.5, ActualActual(ActualActual::ISDA)));
1307 correlations[std::make_pair(
"EQ-1",
"EQ-3")] = Handle<QuantExt::CorrelationTermStructure>(
1308 QuantLib::ext::make_shared<QuantExt::FlatCorrelation>(0, NullCalendar(), 0.4, ActualActual(ActualActual::ISDA)));
1309 correlations[std::make_pair(
"EQ-2",
"EQ-3")] = Handle<QuantExt::CorrelationTermStructure>(
1310 QuantLib::ext::make_shared<QuantExt::FlatCorrelation>(0, NullCalendar(), 0.6, ActualActual(ActualActual::ISDA)));
1314 auto model = QuantLib::ext::make_shared<BlackScholes>(
1315 nPaths, std::vector<std::string>(1,
"USD"), std::vector<Handle<YieldTermStructure>>(1, yts),
1316 std::vector<Handle<Quote>>(), std::vector<std::pair<std::string, QuantLib::ext::shared_ptr<InterestRateIndex>>>(),
1317 std::vector<std::pair<std::string, QuantLib::ext::shared_ptr<ZeroInflationIndex>>>(), indicesStr,
1318 std::vector<std::string>(3,
"USD"),
1322 BOOST_REQUIRE_NO_THROW(engine.
run());
1324 RandomVariable rv = QuantLib::ext::get<RandomVariable>(context->scalars[
"Option"]);
1325 BOOST_REQUIRE(rv.
size() == nPaths);
1328 BOOST_TEST_MESSAGE(
"option value estimation " << avg <<
" (timing " << timer.format(default_places,
"%w") <<
"s)");
1329 BOOST_TEST_MESSAGE(*context);
1332 std::vector<Real> times;
1333 for (
auto const& d : expectedSimDates)
1334 times.push_back(yts->timeFromReference(d));
1335 TimeGrid grid(times.begin(), times.end(), 1);
1336 std::vector<Size> positionInTimeGrid(times.size());
1337 for (Size i = 0; i < times.size(); ++i)
1338 positionInTimeGrid[i] = grid.index(times[i]);
1340 for (Size i = 0; i < times.size(); ++i)
1341 BOOST_TEST_MESSAGE(
"time point #" << i <<
": " << times[i] <<
", position in grid " << positionInTimeGrid[i]);
1342 for (Size i = 0; i < grid.size(); ++i)
1343 BOOST_TEST_MESSAGE(
"grid point #" << i <<
": " << grid[i]);
1345 Matrix correlation{{1.0, 0.5, 0.4}, {0.5, 1.0, 0.6}, {0.4, 0.6, 1.0}};
1346 auto process = QuantLib::ext::make_shared<StochasticProcessArray>(processes, correlation);
1350 constexpr Size nUnd = 3;
1351 const Size nObs = observationDates.size();
1352 for (Size path = 0; path < nPaths; ++path) {
1353 MultiPath p = pg->next().value;
1356 Real StrikePrice = 0.0, KnockInPrice = 0.0, Value = 0.0;
1357 Size terminated = 0, knockedIn = 0;
1358 for (Size u = 0; u < nUnd; ++u)
1359 StrikePrice += p[u][positionInTimeGrid[1 - 1]];
1360 StrikePrice /= nUnd;
1361 KnockInPrice = knockInRatio * StrikePrice;
1362 for (Size v = 0; v < nObs; ++v) {
1364 for (Size u = 0; u < nUnd; ++u) {
1365 Value += p[u][positionInTimeGrid[v + 2 - 1]];
1368 if (Value < KnockInPrice && !
close_enough(Value, KnockInPrice))
1370 if (v == nObs - 1) {
1371 if (knockedIn == 1 && terminated == 0) {
1372 Option = notional * (1 - Value / StrikePrice) *
1373 yts->discount(QuantLib::ext::get<EventVec>(settlementDates[v]).value);
1376 if (v > 0 && terminated == 0) {
1377 if (Value > StrikePrice && !
close_enough(Value, StrikePrice)) {
1379 notional * (v + 1) * 0.06 * yts->discount(QuantLib::ext::get<EventVec>(settlementDates[v]).value);
1390 BOOST_TEST_MESSAGE(
"result with hardcoded script " << avg2 <<
"(timing " << timer.format(default_places,
"%w")
1393 BOOST_CHECK_CLOSE(avg, avg2, 1.0);
1397 BOOST_TEST_MESSAGE(
"Testing nested if-then-else statements...");
1399 std::string
script =
"IF x < 8 THEN\n"
1462 BOOST_REQUIRE(parser.
success());
1463 BOOST_TEST_MESSAGE(
"Parsing successful, AST:\n" <<
to_string(parser.
ast()));
1465 auto context = QuantLib::ext::make_shared<Context>();
1468 for (Size i = 0; i < 16; ++i)
1469 x.set(i,
static_cast<Real
>(i));
1471 context->scalars[
"x"] = x;
1472 context->scalars[
"y"] = y;
1474 ScriptEngine engine(parser.
ast(), context, QuantLib::ext::make_shared<DummyModel>(16));
1475 BOOST_REQUIRE_NO_THROW(engine.
run());
1476 BOOST_TEST_MESSAGE(
"Script Engine successfully run, context is:\n" << *context);
1478 BOOST_REQUIRE(y.
size() == 16);
1479 for (Size i = 0; i < 16; ++i) {
1480 BOOST_CHECK_EQUAL(x.at(i), i);
1488 BOOST_TEST_MESSAGE(
"Running Script Engine on INPUT env variable...");
1490 std::string
script =
"NUMBER i,x;"
1491 "FOR i IN (1,10,1) DO x=x+i; END;";
1492 if (
auto c = getenv(
"INPUT"))
1497 std::cerr <<
"Parsing succeeded\n" <<
to_string(parser.
ast()) << std::endl;
1499 std::cerr <<
"Parsing failed\n" << parser.
error();
1502 auto context = QuantLib::ext::make_shared<Context>();
1506 std::cerr <<
"Script successfully executed, context is:\n" << *context << std::endl;
1507 }
catch (
const std::exception& e) {
1508 std::cerr <<
"ERROR during script execution: " << e.what() << std::endl;
1509 std::cerr << *context << std::endl;
1513BOOST_AUTO_TEST_SUITE_END()
1515BOOST_AUTO_TEST_SUITE_END()
black scholes model for n underlyings (fx, equity or commodity)
builder for an array of black scholes processes
const std::vector< Date > & fixingDates() const
const Date & referenceDate() const override
void run(const std::string &script="", bool interactive=false, QuantLib::ext::shared_ptr< PayLog > paylog=nullptr, bool includePastCashflows=false)
const ParserError & error() const
SafeStack< ValueType > value
dummy model implementation
RandomVariable max(RandomVariable x, const RandomVariable &y)
RandomVariable exp(RandomVariable x)
RandomVariable sqrt(RandomVariable x)
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
RandomVariable log(RandomVariable x)
boost::shared_ptr< MultiPathGeneratorBase > makeMultiPathGenerator(const SequenceType s, const boost::shared_ptr< StochasticProcess > &process, const TimeGrid &timeGrid, const BigNatural seed, const SobolBrownianGenerator::Ordering ordering=SobolBrownianGenerator::Steps, const SobolRsg::DirectionIntegers directionIntegers=SobolRsg::JoeKuoD7)
RandomVariable pow(RandomVariable x, const RandomVariable &y)
bool close_enough_all(const RandomVariable &x, const RandomVariable &y)
RandomVariable expectation(const RandomVariable &r)
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)
bool deterministic(const ValueType &v)
Size size(const ValueType &v)
std::string to_string(const LocationInfo &l)
boost::variant< RandomVariable, EventVec, CurrencyVec, IndexVec, DaycounterVec, Filter > ValueType
Real at(const Size i) const
bool deterministic() const
void set(const Size i, const Real v)
BOOST_AUTO_TEST_CASE(testSimpleScript)