Assigning to Non-DSL Scalar Types
DSL programming is very powerful and easy. But how to exchange data between regular C++ and DSL. Tutorial 207 demonstrates just how this is done.
Recall from previous discussions that it is not allowed to declare series<T> objects as part of the definition of strategy::on_prepare_dsl(). This is so because strategy::on_prepare_dsl() itself is only evaluated once, and any series objects declared in it would simply go out of scope when the function returns. That is why all series used by the DSL mechanism at run-time must be owned by the strategy and must persist until the strategy is destroyed.
You can however have any number of series<T> objects that are strategy members. And you can assign a DSL created series reference to such a series member. In other sections of the strategy, you can access series values using the subscript operator[], where [0] is the most recently added value, and [1] the value from one bar ago.
Updating Scalars From DSL Code
In many cases only the most recently added series value (the current value) is of interest. In such cases it makes sense to skip declaring a series member and simply declare a member of the desired type, and then use the VAR<T>() function to assign the value from a series directly to the given variable. The VAR<T>() function is necessary as the assignment operator=() cannot be overloaded for C++ fundamental types.
Tutorial 207:
class my_strategy : public strategy
{
in_stream mkt;
void on_start(void) override{mkt.as_random_walk(duration::days(1));}
series<double> volat_25; // series member
double volat_50; // fundamental type member
bool just_do_it = false; // Accessed by DSL via TO_SERIES() function
void on_prepare_dsl(void) override
{
// assign entire series-reference to a series member object
volat_25 = VOLATILITY(mkt.close, 25, 255 /* trading days in year*/);
// assign most recently added series value
// to a c++ fundamental type. Use the VAR<T>() template function to do this.
VAR(volat_50) = VOLATILITY(mkt.close, 50, 255);
// here we access a non-DSL strategy member and turn it into a series
// using the TO_SERIES<T>() template function
sref<bool> buy_stocks = TO_SERIES(just_do_it);
}
void on_bar_close(void) override
{
// Access series values. Requires operator[]
if (volat_25[0] > 15) {/* action */}
// Fundamental type can be used as usual.
if (volat_50 > 10) {/* action */}
// Here we can influence what goes on in DSL by setting
// the value on a regular strategy member. See the use of the TO_SERIES<T>() function
// as used in the DSL above.
just_do_it = !just_do_it;
}
};
The primary line of interest is:
VAR(volat_50) = VOLATILITY(mkt.close, 50, 255);
The VAR<T>() function creates an internal functor object that will, at every strategy interval, update the value of the strategy's volat_50 member variable. Since volat_50 is a regular variable of type double, it can be used without restrictions.
Read Scalar Values in DSL Code
Note the just_do_it strategy member. It is a regular boolean variable. Its value is accessed in DSL via the TO_SERIES<T>() function.
sref<bool> buy_stocks = TO_SERIES(just_do_it);
The thing to remember here is that DSL, or rather the logic created by strategy::on_prepare_dsl(), is evaluated *before* the strategy::on_bar_close() member is called at every bar. So, if you use strategy members like just_do_it to communicate with DSL code, any change in value to just_do_it will be reflected in the DSL code only on the subsequent bar. So regular code can communicate with DSL with a one bar lag, whereas DSL can pass on changes to regular code on the current bar, with no delay. |