Developing Algo Trading Systems in C++

The Zorro platform accepts 4 types of algo trading scripts in the Strategy folder: lite-C code (.c), C++ code (.cpp), lite-C executables (.x), and DLLs (.dll). Zorro64 has no lite-C compiler and thus runs only .cpp and .dll scripts. Supported scripts appear in the [Script] scrollbox and compile or start automatically when the [Test], [Train], or [Trade] button is clicked. Zorro S can directly generate .x and .dll files from c. and .cpp sources. For compiling .cpp scripts it utilizes the Visual Studio™ C++ compiler. Theoretically other languages can also be implemented, such as C#, Java, Pascal, Delphi, or Basic, as long as they are able to generate a 32- or 64-bit Windows DLL. The included batch and header files in Source\VC++ can serve as templates for integrating other languages.

Using C++ as script language has many advantages. Additional development tools, such as the Visual Studio debugger and profiler, are available. External libraries, such as Boost, can be integrated without a wrapper. The 64-bit mode can access more memory for backtests. All lite-C functions and all system variables can still be used, but additionally you have C++ classes and templates at your disposal. Error handling is more strict and you'll get warnings about bad code style. The VC++ compiler is a bit slower than the lite-C on-the-fly compiler, but is only invoked when the script was changed. The resulting code runs equally fast or - in 64 bit mode - even faster. The only disadvantage is that you have to download and install Microsoft Visual Studio™ 2017 or above.

Getting started with C++

You can get the free Visual Studio Community Edition from https://visualstudio.microsoft.com/de/downloads. Install it with its C++ desktop applications enviroment. Locate its build path - that's the folder containing vcvars32.bat, for instance "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build" - and enter it in ZorroFix.ini. This enables Zorro S to directly compile and start C++ scripts.

Even though it lacks the direct start feature, you can use C++ also with the free Zorro version. Set up a DLL project as described below and use the Visual Studio IDE to compile your .cpp script to a DLL in the Strategy folder. DLLs are suported by the free version. Only for debugging it with Visual Studio you'll really need Zorro S.  

The VC++ compiler and linker options can be found in Source\VC++\compile.bat and compile64.bat. They are set up for Visual Studio 2022, but should also work for 2017 and 2019. When you know what you're doing, you can edit the batch files and modify the compiler or linker options for enabling special code optimizations or linking additional libraries. If you are unfamiliar with the VC++ options, leave the files untouched.

The 32-bit Zorro version compiles 32-bit DLLs, Zorro64 compiles 64-bit DLLs. When you switch between 32 and 64 bit versions, delete the old .dll file and compile it again. Or use different names for 32 bit and 64 bit .cpp scripts. A 64 bit program cannot load a 32 bit DLL, and vice versa.

DLLs can be used not only for developing strategies, but also for extending the lite-C language with external packages (see using DLLs). The code syntax for strategies in C++ is similar to lite-C; the example file MyStrategy+.cpp contains C++ versions of some of the included scripts.

VC++ vs. lite-C

Lite-C contains 'abbreviations' to make code shorter and less complex. Most of them work also with 'real' C++. Therefore a .c script looks normally almost the same as its .cpp equivalent. But the emphasis is on 'almost'. When converting to C++, please mind the differences in the list below. For conversion the other way, see lite-C for C/C++ programmers.

// lite-C strategy (e.g. "test.c")


function run()
{
  vars Prices = series(price());
  vars SMA100 = series(SMA(Prices,100));
  vars SMA30 = series(SMA(Prices,30));

  if(crossOver(SMA30,SMA100))
    enterLong();
  if(crossUnder(SMA30,SMA100))
    enterShort();
}
// C++ strategy (e.g. "test+.cpp")
#include <zorro.h>

DLLFUNC void run()
{
  vars Prices = series(price(0));
  vars SMA100 = series(SMA(Prices,100));
  vars SMA30 = series(SMA(Prices,30));

  if(crossOver(SMA30,SMA100))
    enterLong();
  if(crossUnder(SMA30,SMA100))
    enterShort();
}

Include <zorro.h>

If lite-C scripts have no header, the <default.c> header is automatically included. C++ scripts always need the header <zorro.h>:
#include <zorro.h> // C++ header
...

Exported and void functions

If a function does not return something, it must be defined with the void type. This is optional in lite-C, but mandatory in C++. If functions are exported by the DLL - such as run, main, objective, etc - define them with the DLLFUNC macro. The function keyword is defined as DLLFUNC void in C++.
function click() { ... } // lite-C
DLLFUNC void click() { ... } // C++

No variable initialization

The lite-C compiler initializes all global and local variables and arrays to 0; a normal C++ compiler does not. This means they have random content at start. So take care to properly initialize all variables when necessary (normally also recommended in lite-C code!):
var MyArray[10];  // lite-C; automatically initialized to zero
var MyArray[10] = { 0,0,0,0,0,0,0,0,0,0 }; // C++

Less parameter omitting

Some functions, for instance the date/time and price functions, can be called without parameter in lite-C. The lite-C compiler replaces the missing parameter with 0. Not so in C++. Pass the required parameters.
int CurrentMonth = month();  // lite-C; missing parameter is assumed zero
int CurrentMonth = month(0); // C++
var Close = priceC();  // lite-C
var Close = priceC(0); // C++

Less automatic type conversion

