Strategy development with Visual Studio™
Zorro
supports three types of strategy files in the Strategy folder:
C code (.c),
lite-C executables (.x), and DLLs (.dll). These
3 file types automatically appear in the Script
scrollbox. The free Zorro version can run all 3 strategy types, but can only
create strategies in .c code. Zorro S
can compile .c strategies
to .x executables with the EXE flag
or via command line, and can also be used for
creating and debugging .dll strategies. Any language that can generate a
Win32 DLL can be used, such as C#, Java, Pascal, Delphi, Basic, and of
course C++.
For developing strategies with the lite-C compiler, you need no further tools; the
process is described in the tutorial. For developing strategy DLLs
in other languages you'll need an external development platform with a
compiler. We recommend Visual Studio™
Community Edition. It is a powerful development suite that can be freely downloaded from Microsoft™. Writing a strategy
as a VC++ DLL has several advantages. All tools of Visual Studio can be used,
especially the debugger and profiler. External function libraries, such as
Boost, can be directly integrated without the need to write a wrapper.
With all optimization features enabled, VC++ produces code that runs
slightly faster than lite-C (about 20%). The disadvantages
are that any project must be set up as described below before you can start
coding, and the DLL must be compiled after every code change.
The VC++
debugger displays local and global variables and can step into code without
the need of watch statements (which are not
supported by VC++ anyway). It is better suited than lite-C for finding bugs
in the code. For finding bugs or flaws in the trading logic, the
visual debugger can still be used even with DLL
based projects.
DLLs can be used not only for developing strategies, but also for
extending Zorro's C language with external packages (see
using DLLs). The code syntax for strategies in VC++ is
almost the same as in lite-C (differences are listed below), and functions and system variables can be used in the same way.
Additionally
you have all object oriented language features of C++ at your disposal.
Setting up VC++
The dialogs and setup procedures shown below are slightly different for any VC++ version.
A project setup (.vcxprj) is included, but even with it you
need to adapt the Zorro paths to your Zorro folder. Here's the detailed
setup for Visual C++ 2017 Community Edition; the setup for VC++ 2019 is very similar:
- You will need Zorro S for debugging DLL strategies. Download and install Visual Studio
2017 from
https://visualstudio.microsoft.com/de/vs/older-downloads. Install it with all needed modules for generating C++
desktop applications and
Windows DLL projects. Make yourself familiar with its editor and tools - there are tons of books and courses for VC++.
At the time of this writing (June 2019), Visual Studio 2019
was reported to sometimes display wrong or misleading error messages when
the code had a syntax error. Until this problem is
fixed, Visual Studio 2017 is preferable for strategy projects because it indicates syntax errors correctly.
- For writing a DLL strategy, first create a new project (File > New > Project). Select
Visual C++, Windows Desktop,
Dynamic-Link Library. In the lines at the bottom
of the form, enter the project name and folder
location, then click Ok..

- VC++ will now create a folder with a new empty DLL project. Check
the Source Files list in the Solution Explorer. It
will probably, among other files, contain a dllmain.cpp
file. Remove it from the project since you won't need it (right click > Remove).
- From Zorro\Source\VC++, copy the ZorroDLL.cpp
file into your project folder. Add ZorroDLL.cpp to your project (Source
Files > right click > Add > Existing Item). It contains
the DLL entry point and code for initializing the function and variable
lists. The project folder should now look as below. Depending on the
VC++ version some project files may be different, f.i. pch
insteasd of stdafx:

- Now set up some project properties. Right click on the project name
in the Solution Explorer and select Properties.
Under General, select
Multi-Byte Character Set for all configurations. This causes
Windows functions to expect text strings in ANSI and not in Unicode.

- If you want to distribute your strategy DLL to other people, select C/C++ > Code Generation, and change the
Runtime Library
of the Release configuration from Multi-threaded DLL (/MD) to
Multi-threaded (/MT). Otherwise people could not use
the DLL without installing the VC++ Runtime Redistributable before.

