DSL Functions
Lets explore the DSL functionality some more. Is there a way to simplify lengthy DLS logic? The answer is yes, you don't have to pack all your DSL logic directly into the strategy's on_prepare_dsl() member. You can break up your code into manageable chunks by defining DSL functions.
Tutorial 201 shows how the logic for calculating volatility is defined as a stand-alone DSL function. In fact most technical indicators and oscillators of the API are implemented in this manner. DSL functions can either be declared as strategy members or as stand-alone functions. Its usually better to declare them as stand alone functions to promote reuse in other strategies.
Tutorial 201:
(1)
(2)
(3)
(4)
(5)
(6)
(7)
|
#include "tsa.h" #include "tsa-graphics.h"
using namespace tsa;
sref<double> VOLATILITY_DEMO(sref<double> close, size_t period, size_t num_intervals_in_year) { auto ch = close / close(1); // operator() shifts the series. Same as SHIFT() auto ln_ch = LN(ch); auto stdev_ln_ch = STDEVP(ln_ch, period); auto annu_vol = stdev_ln_ch / std::sqrt(1.0 / (double)num_intervals_in_year); return annu_vol.plot_as("VOLAT", { period, num_intervals_in_year }); }
class my_strategy : public strategy { in_stream in; chart ch;
void on_start(void) override { in.as_random_walk(duration::days(1)); }
series<double> volat_25, volat_50, volat_100;
void on_prepare_dsl(void) override { const int trading_days_in_year = 255; volat_25 = VOLATILITY_DEMO(in.close, 25, trading_days_in_year); volat_50 = VOLATILITY_DEMO(in.close, 50, trading_days_in_year); volat_100 = VOLATILITY_DEMO(in.close, 100, trading_days_in_year); }
void on_bar_close(void) override { ch << chart::pane(250) << plot::ohlc(in, color::gold) << ":random-walk" << chart::pane(200) << volat_25 << volat_50 << volat_100; } };
my_strategy s; s.name("201_DSL"); s.output_base_path(os::path("output")); s.enable_reports(); s.auto_open_reports(); s.run("2010-02-01", "2012-03-01"); |
Program Output |
|
(1) (2) |
sref<double> VOLATILITY_DEMO(sref<double> close, size_t period, size_t num_intervals_in_year) { auto ch = close / close(1); auto ln_ch = LN(ch); auto stdev_ln_ch = STDEVP(ln_ch, period); auto annu_vol = stdev_ln_ch / std::sqrt(1.0 / (double)num_intervals_in_year); return annu_vol.plot_as("VOLAT", { period, num_intervals_in_year }); }
To calculate volatility the following steps are performed: •First calculate the ratios between closing prices. Note that when using DSL code, you cannot access a single value in a series! You must shift the entire series back using sref<T>::operator() (or the SHIFT() function) to calculate the ratio between two series. •Then the natural logarithm of the ratios is calculated. •Then the standard deviation of the ratios is calculated over the given period. •Finally the standard deviation is annualized. Remember that non-series arguments to arithmetic operators are automatically cast to series. We could have explicitly used CONST() on the output of std::sqrt().
|
|
(3)
|
return annu_vol.plot_as("VOLAT", { period, num_intervals_in_year });
Adds plot name and period information to be displayed in the chart legend. Note that it is possible to pass period information as a std::vector, constructed with { period, num_intervals_in_year }. This is necessary as some indicators have multiple relevant values that need to be displayed in the legend. |
|
(4) |
void on_start(void) override { in.as_random_walk(duration::days(1)); }
By default the stream's as_random_walk() member is passed a duration::days(1) default argument. Daily intervals are important in this tutorial because the VOLATILITY_DEMO() function needs to know how may bars there are per year so that it can annualize the final volatility value accordingly. |
|
(5) |
series<double> volat_25, volat_50, volat_100;
Here we declare the the series members that will hold the values produced by the VOLATILITY_DEMO(). These series can then be accessed by other parts of the strategy. |
|
(6) |
const int trading_days_in_year = 255; volat_25 = VOLATILITY_DEMO(in.close, 25, trading_days_in_year); volat_50 = VOLATILITY_DEMO(in.close, 50, trading_days_in_year); volat_100 = VOLATILITY_DEMO(in.close, 100, trading_days_in_year);
These lines call our new function and assign the values returned to the strategy's series members. |
|
(7) |
<< chart::pane(200) << volat_25 << volat_50 << volat_100;
This plots the volatility series. Note that we do not need to add any formatting information here since we already did this as part of our function definition. As we did not however pass any color information to series<T>::plot_as(), the default color::auto_color is used, so each line automatically gets its own color (See chart above). |