Debugging DSL
As a functional language, DSL does not use pointers, does not allow any explicit allocation of memory, and has no control of flow. Henceforth nothing much can go wrong. Programming DSL is very much like programming spreadsheet formulas. The primary difference is that a spreadsheet displays values in a visible way. Much of DSL code however just runs in the background, possibly nested deep in function code. What if you wanted to inspect the current value in a series while the strategy is running, or assert that his has a certain value? Well, there are a number of debugging aids for DSL:
DPRINT() |
This macro prints the name of the variable or expression and its value to standard output. |
ASSERT() |
This macro is only enabled in debug mode and asserts that an expression is true. IF the expression is false an exception is thrown with line and file information as well as the strategy bar timestamp. |
VERIFY() |
The equivalent of ASSERT() but enabled also during release mode. |
Floating Point Rounding Errors
For unit-testing purposes, we often need to check for equality between two floating point values that should be identical, but are no because of floating point rounding errors. This often happens when testing the equivalence of an optimized algorithm versus its non-optimized counterpart. In such cases you can use ASSERT() in conjunction with the FP_EQUAL(sref<double> a, sref<double> b, double precision = 1e-12) function which checks that two floating point numbers are identical up to a certain tolerance. This tolerance is not a fixed number of decimal points but rather depends on the size of the larger of the first two arguments.
A Demonstration - Tutorial 206
Tutorial 206 reuses the lambda functions that were defined for tutorial 203. In this case, we simply compare the output of the DSL functions implemented via lambdas with the output of the library's own DSL functions.
void on_prepare_dsl(void) override
{
auto rand = RAND(-5.0, 5.0); // Generate a random variable
auto abs = ABS_FROM_LAMBDA(rand); // Absolute value of random variable
DPRINT(rand); // Print random variable to standard output
DPRINT(abs); // Print absolute value to standard output
ASSERT(abs == ABS(rand)); // checking against built in function
auto avg = AVERAGE_FROM_LAMBDA(rand, 10);
ASSERT(FP_EQUAL(avg, AVERAGE(rand, 10))); // checking against library function
// with some tolerance for floating point
// rounding errors. Tolerance can be set as
// argument to FP_EQUAL
VERIFY(CONST(1) + CONST(1) == CONST(2));
DPRINT(CONST(1) + CONST(1) == CONST(2));
}
The on_prepare_dsl() member is the only code of interest here. We already covered the lambda functions in the previous section. The output of this code is as follows:
=========================================
Tutorial: 204
=================================
'rand': -2.96560253900606252
'abs': 2.96560253900606252
'CONST(1) + CONST(1) == CONST(2)': true
'rand': -3.51353829924798067
'abs': 3.51353829924798067
'CONST(1) + CONST(1) == CONST(2)': true
'rand': 4.54656397913269927
'abs': 4.54656397913269927
'CONST(1) + CONST(1) == CONST(2)': true
'rand': 3.56687167160372987
'abs': 3.56687167160372987
'CONST(1) + CONST(1) == CONST(2)': true
'rand': 1.05604712293856906
'abs': 1.05604712293856906
'CONST(1) + CONST(1) == CONST(2)': true
'rand': -2.81035807864050335
'abs': 2.81035807864050335
'CONST(1) + CONST(1) == CONST(2)': true
'rand': 3.74160828505214482
'abs': 3.74160828505214482
'CONST(1) + CONST(1) == CONST(2)': true
'rand': 1.19618170107625854
'abs': 1.19618170107625854
'CONST(1) + CONST(1) == CONST(2)': true
'rand': -4.44008395994084015
'abs': 4.44008395994084015