- Next add your Zorro include folder to the VC++ include
directories. It contains the headers that your code needs to compile.
Make sure to add the folder at the end of the path list - not at the
beginning!

- Set the Zorro Strategy folder as link target for the
Debug and for the Release configurations.
This way the DLL is created already in the right place and can be directly
tested.

- For later debugging your strategy with Visual C++, either run Zorro
with the debug version of the DLL and select Attach
To Process. Or set up executable, command line, and
folder in the Configuration Properties:

- Now you're all set to develop and debug your strategy. Always include
<zorro.h> and
put the strategy code into a DLLFUNC void run(). The
stdafx.h header includes the default Windows headers.
The zorro.h
header includes all needed variables and function definitions.
Functions to be exported by the DLL, especially user provided functions such
as run, tick, evaluate
etc, must be preceded with the DLLFUNC macro.
The calling convention is __cdecl. For debugging a
system variable, look into the header variables.h that
contains all system variable definitions and their equivalents in the
g singleton struct.
.
Example with the code from Workshop4::

- For writing another strategy DLL, you need not necessarily repeat the
whole setup procedure. It's normally more convenient to just duplicate
the project folder, and rename the source file and the project name in
the .vcxproj file, which is a simple text file.
- Mistakes - for instance, a wrong
compiler setup or a missing ZorroDLL.cpp - will cause
Error 062 when starting the DLL. In that
case check your project for a correct setup.
- If a strategy DLL works on your PC, but produces Error 062
on another PC, the reason is most likely a missing module. Use a tool
such as
Dependency Walker for finding out which module is missing on that
particular PC.
The file MyStrategy.cpp in the Source\VC++
folder contains VC++ versions of Workshops 6..8, the RTest script, and the
DeepLearn script. 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.
Migrating a lite-C script to VC++
Lite-C contains 'abbreviations' to make code shorter and less
complex. Most of them, but not all, work also with 'real' C++. When converting
a lite-C strategy to
C++, please mind the following subtle differences (see also lite-C for C/C++ programmers):
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++
No parameter omitting
Some functions, for instance the date/time functions, can be called without
parameter. The lite-C compiler replaces the missing parameter with 0. Not so
in C++. Pass all required parameters.
int CurrentMonth = month(); // lite-C; missing parameter is assumed zero
int CurrentMonth = month(0); // C++
No automatic type conversion
In lite-C, 32-bit integer types or pointers like char*,
void*, int, long,
DWORD etc are converted without explicit typecasting. Not so
in C++. Use typecast operators when required.
int Pos = brokerCommand(GET_POSITION,Asset); // lite-C
int Pos = brokerCommand(GET_POSITION,(DWORD)Asset); // C++
Early comparison abort
In lite-C, the order of expressions in a comparison plays no role. 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) dependent on what's returned by test(x)
Exported and void functions
If functions do not return something, define them with void. A function defined with function must return an int. If functions are exported by the DLL - such as run, main, objective, etc - define them also with the DLLFUNC macro:
function run() { ... } // lite-C
DLLFUNC void run() { ... } // C++
No parameter skipping
In lite-C, function parameters can often be omitted when they are 0. For this purpose, the lite-C compiler offers a variable that contains the number of parameters. This variable is not available in C++, so default parameters must be explicitely defined. The zorro.h include file has such definitions for most functions with multiple parameters, but not for single-parameter functions.
var Close = priceClose(); // lite-C
var Close = priceClose(0); // C++
Different string type
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 type
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 Win32 BOOL type. For avoiding confusion,
you might prefer to use int instead of bool / BOOL.
No watch() statements
Zorro's single-step debugger can still be used with a DLL, but the watch statement can not.
Use printf() or print(TO_WINDOW,...) instead.
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 dynamic link libraries. In that case you must adapt the headers to the used language, and also adapt the ZorroDLL.cpp. 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 32-bit addresses of the 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 structs with dummy elements for aligning the struct members. If available, set the struct alignment to 4 bytes or below. This is needed to ensure that the structs have identical size in your DLL and in Zorro.
See also:
API,
lite-C vs C++,
migration, engine API
► latest
version online