In lite-C, 32-bit integer types or pointers like char*, void*, int, long, DWORD etc are converted into each other without explicit typecasting. Not so in C++. Use typecast operators, either in C style or (preferably) in C++ style. The intptr_t type converts to a 32-bit word or a 64-bit word depending on the platform:
int Pos = brokerCommand(GET_POSITION,Asset);  // lite-C
int Pos = brokerCommand(GET_POSITION,(intptr_t)Asset);  // C++, C style
int Pos = brokerCommand(GET_POSITION,static_cast<intptr_t>(Asset));  // C++ style

Early comparison abort

In lite-C, the order of expressions in a comparison does not matter. In C/C++, comparisons with && or || are order sensitive. The comparison is aborted when a && is encountered and the expression so far evaluated to false, or when a || is encountered and the expression evaluated to true. This can cause a different behavior when the comparison contains function calls:
if(test(x) && test(y)) .. // lite-C always calls test(x) and test(y)
if(test(x) && test(y)) .. // C++ calls test(y) only when test(x) returns nonzero

Different string comparing

In lite-C, strings can be compared with string constants using the normal comparison operators, such as ==, !=, or switch...case. In the C++ DLL headers, a string is typedef'd as a char* pointer, and comparison with a constant would always result in false. Either use C++ types like CString or basic_string for string handling (but make sure to call lite-C functions always with C-style, null-terminated strings), or use the str... functions from the C library.
if(Algo == "TRND") ... // lite-C
if(0 == strcmp(Algo,"TRND")) ... // C++

Different bool size

In lite-C, bool has a size of 4 bytes, in C++ it's 1 byte. The lite-C bool type is equivalent to the C++ BOOL type. For avoiding confusion, you might prefer to use int instead of bool / BOOL.

Different pointer size

In lite-C a pointer has always a size of 4 bytes. In C++ it can have 4 or 8 bytes, depending on whether you compile with Zorro or Zorro64. Consider this when coding pointer arithmetics. The predefined variable types size_t and PWORD also change between 4 and 8 bytes.

No watch()ed variables

Zorro's single-step debugger can still be used with a DLL, and the watch statement can still set breakpoints or identify script crashes, but its variables are ignored. Use printf() or print(TO_WINDOW,...) instead for printing variables.
watch("My Variable: ",MyVar) // lite-C
print(TO_WINDOW,"\nMy Variable: %.2f",MyVar) // C++

Colliding definitions

Many lite-C variables and flags are pre-defined in variables.h and trading.h. If you're using a C++ library with a function, variable, or flag that has the same name, undefine the lite-C keyword with an #undef statement after including zorro.h and before including the headers of your library. Otherwise you'll get compiler errors.

Using DLL functions

In lite-C, the API macro imports functions from a DLL library. In C/C++, use the LoadLibrary and GetProcAddress standard functions for that:
int __stdcall MyDLLFunction(double a,double b);
API(MyDLL,MyDLLFunction) // lite-C
int (__stdcall *MyDLLFunction)(double a,double b);
HMODULE DllHandle = LoadLibrary("MyDLL");  // C/C++
if(DllHandle) {
   *(FARPROC*)&MyDLLFunction = GetProcAddress(DllHandle,"MyDLLFunction");
   ...
}
...
if(DllHandle) FreeLibrary(DllHandle);

Lite-C libraries

Some lite-C libraries such as profile.c or r.h contain function bodies, so they can be included in the main strategy code only, but not in multiple source modules.

Click and evaluate

In lite-C, click and evaluate functions could be triggered by button clicks even after the script run, as long as no other script was selected. DLL functions can only be triggered while the script is running.
 

Using other languages

You can write strategy DLLs in any language that supports 32-bit or 64-bit dynamic link libraries. In that case you must write new headers and a ZorroDLL.cpp version in the new language. Aside from the DLL entry point that is unused, it contains a zorro() function that is called upon strategy start, and receives a pointer to the GLOBALS struct. The GLOBALS struct is defined in trading.h. It is a singleton with all predefined variables and functions. Its Functions pointer leads to a list of addresses of all Zorro functions in the same order as listed in functions.h. The functions must be called with __cdecl calling convention.

When importing the GLOBALS struct, set up the compiler so that it does not pad the struct with dummy elements for aligning the struct members. If available, use a #pragma or similar statement to set the struct alignment to 4 bytes or below for all structs in the trading.h header. This is needed to ensure that the structs have identical size in your DLL and in Zorro. 

 

VC++ project setup for the Visual Studio Debugger

The Visual Studio debugger displays local and global variables and can step into code without the need of watch statements (which won't display C++ variables anyway). It is better suited than lite-C for finding hidden bugs or crashes in the code. For finding bugs or flaws in the trading logic, the Zorro visual debugger is still the best way.

For using the Visual Studio debugger, or for using C++ with the free Zorro version, for special purposes such as adding external C++ libraries to your script, you need to set up a Visual Studio DLL project. The dialogs and setup procedures shown below are slightly different for any Visual Studio version. Here's the setup for Visual C++ 2017 Community; the setup for VC++ 2019 or 2022 is very similar:

If you have a mix of lite-C and C++ scripts, you can add a '+' to a .cpp script name for marking it in the scrollbox. Since .c overrides .cpp, don't use the same name for both variants. The example file MyStrategy+.cpp contains C++ versions of workshops and example scripts. Which one to compile can be selected with a #define at the begin. Check it out for learning the differences of writing a strategy in VC++ instead of lite-C. All known code differences are listed below. If you encounter any other incompatility with a function or variable, please report on the user forum or to Zorro support. An alternative framework for creating Zorro strategies with VC++ has been developed by a user and is available on https://github.com/xezon/zorro-dll.

See also:

API access, lite-C vs C++, migration, engine API

► latest version online