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). The 64-bit version Zorro64 has no lite-C compiler and thus accepts only .cpp and .dll scripts. Scripts of supported types appear in the [Script] scrollbox and run automatically when the [Test], [Train], or [Trade] button is clicked.

 Zorro S can compile .x and .dll files from c. and .cpp sources automatically. For compiling .cpp scripts it utilizes the Visual Studio™ VC++ 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.

When should you use lite-C and when C++? Most scripts and strategies have a simple structure, and lite-C is absolutely sufficient. But as soon as a script becomes more complex end exceeds 100 lines of code, you might want to switch it to C++. 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 has access to the whole PC 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 install the free Microsoft Visual Studio™ 2022 or above.

The Visual Studio Debugger can display local and global variables, can profile code execution, and can step into code without the need of watch statements. Use if when you It is better suited than lite-C for finding hidden bugs or crashes in the code. However for finding bugs or flaws in the trading logic, the Zorro visual debugger is still the best way, and works with C and C++ projects as well.

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 the file vcvars32.bat, normally "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, and automatically generate a VC++ project from a script.

You can use C++ also with the free Zorro version. It's a bit more complicated though. For any script you need to 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 supported 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++ command line options, leave the files untouched.

The 32-bit Zorro version compiles 32-bit DLLs, Zorro64 (see Setup) 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.

C++ vs. lite-C: Code differences

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

String vs. char* vs. const char*

In lite-C, there's no difference between strings, char pointers, and constant char pointers. In C++, string is defined as char*, and is different to the type const char*.
string Name = "Sherlock"; // lite-C
const char* Name = "Sherlock"; // C++
If a function expects const char* and you want to pass  a string, use the (const char*) typecast. Vice versa, if you have defined a string constant like "ABCDEF" and you want to pass it to a function that expexts a string, use the (string) or (char*) typecast.

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 intptr_t also change between 4 and 8 bytes. Make sure to recomplie DLL-based strategies when using a newer or different Zorro version, since the size of global structs may be different.  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 constant precision

In lite-C, floating point constants in the code are read with 7 digits precision. In C++ it's 14 digits. 
printf("\n %.10f", 0.55555555); // lite-C prints 0.555555555224
printf("\n %.10f", 0.55555555); // C++ prints 0.555555555000

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. 

Calling external 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++ setup for a DLL project (free Zorro version)

If you own Zorro S, you need not read this chapter, since Zorro S automatically generates a VC++ project file (*.vcxproj) when compilig a C++ script in the Strategy folder. You only need to click on it for invoiking the Visual Studio environment. However with the free version, you need to set up a VC++ project for any C++ script. The dialogs shown below are for Visual C++ 2017, but for later versions like VC++ 2019 and 2022 they are very similar:

If you have a mix of lite-C and C++ scripts, we recommend to 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.

A template for a VC++ zorro project by a user is available on https://github.com/szabonorbert/zorro-project-template.

See also:

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

► latest version online