Variables, Arrays, Strings

Variables store numbers. For defining variables and giving them initial values, use a declaration like this:

var Number;	// uninitialized variable of type 'var'
int Number = 123; // initialized variable of type 'int'

This declaration creates a variable of the given type with the given name. The name can contain up to 30 characters, and must begin with A..Z, a..z, or an underscore _. Variables must never have names that are already used for functions and for other previously-defined variables.

Also constant expressions can be assigned to variables. Example:

int Size = 100/2; // assign the value 50

Variable types

Computers calculate with finite accuracy. Therefore all normal variable types are limited in precision and range. Lite-C supports the following variable types:

Type Size (bytes) Range Step Digits Used for
double, var 8 -1.8·10308 to 1.8·10308 2.2·10-308 ~14 high precision numbers
float 4 -3.4·1038 to 3.4·1038 1.2·10-38 ~7 script constants, compact numbers
fixed 4 -1048577.999 to 1048576.999 0.001 ~9 special purposes (HFT)
int, long 4 -2147483648 to 2147483647 1 ~10 script constants, counting, flags
short 2 0 to 65536 1 ~5 compact integers
char, byte 1 0 to 256 1 ~2 text characters
bool, BOOL 4 true, false or 1,0 - - yes/no decisions
size_t, intptr_t 4 or 8 0 to 4294967295 / 18446744073709551615 - - sizes, addresses, long integers
char*, string characters+1 "..." - - text
var*, vars elements*8 -1.8·10308 to 1.8·10308 2.2·10-308 ~14 arrays, simple time series
DATE 8 12-30-1899 00:00 to doomsday ~0.1 ms - time, date
mat rows*cols*8 -1.8·10308 to 1.8·10308 2.2·10-308 ~14 vectors, matrices
dataset records*(fields+1)*4 -3.4·1038 to 3.4·1038 1.2·10-38 ~7 candles, complex time series

 !!  The limited accuracy of variable types affects numerical expressions. If an expression contains only integers (numbers without decimals), the result is also an integer. For instance, the expression (99/2) is 49, but (99.0/2.0) is 49.5. The expression 1.0/4 is 0.25, but 1/4 is 0. Integer numbers in the script - such as character constants ('A'), numeric constants (12345) or hexadecimal constants (0xabcd) are treated as int with 10 significant digits; numbers or constant expressions containing a decimal point (123.456) are treated as float with 7 significant digits. Due to the 7 digit limit, var X = 123456789.0; would in fact set X to 123456792.0; but var X = 123456789; sets it to 123456789.0 since int has 10 significant digits.

var Half = 1/2;  // wrong!
var Half = 1./2; // ok! 
Since constants or constant expressions are float, avoid arithmetic with constants when higher precision is needed. Use var or double variables instead:
var Days = Seconds/(24.*60.*60.);  // low precision
var SecondsPerday = 24*60*60; 
var Days = Seconds/SecondsPerday; // high precision 

Typecast operators

Variables are normally automatically converted to another type. So you can assign var to int variables or vice versa without having to care about type conversion. However, sometimes you'll need to convert expressions or variables when the conversion is not obvious - for instance when calling an overloaded function that accepts several variable types. In this case you can enforce a type conversion with a typecast operator. A typecast (or cast) operator is the target variable type in parentheses - f.i. (int) or (var) - placed before the expression or variable to be converted. Example:
var dx = 123.456;
float fy = 0.12345; printf("The integer value of dx is %i, fy is %f",(int)dx,(var)fy); // %i expects the "int" variable type, %f expects "var"
Be aware of the above mentioned limited accuracy when using floating point arithmetic and then typecasting the result to an integer. Operations with the theoretical result 1 can in practice return 1.000001 or 0.999999, and cause unexpected results. If in doubt: round.  

Global, local, and static variables

Variables can be defined outside functions or inside functions. When defined outside, they are called global variables and are accessible by all functions. They keep their values until the script is compiled the next time. Variable definitions inside functions produce local variables. Local variables only exist within the scope of the function. They are unknown outside the function, and lose their values when the function returns. If a global variable with the same name as a local variable exists, the local variable has priority inside it's function. If you declare a variable multliple times, the most recent declared copy is used. But for your own sake you should better avoid using the same name for different variables. 

A local variable can be declared as static. It is then treated as a global variable even if it is declared within a function, and keeps its content when the function terminates or when another instance of the function is called. This means that it is initialized only the first time the function is called. Example:

function foo() 
{
  static bool Initialized = false;
  if(!Initialized) { initialize(); } // initialize only once
  Initialized = true;
  ...
}
 !!  Static and global variables keep their values until the script is compiled again. So make sure to initialize them at script start - if(Init) - to their initial values if required.

The value of another variable, or the return value of a function can be assigned to a local variable at declaration, but not to a global or static variable. Use only constants for a declaration of a global or static variable.

int GlobalCounter = count(); // wrong!

function foo() 
{
  int FooCounter = count(); // ok!
  static int FooCounter2 = FooCounter; // wrong!
  ...
}

Arrays

If you group several variables of the same type together, you have an array:
var name[N]; // uninitialized array
var name[N] = { 1, 2, ... N }// initialized global array
This creates a variable that contains N numbers, and optionally gives them default values as in the second line. Note the winged brackets { } that must surround the set of default values. Such a multi-number variable is called an array. The number N is called the length of the array. It must be really a number; arithmetic expressions or formulas cannot be used for defining a fixed-length array,  Example:
int my_array[5] = { 0, 10, 20, 30, 40 }; 

This creates an array of 5 numbers that can be accessed in expressions through my_array[0]...my_array[4]. In such an expression, the number in the [ ] brackets - the index - tells which one of the 5 numbers of the array is meant. Note that there is no my_array[5], as the index starts with 0. The advantage of using an array, compared to defining single variables, is that the index can be any numeric expression. Example:

int i;
for (i=0; i<5; i++) 
  my_array[i] = i; // sets the array to 0,1,2,3,... etc.

Care must be taken that the index never exceeds its maximum value, 4 in this example. Otherwise the script will crash.

 !!  Initializing in the definition works only for simple arrays; multidimensional arrays, arrays that contain strings, structs, or anything else than numbers, or several arrays defined together in a single logical line must be initialized by explicitly setting their elements. Initializing local arrays makes them static (see below), meaning that they keep their previous values when the function is called the next time. For initializing them every time the function is called, explicitly set them to their initial values, like this:

function foo()
{
  var my_vector[3];
  my_vector[0] = 10;
  my_vector[1] = 20;
  my_vector[2] = 30;
  ...
  var my_static[3] = { 10, 20, 30 }; // initializing local arrays makes them static 
  ...
} 

 !!  All local variables are stored on a special memory area called the stack. The stack has a limited size that is sufficient for many variables, but not for huge arrays. Exceeding the stack size causes a Error 111 message at runtime, so when you need large arrays of more than 100 variables, better declare them global, i.e. outside the function. When you want to determine the array size dynamically, use the malloc / free method. When you need a data structure whose size can grow or change at runtime, use no array, but a dataset.

In C, arrays are in fact pointers to the start of a memory area containing the array elements. So, my_vector[3] is the same as *(my_vector+3).

Arrays can contain not only variables, but also complex data types such as structs. For sorting or searching arrays, the standard C functions qsort and bsearch can be used. They are documented in any C book and available in the stdio.h include file.

Multidimensional arrays can be defined by using several indices:

var Map[10][20];
... Map[j][i] = 10; // j = 0..9, i = 0..19
Computer memory has only one dimension, so the above defined array is in fact still a pointer to a one-dimensional memory area of the size 200 (10*20). Pointer offsets or the & operator can be used to address array elements or pointers to array elements. In the above example, Map[j][i] is the same as *(Map+j*20+i), and &(Map[j][i]) is the same as (Map+j*20+i).

typedef

You can define your own variable types with typedef statements. Example;
typedef long DWORD;
typedef char byte;

...

DWORD dwFlags;
byte mypixel; 
typedef can be used to redefine variables and structs; it can not be used for arrays.
 

Strings

Strings are arrays of char variables that contain alphanumerical characters - letters, numbers or symbols - which can be used for messages or names. They are defined this way:

string Name = "characters"; 

This defines a string with the given name and initializes it to the content characters between the quotation marks. Special characters within a string must be preceded by a '\': \n = Line feed; \\ = Backslash; \" = Quotation mark. The string type is simply a char pointer (char*) that points to the first character. The last character is followed by a 0 byte to indicate the end of the string.

Any static char array can serve as a string with a fixed maximum length and variable content:

char Name[40];
...
strcpy(Name,"My String!"); 

After filling this array with characters, it can be used as a string. Care must be taken not to exceed 39 characters, as the last one is the end mark 0. String functions can be used for setting and manipulating string content.

The content of a string can be compared with a string constant using the '==' operator:

string MyAsset = "AAPL";
...
if(MyAsset == "AAPL")
  ...
 !!  Comparing strings with string constants is not the same as comparing two strings (f.i. if(MyAsset1 == MyAsset2) ...). The latter expression is only true when the two string pointers are really identical. For comparing the contents of two non-constant strings, use standard C library functions such as strstr or strcmp.

 !! 
For convenience reasons, some string functions return temporary strings that are allocated on the CPU stack. Temporary strings can not be used for permanently storing content, as they are eventually overwritten by further string functions calls (see string function remarks).

Series

Series are a special array of var variables, normally used for time series of price data, indicators, or data curves. If not given otherwise, the length of a series is determined by the global variable LookBack. Series data is stored in reverse order, so the most recent data is at the begin of the array.

See also:

pointers, structs, functions, series

 

► latest version online abs

abs(int x): int

abs(var x): var

Absolute amount of x.

Parameters:

x - any var or int.

Returns:

x >= 0 then x
x < 0 then  -x

Example:

x = abs(2); // x is now 2
x = abs(-599); // x is now 599 
x = abs(0); // x is now 0 

See also:

 sign

► latest version online

Asset list, account list

Asset Lists and Account Lists

Any asset used in a trading script takes its parameters and symbols from an entry in an asset list in the History folder (unless the asset was added by script). This entry determines all properties of the asset. The default asset list, AssetsFix.csv, also determines the assets that initially appear in the [Asset] scrollbox. Since any broker has his individual asset parameters and symbols, different asset lists are used for different brokers and accounts. The asset list can be selected either by script through the assetList command, or automatically with an account list (see below). If no asset list is selected, the default list is used.

The parameters in the asset list are used for training and testing. In live trading, asset parameters are not read from the list, but taken directly from the broker API. However if parameters are not provided by the broker API, they are also taken from the asset list. Since broker API parameters are not always reliable, the SET_PATCH command can be used to override the API and enforce the asset list parameters.

The asset parameters need not be 100% precise. A backtest with an asset list for a different broker or account will normally also produce a useful result. But the parameters should not be totally off, especially not leverage and lot amount. If asset list parameters appear suspicious or deviate strongly from live parameters, a warning is issued at start of a live trading session.

Asset lists are simple comma separated spreadsheet files (CSV files) that can be edited with Excel or with a text editor for adding new assets, or for modifying parameters. Every asset is represented by a line in the CSV file. New assets can be added either manually - by editing the file and entering a new line - or semit-automatically from Log\Assets.csv as described below. For adding a new asset to the scroll box, edit AssetsFix.csv with the script editor and add a new line with the asset name and parameters.

The first line is the header line. A line is ignored when its Name begins with "#"; this way assets can be temporarily 'commented out'. Names should contain no special characters except for '_' and '/', but their assigned symbols can contain special characters except for blanks, commas, and semicolons. Zorro also accepts semicolon separated csv files with commas instead of decimal points, but not a mix of both in the same file. Excel uses your local separation character, so it is recommended to set up your PC regional settings that it's a comma and the decimal is a point, otherwise Excel cannot read standard csv files.

The asset parameters are to be set up as described below. In the description, a contract is 10,000 units for forex pairs, and a single unit for all other assets (brokers can have different individual contract sizes, see examples!). A lot is defined as the minimum tradeable number of units for forex pairs, and the minimum tradeable number of units or fraction of a unit for all other assets. Some parameters have a slightly different meaning when negative, f.i. a percentage instead of an absolute value. For most parameters exist an equivalent script variable, which is set in live trading from the broker API if available, and from the asset list otherwise.

Name Name of the asset, f.i. "EUR/USD". Up to 15 characters, case sensitive, with no blanks and no special characters except for slash '/' and underline '_'. This name appears in the Asset scrollbox and is used in the script, f.i. by the asset function. It also determines the names of the historical data files (f.i. "EURUSD_2020.t6"). The name is the same for all brokers, while the asset symbol can be different for any broker. The asset is ignored when the name begins with a hash character "#".
Price Ask price per unit, in counter currency units. Accessible with the InitialPrice variable. For non-forex assets the counter currency is usually the currency of the exchange place, such as USD for US stocks, or EUR for the DAX (GER30). For digital coins it's usually another coin, such as Bitcoin The Price parameter is normally for information only and not used in the backtest.
Spread The current difference of ask and bid price, in counter currency units. Accessible with the Spread variable. Used for backtests with constant spread. If at 0, ask/bid spread must be determined by script.
RollLong
RollShort
Rollover fee (Swap) in account currency units for holding overnight a long or short position per calendar day and per 10000 units for currencies, or per unit for all other assets. Accessible with the RollLong/Short variables. This is normally the interest that is daily added to or subtracted from the account for holding positions and borrowing margin. Some brokers have a three times higher rollover cost on Wednesdays or Fridays for compensating the weekend. When manually entering rollover fees, make sure to convert them to per-10000 for forex pairs. Zorro multiplies the fees with the trade duration in days for determining the total rollover cost of a trade. Use the SOFTSWAP flag for continuous swap costs.
  If rollover is unknown, it can be estimated from the current interest rate of the counter currency divided by 365 and multiplied with asset price*(1-1/leverage). If the broker charges a flat interest fee on margin loan, set RollLong/RollShort to 0 and use the Interest variable.
PIP Size of 1 pip in counter currency units; accessible with the PIP variable. Equivalent to the traditional smallest increment of the price. The usual pip size is 0.0001 for forex pairs with a single digit price, 0.01 for stocks and for forex pairs with a price between 10 and 200 (such as USD/JPY), and 1 for CFDs with a 4- or 5-digit price. Prices in the log are normally displayed with as many decimals as given by the pip size.
PipCost Value of 1 pip profit or loss per lot, in units of the account currency. Accessible with the PipCost variable and internally used for calculating trade profits or losses. When the asset price rises or falls by x, the equivalent profit or loss in account currency is x * Lots * PIPCost / PIP. For assets with pip size 1 and one contract per lot, the pip cost is the conversion factor from counter currency to account currency. For calculating it manually, multiply LotAmount with PIP; if the counter currency is different to the account currency, multiply the result with the counter currency exchange rate. Example 1: AUD/USD on a micro lot EUR account has PipCost of 1000 * 0.0001 * 0.9 (current USD price in EUR) = 0.09 EUR. Example 2: AAPL stock on a USD account has PipCost of 1 * 0.01 = 0.01 USD = 1 cent. Example 3: S&P500 E-Mini futures (ES) on a USD account have PipCost of 12.5 USD (1 point = 25 cent price change of the underlying is equivalent to $12.50 profit/loss of an ES contract). This paramter is affected by LotAmount.
MarginCost Margin required for purchasing and holding 1 lot of the asset, either in units of the account currency or in percent of the position value. Depends on account leverage, account currency, counter currency, and LotAmount. If the broker has different values for initial, maintenance, and end-of-day margin, use the highest of them. Accessible with the MarginCost variables. Internally used for the conversion from trade Margin to Lot amount: the number of lots that can be purchased with a given trade margin is Margin / MarginCost. Also affects the Required Capital and the Annual Return in the performance report.
   Negative MarginCost is interpreted as a percentage of the position value, and internally converted to a leverage accessible with the Leverage variable. For instance, a 25% end-of-day margin is entered as -25 and equivalent to leverage 4. For no leverage, enter -100.
  MarginCost and Leverage can be converted to each other with the formula MarginCost = Asset price / Leverage * PipCost / PIP. When the broker uses a leverage or margin percentage, the margin per purchased lot depends on the current price of the asset. When the broker uses positive MarginCost, the margin is independent of the current asset price, therefore the broker will adapt MarginCost from time to time when the price has moved far enough. This is typical for Forex/CFD brokers.
Market
(Leverage)
Time zone and market hours in the format ZZZ:HHMM-HHMM, for instance EST:0930-1545. Available to the script in the variables AssetMarketZone, AssetMarketStart, AssetMarketEnd, for skipping data series or preventing orders outside market hours. Old Zorro versions prior to 2.29 had the leverage in this field.
Multiplier /
LotAmount
Number of units for 1 lot of the asset; accessible with the LotAmount variable. Smallest amount that you can buy or sell with your broker without getting the order rejected or an "odd lot size" warning. For forex the LotAmount is normally 1000 on a micro lot account, 10000 on a mini lot account, and 100000 on standard lot accounts. Index CFDs or crypto currencies can have a LotAmount less than 1, such as 0.1 CFD contracts. For stocks and most other assets the lot amount is normally 1. The lot amount affects PipCost and MarginCost.
   Cryptocurrency broker APIs often support the SET_AMOUNT command. You can then set any desired lot amount in this field, such as 0.0001 Bitcoins. 10000 Lots are then equivalent to 1 Bitcoin. 
   A negative value in this field is interpreted as a multiplier for option or future contracts, and accessible with the Multiplier variable. The LotAmount is then set to 1.
Commission Roundturn commission amount in account currency units for opening and closing one contract, per 10000 units for currencies, per unit for other assets, per underlying unit for options, or as percent of the trade value when negative. Accessible with the Commission variable. When manually entering the commission, double it if was single turn. For forex pairs make sure to convert it to per-10000. If an asset is a forex pair can be determined with the assetType function. A forex commission in pips is converted by Commission = CommissionInPips * 10000 * PIP.
  If the commission is a percentage of the trade value, enter the negative percent value, f.i. -0.25 for 0.25%. A percentage can be converted to commission amount with the formula Commission = -Percent/100 * Price / LotAmount * PIPCost / PIP.
Symbol Broker symbol(s) of the asset. Up to 3 sources and symbols can be entered for trading, receiving live prices, and receiving historical prices, separated by '!' exclamation marks in the form trade!live!history. Leave this field empty or enter '*' when the symbol is identical to the asset name or is automatically converted by the broker plugin. Up to 128 characters, case sensitive.
AssetVar Up to 8 optional asset specific variables or strings, for evaluation in the script through AssetVar[0]..[7] or AssetStr[0]..[7]. They can contain multipliers, asset types, trading hours, portfolio weights, or similar parameters. Text strings must not exceed 7 characters.

Remarks:

Parameter calculation examples (EUR account, EUR/USD at 1.15):

Broker website: Ticker EURUSD, Spread 0.3 pips, Margin 3.33%, LotSize 0.01 contracts, ContractSize 100000 EUR, PipValue 10 USD, SwapLong -5.70 USD, SwapShort -2.50 USD, OrderCommission 2.5 EUR. All parameters per 100000 EUR contract.
 
=> Asset list: Name EUR/USD, Price 1.15, Spread 0.00003 (= 0.3 pips * PIP), RollLong -0.4956 (= -5.70 * 10000/100000 / 1.15), RollShort  -0.2174 (= -2.50 * 10000/100000 / 1.15), PIP 0.0001 (= 10 / 100000), PIPCost 0.087 (PIP * LotAmount / 1.15), MarginCost -3.33, LotAmount 1000 (= 0.01 * 100000), Commission 0.5 (= 2 * 2.5 * 10000/100000), Symbol EURUSD.

Broker website: Ticker USDJPY, Spread 0.4 pips, Leverage 30, PIP 0.01, PipValue/Lot 1000 JPY, MinLot 0.01. Variable swap, no commission.

=> Asset list: Name USD/JPY, Price 105, Spread 0.004 (= 0.4 pips * PIP), RollLong/Short -0.01 (some assumed value), PIP 0.01, PIPCost 0.083 (PipValue * MinLot / Price / 1.15), MarginCost -3.33 (-100/Leverage), LotAmount 1000 (PipValue / PIP), Commission 0, Symbol USDJPY.

Broker website: Ticker XAGUSD, Price 17.5, Spread 0.02 points, Margin 10%, LotSize 0.01 contracts, ContractSize 5000 XAG, TickValue 5 USD, SwapLong -3.50 USD, SwapShort -3.00 USD, OrderCommission 0.0025%. All parameters per 5000 XAG contract.
 
=> Asset list: Name XAG/USD, Price 17.5, Spread 0.02, RollLong -0.00061 (= -3.50 / 5000 / 1.15), RollShort  -0.00052 (= -3.00 / 5000 / 1.15), PIP 0.01 (by tradition), PIPCost 0.435 (PIP * LotAmount / 1.15), MarginCost -10, LotAmount 50 (= 0.01 * 5000), Commission -0.005 (= -2 * 0.0025%), Symbol XAGUSD.

Broker website: Ticker DE30, Price 12567, Pip size 1, Avg Spread 0.5 pips, Leverage 1:100, LotSize 0.01 contracts, TickValue 1 EUR, SwapLong -3.0 %, SwapShort -6.0 %, Commission 5 / mio.
 
=> Asset list: Name GER30, Price 12567, Spread 0.5, RollLong -1.03 (= -3,0% / 100% * Price / 365), RollShort  -2.06 (= -6.0% / 100% * Price / 365), PIP 1, PIPCost 1, MarginCost -1.0 (-100 / Leverage), LotAmount 0.01, Commission -0.00005 (= 5 / 1000000 * 100%), Symbol .DE30.

Included asset lists

No guarantee of completeness or correctness! The lists below can be used as examples or templates for your own account specific asset list.

AssetsFix.csv - The default asset list when no different list is specified in the script; determines the assets initially available in the scrollbox. Based on an Oanda™ 100:1 USD account with lot size 1000.

AssetsCur.csv - 35 currency pairs including all pairs of the 7 major currencies EUR, USD, AUD, GBP, CHF, NZD, CAD. For trading with a multitude of currencies. Based on a 100:1 EUR account.

AssetsFUT.csv - 20 frequently used futures contracts of currencies, commodities, and indexes. Adapt the symbols to the expiration date of the desired contract.

AssetsArb.csv - 3 Forex pairs from different brokers as an example of broker arbitrage.

AssetsOanda.csv* - 15 Forex/CFD assets on an Oanda 100:1 USD account with lot size 1.

AssetsOandaEUR.csv* - 15 Forex/CFD assets on an Oanda 20:1 EUR account.

AssetsFXCM.csv* - 15 Forex/CFD assets on a FXCM 30:1 EUR account.

AssetsGP.csv* - 15 Forex/CFD assets on a Global Prime 100:1 USD account.

AssetsDarwinex.csv* - 15 Forex/CFD assets on a Darwinex 100:1 EUR account.

AssetsIB.csv* - Forex pairs, CFDs, and 40 major stocks on an Interactive Brokers™ USD account.

AssetsBittrex.csv* - Example cryptocurrencies on a Bittrex Bitcoin account.

AssetsSP30.csv* - Top 30 stocks from the S&P500 index, for downloading historical prices from the broker.

AssetsSP50.csv* - Top 50 stocks from the S&P500 index, with symbols for downloading historical prices from Stooq or Yahoo.

AssetsSP250.csv* - Top 250 stocks from the S&P500 index. Plain list, no symbols or parameters.

AssetsRussell2000.csv* - 2000 small cap stocks from the Russell 2000 index, prices from Yahoo.

AssetsCFD200.csv - 200 stock CFDs with 10:1 leverage for trading with IB.

AssetsZ8.csv - 20 stocks and ETFs for a Markowitz portfolio rotation strategy, prices from Stooq, trading with IB.

AssetsZ9.csv - 10 US sector ETFs and bonds for a momentum portfolio rotation strategy, prices from Stooq, trading with IB.

AssetsZ9E.csv - 10 European sector ETFs and bonds for a momentum portfolio rotation strategy, prices from Yahoo, trading with IB.

AssetsZ10.csv - 10 major cryptocurrencies, prices from Cryptocompare, trading with Binance.

*  !!  Broker-specific asset lists do not necessarily match your account, even with the same broker. Asset parameters and trading costs are often different in different countries and change all the time. We do not permanently observe brokers or stock indices lists for updating the above asset lists. For making sure that your asset list is correct, get the recent parameters from the broker API or the broker's website, compare with your asset list, and adapt it if necessary.
 

The account list (Accounts.csv)

Account lists are supported with Zorro S. By default, only a Demo and a Real account is available with the [Account] scrollbox. Individual accounts, with user-defined parameters and separate brokers and price sources, can be added with an account list. It is stored in a spreadsheet file named Accounts.csv in the History folder. This file is a convenient way to manage many broker accounts with different logins, passwords, asset parameters, and special modes. The Accounts.csv file can be edited with Excel or with the script editor. Zorro must be restarted when the file was modified. It contains the account parameters in comma separated spreadsheet format.

An example file AccountsExample.csv is contained in the History folder as a template for creating your own Accounts.csv file. Every account in the scrollbox corresponds to a line in the file with the following parameters, separated with commas:

Name Account name (no blanks) that appears in the account scrollbox. A line beginning with '#' is outcommented.
Server Broker name, server URL, or some other info identifying the connection (no blanks). Sent to the API plugin with the SET_SERVER command before login.
AccountId Account/subaccount identifier sent to the broker, or 0 when no subaccounts are used.
User User ID for the login, or 0 for manually entering the user name.
Pass Password for the login, or 0 for manually entering the password.
Assets Name of the default asset list (see above) for this account, with no blanks and without the .csv extension. This is the list used for live trading and backtesting when the account is selected and no assetList function is called in the script.
CCY Name of the account currency, f.i. EUR or USD. Sent to the API plugin with the SET_CCY command before any account request. Optionally an ANSI currency symbol and the number of digits to display for account values can be added after a decimal point. By default, the currency symbol is '$' and account values are displayed with 2 decimals when less than 100, and without decimals from 100 on.
Real 0 for a demo account, 1 for a real account, or 3 for a real account with no trading. In 'no trading' mode all trades are opened as phantom trades and not sent to the broker.
NFA Compliance of the account. Affects default state of NFA flag and Hedge mode: 0 for no restrictions, 2 for Hedge = 0 (no hedging), 14 or 15 for NFA = on (full NFA compliance).
Plugin File name of the broker plugin in the Plugin folder, case sensitive (.dll extension can be omitted). The same plugin can used for several price sources by copying the plugin to a different name (see below). In the example, ZorroMT41.dll and ZorroMT42.dll are both copies of the ZorroMT4.dll. If the field is empty, the broker plugin is selected with the scrollbox.

The first line in the CSV file must be the header line. Names and strings must contain no blanks (check for blanks at the end that may be accidentally added!) and no special characters except for '-' and '_'. Zorro also accepts semicolon separated csv files with commas instead of decimal point, but not a mix of both in the same file. User names and passwords are stored in unencrypted text in the spreadsheet, so leave those parameters at 0 when other people have access to your PC.

In the above example, the account ABC_Real is prepared for trading simultaneously with ABC_Demo, one used as price source, the other for entering trades by setting Asset List / Symbol accordingly (see above). Since two broker connections cannot use simultaneously the same plugin, the Zorro user with the above account list has made two copies of the ZorroMT4.dll, and renamed them to ZorroMT41.dll and ZorroMT42.dll. The connected MTR4 client must also be duplicated this way, since MTR4 can also not handle two different accounts simultaneously.

When the AssetsFix.csv or Accounts.csv lists were changed, Zorro must be restarted for the changes to have an effect.

Remarks

See also:

assetAdd, assetList, symbols, broker arbitrage, TradeMode, Testing

► latest version online

break

break

The break statement terminates a while or for loop or a switch..case statement, and continues with the first instruction after the closing bracket.

Example:

while (x < 100)
{ 
  x+=1;
  if (x == 50) break; // loop will be ended prematurely
} 

See also:

while, for, switch, continue ► latest version online continue

continue

Jumps to the begin of a while loop or the continuation part of a for loop.

Example:

int x = 0;
int y = 0;
while (x < 100)
{ 
 	x+=1;
 	if(x % 2) // only odd numbers
 	  continue; // loop continuing from the start
 	y += x; // all odd numbers up to 100 will be sum
} 

See also:

while, for, break

► latest version online Machine Learning: adviseLong, adviseShort

Machine learning

Zorro can utilize included or external machine learning algorithms, for instance from R packages, for applying 'artificial intelligence' to algorithmic trading. Three internal algorithms for price prediction are available: a decision tree, a simple neural network, and a signal pattern learning system. They generate trading rules separately for any WFO cycle, asset, algo, and long or short trades. You can also provide your own function for training an individual model, or use functions from R or Python packages. An introduction into various machine learning methods can be found on Financial Hacker.

The advise functions are used to implement a machine learning algorithm just like a standard indicator:

adviseLong (int Method, var Objective, var Signal0, var Signal1, var Signal2, ..., var Signal19): var

adviseShort (int Method, var Objective, var Signal0, var Signal1, var Signal2, ... var Signal19): var

adviseLong (int Method, var Objective, var* Signals, int NumSignals): var

adviseShort (int Method, var Objective, var* Signals, int NumSignals): var

Call a machine learning algorithm for training or for prediction. In [Train] mode the function trains the algorithm to predict either a subsequent trade return or a training target (Objective). In [Test] or [Trade] mode the function calls the algorithm and returns the predicted value. Depending on algorithm, training can generate a binary model or a prediction function in C code that is stored in the Data folder. The only difference of the ..Long and ..Short variants is that they can train on the returns of long and short trades; otherwise they are identical. They can thus alternatively be used for generating two different models per algo/asset component.

Parameters:

Method

SIGNALS - don't train, but only export Signals and Objective in [Train] mode to a Data\*.csv file.
NEURAL - train and predict with a user-supplied or external machine learning algorithm. See below.
DTREE
- train and predict with a decision tree (20 signals max).
PERCEPTRON - train and predict with logistic regression (20 signals max).
PATTERN - train and predict with a signal pattern analyzer (20 signals max).
+FAST - fast pattern finding with large patterns (for PATTERN).
+FUZZY - fuzzy pattern finding for PATTERN; analog output for PERCEPTRON.
+2 .. +6 - number of pattern groups (for PATTERN).
+BALANCED - enforce similar numbers of positive and negative target values by replication (for SIGNALS, NEURAL, DTREE, PERCEPTRON).
+RETURNS - use the subsequent long or short trade for the training target, instead of the Objective parameter. The target is the profit or loss including trading costs of the immediately following trade with the same asset, algo, and matching long/short trade direction.
0 or omitted - use the method and signals of the last advise call. Only when signal parameters and trade returns are used.

Objective The training target. For instance an indicator value at the next bar, or a binary value like +1 / -1 for the sign of the next price change. The value should be in the same range as the signals. If persistent at 0, the next trade result is used, as if the RETURNS flag were set. The Objective parameter is only used in the training run and has no meaning in test or trade mode. Set the PEEK flag for accessing future prices in training.
Signal0,
... Signal19
3..20 parameters that are used as features to the machine learning algorithm for training or prediction (less than 3 signals would be interpreted as a Signals array). Use signals that carry information about the current market situation, for instance candle patterns, price differences, indicators, filters, or statistics functions. All signal values should be in the same range, for instance 0..1, -1..+1, or -100..+100, dependent on machine learning method (see remarks). Signals largely outside that range will generate a warning message. If the signals are omitted, the signals from the last advise call are used.
Signals Alternative input method, a var array of arbitrary length containing the features to the machine learning algorithm. Use this for less than 3 or more than 20 signals.
NumSignals Array length, the number of features in the Signals array.

Returns:

[Train] mode: 100 when trade results are to be trained, i.e. the RETURNS flag is set or Objective is zero. Otherwise 0.
[Test], [Trade] mode: Prediction value returned from the trained algorithm. Can be used as a signal for entering, exiting, or filtering trades. The DTREE, PERCEPTRON, PATTERN algorithms normally return a value in the -100 .. +100 range.
 

DTREE (Decision Tree)

A decision tree is a tree-like graph of decisions by comparing signals with fixed values. The values and the tree structure are generated in the training run. For this the training process iterates through the sets of signals and finds the signal values with the lowest information entropy. These values are then used to split the data space in a profitable and a non profitable part, then the process continues with iterating through the parts. Details about the decision tree algorithm can be found in books about machine learning.

The signals should be normalized roughly to the -100..100 range for best precision. They should be carefully selected so that the displayed prediction accuracy is well above 60% in all WFO cycles. Decision trees work best with signals that are independent of each other. They do not work very well when the prediction depends on a linear combination of the signals. In order to reduce overfitting, the resulting trees are pruned by removing non-predictive signals. The output of the tree is a number between -100 .. +100 dependent on the predictive quality of the current signal combination.

The decision tree functions are stored in C source code in the \Data\*.c file. The functions are automatically included in the strategy script and used by the advise function in test and trade mode. They can also be exported for using them in strategy scripts or expert advisors of other platforms.The example below is a typical Zorro-generated decision tree:

int EURUSD_S(var* sig)
{
if(sig[1] <= 12.938) {
if(sig[3] <= 0.953) return -70;
else {
if(sig[2] <= 43) return 25;
else {
if(sig[3] <= 0.962) return -67;
else return 15;
}
}
}
else {
if(sig[3] <= 0.732) return -71;
else {
if(sig[1] > 30.61) return 27;
else {
if(sig[2] > 46) return 80;
else return -62;
}
}
}
}

The advise() call used 5 signals, of which the first and the last one - sig[0] and sig[4] - had no predictive power, and thus were pruned and do not appear in the tree. Unpredictive signals are displayed in the message window.

Example of a script for generating a decision tree:

void run()
{
  BarPeriod = 60;
  LookBack = 150;
  TradesPerBar = 2;
  if(Train) Hedge = 2;
  set(RULES|TESTNOW);
// generate price series
  vars H = series(priceHigh()), 
    L = series(priceLow()),
    C = series(priceClose());

// generate some signals from H,L,C in the -100..100 range
  var Signals[2]; 
  Signals[0] = (LowPass(H,1000)-LowPass(L,1000))/PIP;
  Signals[1] = 100*FisherN(C,100);

// train and trade the signals 
  Stop = 4*ATR(100); 
  TakeProfit = 4*ATR(100);
  if(adviseLong(DTREE,0,Signals,2) > 0)
    enterLong();
  if(adviseShort(DTREE,0,Signals,2) > 0)
    enterShort();
}

PERCEPTRON

A perceptron, also called a logistic regression function, is a simple neural net consisting of one neuron with one output and up to 20 signal inputs. It calculates its predictions from a linear combination of weighted signals. It has some similarity to the polyfit algorithm, but with arbitrary variables instead of powers of a single variable, and with a binary output. A short preceptron algorithm description can be found on the machine learning overview. The signal weights are generated in the training run for producing the best possible prediction. The signals should be in the single digit range, like -1...1.

The perceptron algorithm works best when the weighted sum of the signals has predictive power. It does not work well when the prediction requires a nonlinear signal combination, i.e. when trade successes and failures are not separated by a straight plane in the signal space. A classical example of a function that a perceptron can not emulate is a logical XOR. Often a perceptron can be used where a decision tree fails, and vice versa.

The perceptron learning algorithm generates prediction functions in C source code in the \Data\*.c file. The functions are automatically included in the strategy script and used by the advise function in test and trade mode. They can also be exported for using them in strategy scripts or expert advisors of other platforms. The output is >0 for a positive or <0 for a negative prediction. The output magnitude is the probability associated with the prediction, in percent, f.i. 70 for 70% estimated probability. A generated perceptron function with 3 signals and binary output looks like this:

int EURUSD_S(var* sig)
{
if(-27.99*sig[0]+1.24*sig[1]-3.54*sig[2] > -21.50)
return 70;
else
return -70;
}

In FUZZY mode the output magnitude is equivalent to the prediction strength, thus allowing to ignore signals below a threshold. The scaling factor, 2.50 in the example below, is calculated so that the average perceptron return value of the training set has a magnitude of 50. Example of a fuzzy perceptron:

int EURUSD_S(var* sig)
{
return (-27.99*sig[0]+1.24*sig[1]-3.54*sig[2]-21.50)*2.50;
}
Signals that do not contain predictive market information get a weight of 0.
 

PATTERN (Pattern Analyzer)

The pattern analyzer is an intelligent version of classic candle pattern indicators. It does not use predefined patterns, but learns them from historical price data. It's normally fed with up to 20 open, close, high or low prices of a number of candles. It compares every signal with every other signal, and uses the comparison results - greater, smaller, or equal - for classifying the pattern.

The signals can be divided into groups with the PATTERN+2 .. PATTERN+6 methods. They divide the signals into up to six pattern groups and only compare signals within the same group. This is useful when, for instance, only the first two candles and the last two candles of a 3-candle pattern should be compared with each other, but not the first candle with the third candle. PATTERN+2 requires an even number of signals, of which the first half belongs to the first and and the second half to the second group. PATTERN+3 likewise requires a number of signals that is divisible by 3, and so on. Pattern groups can share signals - for instance, the open, high, low, and close of the middle candle can appear in the first as well as in the second group - as long as the total number of signals does not exceed 20.

Aside from the grouping, Zorro makes no assumptions of the signals and their relations. Therefore the pattern analyzer can be also used for other signals than candle prices. All signals within a pattern group should have the same unit for being comparable, but different groups can have different units. For candle patterns, usually the high, low, and close of the last 3 bars is used for the signals - the open is not needed as it's normally identical with the close of the previous candle. More signals, such as the moving average of the price, can be added for improving the prediction (but in most cases won't).

For simple patterns with few signals, the pattern analyzer can generate direct pattern finding functions in plain C source code in the \Data\*.c file. These functions are automatically included in the strategy script and used by the advise function in test and trade mode. They can also be exported to other platforms. They find all patterns that occurred 4 or more times in the training data set and had a positive profit expectancy. They return the pattern's information ratio - the ratio of profit mean to standard deviation - multiplied with 100. The better the information ratio, the more predictive is the pattern. A typical pattern finding function with 12 signals looks like this:

int EURUSD_S(float* sig)
{
  if(sig[1]<sig[2] && eqF(sig[2]-sig[4]) && sig[4]<sig[0] && sig[0]<sig[5] && sig[5]<sig[3]
    && sig[10]<sig[11] && sig[11]<sig[7] && sig[7]<sig[8] && sig[8]<sig[9] && sig[9]<sig[6])
      return 19;
  if(sig[4]<sig[1] && sig[1]<sig[2] && sig[2]<sig[5] && sig[5]<sig[3] && sig[3]<sig[0] && sig[7]<sig[8]
    && eqF(sig[8]-sig[10]) && sig[10]<sig[6] && sig[6]<sig[11] && sig[11]<sig[9])
      return 170;
  if(sig[1]<sig[4] && eqF(sig[4]-sig[5]) && sig[5]<sig[2] && sig[2]<sig[3] && sig[3]<sig[0]
    && sig[10]<sig[7] && eqF(sig[7]-sig[8]) && sig[8]<sig[6] && sig[6]<sig[11] && sig[11]<sig[9])
      return 74;
  if(sig[1]<sig[4] && sig[4]<sig[5] && sig[5]<sig[2] && sig[2]<sig[0] && sig[0]<sig[3] && sig[7]<sig[8]
    && eqF(sig[8]-sig[10]) && sig[10]<sig[11] && sig[11]<sig[9] && sig[9]<sig[6])
      return 143;
  if(sig[1]<sig[2] && eqF(sig[2]-sig[4]) && sig[4]<sig[5] && sig[5]<sig[3] && sig[3]<sig[0]
    && sig[10]<sig[7] && sig[7]<sig[8] && sig[8]<sig[6] && sig[6]<sig[11] && sig[11]<sig[9])
      return 168;
  ....
  return 0;
}

The eqF function in the code above checks if two signals are equal. Signals that differ less than the FuzzyRange are considered equal.

There are two additional special methods for the pattern analyzer. The FUZZY method generates a pattern finding function that also finds patterns that can slightly deviate from the profitable patterns in the training data set. It gives patterns a higher score when they 'match better'. The deviation can be set up with FuzzyRange. A typical fuzzy pattern finding function looks like this:

int EURUSD_S(float* sig)
{
  double result = 0.;
  result += belowF(sig[1],sig[4]) * belowF(sig[4],sig[2]) * belowF(sig[2],sig[5]) * belowF(sig[5],sig[3]) * belowF(sig[3],sig[0])
    * equF(sig[10],sig[11]) * belowF(sig[11],sig[7]) * belowF(sig[7],sig[8]) * belowF(sig[8],sig[9]) * belowF(sig[9],sig[6]) * 19;
  result += belowF(sig[4],sig[5]) * belowF(sig[5],sig[1]) * belowF(sig[1],sig[2]) * belowF(sig[2],sig[3]) * belowF(sig[3],sig[0])
    * belowF(sig[10],sig[7]) * belowF(sig[7],sig[11]) * belowF(sig[11],sig[8]) * belowF(sig[8],sig[9]) * belowF(sig[9],sig[6]) * 66;
  result += belowF(sig[4],sig[1]) * belowF(sig[1],sig[2]) * belowF(sig[2],sig[0]) * belowF(sig[0],sig[5]) * belowF(sig[5],sig[3])
    * belowF(sig[10],sig[11]) * belowF(sig[11],sig[7]) * belowF(sig[7],sig[8]) * belowF(sig[8],sig[6]) * belowF(sig[6],sig[9]) * 30;
  result += belowF(sig[1],sig[4]) * belowF(sig[4],sig[2]) * belowF(sig[2],sig[5]) * belowF(sig[5],sig[3]) * belowF(sig[3],sig[0])
    * belowF(sig[7],sig[10]) * belowF(sig[10],sig[11]) * belowF(sig[11],sig[8]) * belowF(sig[8],sig[6]) * belowF(sig[6],sig[9]) * 70;
  result += belowF(sig[4],sig[5]) * belowF(sig[5],sig[1]) * belowF(sig[1],sig[2]) * belowF(sig[2],sig[3]) * belowF(sig[3],sig[0])
    * belowF(sig[7],sig[10]) * belowF(sig[10],sig[8]) * belowF(sig[8],sig[11]) * belowF(sig[11],sig[9]) * belowF(sig[9],sig[6]) * 108;
  ...
  return result;
}

The belowF function is described on the Fuzzy Logic page.

The FAST method does not generate C code; instead it generates a list of patterns that are classified with alphanumeric names. For finding a pattern, it is classified and its name compared with the pattern list. This is about 4 times faster than the pattern finding function in C code, and can also handle bigger and more complex patterns with up to 20 signals. It can make a remarkable difference in backtest time or when additional parameters have to be trained. A pattern name list looks like this (the numbers behind the name are the information ratios):

/* Pattern list for EURUSD_S
HIECBFGAD 61
BEFHCAIGD 152
EHBCIFGAD 73
BEFHCIGDA 69
BHFECAIGD 95
BHIFECGAD 86
HBEIFCADG 67
HEICBFGDA 108 ...*/

The FAST method can not be used in combination with FUZZY or with FuzzyRange. But the FAST as well as the FUZZY method can be combined with pattern groups (f.i. PATTERN+FAST+2).

The find rate of the pattern analyzer can be adjusted with two variables:

PatternCount

The minimum number of occurrences of the found patterns in the analyzed price curve; default = 4.

PatternRate

The minimum win rate of the found patterns, in percent; default = 50.

An example of a pattern trading script can be found in Workshop 7.
 

NEURAL (General Machine Learning)

The NEURAL method uses an external machine learning library, for instance a support vector machine, random forest, or a deep learning neural network for predicting the next price or next trade return in an algo trading system. Many machine learning libraries are available in R packages or Python libraries; therefore the NEURAL method will often call R or Python functions for training and prediction. But any DLL-based machine learning library can be used as long as the neural function - see below - is adapted to it.

 

neural (int Status, ínt Model, int NumSignals, void* Data): var

This function is automatically called several times during the training, test, or trade process. It has access to all global and predefined variables. Its behavior depends on Status:

Status Parameters Called Description
NEURAL_INIT --- After the INITRUN Initialize the machine learning library (f.i. by calling Rstart); return 0 if the initialization failed, otherwise 1. The script is aborted if the system can not be initialized.
NEURAL_EXIT --- After the EXITRUN Release the machine learning library if required.
NEURAL_TRAIN Model, NumSignals, Data After any WFOCycle in train mode

Batch training. Train the model with the given Model index with all data samples collected in the previous training run or WFO training cycle. The Model index is incremented for any asset, algo, and long/short combination. The list of samples is given in CSV format in the Data string. The columns of the CSV table are the signals, the last column is the Objective parameter or the trade result. The accuracy or loss in percent can be optionally returned by the neural function; otherwise return 1 if no accuracy or loss is calculated, or 0 for aborting the script when the training failed.

NEURAL_PREDICT Model, NumSignals, Data By any advise call in test/trade mode Return the value predicted by the model with the given Model index, using the signals contained in the Data double array.
NEURAL_SAVE Data After any WFOCycle in train mode Save all models trained in the previous training run or WFO training cycle to the file with the name given by the string Data.
NEURAL_LOAD Data Before any WFOCycle in test mode, at session begin in trade mode.  Prepare the prediction by loading all trained models from the file with the name given by the string Data. Also called in trade mode whenever the model file was updated by re-training.

Model is the number of the trained model, for instance a set of decision rules, or a set of weights of a neural network, starting with 0. When several models are trained for long and short predictions and for different assets or algos, the index selects the suited model. The number of models is therefore normally equal to the number of advise calls per run cycle. All trained models are saved to a *.ml file at the end of every WFO cycle. In R, the models are normally stored in a list of lists and accessed through their index (f.i. Models[[model+1]]). Any aditional parameter set generated in the training process - for instance, a set of normalization factors, or selection masks for the signals - can be saved as part of the model.

The NumSignals parameter is the number of signals passed to the advise function. It is normally identical to the number of trained features.

The Data parameter provides data to the function. The data can be of different type. For NEURAL_LEARN/NEURAL_PREDICT it's a pointer to a double array of length NumSignals+1, containing the signal values plus the prediction objective or trade result at the end. Note that a plain data array has no "dim names" or other R gimmicks - if they are needed in the R training or predicting function, add them there. For NEURAL_TRAIN the Data parameter is a text string containing all samples in CSV format. The string can be stored in a temporary CSV file and then read by the machine learning algorithm for training the model. For NEURAL_SAVE/NEURAL_LOAD the Data parameter is the suggested file name for saving or loading all trained models of the current WFO cycle in the Data folder. Use the slash(string) function for converting backslashes to slashes when required for R file paths.

The code below is the default neural function in the r.h file for using a R machine learning algorithm. A 64-bit variant for Python can be found in pynet.cpp. If required for special purposes, the default neural function can be replaced by a user-supplied function.

var neural(int Status, int model, int NumSignals, void* Data)
{
if(!wait(0)) return 0;
// open an R script with the same name as the stratefy script
if(Status == NEURAL_INIT) {
if(!Rstart(strf("%s.r",Script),2)) return 0;
Rx("neural.init()");
return 1;
}
// export batch training samples and call the R training function
if(Status == NEURAL_TRAIN) {
string name = strf("Data\\signals%i.csv",Core);
file_write(name,Data,0);
Rx(strf("XY <- read.csv('%s%s',header = F)",slash(ZorroFolder),slash(name)));
Rset("AlgoVar",AlgoVar,8);
if(!Rx(strf("neural.train(%i,XY)",model+1),2))
return 0;
return 1;
}
// predict the target with the R predict function
if(Status == NEURAL_PREDICT) {
Rset("AlgoVar",AlgoVar,8);
Rset("X",(double*)Data,NumSignals);
Rx(strf("Y <- neural.predict(%i,X)",model+1));
return Rd("Y[1]");
}
// save all trained models
if(Status == NEURAL_SAVE) {
print(TO_ANY,"\nStore %s",strrchr(Data,'\\')+1);
return Rx(strf("neural.save('%s')",slash(Data)),2);
}
// load all trained models
if(Status == NEURAL_LOAD) {
printf("\nLoad %s",strrchr(Data,'\\')+1);
return Rx(strf("load('%s')",slash(Data)),2);
}
return 1;
}

The default neural function requires an R script with same name as the strategy script, but extension .r instead of .c. There is also a Python version available. The R or Python script must contain the following functions:

neural.init()  initializes the machine learning package, defines a model list, sets the initial seed, number of cores, GPU/CPU context, etc.

neural.train(Model, XY)  trains the the algorithm and stores the trained model in the model list. XY is a data frame with signals in the first NumSignals columns and the objective in the last (NumSignals+1) column. The samples are the rows of the data frame. Model is the number in the model list where the trained model is to be stored. The model can be any R object combined from the machine learning model and any additional data, f.i. a set of normalization factors. In the Python version, XY is the file name of a .csv file with the data.

neural.save(FileName)  stores all models from the model list to a file with the given name and path. This function is called once per WFO cycle, so any cycle generates a file of all models of this cycle.

neural.load(FileName)  loads back the previously stored model list for the current WFO cycle. If this optional function is missing, the model list is loaded with the R load() function. Otherwise make sure to load the list into the global R environment so that it is accessible to neural.predict, f.i. with load(FileName, envir=.GlobalEnv).

neural.predict(Model, X)  predicts the objective from the signals. X is a vector of size NumSignals, containing the signal values for a single prediction. Model is the number of the model in the model list. The neural.predict return value is returned by the advise function. The neural.predict function could also support batch predictions where X is a data frame with several samples and a vector Y is returned. Batch predictions are not used by Zorro, but useful for calibrating the algorithm with a training/test data set generated in SIGNALS mode.

 An example of such a deep learning system can be found on Financial Hacker. A simple machine learning strategy DeepLearn.c is included, together with R scripts for running it with the Deepnet, H2O, MxNet, or Keras deep learning packages. A .cpp version is available for PyTorch. The neural function is compatible with all Zorro trading, test, and training methods, including walk forward analysis, multi-core parallel training, and multiple assets and algorithms. A short description of installation and usage of popular deep learning packages can be found here.

Remarks:

See also:

optimize, frechet, polyfit, predict, Workshop 7, R Bridge, deep learning

► latest version online F.A.Q.

Support - Frequently Asked Questions

When you're a Zorro Sponsor, you are entitled to a time period of free technical support by email. Otherwise you can post your question on the user forum, or subscribe a support ticket in the web store. For technical support please email your question to support (at) opgroup.de, and give your support ticket number or your user name that is displayed in Zorro's startup message.

General questions about financial trading are answered on the Trading FAQ page. The most frequent technical questions are listed below.

General questions

Q. I am a C programmer and have read the Zorro tutorial. Does it still make sense for me to get the Black Book or take the Algo Bootcamp?.
A. Depends on your experience with strategy development. The Black Book and especially the Algo Bootcamp cover all details involved in writing a trading strategy.

Q. How do I search for a certain topic in the manual that pops up when I click on [Help]?
A. Click on [Search]
.

Q. Can I copy the Zorro folder on a USB drive and run Zorro from there?
A. Yes. Make sure that you have full access rights to that drive.

Q. How can I add another asset to the Asset scrollbox?
A. Open the History\AssetsFix.csv spreadsheet with Excel or with the script editor, and add a new line with the parameters of the new asset. The details are described in the manual under Asset List.
For backtesting that asset you'll also need to download its price history.

Q. Why do I get different results for the trading systems from the tutorial / the Black Book?
A. Because you're testing them probably with different asset parameters. Margin, leverage, and transaction cost change from week to week and from broker to broker.

Q. Why do I get different backtest results with Zorro than with my other trading platform?
A. Read here why.

Q. How can I get historical data for training and backtests?
A. Recent data for the Z systems is available on the Download page. Forex, index, or EOD data can be downloaded with the Download script. M1 data for US stocks and EOD options data can be purchased from info@opgroup.de.

Q. How can I use an MT4 expert advisor with Zorro?
A. Read here how to convert MT4 indicators or "expert advisors" to Zorro scripts.

Q. I have the C source code of an indicator. How can I use it with Zorro?
A. Coding indicators is described in workshop 4a and in Petra's articles on Financial Hacker. The plain C code can normally run unchanged, but you need to remove its framework, such as main() functions, DLL export declarations, or C library headers. You can find an example here. Read here about the differences between standard C code and Zorro's "lite-C" code.

Q. Sometimes I edit a strategy, but the changes I made are not reflected in the test. Sometimes I can't even save my changes, and cannot find the .log file or the .csv trade history in the Log folder. What happened to the changed files?
A. You have probably installed Zorro not in the recommended folder, but in a folder with only limited access (such as Program Files).

Q. How can I compile my script to an executable for avoiding to have its source code on a server?
A. Select Compile to Exe in the [Action] scrollbox. Your script will be compiled to a machine code .x file from which the source code can not be retrieved anymore. You need Zorro S for this.

Q. I have my own set of signals in an Excel file with time stamps. How can I test them with Zorro?
A. Export them from Excel to a .csv file. In your Zorro script, import them in a dataset with the dataParse function. Then use the dataFind function at any bar to find the record matching the current date/time, and retrieve your signals from the record.

Q. I made a small change to my script, but now get totally different results in the backtest. Surely this must be a Zorro bug?
A. You can quickly find out by following the procedure under Troubleshooting. Generate logs from both script versions and compare them. Especially, compare the first two trades from start to end. You'll normally see then immediately the reason of the different results.

Q. My script does not work. I get: "Error 111 - Crash in run()". I don't know what's wrong - how can I fix it?
A. Finding and fixing bugs is also described
under Troubleshooting.

Q. It does not still work. I have a support ticket - please fix my script!
A. Zorro Support can answer technical questions, help with technical issues, and suggest solutions. But we are
not permitted to write, modify, analyze, debug, or fix user scripts. If you need someone to debug and fix your code, we can put you in contact with a programmer.

Q. What's the price for coding a strategy?
A. Depends on the strategy. Development fees start at EUR 170 for a simple indicator-based strategy. Complex options trading strategies or machine learning systems can be in the EUR 1000 or 2000 range. Send a description of your system and you'll get a quote for the development. The simpler the system, the smaller the cost. If you want, download our NDA before.

Strategy development

Q. I'm using global variables in my script. When I run it the second time, it behaves differently than the first time.
A. Static and global variables keep their content until the script is modified or compiled. If necessary, set them to their initial values on script start, like this: if(Init) MyStaticVar = 0;. Read more about local, global, and static variables here.

Q. How can I find out if the last trade was a winner or a loser?
A. If LossStreakTotal is > 0, the last trade was lost.

Q. How do I place two pending orders so that the order that's opened first cancels the other one?
A. Place the first pending order, and check in its TMF if the entry condition of the second order is fulfilled. In that case enter the second order at market, and close the first while it's still pending.

Q. How can I count the number of winning trades within the last N trades?
A. Use a for(all_trades) loop, and count the number of closed trades. As soon as the number is above the total number of closed trades (NumWinTotal+NumLossTotal) minus N, start counting the number of winning trades.

Q. How can I place two different profit targets so that half of the position closes when the first one is hit, and the rest on the second?
A. The best and simplest solution is opening two trades with two different profit targets, each with half the lot volume. If your broker does not allow opening several trades at the same time, use OrderDelay for delaying the second trade a few seconds.

Q. How can I run a function on every price tick of a certain asset?
A. Use a tick function. It will be executed every time when a new price quote arrives. Note that the tick execution frequency will be different in real trading and in the backtest, due to the fewer price ticks in M1 price data. If this is an issue for the particular strategy, use t1 price data.

Q. My candles appear time shifted compared to the same candles in another trading platform.
A. The other platform probably uses a different time zone, and/or timestamps from the candle open prices. Zorro uses UTC time and timestamps from the candle close prices. Read here about bars and time stamps.

Q. How can I skip all bars that fall outside market hours?
A. Bars are normally added whenever price quotes of the traded assets arrive from the broker API, regardless of the market hour. For skipping them, either set the BR_MARKET flag, or use the TimeFrame mechanism with individual AssetMarket time zones. If you only want to skip bars for a certain indicator, you can use a static series and shift it during market hours.

Q. How can I store variables so that a new trading session automatically starts with their their last values?
A. Use AlgoVar or TradeVar variables. They are preserved among sessions. If you have to store a lot more data, save and load them with file functions.

Q. How can I evaluate online data that is available on a website?
A. Use a http function (for accessing data directly from a website) or the dataDownload or assetHistory functions (for retrieving data from a web service). Google, Stooq, AlphaVantage, Quandl, CryptoCompare, and many other services provide historical or current data for most existing assets and indices, so you can use them for live trading as well as in the backtest.

Live trading

Q. How many Zorro S instances can simultaneously trade on a PC or VPS?
A. The number of parallel running programs on a PC is limited by the available memory, the internet bandwidth, and the CPU performance. In the case of Zorro, on an average PC the limit is about 8..10 instances.

Q. I'm getting error messages when I try to connect to IB / Oanda / FXCM / MT4.
A. Read the manual about IB / Oanda / FXCM / MT4 etc for learning how to connect to your broker and which assets are available. The 3 most frequent reasons for connection failure are wrong login data (user / password), a wrong account type (demo / real) or a server outage (at weekends or holidays). The reasons of the errors are printed in the Zorro log or in the case of MT4 or MT5, in their "Experts Log".

Q. I want to trade with the MT4 bridge, but my MT4 has no Zorro EA.
A. You must install the MT4 bridge as described here.
Please also read the remarks that cover all known issues of MT4 or MT5.

Q. Ok, I have now installed the MT4 bridge, but it doesn't work. I'm getting error messages.
A. You made a mistake with the installation. Just try again. You will succeed when you follow the instructions in the manual – we guarantee that. You can repeat the MT4/MT5 bridge installation as often as you want.
  Since installing the bridge involves an unzip and copy operation on your PC, some basic PC knowledge is required.
If you cannot do it, ask someone for assistance. Alternatively, oP group offers a Zorro/MT4 installation service by remote access. This, also, has 100% success guarantee.

Q. I've set the stop loss in my script, but it does not appear in my broker's trading platform.
A. Zorro won't tell your broker about your stop loss unless you set it up to do so. Read under StopFactor how a stop loss differs in Zorro and in your broker's platform.
 

Q. My system does not close trades in live trading. It worked in the backtest.
A. In the US, NFA compliant brokers do not allow you to close Forex trades (no joke!); instead you have to open a position in opposite direction. Zorro handles this automatically in NFA compliant mode.

Q. My live system does often not open or close trades - - and I'm sure that I don't live in the US.
A. There can be many reasons for rejecting orders, but unless you're trading outside market hours or with a very illiquid asset, it won't happen often. You can see the reason of the rejection in the error message from your broker (such as: "No tradeable price"). If an open order failed, enterLong / enterShort return 0.

Q. Where is the live chart? How can I visualize the price curve and the trade entries and exits in real time?
A. For live charts use your broker's trading platform. Zorro is purely for automated trading and does not display live charts, aside from the profit curve on the trade status page.

Q. The candles on the Zorro chart are different to the candles that I see on the MT4 chart, and the prices in the Zorro log are also different to the open/close prices in my platform log. 
A. Zorro displays generally ask prices in logs and on charts, while some platforms, f.i. MT4, display bid prices on charts by default. And your live chart is usually based on a different time zone and thus will display different candles. Check the help file of your platform about switching it to ask prices and UTC time zone.

Q. How can I change the sounds for entering, winning, or losing trades?
A. In your Zorro folder you'll find sound files with names like "win.wav", "loss.wav", or "open.wav". Replace them with your own sounds in .wav format. Use Mute in Zorro.ini for permanently disabling sounds.

Q. I run the same strategy with the same broker on two different VPS. Both versions open different trades although they should be identical.
A. With the same broker and account you'll normally get rather similar trades. But they are not necessarily identical, especially not with Forex or CFDs since any trade entry and exit is based on a individual price quote. Differences in latency can also contribute to different trade entries and exits.

Q. I'm a Zorro S owner and use two Zorros for trading two strategies on the same real money account. In the moment when the second Zorro logs in to the account, the first one logs off and the server indicator gets red.
A. Your account is limited to a single session by your broker. Contact your broker and ask for activation of multiple sessions for your account.

Q. Sometimes my system opens trades that do not appear in the broker platform.
A. Those are phantom trades, used for equity curve trading or for virtual hedging. You can distinguish them in the log from real trades by their winged brackets {..} as opposed to the rectangular brackets [..] of real trades.

 

► latest version online algo

algo (string name): int

Sets the algorithm identifier for identifying trades. Using algorithm identifiers is recommended in portfolio strategies that contain different trade algorithms; they are used to create separate strategy parameters, rules, and capital allocation factors per algorithm.

Parameters:

name The algorithm identifier (max. 15 characters, no spaces). If name ends with ":L", the algo is for long trades only; if it ends with ":S" the algo is for short trades only.

Returns:

0 when the name string is NULL or empty, otherwise nonzero.

Usage:

algo("TREND:L"); sets up the identifier "TREND:L" for the current algorithm for long trades. Short trades are suppressed.

Remarks:

Example:

algo("TREND:L"); // algorithm identifier for a trend trading algorithm with long trades
...
if(short_condition) 
  enterShort(ifelse(strstr(Algo,":L"),0,Lots)); // suppress short trades when Algo ends with ":L"

See also:

enterLong, enterShort, loop, string, asset, Algo, AlgoVar, TradeAlgo

► latest version online AlgoVar, AssetVar

AlgoVar[0] .. AlgoVar[7], AlgoVar2[0] .. AlgoVar2[7]

16 general purpose variables for storing values specific to the current asset/algo combination. Every strategy component has its own set of AlgoVar variables; the sets are automatically switched with any algo or asset call. The variables are stored in STATUS structs and can be used in portfolio strategies for values that are common to the algorithm, but different for every component of the strategy. They can also be used to pass asset/algorithm specific parameters to a TMF, or for storing parameters when a system is stopped and restarted.

AlgoPtr[0] .. AlgoPtr[7], AlgoPtr2[0] .. AlgoPtr2[7]

15 general purpose void* pointers, for storing algo specific series, arrays, or other pointers. Shared with AlgoVar. Supported in 32 and 64 bit modes, but with different pointer size, so keep them at the begin of the array starting with [0]. Requires a typecast in .cpp code.

Types:

var, void'
 

AssetVar[0] .. AssetVar[15]

AssetStr[0] .. AssetStr[15]

16 general purpose locations for storing either numeric values, or strings specific to the current asset. Every asset has its own set of AssetVar/AssetStr variables; the sets are automatically switched with any asset call. The locations are shared, i.e. either AssetVar[N] or AssetStr[N] can be used, but not both with the same index N. The first 8 variables or strings can be read at start from the asset list and stored in the ASSET structs. They can be used in portfolio strategies for parameters that are common to the algorithms, but different for every asset. AssetStr can only be modified with strcpy and has a maximum length of 7 characters. Use double quotes (like "Text") for AssetStr strings in the asset list for distinguishing them from numbers.

AssetInt[0] .. AssetInt[31]

AssetFloat[0] .. AssetFloat[31]

32 general purpose int or float variables for internally storing integers or floats specific to the current asset. They are script-only and not read from the asset list. Two subsequent AssetInt or AssetFloat are shared with AssetVar/AssetStr at half the index, i.e. AssetVar[10] shares its location with AssetInt[20] and AssetInt[21]. AssetInt can also be used to store asset-specific series or pointers in 32-bit mode.  

AssetPtr[0] .. AssetPtr[15]

15 general purpose void* pointers, for storing asset specific series, arrays, or other pointers. Shared with AssetVar. Supported in 32 and 64 bit modes, but with different pointer size, so keep them at the begin of the array starting with [0]. Requires a typecast in .cpp code.

Types:

var, char*, int, float, void'
 

AssetO, AssetH, AssetL, AssetC, AssetP

Arrays of size NumBars in ascending order (no series), containing the open, high, low, close, and mean prices of the current asset for direct access. AssetC[Bar] is the current close price. In portfolio strategies with assets of different history lengths, not all elements of the arrays are filled. Unfilled elements are zero.

Type:

var*
   

Remarks:

Examples:

// in the run function
...
algo("XYZ");
AlgoVar[0] = 123;
...

// in the TMF
...
algo("XYZ");
var MySpecificValue = AlgoVar[0];
...

See also:

algo, asset, TradeVar, SAV_ALGOVARS

 

► latest version online Alpaca

Alpaca Plugin

Alpaca is a commision-free brokerage for algorithmic trading. The Alpaca API allows a trading algo to access real-time price, place orders and manage portofolios through a REST or a streaming interface.

The Alpaca plugin was developed by Kun Zhao and uses V2 websocket for live data, and Alpaca V1 or alternatively Polygon.io data for historical data. It can be downloaded from the author's GithHub page, Place the Alpaca.dll file into the Plugin folder under Zorro's root path, and enter the Alpaca specific settings to Zorro.ini or ZorroFix.ini as described on Github. Generate an API key for your account on the Alpaca website.

Zorro login fields for Alpaca:

User: Alpaca API key
Password: Alpaca Secret

Remarks

Supported broker commands

The plugin supports the brokerCommand function with the following commands:

 

See also:

Links, order, brokers, broker plugin, brokerCommand

► latest version online

PI

PI

This constant has the value 3.14159265.

Range:

3.14159265

Example:

var area_circle(var radius)
{
  return(radius * radius * PI); // pi * r²
}

See also:

return, var
Pointers

Pointers

Pointers store references to variables. They point to the location in memory where the variable stores it's content. Pointers allow, for instance, the same function to do the same with different global variables, depending on what variable is currently referenced through a certain pointer. Pointers can be used like synonyms or alternative names for single variables or for arrays of variables.

A pointer is defined by adding a '*' to the name, like this:

var *mypointer; // defines a pointer of type var with the name mypointer

The '*' is also used for multiplication, but the compiler knows from the context if a pointer or a multiplication is meant.

You can get a pointer to any variable by adding a '&' to the variable name:

var myvar = 77;
mypointer = &myvar; // now mypointer points to myvar

You can see the '&' as the opposite of '*'. For accessing a variable that is the target of the pointer, add a '*' to the pointer name, just as in the pointer definition. This way the variable can be directly read or set:

*mypointer = 66; // now myvar contains 66

Pointers can also point to variable arrays, and can access their elements just by adding the usual [0], [1], ... etc. to the pointer name. In fact pointers and arrays are the same internal type. When mypointer is a pointer to an array, mypointer+n is a pointer to the n-th element of that array. Therefore for accessing elements of the array, *mypointer points to the same as element as mypointer[0] and *(mypointer+n) points to the same element as mypointer[n].

Variable pointers in functions

There can be some situation where variable pointers might be useful. Normally if you pass a variable to a function, the function works merely with a copy of that variable. Changing the variable within the function only affects the copy. However if you pass the pointer to a variable, the function can change the original variable. For getting a pointer to a variable, just place a '&' before the variable name. Example:
function change_variable(var myvar)
{
  myvar += 1;
}


function change_variable_p(var *myvar)
{
  *myvar += 1;
}
...
var x = 10;
change_variable(x);   // now x is still 10
change_variable_p(&x); // now x is 11

Lite-C automatically detects if a function expects a variable or a pointer to a variable, so you can usually omit the '&' and just write:

change_variable_p(x); // now x is 1

Arrays of pointers or series

When accessing elements in an array of pointers or series, parentheses must be used:
vars MySeriesArray[3]; // declars an array of series 
...
for(i=0; i<3; i++) MySeriesArray[i] = series();
...
(MySeriesArray[0])[0] = 123; // access the first element of the first array. Mind the parentheses!

Function pointers

A function pointer is defined just as a function prototype with return and parameter types. Example:
float myfunction(int a, float b); // define a function pointer named "myfunction"  

float fTest(int a, float b) { return (a*b); }
...
myfunction = fTest;
x = myfunction(y,z);
For storing arrays of function pointers in C, void* arrays can be used. Example:
float myfunction(int a, float b); // define a function pointer

void* function_array[100];        // define a pointer array

float fTest(int a, float b) { return (a*b); }
...
function_array[n] = fTest;
myfunction = function_array[n];
x = myfunction(y,z);

See also:

Variables, Structs, Functions

► latest version online

asset, assetType

asset (string Name) : int

Selects an asset from the asset list, and loads its price history in the initial run from the broker or historical data. Price and trade functions, and all asset related variables (Spread, Symbol, AssetVar etc.) are automatically switched to the new asset. Sets AssetPrev to the previous asset name. Must be called in the first run (INITRUN) for any asset used of the script.

Parameters:

Name The name of the asset, as in the asset list or the [Asset] selector. An empty string "" or a name beginning with '#' creates a dummy asset with flat price history. Up to 15 characters, uppercase, with no blanks and no special characters except for slash '/' and underline '_'.

Returns:

0 when the Name string is NULL or empty, or when the asset or its prices are not available; otherwise nonzero.

Usage:

asset("EUR/USD"); selects the EUR/USD pair.
 

assetAdd (string Name)

assetAdd (string Name, string Symbol)

assetAdd (string Name, var Price, var Spread, var RollLong, var RollShort, var PipVal, var PipCost, var MarginCost, var Leverage, var LotAmount, var Commission, string Symbol)

Selects an asset and optionally updates its parameters in the INITRUN. If the asset was not yet in the asset list, it is added and also appears in the [Asset] scrollbox. Unlike asset(), the asset history is not yet loaded and the asset is not yet subscribed. For creating a dummy asset for test purposes, let Name begin with a '#' hash - this will generate artificial bars with a flat price history. Selecting an asset before loading its price history can be useful when asset specific parameters like Centage affect the subsequent history download or its Symbol, price source, or assigned account has to be set up by script.

Parameters:

Name Name of the asset. A name beginning with '#' creates a dummy asset that will also appear in the scrollbox.
Symbol Symbol of the asset, with optional source, in the format described under asset list.
Price, ... Optional asset parameters as described under asset list. When at 0, the parameter is not changed.

Usage:

assetAdd("AAPL",150,0.01,0,0,0.01,0.01,0,2,1,0.02,"STOOQ:AAPL.US");
 

assetList (string Filename, string Select): int

Loads an alternative asset list, adds its assets to the [Asset] scrollbox, and selects an asset from the new list. Any asset used in the script must be either in that list, or added by script with assetAdd. The asset list must be loaded in the first run (INITRUN) of the script.before its assets can be selected. If this function is not called, the default list of the currently selected account is used; if no such list exists, it's the AssetsFix.csv list with some Forex pairs and CFDs. If Select is omitted or 0, a default asset - usually the first asset in the list - is selected.

Parameters:

FileName File name of the asset list, f.i. "AssetsIB". The .csv extension and the path can be omitted for asset lists in the History folder.
Select Name of the asset to be selected in the scrollbox at first run, f.i. "EUR/USD". 0 for selecting a default asset.

Returns:

Number of loaded assets, or 0 when no assets were loaded. The number of assets is also available through NumAssetsListed.

Usage:

assetList("Strategy\\MyNewAssets.csv",0); 
 

assetSelect ()

Sets the [Asset] scrollbox to the current asset.
 

assetType (string Name) : int

Attempts to determine the type of an asset from its name. If the name begins and ends with the 3-letter abbreviation of a currency, it is identified as Forex; if it ends with a number, it is identified as an index.

Parameters:

Name Name of the asset

Returns:

0 when the type can not be identified; otherwise FOREX (1) or INDEX (2).
 

Remarks:

Examples:

// trade multiple strategies and assets in a single script
function run()
{
  BarPeriod = 240;
  StartDate = 2010;
  set(TICKS); // set relevant variables and flags before calling asset()
  
// call different strategy functions with different assets
  asset("EUR/USD");
  tradeLowpass();
  tradeFisher();
  
  asset("GBP/USD");
  tradeAverage();
  
  asset("SPX500");
  tradeBollinger();  
}
// set all asset symbols to a new source
if(Init) {
  assetList("MyAssetList.csv");
  for(listed_assets)
    assetAdd(Asset,strf("MyNewSource:%s",SymbolLive));
}
// Basket trading - generate a snythetic asset "USD" 
// combined from the USD value of EUR, GBP, and AUD
var priceUSD()
{
  var p = 0;
  asset("GBP/USD"); p += price();
  asset("AUD/USD"); p += price();
  asset("EUR/USD"); p += price();
  return p;
}

// basket trade function with stop limit
int tradeUSD(var StopUSD)
{
  if((TradeIsLong && priceUSD() <= StopUSD) 
    or (TradeIsShort && priceUSD() >= StopUSD)) 
      return 1;   // exit the trade
  else return 0;  // continue the trade
}

// open a trade with the synthetic asset and a stop loss  
void enterLongUSD(var StopDistUSD)
{
  var StopUSD = priceUSD()-StopDistUSD;
  asset("GBP/USD"); enterLong(tradeUSD,StopUSD);
  asset("AUD/USD"); enterLong(tradeUSD,StopUSD);
  asset("EUR/USD"); enterLong(tradeUSD,StopUSD);
}

void enterShortUSD(var StopDistUSD)
{
  var StopUSD = priceUSD()+StopDistUSD;
  asset("GBP/USD"); enterShort(tradeUSD,StopUSD);
  asset("AUD/USD"); enterShort(tradeUSD,StopUSD);
  asset("EUR/USD"); enterShort(tradeUSD,StopUSD);
}
 
// plot a price curve of the synthetic asset
// (the plot command is linked to the last used asset -
// so "EUR/USD" must be selected in the scrollbox)
function run() 
{
  set(PLOTNOW);
  plot("USD",priceUSD(),0,RED);
}

See also:

enterLong/Short, loop, algo, Asset, AssetZone, AssetVar, Detrend, assetHistory, price

► latest version online AssetMode

AssetMode

Asset specific flags that affect the currently selected asset. The following flags can be set or reset with the setf and resf macros:

Flags:

NOPRICE Prevents price updates in live trading while set. Can reduce bandwidth when an asset is temporarily not used or its price is not relevant.
SOFTSWAP Causes rollover to be continuously accumulated, rather than once per day.

Remarks:

Example:

setf(AssetMode,NOPRICE);  // disable asset updates

See also:

asset, setf, resf
► latest version online BarZone,AssetMarket,AssetFrame

Time zone handling

Zorro's standard time zone is UTC (Greenwich mean time), which is also the time zone of historical data, logs, and charts. Time and date functions and variables are normally also based on UTC time. Alternatively, local time zones can be used either on the bar or on the asset level. For handling or converting time zones, the following variables are available:

BarZone

Time zone of bars (default: UTC). Affects bar generation: daily bars will start and end at BarZone midnight plus BarOffset. For this, set BarZone before the first asset call. BarZone is also used by several date/time functions that use local time. For using specific asset-dependent time zones with intraday bars, see AssetFrameZone and AssetMarketZone

HistoryZone

Time zone of the historical data files, for converting them to UTC. This variable is rarely needed since timestamps in historical data files should be already in UTC. If not, set HistoryZone to the time zone in the data. The time stamps in historical data files and dataset files are then automatically converted to UTC on dataLoad or dataParse.

BrokerZone

Time zone of the broker plugin. This variable is rarely needed since broker plugins normally return timestamps in UTC. Otherwise the broker plugin is supposed to set BrokerZone with the GET_BROKERZONE command. The broker API time stamps are then converted to UTC on import.

LogZone

Time zone of the log and chart. If not set, all dates and times in the log and chart are in UTC.
  

AssetMarketZone

Time zone of the current asset; initally read from the Market field of the asset list, otherwise copied from BarZone or set by script.

AssetFrameZone

Time zone of the current asset for daily trading; used to set AssetFrame to a daily TimeFrame that begins at FrameOffset in local time. 

Type:

int, UTC for UTC time (default), EST for New York, WET for London, CET for Frankfurt, AEST for Sydney, JST for Tokyo, or any number from -23..+23 that gives the time zone offset in hours to UTC. Daylight saving time is used, except for UTC and for time zones at or beyond JST.
 

AssetFrame

Asset specific time frame, automatically set by AssetFrameZone. 0 when the current asset had no price quotes in the current bar or when its market is closed; negative number of skipped bars when the market opens; 1 otherwise. Normally used to set TimeFrame = AssetFrame for skipping bars outside market hours, or for trading on different time zones (see example). 

Type:

int, read/only
 

Remarks:

Examples:

// trade daily at 15:30 New York time
BarPeriod = 1440;
BarOffset = 15*60+30;
BarZone = EST;
...

// trade two assets on different time zones
BarPeriod = 60;
FrameOffset = 10; // trade both assets at 10:00 am local time
while(asset(loop("EUR/USD","USD/JPY")))
{
  if(strstr(Asset,"EUR"))
    AssetFrameZone = WET;
  else if(strstr(Asset,"JPY"))
    AssetFrameZone = JST;
  TimeFrame = AssetFrame; // use a daily time frame changing at 10:00 local time
  ...
}

See also:

TimeFrame, StartMarket, BarMode, Time/Date functions,. asset

 

► latest version online Trigonometry

Basic Trigonometry

There are six basic trigonometric functions. Consider a right-angled triangle, one of whose other angles is x as in the figure below:

The basic functions are defined in terms of the length of the Hypotenuse (the longest side), the side Opposite the angle x, and the side Adjacent to angle x.

The sine of x,  written as sin(x) is defined as:

sin (x) = Opposite
Hypotenuse

The cosine of x, written as cos(x) is defined as:

cos (x) = Adjacent
Hypotenuse

  The tangent of x, written as tan(x) is defined as:

tan (x) = Opposite
Adjacent

Note that tan(x) is undefined where x = 0.

Using these functions, if an angle and the length of one side of a right-angled triangle are known, the length of the other two sides can easily be calculated.

For angles between 90 and 180 degrees:
    sin(x) == sin(Π - x)
    cos(x) == -cos(Π - x)
    tan(x) == -tan(Π - x)
 
For angles between 180 and 270 degrees:
    sin(x) == - sin(Π + x)
    cos(x) == cos(Π + x)
    tan(x) == - tan(Π + x)
For angles between 270 and 360 degrees:
    sin(x) == -sin(2Π-x)
    cos(x) == -cos(2Π-x)
    tan(x) == tan(2Π-x)

The other three functions are the secant, cosecant, and cotangent - these are the reciprocal of the sine, cosine and tangent respectively.  This is important, because the notation sin-1(x)  is used to indicate the inverse sine function, or arc sine - that is, the angle whose sine is x.  Similar notations apply to the arc cosine and arc tangent.  Quick examination of the table above shows that the arc functions can deliver multiple answers for any given value - physical examination of the problem is usually required in order to determine which range the angle falls into.

Some useful functions:

tan(x) = sin(x) / cos(x)
sin2(x) + cos2(x) = 1
sin(x+y) = sin(x) cos(y) + cos(x) sin(y)

sin(x-y) = sin(x) cos(y) - cos(x) sin(y)

cos(x+y) = cos(x) cos(y) - sin(x) sin(y)

cos(x-y) = cos(x) cos(y) + sin(x) sin(y)

tan(x+y) = (tan(x) + tan(y)) / (1 - tan(x) tan(y))

tan(x-y) = (tan(x) - tan(y)) / (1 + tan(x) tan(y))

By substituting x = y in the above formulae, we obtain:

sin(2x) = 2 sin(x) cos(x)

cos(2x) = cos2(x) - sin2(x)

tan(2x) = 2 tan(x) / (1 - tan2(x))

 

asinv

asin(var x): var

acos(var x): var

atan(var x): var

atan2(var a, var b): var

Arc functions - the opposite of the sin, cos, tan function, return an angle for a given value. atan2 is used for higher precision when a is a sine and b a cosine.

Parameters:

x - any var.
a, b - any var; the tangent value is a/b.

Returns:

Angle in radians.

Example:

x = asin(1); // x is pi/2
x = asin(0.707); // x is pi/4

See also:

Trigonometry, sin, cos, tan

► latest version online

exp

exp(var x): var

e power x.

Parameters:

x - any var.

Returns:

ex

Example:

x = exp(1); // x is now 2.718 
x = exp(0); // x is now 1 

See also:

log, pow

► latest version online

log

log(var x): var

Logarithm of x with base e.

Parameters:

x - any var.

Returns:

ln(x)

Remarks:

Examples:

x = log(1); // x == 0 
x = log(2.718); // x == 1 (2.718 is an approximation to the constant e).
x = log(y)/log(2); // x == log2(y), i.e. log(y) base 2.

See also:

exp, pow, sqrt ► latest version online pow

pow(var x, var y): var

Computes x raised to the power y. An error occurs if x is zero and y is less than or equal to 0, or if x is negative and y has a fractional part. Note that x*x is the same, but is computed faster than pow(x,2) .

Parameters:

x - any var.
y - any var.

Returns:

xy

Example:

x = pow(100,1); // x is now 100 
x = pow(2,8); // x is now 256 
x = pow(1.414,2); // x is now 1.999

See also:

exp, log, sqrt ► latest version online Expressions, operators

Expressions and operators

An expression is an arithmetical operation that delivers a result, which can then be assigned to a variable or object parameter. The arithmetic expression may be composed of any numbers, further variables or object parameters, function calls, brackets, and arithmetic operators.

The following operators are available in expressions:
= Assigns the result right of the '=' to the variable left of the '='.
+-*/ The usual mathematical operators. * and / have a higher priority than + and -.
% Modulo operator, the remainder of an integer division (see also fmod).
| Bitwise OR, can be used to set certains bits in an integer variable.
^ Bitwise exclusive OR, can be used to toggle certain bits in an integer variable.
~ Bitwise invert, toggles all bits of an integer variable.
& Bitwise AND, can be used to reset certains bits in an integer variable.
>> Bitwise right shift, can be used to divide a positive integer value by 2.
<< Bitwise left shift, can be used to multiply a positive integer value by 2.
() Brackets, for defining the priority of mathematical operations. Always use brackets when priority matters!

Examples:

x = (a + 1) * b / c;
z = 10;
x = x >> 2; // divides x by 4 (integer only)
x = x << 3; // multiplies x by 8  (integer only)
x = fraction(x) << 10; // copies the fractional part of x (10 bits) into the integer part

Assignment operators

The "="-character can be combined with the basic operators:

+= Adds the result right of the operator to the variable left of the operator.
-= Subtracts the result right of the operator from the variable left of the operator.
*= Multiplies the variable left of the operator by the result right of the operator.
/= Divides the variable left of the operator by the result right of the operator.
%= Sets the variable left of the operator to the remainder of the integer division by the result right of the operator.
|= Bitwise OR of the result right of the operator with the variable left of the operator.
&= Bitwise AND of the result right of the operator with the variable left of the operator.
^= Bitwise exclusive OR of the result right of the operator and the variable left of the operator.
>>= Bitwise right shift the variable left of the operator by the result right of the operator.
<<= Bitwise left shift the variable left of the operator by the result right of the operator.

Increment and decrement operators

By placing a '++' at the end of a variable, 1 is added; by placing a '--', 1 is subtracted. This is a convenient shortcut for counting a variable up or down.

Examples:

x = x + 1; // add 1 to x
z += 1; // add 1 to x
x++; // add 1 to x (integer only)

Remarks:

See also:

Functions, Variables, Pointers, Comparisons

 

► latest version online sin, cos, tan

sin(var x): var

cos(var x): var

tan(var x): var

Trigonometry functions.

Parameters:

x - angle in radians.

Returns:

Sine, cosine, tangent of angle x

Example:

x = sin(pi/2); // x is 1

See also:

Trigonometry, asin, acos, atan

► latest version online

sin, cos, tan

sinh(var x): var

cosh(var x): var

tanh(var x): var

Hyperbolic functions.

Parameters:

x - input variable.

Returns:

Hyperbolic sine, cosine, tangent of x

Example:

y = tanh(x); 

See also:

asin, acos, atan, sin, cos, tan

► latest version online

sqrt

sqrt(var x): var

Square root of x.

Parameters:

x - any var.

Returns:

Square root of x.

Example:

x = sqrt(2); // x is now 1.414
x = sqrt(64); // x is now 8 
x = sqrt(1); // x is now 1 

See also:

exp, log, pow

► latest version online

Balance, Equity, TradeVal, MarginSum

Account Parameters

Balance

The current amount in the broker account. In live trading, this parameter is updated from the broker API if available; otherwise it is calculated from Capital + Wintotal - LossTotal.

Equity

The current value of the account including all open trades. In live trading, this parameter is updated from the broker API if available; otherwise it is calculated from Balance + WinValTotal - LossValTotal.

TradeVal

The current value of all open trades by the strategy. This is the profit or loss if all trades would be closed immediately. The value of all open trades of the account is Equity - Balance.

RiskTotal

The estimated maximum risk of all open trades by the current script. The risk is estimated as trade costs plus loss at either the initial stop loss distance, or at an 1% adverse price move when no stop is used. Due to trailing and exit mechanisms, the real risk is normally smaller than the estimated risk, but can also be higher in cases of large price moves or large exit slippage.

MarginTotal

The current allocated margin of all open trades of the strategy, calculated through MarginCost under assumption of a constant margin requirement of open trades. This can be different in live trading when brokers adapt the margin requirement to the market situation or asset price. A margin call is simulated in test and training mode when Capital > 0 and Equity - MarginTotal reaches zero.

MarginVal

The current allocated margin of the account. The account is threatened by a margin call when Equity - MarginVal approaches zero. In live trading, this parameter is updated from the broker API if available; in test and training it follows MarginTotal.

Range:

Account currency

Type:

var, read/only

Remarks:

Example:

// stop trading when there's not enough money in the acount
if(IsTrading && Equity - MarginVal < 1000)) 
Lots = 0;
else
Lots = 1;

See also:

enterLong/Short, Win / Loss, Capital

 

► latest version online bar

User-defined bars

Instead of fixed bar periods, bars can also cover fixed price ranges or can be defined by other criteria. Some traders believe that those special bars help their trading, because they make the chart look simpler. Zorro supports a user-defined bar function for special bars of any kind. Predefined functions for Range, Renko, Haiken Ashi, or Point-and-Figure bars are included. The bar function can be used for single-asset or multiple-asset strategies, or for strategies that use multiple bar types simultaneously. In the case of single-asset strategies the individual bars are also plotted in the chart.


Range bars (see code)


 

bar(vars Open, vars High, vars Low, vars Close, vars Price, DATE Start, DATE Time): int

User-supplied function for defining a bar. Used to create special bar types or to replace the mean price by a different algorithm. The bar function is automatically called whenever a new price quote arrives or a price tick is read from a historical file. It can modify the candle and determine when the bar ends.

Parameters:

Open, High,
Low, Close,
Price

Series with two elements of the corresponding price; f.i. Close[1] is the close price of the previous bar, and Close[0] is the most recent price. The prices of the current bar can be modified by the bar function. Price is the mean price and can be omitted when not used in the function.

Start Optional start time of the bar in the OLE DATE format. Can be omitted when not used in the function.
Time Optional time of the most recent tick or price quote. Can be omitted when not used in the function.

Returns:

0 - close the bar at the normal end of the BarPeriod.
- close the bar now, and call the bar function again at the next price tick.
4 - keep the bar open, and call the bar function again at the next price tick.
8 - call the bar function only at the end of every bar.

Remarks:

Examples (see also SpecialBars.c):

var BarRange = 0.0030; // 0.3 cents bar range

// Range Bars, standard (high-low)
int bar(vars Open,vars High,vars Low,vars Close)
{
  if(Open[0] != Close[1]) {
    High[0] = max(Open[0],Close[1]);
    Low[0] = min(Open[0],Close[1]);
    Open[0] = Close[1];
  }
  if(High[0]-Low[0] >= BarRange)
    return 1;
  return 4;
}

// Renko Bars, standard
int bar(vars Open,vars High,vars Low,vars Close)
{
  Open[0] = roundto(Close[1],BarRange);
  if(Close[0]-Open[0] >= BarRange) {
    Close[0] = Open[0]+BarRange;
    High[0] = Close[0];
    Low[0] = Open[0];
    return 1;
  }
  if(Open[0]-Close[0] >= BarRange) {
    Close[0] = Open[0]-BarRange;
    High[0] = Open[0];
    Low[0] = Close[0];
    return 1;
  }
  return 4;
}

// Renko Bars with wicks
int bar(vars Open,vars High,vars Low,vars Close)
{
  Open[0] = roundto(Close[1],BarRange);
  if(Close[0]-Open[0] >= BarRange) { 
    Close[0] = Open[0]+BarRange;
    return 1;
  }
  if(Close[0]-Open[0] <= -BarRange) { 
    Close[0] = Open[0]-BarRange;
    return 1;
  }
  return 4;
}

// Renko Bars, variant
int bar(vars Open, vars High, vars Low, vars Close)
{
  var OpenDiff = abs(Close[0]-Open[1]);
  var CloseDiff = abs(Close[0]-Close[1]);
  if(OpenDiff < CloseDiff) // we have a valley or peak
     Open[0] = Open[1];
  else  // we are moving with the trend
     Open[0] = roundto(Close[1],BarRange);
  if(Close[0]-Open[0] >= BarRange) {  // going up
    Close[0] = Open[0]+BarRange;
    High[0] = Close[0];
    Low[0] = Open[0];
    return 1;
  }
  if(Open[0]-Close[0] >= BarRange) { // going down
    Close[0] = Open[0]-BarRange;
    High[0] = Open[0];
    Low[0] = Close[0];
    return 1;
  }
  return 4;
}

// Mean Renko Bars
int bar(vars Open, vars High, vars Low, vars Close)
{
Open[0] = 0.5*(Close[1]+Open[1]);
if(Close[0] <= Open[0] - BarRange) {
Close[0] = Open[0] - BarRange;
return 1;
} else if(Close[0] >= Open[0] + BarRange) {
Close[0] = Open[0] + BarRange;
return 1;
}
return 4;
}
// Haiken Ashi Bars int bar(vars Open,vars High,vars Low,vars Close) { Close[0] = (Open[0]+High[0]+Low[0]+Close[0])/4; Open[0] = (Open[1]+Close[1])/2; High[0] = max(High[0],max(Open[0],Close[0])); Low[0] = min(Low[0],min(Open[0],Close[0])); return 8; } // Point-and-Figure Bars int bar(vars Open,vars High,vars Low,vars Close) { static int direction = 0; if(direction == 1 && High[0]-Close[0] >= BarRange) { Open[0] = round(Low[0],BarRange); Close[0] = round(High[0],BarRange); Low[0] = Open[0]; High[0] = Close[0]; direction = 0; return 1; } if(direction == 0 && Close[0]-Low[0] >= BarRange) { Open[0] = round(High[0],BarRange); Close[0] = round(Low[0],BarRange); High[0] = Open[0]; Low[0] = Close[0]; direction = 1; return 1; } return 4; }

See also:

Bars and Candles, Bar, BarPeriod, tick, TMF, user supplied functions

► latest version online BarMode

BarMode

Determines bar creation and trading behavior when the market is inactive, as on weekends, outside market hours, and on business holidays. By default, any bar contains at least one valid price quote. When no price quotes arrive due to market inactivity, or when it's weekend or a holiday, no bars are created. This behavior can be changed with the following BarMode flags. Use | or + for combining flags and setf and resf for setting and resetting single flags.

Flags:

BR_FLAT Generate bars even when they contain no price quote. These bars get flat candles with the close price of the last quote. For special purposes only; price and indicators series should normally skip flat periods. This flag affects bar creation and must be set before the first asset call.
BR_WEEKEND Don't generate bars that end on holidays or at or after EndWeek and before StartWeek in the bar time zone (BarZone), even when they contain price quotes. This flag is set by default. It is for intraday bars, but also affects EOD bars. It affects bar creation and must be set or reset before the first asset call.
BR_MARKET Don't generate bars that end at or after EndMarket and before StartMarket in the bar time zone (BarZone), even when they contain price quotes. This flag is for intraday bars only. It affects bar creation and must be set before the first asset call.
BR_NOSHIFT Don't shift data series on intraday bars that end at or after EndMarket and before StartMarket in the bar time zone (BarZone). This prevents series-based indicators from updating or generating trade signals outside market hours. Start and end time and zone can be individually set up in the asset list.
BR_LOCAL Automatically set Start/EndMarket on any asset call to the local asset market hours (AssetMarketStart/End), converted from AssetMarketZone to BarZone
BR_LEISURE Don't enter or manage trades at or after EndMarket and before StartMarket, on holidays, and on weekends by EndWeek/StartWeek in the bar time zone (BarZone).
BR_SLEEP Don't request or process price quotes when the market is closed due to BR_WEEKEND and BR_MARKET. Since no prices are updated, tick functions will not run and price-dependent events will not to be triggered during that time. Use this for special purposes only.
BR_LOGOFF Log off on weekends or when the market is closed. BR_SLEEP and BR_WEEKEND must be set. Broker API settings, such as order texts or price or volume types, are usually reset when logging off. Use this for special purposes only, such as working around server maintenance or API unresponsiveness on weekends. Some broker APIs do not support logging off during a trading session; in that case BR_LOGOFF will suspend the session until it is manually restarted.

Type:

int

Remarks:

Example:

function run()
{
  ...
  StartWeek = 10400; // start Monday 4 am
  EndWeek = 51900; // end Friday 7 pm
  BarMode = BR_WEEKEND+BR_SLEEP; // don't process quotes during the weekend and don't generate bars
  ...
}

See also:

Bars, BarPeriod, NumBars, BarZone, MinutesPerDay, AssetZone, StartWeek/EndWeek, Holidays

 

► latest version online BarPeriod, TimeFrame

BarPeriod

The duration of one bar in minutes (default = 60). The theoretical minimum bar period is one second (1./60) for normal scripts, and 1 ms (0.001/60) for HFT simulation. Typical price curve inefficiencies disappear on low time frames, so don't go below 60 for indicators, filters, or analysis functions. Use 1440 for daily and 10080 for weekly bar periods. The bar period is the basic time frame in the script. It determines the width of a candle in the chart, and the frequency of the run function that is called after every bar. If not explicitly set up in the script, it can be determined with the [Period] slider. User-defined bars (see bar function) have no fixed bar period; in that case BarPeriod should be set to the average duration of a bar.

Type:

var

BarOffset

Bar start/end time offset in minutes; 0 or a positive number smaller than BarPeriod (default = 940 with daily bars, otherwise 0). Bars and frames normally start at a date/time boundary; f.i. 60-minute bars start at every full hour, and daily bars normally start at UTC midnight when BarOffset is 0. For daily bars, use BarOffset shift the bar begin to the desired trading time of the day; the 940 default sets it to 15:40 in the bar time zone. BarOffset is also automatically set by NumSampleCycles for dividing a bar into equal time intervals and decreasing the offset by one interval per cycle; on the last cycle, BarOffset is 0.  

TimeFrame

Time frame in bars (default = 1) used for all subsequent price, time, series, advise, and trade calls. This variable can be set within a strategy for using multiple time frames, for skipping bars, for synchronizing time frames to external events, or for for emulating special bars such as Range or Renko Bars. For instance, with a bar period of 5 minutes (BarPeriod = 5) and a time frame of 12 bars (TimeFrame = 12), series and trade signals use a one hour time frame (5*12 = 60 minutes).
  For synchronizing time frames to a certain event, time, or date, skip the bars inbetween by setting TimeFrame to 0. Count the skipped bars, and set TimeFrame to the negative number of skipped bars when the event happens. The time frame synchronizing mechanism only affects the shifting of series; price and other functions are unaffected. See remarks and example; see also frameSync and AssetFrame.

FrameOffset

Time frame offset in bars (default = 0 = no offset) used for all subsequent price, time, series, and trade calls; must be smaller than TimeFrame. When fixed time frames are used, this variable determines the bar number within a time frame when series are shifted and trades are executed. This variable allows to generate trade signals at different times dependent on the asset.

Type:

int

Remarks:

TimeFrame vs. BarPeriod:

Examples:

#define H24 (1440/BarPeriod)
#define H4 (240/BarPeriod)
#define H1 (60/BarPeriod)
... 
BarPeriod = 60; // 1 hour (60 minutes) bars
...
// create a 4-hour price series ///////////////////////////////////
TimeFrame = H4;
vars PriceH4 = series(price());
 

// create a 24-hour price series /////////////////////////////////
TimeFrame = H24;
vars PriceH24 = series(price());

// skip bars outside market hours
static int SkippedBars = 0;
if(!market(ET,0)) {
TimeFrame = 0;
SkippedBars--; // count negative number of bars outside market hours
} else if(TimeFrame == 0) {
TimeFrame = SkippedBars;
SkippedBars = 0;
} else
TimeFrame = 1;
vars PriceInMarketHours = series(price()); // create a daily price series (equivalent to AssetZone) //////// static int BarsPerDay = 0; if(hour(0) < hour(1)) { // day change TimeFrame = BarsPerDay; // end the frame BarsPerDay = 0; } else { TimeFrame = 0; // inside the frame BarsPerDay--; // count negative number of bars per day } vars PriceD1 = series(price()); // alternative: a midnight-aligned price series using frameSync() StartWeek = 10000; TimeFrame = frameSync(H24); vars PriceD1 = series(price()); // back to 1-hour time frames TimeFrame = 1; ...

See also:

Bars and Candles, NumBars, SampleOffset, LookBack, dayHigh, Date, BarMode, AssetFrame, frameSync, bar

 

► latest version online Bars and Candles | Zorro Project

Bars, Ticks, Candles. Glossary of Terms.

The behavior of an algorithmic trading strategy is determined by bars or by ticks - often by both. A bar is normally a fixed time period, like one minute or one day. In the chart below that displays the EUR/USD price curve in summer 2009, every bar is represented by a red or green candle. The horizontal width of the candle is the bar period - here one day - and its vertical height is the price movement of the asset during that period. On green candles the price went up during the bar period; on red candles it went down. By default, Zorro displays up and down candles in white and black colors. The thin lines above and below the candles - the 'wicks' - represent the highest and the lowest price during the bar period.

For example, when you look at the candle of June 1 (at the vertical line below marked Jun), you see that the day started at a price of 1.4125. During the day the price went as high as 1.4225, and as low as 1.4110. The day ended with the price at 1.4135.

In past times, a bar was really equal to a day. The closing price was the price of the asset in the afternoon when the market closed, and the opening price was the first price next morning at 9:30 after the traders had contemplated their strategies all night. In modern times, many assets are traded online 24 hours a day, and some, such as digital coins, even at weekends and holidays. Due to the trading around the clock, you can see that in the above chart the closing price of one bar is almost identical with the opening price of the next bar. This is different when an asset is traded at a single stock exchange and has little or no trading volume outside its business hours. In that case, daily candles have a gap between the close of a bar and the open of the next bar.

The same price curve can look very different depending on the used trading platform or charting program - especially with forex pairs or CFDs that are traded around the clock. The reason are different time conventions. The open, high, low, and close price of any candle depends on the time zone of the charting software and of the open/close time of the bars. The EUR/USD chart above is based on Greenwich Mean Time (GMT, UTC). The same chart in New York local time (EST) had quite different candles. By Zorro convention, charts and logs are always in UTC time, and the time stamp of any bar is the current time at bar end. Thus, a 12:00 bar ends at 12:00 UTC. This avoids 'peeking bias' when the close price of a bar lies in the future. Some trading platforms stamp the bar with the time from its begin or center, and some use local time or EST time. Be aware of that when comparing logs or charts of different platforms.

In charts a candle is usually printed centered about its timestamp, not in front of it as would be correct. And indicators are usually printed with smooth lines, not with discrete steps at any candle as would be correct. Therefore, the begin and end of candles and the crossings of indicators normally look slightly off in charts. This can confuse beginners. For getting the exact position of indicator crossings, don't use a magnifying glass on the chart, but look in the backtest or trading log.

Bar periods and time frames

Trading algorithms can be based on ticks, bar periods, and time frames. The main strategy runs at any bar, but it can also have a tick function that runs at any incoming tick. In live trading, a tick is the arrival of a new price quote; in the backtest, a tick is a single record in a historical price data file. Any tick got a timestamp from the exchange or broker where it originated. Any bar contains all ticks whose timestamps fall into the bar period. When one bar ends, the next bar begins; there are no 'gaps' between bars. A 1-hour bar ending at 12:00 contains all ticks with timestamps greater than 11:00:00 and less or equal to 12:00:00. A tick with the timestamp 12.00:00.001 already belongs to the 13:00 bar, even though it might display in a spreadsheet or history editor as 12:00:00. Zorro timestamps have a precision of 100 microseconds.

Since ticks arrive rarely or not at all during weekends or holidays, and since Zorro allows bar periods down to 1 millisecond for HFT simulation, a bar period can be shorter than the time between two subsequent ticks. The bar is then normally extended by a multiple of the bar period until it contains at least one tick. Consequently, bars are normally also extended by weekends, holidays, or market closure. Different behavior can be set up with BarMode flags.

A time frame can cover several bars, and is used as a time basis for technical indicators and for price series. A strategy has only one bar period, but can use any number of different time frames. For instance, TimeFrame = 4 merges 4 subsequent bars to a single time frame, and TimeFrame = framesync(24) merges all 1-hour bars that belong to a full day. Bars be be merged by any criteria or condition set up in the script, allowing time frames to be synchronized to certain events. Since many assets are traded in sessions and have no or infrequent price quotes outside their market hours, it is normally desired to merge out-of-market bars to a single bar. Alternatively, time series can ignore out-of-market bars when the NOSHIFT flag is set during that time.

Some traders believe that they get a better insight into the market with bars that cover not a fixed time period, but a fixed price movement. With a focus on price movement, long periods of consolidation are condensed into just a few bars, thus highlighting "real" price trends. There are many special bar types: Tick Bars, Renko Bars, Range Bars, Momentum Bars, Point-and-Figure Bars, or Haiken Ashi Bars. Their usefulness for algorithmic trading is doubtful, but Zorro allows any imaginable combination of price and time for constructing user-defined bars with the bar function.

Prices and how to get them

Strategy development involves testing the strategy with historical price data. The prices are read from files in Zorro's History folder. For keeping the files at a reasonable size, they normally do not contain all the price quotes, but only one-minute candles (M1 data) or daily candles (D1 data). Files containing no candles, but direct price quotes (T1 data), can also be used for special purposes, like testing scalping or HFT strategies. Historical data should have at least the same resolution as the bar period, but preferably higher resolution.

Historical M1 data for main currencies, indices, and commodities is available on the Zorro Download Page. Data that is not found there can be downloaded (usually free) from brokers or from Internet data providers (Google™, Quandl™, etc.). Data in high resolution or with special content - for instance, option chains - is normally not free. It can be purchased from data vendors; we also provide M1 price data and EOD options data for US stocks and ETFs in the Zorro data format. Other vendors specialize on certain data types or exchanges - for instance, iVolatility™ on options and futures, Nanotick™ on Chicago traded assets, or Nanex™ on New York traded assets. Most have data in CSV format that can be convert to Zorro's data formats using the dataParse function with a special format string. Examples for conversion scripts can be found in the Strategy folder.

Glossary of terms

Bars, candles, ticks, or quotes are often confused, and the meanings of those terms can also vary from platform to platform. Throughout this manual the following terms are used:

Asset - the traded financial product. Multiple synonyms are used in the trading literature, such as Instrument, Ticker, Symbol, Issue, Market, or Security.

Quote - online offer by a market participant to sell or buy an asset at a certain price. The most recent price quote is the current bid or ask price of an asset. Sometimes other methods are used to define the current price, for instance the best recent quote or the last traded price.

Tick - a price in combination with a time stamp. In live trading, a tick is generated by an incoming new price quote. In historical data files, a tick is a price sample with its associated time. If a tick is sampled together from several quotes, as in most historical data files, its time stamp is the time of its most recent quote.

Candle - price info covering a time interval with an open, close, high, and low price. T1 data contains only a single price quote per tick, so its open, close, high and low is the same price. Price ticks in historical files are usually candles with the first, last, highest, and lowest price of all quotes they are sampled from. Forex, CFD, or cryptocurrency candles from different brokers often differ strongly.

Bar - basic time interval. The bar period determines the width of a candle, the time resolution of the price curve, and the execution interval of a trading strategy. The time scale on a chart is divided into bars that cover at least one tick, but normally many ticks. The bar time can vary from bar to bar when special bar types, such as Range, Renko, or Point-and-Figure bars, are used, or when bars are extended due to weekends, holidays, or market closure.

Intrabar - inside a bar. Simulating intrabar events in test/train mode requires the TICKS flag. The tick or tock functions or trade management functions can run at any time inside a bar.

Time frame - basic time unit of algorithms and indicators in a trading strategy. It is often identical to a bar, but can also cover multiple bars in multi-timeframe strategies. A time frame can be synchronized to a full hour, day, or week. In that case the number of bars per time frame can vary.

Read on:

Zorro software, algo trading strategies, BarPeriod, NumBars, historical data

► atest version online

between

between(int x, int lower, int upper): bool

between(var x, var lower, var upper): bool

Returns true when the variable x lies at or within a lower and upper border. When lower is above upper, returns true when either x >= upper or x <= lower, this way allowing the comparison of cyclic ranges such as hours of the day or months of the year.

Parameters:

x, lower, upper - any var or int.

Algorithm:

if(lower <= upper) 
  return (x >= lower) and (x <= upper); 
else<
  return (x >= lower) or (x <= upper);

Example:

if(between(x,0.,1.))
  ...  // executed when x is between 0..1
if(between(hour(0),22,4))
  ...  // executed when hour(0) is at or above 22 or at or below 4

See also:

sqrt, abs, sign, clamp, min, max, ifelse

► latest version online

Broker Arbitrage with Zorro

Broker Arbitrage: Trading with multiple brokers

Trading simultaneously with multiple accounts, brokers, exchanges, or data feeds allows broker arbitrage, i.e. exploiting prices differences of the same asset between different market places. Arbitrage opportunities appear for instance when Forex liquidity providers deviate from each other, or when market makers do not precisely follow the market. There is no central exchange for currencies and CFDs, so their prices vary from broker to broker. If the price differences temporarily exceed trading costs, you can collect risk-free profits.

Zorro S can simultaneously trade with up to 12 brokers or price data feeds. It allows trading strategies to compare currency or CFD prices between broker A and broker B, and enter a long position with the cheaper broker and a short position with the other. Here's the step by step setup and a code example for taking advantage of Forex/CFD price differences. But be aware that brokers dislike broker arbitrage practices, and may close accounts when they learn that they were exploited in this way.

Example:

// Simple broker arbitrage example ////////////////////////////

function tick()
{
  asset("EURUSD_A");
  var SpreadA = Spread, PriceA = priceClose(), 
    CommissionA = Commission*LotAmount/10000*PIP/PIPCost; // convert commission to price difference 
  asset("EURUSD_B");
  var SpreadB = Spread, PriceB = priceClose(), 
    CommissionB = Commission*LotAmount/10000*PIP/PIPCost;

  var Threshold = 1.5*(SpreadA+SpreadB+CommissionA+CommissionB); // arbitrage threshold
  var Difference = PriceA - PriceB;

  asset("EURUSD_A");
  if(NumOpenShort && Difference < 0)
    exitShort();
  else if(NumOpenLong && Difference > 0)
    exitLong();
  else if(!NumOpenShort && Difference > Threshold) // go short with the expensive asset
    enterShort();
  else if(!NumOpenLong && Difference < -Threshold) // go long with the cheap asset
    enterLong();

  asset("EURUSD_B");
  if(NumOpenShort && Difference > 0)
    exitShort();
  else if(NumOpenLong && Difference < 0)
    exitLong();
  else if(!NumOpenShort && Difference < -Threshold)
    enterShort();
  else if(!NumOpenLong && Difference > Threshold)
    enterLong();
}

function run()
{
  StartDate = EndDate = 2017;
  LookBack = 0;
  set(TICKS);
  History = "*.t1";
  assetList("AssetsArb.csv");
  asset("EURUSD_A");
  asset("EURUSD_B");
}
// Generate a multi-broker asset list ///////////////////////

#define LISTS "FXCM","Oanda"
#define ASSETLIST "AssetsMulti"

function main() 
{
  char OutName[NAMESIZE2]; // to store a temporary string
  strcpy(OutName,strf("History\\%s.csv",ASSETLIST));
  file_delete(OutName);
  string Name, Source = 0;
  while(Name = of(LISTS))
  {
    static char Dest[40000]; 
    file_read(strf("History\\Assets%s.csv",Name),Dest,0);
    if(!*Dest) return quit("Source not found!");
    string Pos = strchr(Dest,'\n');
// skip header line after first file
    if(!Source) Source = Dest;
    else Source = Pos;
    while(Pos) {
      Pos = strchr(Pos+1,',');
      if(!Pos) break;
// append list name to asset name
      strcatf(Pos,strf("_%s",Name));
      int i;
      for(i=0; i<11; i++) // find Symbol column
        Pos = strchr(Pos+1,',');
// append list name as source to symbol
      strcatf(Pos+1,strf("%s:",Name));
      Pos = strchr(Pos,'\n');
    }
    file_append(OutName,Source,0);
  }
}

See also:

Account list, Symbols, Brokers, TradeMode

► latest version online

brokerCommand

brokerCommand (int Command, intptr_t Parameter): var

brokerCommand (int Command, string Text)

brokerCommand (int Command, var* Parameters)

Sets various broker plugin parameters or retrieves asset specific data for special purposes in a script in [Trade] mode. The functions can be optionally provided by the broker plugin; they return 0 when they are not supported or no broker connection is established. The number and types of parameters depend on Command. User specific commands to the broker API can be added.The following commands are predefined:
 
Command
Parameter
Internal
Returns
GET_TIME 0 Last incoming quote time in the server time zone, OLE DATE format.
GET_DIGITS Symbol   Number of relevant digits after decimal point in the price quotes.
GET_STOPLEVEL Symbol   The 'safety net' stop level of the last order (non-NFA accounts only).
GET_TRADEALLOWED Symbol   Trade is allowed for the asset.
GET_MINLOT Symbol   Minimum permitted amount of a lot.
GET_LOTSTEP Symbol   Step for changing lots.
GET_MAXLOT Symbol   Maximum permitted amount of a lot.
GET_MARGININIT Symbol   Initial margin requirements for 1 lot.

GET_MARGINMAINTAIN

Symbol   Margin to maintain open positions calculated for 1 lot.
GET_MARGINHEDGED Symbol   Hedged margin calculated for 1 lot.
GET_COMPLIANCE 0   Account restrictions, a combination of the following flags: 1 - no partial closing; 2 - no hedging; 4 - FIFO compliance; 8 - no closing of trades; 15 - full NFA compliant account.
GET_SERVERSTATE 0   State of the broker server: 1 - server connected; 2 - server disconnected; 3 - server was temporarily disconnected since the last GET_SERVERSTATE command.
GET_NTRADES 0   Number of all open trades in this account.
GET_POSITION Symbol Net open amount (as in BrokerBuy2) of the given symbol; negative values for short positions. For brokers with varying lot amounts, call SET_AMOUNT before.
GET_AVGENTRY 0   Average entry price for the preceding GET_POSITION command, or 0 when no position was open. The raw entry price from the broker is returned; in case of options it might be required to divide it by the Multiplier.
GET_FILL Trade ID   Deprecated; replaced by BrokerTrade.  Returned the current fill amount of the open order, or -1 when the order was not found or was not open anymore.
GET_ACCOUNT String   Fills the string with the account name.
GET_DATA String   General request to a REST API. The string contains the request, usually a JSON object. It can optionally begin with a '#' hash character and the last part of the post address ending with a blank, f.i. "#data "). The string is then filled with the response from the API. Returns the response size in characters, or 0 when no valid response was returned.
GET_BOOK T2* Fills the T2 array with current quotes from the order book. The asset is set with SET_SYMBOL; bid prices are negative, the time field is optional. The last T2 element is set to 0 for indicating the end of the array. Returns the number of quotes.
GET_TRADES TRADE* Fill the given TRADE array with the parameters of all open positions from the current account (see remarks). Return the number of positions.
GET_OPTIONS CONTRACT* Fill the given CONTRACT array with the option chain of the underlying set with SET_SYMBOL (see remarks).
GET_FUTURES CONTRACT* Fill the given CONTRACT array with the futures chain of the underlying set with SET_SYMBOL (see remarks).
GET_FOP CONTRACT* Fill the given CONTRACT array with the options of futures chain of the underlying set with SET_SYMBOL (see remarks).
GET_UNDERLYING 0 Get the underlying price for the preceding brokerAsset call when the asset was an option.
GET_GREEKS var[5]   Get the greeks for a preceding brokerAsset or contractPrice call. Fills the parameter array with P[0] = Implied Volatility; P[1] = Delta; P[2] = Gamma; P[3] = Vega; P[4] = Theta. Greeks cannot be retrieved with 'fast' price type.0
SET_DELAY Time in ms   Set delay in ms between commands sent to the broker (normally 20).
SET_WAIT Time in ms   Set maximum wait time in ms for confirmation after a command was sent to the broker. Default depends on the broker plugin; normally 60000. Increase the time when trades need longer than 1 minute to open.
GET_MAXTICKS 0 Return the maximum number of ticks to be filled by BrokerHistory2. If this command is not supported, it's 300 ticks. This command is called at login to set the MaxTicks variable.
GET_MAXREQUESTS 0 Return the maximum number of requests per second allowed by the broker API. Sets up MaxRequests if supported.
GET_HEARTBEAT 0 Return the maximum time in ms that the API tolerates without a request. Sets up TickTime if supported.
GET_BROKERZONE 0 Return the time zone of the timestamps set by the BrokerHistory2 function. Return 0 for UTC. Used for setting BrokerZone.
SET_DIAGNOSTICS 0, 1   1 to enable, 0 to disable diagnostics output. When enabled, communication between plugin and API and other plugin messages will be recorded in the log for diagnostic purposes.
GET_LOCK 0   Returns 1 if broker API calls must be locked for synchonizing them among several connected Zorros; returns -1 if broker API need not be locked. In this case the NOLOCK flag is automatically set.
SET_LOCK 0, 1   Set (1) or release (0) a lock for synchonizing commands among several connected Zorros (see remarks). Returns 0 if the lock was already set, otherwise 1.
SET_SLIPPAGE Pips   Set the maximum allowed slippage (default = 5 pips) in adverse direction for subsequently opening or closing trades. Higher allowed slippage causes less requotes, but allows trades to be entered at a worse price. Note that the allowed slippage is not guaranteed; trades can be still entered at higher slippage dependent on the brokerm, market access method, and server setup.
SET_SYMBOL Symbol Set the symbol for subsequent commands.
SET_MAGIC Number   Set a "magic number" for identifying trades opened by the current script.
SET_MULTIPLIER Number Set the multiplier for retrieving option and future chains.
SET_CLASS Name   Set the name of the trading class. Call this before setting the symbol for option and future chains; use an empty string ("") for all trading classes.
SET_AMOUNT var pointer Set the lot size for buy/sell orders; for assets with arbitrary lot sizes. Automatically sent before any order if the current LotAmount is less than 1.
SET_LEVERAGE Number Set the leverage for buy/sell orders; for assets with adjustable leverage. Automatically sent before any order with the current Leverage value.
SET_PATCH Patch value   Work around broker API issues that cause wrong account or asset parameters. Retrieve the following parameters in trade mode not from the broker API, but from the asset list or by calculating them on the Zorro side (numbers can be combined by addition):
1  - Balance and Equity;
2  - TradeProfit of open trades;
4  - TradeProfit of all trades;
8  - Server time;
16 - Rollover and Commission;
32 - Leverage and MarginCost;
64 - PIP, PIPCost, LotAmount.
SET_HWND HWND Called before login with Zorro's window handle. The window handle can be used to trigger asynchronous events and send messages to Zorro with the PostMessage function.
SET_FUNCTIONS Function pointer Called before login with a pointer to Zorro's function list, an array of function pointers in the order given by include\func_list.h. This allows the plugin to call Zorro functions, such as HTTP or hash functions for REST APIs, by their number (see example). 
GET_CALLBACK Address
pointer
Get the address of a plugin-supplied callback function that is called at any WM_APP+3 message; automatically called after login.
SET_BROKER Text   Set the broker or exchange name when a plugin supports multiple brokers or exchanges. Returns 0 when the name did not match any, otherwise nonzero.
SET_SERVER Text Send the optional URL in the account list Server field to the broker API. Automatically called before login. Can be used to set a different URL for REST endpoints.
SET_CCY Text Send the currency part of the account list CCY field to the broker API. Automatically called before any BrokerAccount call.
SET_COMMENT Text   Display the given text (255 characters max) in the broker platform, usually at the top of the chart window.
SET_COMBO_LEGS 2, 3, 4 Declare the given number of following option trades as a combo order. Most brokers offer reduced margin and commission on combos. To use, set the combo leg number and immediately call the enter commands for the contracts. The order will be processed after the last enter command is received. If the order fails, the last enter will return 0. The script must then cancel the prior trades. All combo trades must have matching expiration dates  and underlying symbols; otherwise the order will not be accepted.
SET_ORDERTYPE 0 ... 11 Switch between order types and return the type if supported by the broker plugin, otherwise 0. Automatically called at any order entry depending on TradeMode and StopFactor.
0  - Broker default (highest fill probability)
1  - AON (all.or-none); prevents partial fills
2  - GTC (good-till-cancelled); order stays open until completely filled
3 -  AON+GTC
4 - Broker specific special order type
+8 - STOP; add a stop order at distance Stop*StopFactor on NFA accounts (see remarks). 
SET_ORDERTEXT Text   Set an arbitrary order comment for the next order (255 characters max). Often also used for special orders, f.i. for trading binary options with MT4, or for special order types like "STP LMT" with IB. Reset with an empty string.
SET_ORDERGROUP Text   Set an order group name for the next orders (255 characters max). Often used for grouping orders together, f.i. for One-Cancels-Another (OCA) orders, depending in the broker. Reset with an empty string.
SET_PRICETYPE 0 ... 8   Set the type of prices returned by BrokerAsset and BrokerHistory2, if applicable. Return the type if supported by the broker plugin, otherwise 0.
0
- Broker default (ask/bid if available, otherwise last trade price);
1
- enforce ask/bid quotes;
2
- enforce last trade price;
3
- special (broker specific);
4
- suppress price requests;
6
- adjusted last trade price (for historical data);
8
- fast price requests: ask, bid, or trade, whatever received first.
The spread is normally only updated when ask/bid quotes are returned.
SET_VOLTYPE 0 ... 7   Set the type of volume data returned by BrokerAsset and BrokerHistory2, if applicable. Return the type if supported by the broker plugin, otherwise 0.
0 - Broker default, usually quote size;
1
- no volume;
2
- tick frequency;
3
- quote size (ask+bid);
4
- trade volume;
5
- ask size;
6
- bid size;
7
- open interest. 
SET_VALTYPE 0 .. 3   Set the type of marketVal data returned by BrokerAsset and BrokerHistory2, if applicable. Return the type if supported by the broker plugin, otherwise 0.
1
- no data;
2 - spread;
3 - WAP.
GET_UUID String Copies the UUID of the last opened trade to the given string.
SET_UUID String Sets the UUID for the next command from the given string.
DO_EXERCISE Lots Exercise the given number of contracts of the option type set with SET_SYMBOL.
DO_CANCEL Trade ID Cancel the remaining unfilled amount of the order with the given trade ID. If ID == 0, all open orders are cancelled. If ID == -1, cancel the order with the UUID set before by SET_UUID.  Otherwise, use a valid ID of an open position. Returns 1 when the order was cancelled, or 0 when the order was not found or could not be cancelled.
PLOT_HLINE var[5]   Place a horizontal line at a given price in the chart window of the broker platform. 5 parameters are used: P[0] = always 0; P[1] = price position; P[2] = line color; P[3] = line width; P[4] = line style. Return the identfier number of the line.
PLOT_TEXT var[4]   Place a text element at a price position at the right border of the chart window. 5 parameters are used: P[0] = always 0; P[1] = price position; P[2] = text color; P[3] = text size in points. Return the identfier number of the text element.
PLOT_MOVE var[3]   Move the graphical element with the identifier given by the first parameter P[0], the horizontal position given by P[1] and the vertical position given by P[2].
PLOT_STRING Text   Set or modify the text content for the last created or moved text element.
PLOT_REMOVE Identifier   Delete the graphical element with the given identifier.
PLOT_REMOVEALL 0   Remove all graphical elements from the chart.
2000..2999 var   User supplied command with a single numerical parameter.
3000..3999 P[8]   User supplied command with an array of 8 var parameters..
4000..5999 char*   User supplied command with a text string.

Parameters:

Command Input, one of the commands from the list above.
Parameter Input, parameter or data to the command. A 32 bit int or pointer for 32 bit plugins, or a 64 bit long int or pointer for 64 bit plugins.
Parameters, P Input, array of up to 8 vars for commands that set or retrieve multiple parameters.
Symbol Input, char*, broker symbol of an asset (see Symbol). Complex symbols, as for selected option contracts, can have the underlying, exchange, expiry, and other parameters coded in the name as described under IB Bridge.
Text Input, char*, for commands that require a text string.
 

Returns:

0 when the command is not supported by the broker plugin, otherwise the data to be retrieved.
 

brokerAsset (string Symbol, var* pPrice, var* pSpread, var *pVol)

Call the BrokerAsset function with the given Symbol (f.i. SymbolLive) and return the price, spread, and volume.

brokerAccount (string Name, string AccountID, var *pBalance, var *pTradeVal, var *pMarginVal);

Call the BrokerAccount function for the account list entry given by Name and an optional account identifer, and return the balance, open trade value, and allocated margin. Can be used to retrieve account information when trading with multiple brokers.

brokerRequest (string Path, string Method, string Data): string

Call the BrokerRequest function for sending arbitrary HTTP requests to the connected REST API. Path is the relative URL including the query string, but without the endpoint root. If Method begins with '$', the request will be signed. Data is the optional request body. If no particulat Method and no body is given, a GET request is sent. The response is returned and can be evaluated.

brokerTrades (int Filter): int

Call the GET_TRADES command. Cancel all currently open trades, and replace them with the open positions on the broker account (if any).
Filter = 0
loads all positions,
Filter = 1
loads only positions matching the asset list,
Filter = 2
loads only positions of assets that were already selected in the script,
Filter = 4 loads only positions of the current asset and prevents the previous cancelling of open trades.
Add 16 to Filter for adjusting TradeUnits and TradePriceOpen by the Multiplier, as needed for option contracts with some broker APIs.
The function returns the number of entered positions. It can synchronize script trades with the broker account. The account must support the GET_TRADES command and have only open positions from the current Zorro script. The enterTrade function is used for entering the new trades. The entered trades are plain, without exit limits, TMFs, flags, TradeVars, or any other trade parameter that is not stored on the broker account. The algo identifiers of the trades are set to the current algo. For correctly updating the trades, all assets must have been used in the script.     

Remarks:

Examples:

brokerCommand(SET_COMMENT,strf("Algo %s",Algo)); // set a comment

brokerCommand(SET_PATCH,16+32); // take swap, commission, leverage from the asset list

var Greeks[5];
contractPrice(ThisContract);
brokerCommand(GET_GREEKS,Greeks); // get greeks of the current contract
// Read all positions from an asset list ///////////////////////
void main() 
{
  assetList("AssetsFix");
  string Name;
  while(Name = loop(Assets)) {
    var Position = brokerCommand(GET_POSITION,Name);
    printf("\n%6.4f %s",Position,Name);
    if(!wait(10)) break;
  }
}

// read and print the order book
void main() 
{
  static T2 Quotes[MAX_QUOTES];
  brokerCommand(SET_SYMBOL,"AAPL");
  int i,N = brokerCommand(GET_BOOK,Quotes);
  printf("\nOrderbook: %i quotes",N);
  for(i=0; i<N; i++)
    printf("\nPrice %.2f Vol %.2f",(var)Quotes[i].fVal,(var)Quotes[i].fVol);
}

// read JSON data string from the Coinigy REST API
void main() 
{
  brokerCommand(SET_BROKER,"GDAX");
  static char Buffer[MAX_BUFFER];
  strcpy(Buffer,"#data "); // target point
  strcat(Buffer,"{\n\"exchange_code\": \"GDAX\",");
  strcat(Buffer,"\n\"exchange_market\": \"BTC/USD\",");
  strcat(Buffer,"\n\"type\": \"bids\"\n}");
  int Size = brokerCommand(GET_DATA,Buffer);
  if(Size)
    printf("\nResponse: %s",Buffer);
}
// Use Zorro HTTP functions in a REST API plugin
int (__cdecl *http_send)(const char* url,const char* data,const char* header) = NULL;
long (__cdecl *http_status)(int id) = NULL;
long (__cdecl *http_result)(int id,char* content,long size) = NULL;
int (__cdecl *http_free)(int id) = NULL;
...
DLLFUNC double BrokerCommand(int command,intptr_t parameter)
{
  switch(command) {
    case SET_FUNCTIONS: {
      FARPROC* Functions = (FARPROC*)parameter;
      (FARPROC&)http_send = Functions[139];
      (FARPROC&)http_status = Functions[142];
      (FARPROC&)http_result = Functions[143];
      (FARPROC&)http_free = Functions[144];
      return 1.;
    }
    ...
  }
  return 0.;
}

See also:

Brokers, broker plugin, MTR4 bridge, FXCM plugin, IB plugin, Oanda plugin

► latest version online Broker Plugin | Zorro Project

Writing Broker / Data Feed Plugins

Zorro supports most major brokers either with a direct connection, or by connecting through a broker-supported platform, for instance with the MT4/5 bridge or the Sierra bridge. A direct connection to a broker API, exchange, market data feed, or platform is established with a DLL in Zorro's Plugin folder. Zorro automatically scans that folder at startup, and lists all valid DLLs in the [Broker / Account] scrollbox. The DLL uses the broker's API for placing orders and getting market data. The interface is organized for keeping the DLL as simple as possible.
  Only a few functions are required for basic automated trading. Additional functionality, for instance to activate special order types or getting special data, can be implemented with an optional broker command. If you know programming and have access to the broker's API documentation, you can write a broker DLL in a few hours. For downloading historical data in CSV or JSON format from online data sources you'll need no DLL; a small script is sufficient (see assetHistory).

Writing a broker plugin

Plugins can be written in any language that supports DLLs, such as Java, Pascal, C#, or C++. Many plugins have been written by Zorro users, mostly in C++. We reward proper C++ written plugins with a free Zorro S subscription, license, or update extension. As a starting point you can use the plugin for a cryptocurrency REST API, which can be found in Zorro's Source folder. The source code of other plugins developed by Zorro users is available on the GitHub pages of their authors.

An article about writing a broker plugin can be found on Financial Hacker.

The source code of included broker plugins is available on request to Zorro S users who are developing a similar plugin for the community. Please contact us with a short description of your project and your prior C++ experience. You'll need to sign a non-disclosure agreement, and send us back the final plugin with source code for review. Technical support is free for developing broker plugins; for this you'll need no support ticket or Zorro S subscription.

You can also contact us for outsourcing a plugin development.

Setting up VC++

Creating a VC++ DLL project with access to all Zorro functions is described under VC++ project setup. Zorro functions, such as for HTTP requests or signatures, are often useful for the implementation. But if you want a plain DLL with no Zorro stuff, do not bind the ZorroDll.cpp source file, but use the dllmain.cpp file that's automatically created by VC++. Its DllMain function is the main entry point of the broker DLL, and you can leave that function unchanged. Broker functions require the DATE and T6 data types, thus you need to define DATE and include the trading.h header, like this:

typedef double DATE;
#include <trading.h>

If your broker DLL accesses Zorro-specific structs, set the struct alignment to 4 with a #pragma pack(4) statement before including trading.h. Otherwise structs have different sizes in Zorro and in your DLL.

For a DLL to appear in the scrollbox, it must be located in the Plugin folder. Zorro first checks at start if the DLL can be opened with LoadLibrary(). If so, it checks if there is a BrokerOpen function (see below) and if it returns a valid version number. If both conditions are fulfilled, the DLL is registered and appears in the scrollbox. LoadLibrary will fail when DllMain does not return properly. Most frequent reasons are not creating a plain Win32 DLL (f.i. a 64-bit or MFC DLL) or a missing module that the DLL requires. Thus, the more complex libraries you're using, the more likely is your DLL to fail, probably not on your, but on other people's PCs. For being on the safe side, include in the distribution all modules that your DLLs needs. If in doubt, check your DLL's dependencies with DependencyWalker. For debugging, place breakpoints in DllMain and BrokerOpen and check what's happening when Zorro starts.

On Financial Hacker you can find a step by step instruction for implementing a REST API DLL.

Development and test

The Source folder contains broker plugin examples in .zip archives:

You can find more Zorro broker plugins on Github. Use the best suited as a template for your own plugin. Implement the DLL functions (see below) in this order: BrokerOpen, BrokerLogin, BrokerAsset, BrokerBuy2. These 4 functions (described below) are the minimum required for trading with the broker. Optionally, implement BrokerAccount, BrokerHistory2, BrokerTime, BrokerTrade, BrokerSell2, BrokerStop if supported by the API. Test any function after implementation with the TradeTest script.

Some functions need not be fully implemented, or not at all. The minimum functions for TradeTest to run are BrokerOpen and BrokerLogin. If a function, f.i. BrokerAsset, is not yet available in the DLL, Zorro simulates it with default values. So you can implement and test the functions step by step.

As soon as the BrokerAsset function is correctly implemented, you should see the current price in the Server window. The TradeTest script opens a panel with the following buttons for testing various broker functions:

[Auto On/Off] - Toggle button for a simulated trade session that automatically opens or closes a trade every minute.

[NFA On/Off] - Toggle the NFA flag. Required for most US accounts; not to be set for most Forex/CFD accounts.

[Hedge] - Toggle between Hedge modes 0, 2, 4,and 5. Some brokers do not support full hedging (mode 2) or partial closing (mode 5).

[Order] - Toggle between limit (LMT), market orders (MKT), good-til-cancelled (GTC) and adaptive orders (Adaptive) when supported by the plugin.

[Asset] - Enter the asset symbol to trade (default: asset from the scroll box).

[Buy Long] - Open a long position with the Lots and Stop value set up with the sliders.

[Buy Short] - Open a short position. Dependent on Hedge, close any open position in opposite direction.

[Close Long] - Close the given number of lots from an open long position. Partial closing is not supported by some brokers.

[Close Long] - Close the given number of lots from an open short position.

[Update Stop] - Sets the Stop of all open positions to the value (in Pips) set up with the slider. Stop adjustment is not supported by some brokers. Due to StopFactor, the broker stop is more distant than the real stop.

LMT orders attempt to open the position at half spread, adaptive orders at zero spread. The broker must support real limit orders for this; MT4 "pending positions" are no limit orders and will not work for LMT or adaptive orders. Various trading modes, broker commands, asset lists etc. can be set up in #define statements at the begin of the TradeTest script.

Broker API functions

The broker DLL exports functions that are described in the following list. With VC++, exported DLL functions must be either declared with the extern "C" __declspec(dllexport) attribute, or listed in a .def file. The DLL functions use only a small subset of a usual broker API. In the following list, pointer arguments printed in italic can be NULL; if they are nonzero, the function must fill them with the required data. All data is mandatory if not mentioned otherwise.

BrokerOpen (char* Name, FARPROC fpMessage, FARPROC fpProgress) : int

Called at startup for all broker DLLs found in the Plugin folder. Retrieves the name of the broker, and sets up two callback functions. Should not allocate or load any resources, nor call any Zorro functions - all this can be done in the BrokerLogin function.

Parameters:

Name Output, char[32] array to be filled with the name of the broker, f.i. "FXCM". The name appears in the Account scrollbox adn is used for selecting the plugin.
fpMessage Input, pointer to a int BrokerMessage(const char* Message) function. The plugin can call BrokerMessage for printing messages - usually errors - in Zorro's message window. Using this function is not mandatory, but recommended. If the message string begins with an exclamation mark '!', Zorro opens an alert box for notifying the user that his attention might be required. If it begins with a hash '#', it is printed into the diagnostics file only.
fpProgress Input, pointer to a int BrokerProgress(intptr_t Progress) function. The plugin can call it repeatedly to keep Zorro responsive in long loops or when broker operations take longer than a second, like BrokerHistory2. When Progress is 0, the Zorro UI will only update its controls for preventing unresponsiveness. When it is 1, dots will be printed in the message window for indicating progress of a lengthy operation. When Progress is a pointer and a callback function exists in the script, it is called and the pointer is passed for triggering script functions from the broker API. When BrokerProgress returns 0, someone has hit Zorro's [Stop] button and the current broker operation must be aborted.

Returns:

Broker interface version number; currently 2.
  

BrokerLogin (char* User, char* Pwd, char* Type, char* Accounts): int

Login or logout to the broker's API server; called in [Trade] mode or for downloading historical price data. If the connection to the server was lost, f.i. due to to Internet problems or server weekend maintenance, Zorro calls this function repeatedly in regular intervals until it is logged in again. Make sure that the function internally detects the login state and returns safely when the user was still logged in.

Parameters:

User Input, User name for logging in, or NULL for logging out.
Pwd Input, Password for logging in.
Type Input, account type for logging in; either "Real" or "Demo".
Accounts Input / optional output, char[1024] array, intially filled with the account id from the account list. Can be filled with all user's account numbers as subsequent zero-terminated strings, ending with "" for the last string. When a list is returned, the first account number is used by Zorro for subsequent BrokerAccount calls.

Returns:

Login state: 1 when logged in, 0 otherwise.
 

BrokerTime (DATE *pTimeUTC): int

Optional function that sends a 'ping' to the server and returns connection status and server time. Repeatedly called in short intervals during the trading session. Can be used by the plugin for keeping the session open if required.

Parameters:

pTimeUTC Optional output, current server time in UTC / GMT+0 with no daylight saving. The DATE format (OLE date/time) is a double float value, counting days since midnight 30 December 1899, while hours, minutes, and seconds are represented as fractional days.

Returns:

0 when the connection to the server was lost (see remarks).
1 when the connection is ok, but the market is closed or trade orders are not accepted.
2 when the connection is ok and the market is open for trading at least one of the subscribed assets.

Remarks:

DATE convertTime(__time32_t t32)
{
  return (double)t32/(24.*60.*60.) + 25569.; // 25569. = DATE(1.1.1970 00:00)
} __time32_t convertTime(DATE date) { return (__time32_t)((date - 25569.)*24.*60.*60.);
}
 

BrokerRequest (string Path, string Method, string Data): string

Optional function for sending an arbitrary HTTP request to a REST API. Path is the relative URL including the query string, but without the endpoint root. If Method begins with '$', the request must be signed. If Method and Data are both 0, a GET request should be sent. The response is returned.
 

BrokerAsset (char* Asset, double *pPrice, double *pSpread, double *pVolume, double *pPip, double *pPipCost, double *pLotAmount, double *pMargin, double *pRollLong, double *pRollShort,double *pCommission): int

Subscribes an asset, and/or returns information about it. Zorro subscribes all used assets at the begin of the trading session. Price and spread for all assets are retrieved in TickTime intervals or when BrokerProgress was preciously called by the plugin. Other asset data is retrieved once per bar.

Parameters:

Asset Input, asset symbol for live prices (see Symbols).
pPrice Optional output, current ask price of the asset, or NULL for subscribing the asset. An asset must be subscribed before any information about it can be retrieved.
pSpread Optional output, the current difference of ask and bid price of the asset.
pVolume Optional output, a parameter reflecting the current supply and demand of the asset. Such as trade volume per minute, accumulated daily trade volume, open interest, ask/bid volume, or tick frequency. If a value is returned, it should be consistent with the fVol content of the T6 struct in BrokerHistory2 (see below)..
pPip Optional output, size of 1 PIP, f.i. 0.0001 for EUR/USD.
pPipCost Optional output, cost of 1 PIP profit or loss per lot, in units of the account currency. If not directly supported, calculate it as decribed under asset list.
pLotAmount Optional output, minimum order size, i.e. number of contracts for 1 lot of the asset. For currencies it's usually 10000 with mini lot accounts and 1000 with micro lot accounts. For CFDs it's usually 1, but can also be a fraction of a contract, like 0.1.
pMargin Optional output, either initial margin cost for buying 1 lot of the asset in units of the account currency. Or the leverage of the asset when negative (f.i. -50 for 50:1 leverage).
pRollLong Optional output, rollover fee for long trades, i.e. interest that is added to or subtracted from the account for holding positions overnight. The returned value is the daily fee per 10,000 contracts for currencies, and per contract for all other assets, in units of the account currency.
pRollShort Optional output, rollover fee for short trades.
pCommission Optional output, roundturn commission per 10,000 contracts for currencies, per contract for all other assets, in units of the account currency.

Returns:

1 when the asset is available and the returned data is valid, 0 otherwise. An asset that returns 0 after subscription will trigger Error 053, and its trading will be disabled.

Remarks:

double Price = MarketInfo(Asset,MODE_ASK);
double Spread = Price - MarketInfo(Asset,MODE_BID);
double Volume = 0;
double LotFactor = MarketInfo(Asset,MODE_MINLOT); // correction for different lot scale
double Pip = MarketInfo(Asset,MODE_POINT);
double PipCost = MarketInfo(Asset,MODE_TICKVALUE) * LotFactor;
int DigitSize = MarketInfo(Asset,MODE_DIGITS); // correction for brokers with 5 digits if(DigitSize == 3 || DigitSize == 5) { Pip *= 10.;
PipCost *= 10.; }
double MinAmount = MarketInfo(Asset,MODE_LOTSIZE) * LotFactor;
double Margin = MarketInfo(Asset,MODE_MARGINREQUIRED) * LotFactor; double RollLong = MarketInfo(Asset,MODE_SWAPLONG); double RollShort = MarketInfo(Asset,MODE_SWAPSHORT);
if(MarketInfo(Asset,MODE_SWAPTYPE) == 0.) { RollLong *= PipCost; RollShort *= PipCost; }

BrokerHistory2 (char* Asset, DATE tStart, DATE tEnd, int nTickMinutes, int nTicks, T6* ticks): int

Returns the price history of an asset. Called by Zorro's assetHistory function and at the begin of a trading session for filling the lookback period.

Parameters:

Asset Input, asset symbol for historical prices (see Symbols).
tStart Input, UTC start date/time of the price history (see BrokerTime about the DATE format). This has only the meaning of a seek-no-further date; the relevant date for the begin of the history is tEnd.
tEnd Input, UTC end date/time of the price history. If the price history is not available in UTC time, but in the brokers's local time, the plugin must convert it to UTC.
nTickMinutes Input, time period of a tick in minutes. Usual values are 0 for single ticks (for T1 or T2 data; all prices of a T6 struct get the tick price), 1 for one-minute (M1) historical candles, or a larger value for low-resolution data.
nTicks Input, maximum number of ticks to be filled; must not exceed the number returned by brokerCommand(GET_MAXTICKS,0), or 300 otherwise.
ticks Output, array of T6 structs (defined in include\trading.h) to be filled with the ask prices, close time, and additional data if available, such as historical spread and volume. See history for details. The ticks array is filled in reverse order from tEnd on until either the tick time reaches tStart or the number of ticks reaches nTicks, whichever happens first. The most recent tick, closest to tEnd, is at the start of the array. In the case of T1 or T2 data, or when only a single price is available, all prices in a T6 struct must be set to the same value.

Returns:

Number of ticks returned, or 0 when no ticks could be returned, f.i. when the server was offline, the asset was not subscribed, or price history was not available for the given date/time.
 

BrokerAccount (char* Account, double *pBalance, double *pTradeVal, double *pMarginVal): int

Optional function. Is called by Zorro in regular intervals and returns the current account status. Is also used to change the account if multiple accounts are supported. If the BrokerAccount function is not provided, f.i. when using a FIX API, Zorro estimates balance, equity, and margin from initial values and trade results.

Parameters:

Account Input, new account name or number, or NULL for using the current account.
pBalance Optional output, current balance on the account.
pTradeVal Optional output, current value of all open trades; the difference between account equity and returned balance value. If not available, Zorro estimes the equity from balance and value of all open trades. If no balance was returned, the account equity can be returned in pTradeVal.
pMarginVal Optional output, current total margin bound by all open trades.

Returns:

1 when the account is available and the returned data is valid, 0 when a wrong account was given or the account was not found.
 

BrokerBuy2 (char* Asset, int Amount, double StopDist, double Limit, double *pPrice, int *pFill): int

Sends an order to open a long or short position, either at market, or at a price limit. Also used for NFA compliant accounts to close a position by opening a new position in the opposite direction. The order type (FOK, IOC, GTC) can be set with SET_ORDERTYPE before. Orders other than GTC are cancelled when they are not completely filled within the wait time (usually 30 seconds).

Parameters:

Asset Input, asset symbol for trading (see Symbols).
Amount Input, number of units, positive for a long trade and negative for a short trade. For currencies or CFDs, the number of units is the number of Lots multiplied with the LotAmount. If LotAmount is < 1 (f.i. for a CFD, or for a fractional share with 0.1 contracts lot size), the number of lots is given here instead of the number of units.
StopDist Optional input, 'safety net' stop loss distance to the opening price when StopFactor was set, or 0 for no stop, or -1 for indicating that this function was called for closing a position. This is not the real stop loss, which is handled by Zorro. Can be ignored if the API is NFA compliant and does not support a stop loss in a buy/sell order.
Limit Optional input, fill price for limit orders, set up by OrderLimit, or 0 for market orders. Can be ignored if limit orders are not supported by the API. 
pPrice Optional output, the average fill price if the position was partially or fully filled. If no price was set, Zorro assumes the current price.
pFill Optional output, the fill amount (positive), or 0 for an unfilled order. If no amount was set, Zorro assumes a complete fill.

Returns:

BrokerTrade (int nTradeID, double *pOpen, double *pClose, double *pCost, double *pProfit): int

Optional function that returns the order fill state (for brokers that support only orders and positions) or the trade state (for brokers that support individual trades). Called by Zorro for any open trade when the price moved by more than 1 pip, or when contractUpdate or contractPrice is called for an option or future trade.

Parameters:

nTradeID Input, order/trade ID as returned by BrokerBuy, or -1 when the trade UUID was set before with a SET_UUID command.
pOpen Optional output, the average fill price if the trade was partially or fully filled. If not available by the API, Zorro will estimate the values based on last price and asset parameters.
pClose Optional output, current bid or ask close price of the trade. If not available, Zorro will estimale the value based on current ask price and ask-bid spread.
pCost Optional output, total rollover fee (swap fee) of the trade so far. If not available, Zorro will estimate the swap from the asset parameters.
pProfit Optional output, current profit or loss of the trade in account currency units, without rollover and commission. If not available, Zorro will estimate the profit from the difference of current price and fill price.

Returns:

BrokerSell2 (int nTradeID, int nAmount, double Limit, double *pClose, double *pCost, double *pProfit, int *pFill): int

Optional function; closes a trade - completely or partially - at market or at a limit price. If partial closing is not supported, nAmount is ignored and the trade is completely closed. Only used for not NFA compliant accounts that support individual closing of trades. If this function is not provided or if the NFA flag is set, Zorro closes the trade by calling BrokerBuy2 with the negative amount and with StopDist at -1.

Parameters:

nTradeID Input, trade/order ID as returned by BrokerBuy2, or -1 for a UUID to be set before with a SET_UUID command.
nAmount Input, number of contracts resp. lots to be closed, positive for a long trade and negative for a short trade (see BrokerBuy). If less than the original size of the trade, the trade is partially closed.
Limit Optional input, fill price for a limit order, set up by OrderLimit, or 0 for closing at market. Can be ignored if limit orders are not supported by the API. 
pClose Optional output, close price of the trade.
pCost Optional output, total rollover fee (swap fee) of the trade.
pProfit Optional output, total profit or loss of the trade in account currency units.
pFill Optional output, the amount that was closed from the position, always positive.

Returns:

BrokerCommand (int Command, ...): var

Optional function, directly called by the brokerCommand function for setting special modes or retrievong special information from the broker API.

 

Remarks:

Example: see Source folder

See also:

Brokers, Symbols, brokerCommand, enter, order, DLL, IB, FXCM, Oanda, MT4

► latest version online Brokers, VPS

Zorro and the Brokers

Any algorithmic trading strategy needs a connection from the Zorro platform to a broker or exchange for buying or selling assets. Normally the broker also provides live prices, price history, and other data such as order book content or option chains. Most brokers offer free demo accounts (also called practice, paper, or game accounts) where trading can be tested without risking real money. A demo account with a broker can usually be opened in 5 minutes on their website. In most cases you'll get an MT4 account, and can start trading with Zorro's MT4 bridge. Opening a real account for trading with real money takes longer, as the broker has to confirm your identity through some ID and address verification process. With serious brokers, be prepared to fill in lengthy forms about your risk preferences and prior trading experiences.

Finding a broker

Every broker uses a different trade model, offers a different universe of assets, and has different values for spread, commission, and other asset and account parameters. Here are some simple criteria for selecting the best broker for small-budget algo trading:

All brokers listed under 'Affiliate' on the Zorro download page fulfill the above criteria. If the broker offers the choice between several account types, select the account with the smallest lot size and the highest leverage. Maybe you've read in a trading book to avoid high leverage as it implies "high risk". Not so: A high leverage account just means that you're free to determine your own leverage. Risk comes from trading a too big volume, not from a high leverage account. Small minimum lot sizes are preferable as you can trade with less capital and can better adjust the trade volume. For low-budget forex trading strategies, a micro lot or nano lot account is mandatory.

* In reality, NDD forex brokers have been found to be in control of their main liquidity provider and in this way indirectly hold positions against their clients. Some NDD brokers are rumored to classify their clients in categories A and B, depending on their trade success. Trades from A-clients - usually, algorithmic traders - are transferred to liquidity providers since they tend to win. Trades from B-clients - the vast majority - are not. You should aim for being an A-client.

Connecting to a broker

Zorro can trade with all brokers that offer at least one of the following ways connecting to them:

Backtesting a broker

In backtests, Zorro can simulate any broker and account through asset specific parameter sets. The broker and account specific parameters, such as rollover fee, spread, lot size, pip cost, etc. are taken from a spreadsheet file in the History folder. This file can be either downloaded from the broker API, or edited with Excel or with the script editor for simulating different accounts and assets. For details see Asset List.

Taking advantage of a broker

Market maker brokers can not only play tricks on you, you can also play tricks on them. One of the most favored is broker arbitrage. Since Zorro S can trade with several brokers at the same time, you can compare asset prices from broker A with the same asset from broker B, and enter a long position with the cheap broker and a short position with the other. You can this way collect risk-free profits when the price difference must temporarily exceeds the spread and transaction costs. Of course, brokers get upset when they learn about those practices. So you should not actually brag about them on trader forums.

Trading cryptocurrencies at a digital exchange

The Z10 strategy is a passive portfolio management system for cryptocurrencies. Trading with Ethereum, Ripple, or other digital currencies is a bit different to trading with normal assets, since you normally need bitcoin for this. Some digital exchanges accept funding in Bitcoin only; you'll then need to purchase bitcoin first for opening an account with them. Here are the steps:

See also:

Zorro, Strategy, account list, broker plugin, MT4 plugin, broker arbitrage

► latest version online

History of Bugs | Zorro Project

Bug History

Zorro is one of the most stable and robust development tools, and we're going to great lengths for keeping it that way. New implemented functions pass multiple test levels for making sure that they work as described. Any new Zorro release is beta tested for several weeks by strategy developers and users, thus ensuring that it has no obvious or severe bugs. Still, software can never be 100% bug-safe (click for proof). Below is a list of all bugs ever found in any Zorro release.

Since Zorro serves as a frontend to your script, it's no problem to crash it or let it behave strange. This is not a Zorro bug - read under Troubleshooting how to find and fix those bugs in your script. If you need help, subscribe a support ticket and contact Zorro Support. If you've encountered one of the real Zorro bugs listed below, use the described workaround or get the fixed version on the Download page. If you found a previously unknown bug in the latest Zorro release, please contact support@opgroup.de. Please describe how to reproduce the problem, and always include the script, the log, and all related data such as asset list, asset history, or external logs. Please do not send screenshots, videos, or photos that you took from your monitor (unless we ask for them). Of course you need no support ticket for bug reports. Real bugs are normally fixed within 2-3 days.

Zorro 2.62 bugs and issues

Zorro 2.60 list of bugs

Zorro 2.56 list of bugs

Zorro 2.53 list of bugs

Zorro 2.50 list of bugs

Zorro 2.48 list of bugs

Zorro 2.44 list of bugs

Zorro 2.40 list of bugs

Zorro 2.35 list of bugs

Zorro 2.30 list of bugs

Zorro 2.25 list of bugs

Zorro 2.20 list of bugs

Zorro 2.15 list of bugs

Zorro 2.12 list of bugs

Zorro 2.08 list of bugs

Zorro 2.03 list of bugs

Zorro 1.96 list of bugs

Zorro 1.88 list of bugs

Zorro 1.84 list of bugs

Zorro 1.74 list of bugs

Zorro 1.66 list of bugs

Zorro 1.60 list of bugs

Zorro 1.56 list of bugs

Zorro 1.54 list of bugs

Zorro 1.50 list of bugs

Zorro 1.46 list of bugs

Zorro 1.40 list of bugs

Zorro 1.34 list of bugs

Zorro 1.28 list of bugs

Zorro 1.26 list of bugs

Zorro 1.24 list of bugs

Zorro 1.16 list of bugs

Zorro 1.12 list of bugs


It's unprovable that a sufficiently complex program is bug-free. The proof:

You can never be sure that a program is bug-free and won't crash - for instance, freeze by an endless loop - with all possible parameters that it processes. In the case of Zorro, 'all possible parameters' means all possible scripts and data. Alan Turing found the proof 80 years ago. Consider a function BugFree that can test whether a program with certain entry parameters crashes or not. BugFree looks like this (in pseudo code):

function BugFree(Program,Parameters)
{   
   if(Program does not crash with given Parameters) 
     return 1; 
   else 
     return 0;
}

Of course BugFree must not crash itself, but terminate properly even when the tested Program crashes. Now we define a recursive function TestMe that calls BugFree:

function TestMe(Program)
{
   if(BugFree(Program,Program)) 
     TestMe(Program); 
}

This evil function only terminates when Program does not crash when it gets itself as a parameter (this means here a Zorro running its own source code). Otherwise TestMe calls itself endlessly and freezes. If you now call TestMe with itself as a parameter, you'll get a contradiction:

TestMe(TestMe); 

This call does not crash only when it crashes. Therefore a function like BugFree cannot exist. Therefore we can never know if Zorro won't crash with your script and data before actually running it.

 

► latest version online enterLong, enterShort

enterLong (int Lots, var Entry, var Stop, var TakeProfit, var Trail, var TrailSlope, var TrailLock, var TrailStep): TRADE*

enterShort (int Lots, var Entry, var Stop, var TakeProfit, var Trail, var TrailSlope, var TrailLock, var TrailStep): TRADE*

Enters a long or short trade with optional parameters. Entry and exit conditions are handled by Zorro.

enterLong (function, var V0, ... var V7): TRADE*

enterShort (function, var V0, ... var V7): TRADE*

Enters a long or short script-managed trade with optional user-defined parameters. Entry and exit conditions are handled by a user-provided algorithm in the given trade management function (TMF) dependent on the variables v0 .. v7.

enterTrade (TRADE*): TRADE*

Enters an open, pending, or closed trade from a TRADE struct. Inserts the trade only in the internal trade list; does not send any order to the broker. The asset name can be given through TradeStr[0] resp. the Skill element of the TRADE struct; if it is not set, the current asset is used. The position size (nLots) and the TR_SHORT and TR_OPEN flags must be set in the TRADE struct; other elements are optional. The algo identifier is taken from the TRADE struct; if it has none, it is set to the current algo. This function can be used for running backtests from a list of trades (see Simulate script) or for reading positions from the broker API and converting them to trades. 
 

Parameters:

Lots Optional number of lots when nonzero; overrides the global Lots, Amount, Margin, and Risk variables. A negative number reverses the trade direction: enterLong then opens a short trade and enterShort opens a long trade.
Entry Optional entry stop when > 0, entry limit when < 0 (overrides the global Entry).
Stop Optional stop loss when nonzero (overrides the global Stop).
TakeProfit Optional profit target when nonzero (overrides the global TakeProfit).
Trail Optional trail limit when nonzero (overrides the global Trail).
TrailSlope Optional trailing speed when nonzero (overrides the global TrailSlope).
TrailLock Optional profit lock percentage when nonzero (overrides the global TrailLock).
TrailStep Optional autotrailing step width when nonzero (overrides the global TrailStep).
function Optional pointer of an int function for micro-managing the trade (see TMF).
V0 ... V7 Up to 8 optional numerical variables as further arguments to the TMF.

Returns:

TRADE* - a pointer to the created trade struct (see include\trading.h for the definition of the TRADE struct), or 0 when no trade could be entered because the trade volume was zero, trading was disabled (f.i. weekend, time frame, or lookback period), or the trade was rejected by the broker. For pending trades a nonzero pointer is always returned.

Remarks:

Example 1: Simple SMA crossing

function run()
{
  vars Price = series(priceClose());
  vars SMA100 = series(SMA(Price,100));
  vars SMA30 = series(SMA(Price,30));
 
  if(crossOver(SMA30,SMA100))
    enterLong();
  else if(crossUnder(SMA30,SMA100))
    enterShort();
}

Example 2: Grid trading script

// helper function for finding trades at a grid line
bool noTradeAt(var Price,var Grid,bool IsShort) 
{
  for(open_trades)
    if((TradeIsShort == IsShort)
      and between(TradeEntryLimit,Price-Grid/2,Price+Grid/2))
        return false; // trade found
  return true; // no open/pending trade at that price
}
  
function run() 
{
  BarPeriod = 1440;
  Hedge = 2; 
  EntryTime = ExitTime = 500;  
  var Price;
  var Grid = 100*PIP; // grid spacing
  var Current = priceClose();
// place pending trades at 5 grid lines above and below the current price
  for(Price = 0; Price < Current+5*Grid; Price += Grid) {
    if(Price < Current-5*Grid)
      continue;
    if(Price < Current and noTradeAt(Price,Grid,true))
      enterShort(0,Price,0,Grid);      
    else if(Price > Current and noTradeAt(Price,Grid,false))
      enterLong(0,Price,0,Grid);
  }
}

Example 3: Using a trade management function

// TMF that adjusts the stop in a special way 
int TrailingStop()
{
// adjust the stop only when the trade is in profit
if(TradeProfit > 0)
// place the stop at the lowest bottom of the previous 3 candles TradeStopLimit = max(TradeStopLimit,LL(3));
// plot a line to make the stop limit visible in the chart
plot("Stop",TradeStopLimit,MINV,BLACK); // return 0 for checking the limits
return 0;
} // using the trade function function run() { ... Lots = 1; Stop = 10*PIP; Algo = "SIG"; if(crossOver(MySignal,MyThreshold)) enterLong(TrailingStop); ... }

See also:

exitLong/Short, tradeUpdate, Lots, Risk, Entry, Stop, EntryTime, ExitTime, BarMode, TMF, Fill, Hedge

► latest version online

call

call (int Mode, void* Function, int P1, var P2)

Put a function on a scheduler in order to to be exeuted at a certain event. Two parameters, int and var, can optionally be passed to the function.

Parameters:

Mode Run the function: 1 when the system is idle, 2 at the next incoming tick, 4 after closing a trade, +16 for repeating.
Function Pointer to a script-defined function.
P1 int variable to be passed to the function as first parameter, or 0 when the function has no parameters.
P2 var variable to be passed to the function as second parameter, or 0 when the function has no second parameter.
 

Remarks:

Example:

void calltest(int a,var b)
{
  printf("\nCalled with (%i,%.2f) at bar %i",a,b,Bar);
}

void run()
{
  ...
  call(1,calltest,2,3.45);
  ...
}

See also:

 run, lock, quit, version, wait, click

► latest version online

Candle patterns

Traditional Candle Patterns

Japanese rice market candle patterns, returning an integer value of -100 for bearish patterns, +100 for bullish patterns, and 0 for no pattern match. They use the current asset price series and detect the pattern at the current bar. All candle pattern functions are listed below in alphabetical order. They are based on the TA-Lib indicator library by Mario Fortier (www.ta-lib.org).

Disclaimer: Traditional candle patterns are implemented in Zorro for the sake of completeness, but are not really recommended for serious algorithmic trading. The patterns have been established by Japanese traders for the local rice markets in the 18th century. They probably were indeed useful back then. But no serious tests found any predictive value in any of the patterns for today's stock and forex markets. If you still want to use them, be aware that the same price curve can produce very different candle patterns dependent on time zone, bar mode, and price type. Many of the patterns won't appear in assets that are traded around the clock, such as forex pairs, because their candles have normally no difference between close and next open. For finding patterns with real predictive value, use the pattern analyzer.

CDL2Crows(): int

Two Crows, a bearish candle pattern.

CDL3BlackCrows(): int

Three Black Crows.

CDL3Inside(): int

Three Inside Up/Down.

CDL3LineStrike(): int

Three-Line Strike.

CDL3Outside(): int

Three Outside Up/Down.

CDL3StarsInSouth(): int

Three Stars In The South.

CDL3WhiteSoldiers(): int

Three Advancing White Soldiers.

CDLAbandonedBaby(var Penetration): int

Abandoned Baby. Parameters: Penetration (Percentage of penetration of a candle within another candle).

CDLAdvanceBlock(): int

Advance Block.

CDLBeltHold(): int

Belt-hold.

CDLBreakaway(): int

Breakaway. Bullish + Bearish.

CDLClosingMarubozu(): int

Closing Marubozu.

CDLConcealBabysWall(): int

Concealing Baby Swallow.

CDLCounterAttack(): int

Counterattack.

CDLDarkCloudCover(var Penetration): int

Dark Cloud Cover. Parameters: Penetration (Percentage of penetration of a candle within another candle).

CDLDoji(): int

Doji, single candle pattern. Trend reversal.

CDLDojiStar(): int

Doji Star. Bullish + Bearish.

CDLDragonflyDoji(): int

Dragonfly Doji.

CDLEngulfing(): int

Classic Engulfing Pattern. Bullish + Bearish.

CDLEngulfing0(): int

Engulfing pattern including close-open equality, therefore also usable for Forex. Bullish + Bearish. Source code in indicators.c.

CDLEveningDojiStar(var Penetration): int

Evening Doji Star. Parameters: Penetration (Percentage of penetration of a candle within another candle).

CDLEveningStar(var Penetration): int

Evening Star. Parameters: Penetration (Percentage of penetration of a candle within another candle).

CDLGapSideSideWhite(): int

Up/Down-gap side-by-side white lines.

CDLGravestoneDoji(): int

Gravestone Doji.

CDLHammer(): int

Hammer. Bullish.

CDLHangingMan(): int

Hanging Man. Bearish.

CDLHarami(): int

Harami Pattern. Bullish + Bearish.

CDLHaramiCross(): int

Harami Cross Pattern.

CDLHignWave(): int

High-Wave Candle.

CDLHikkake(): int

Hikkake Pattern. Bullish + Bearish.

CDLHikkakeMod(): int

Modified Hikkake Pattern.

CDLHomingPigeon(): int

Homing Pigeon.

CDLIdentical3Crows(): int

Identical Three Crows.

CDLInNeck(): int

In-Neck Pattern.

CDLInvertedHammer(): int

Inverted Hammer.

CDLKicking(): int

Kicking.

CDLKickingByLength(): int

Kicking - bull/bear determined by the longer marubozu.

CDLLadderBottom(): int

Ladder Bottom.

CDLLongLeggedDoji(): int

Long Legged Doji.

CDLLongLine(): int

Long Line Candle.

CDLMarubozu(): int

Marubozu.

CDLMatchingLow(): int

Matching Low.

CDLMatHold(var Penetration): int

Mat Hold. Parameters: Penetration (Percentage of penetration of a candle within another candle).

CDLMorningDojiStar(var Penetration): int

Morning Doji Star. Parameters: Penetration (Percentage of penetration of a candle within another candle).

CDLMorningStar(var Penetration): int

Morning Star. Parameters: Penetration (Percentage of penetration of a candle within another candle).

CDLOnNeck(): int

On-Neck Pattern.

CDLOutside(): int

Engulfing including wicks. Bullish + Bearish. Source code in indicators.c.

CDLPiercing(): int

Piercing Pattern.

CDLRickshawMan(): int

Rickshaw Man.

CDLRiseFall3Methods(): int

Rising/Falling Three Methods.

CDLSeperatingLines(): int

Separating Lines.

CDLShootingStar(): int

Shooting Star.

CDLShortLine(): int

Short Line Candle.

CDLSpinningTop(): int

Spinning Top.

CDLStalledPattern(): int

Stalled Pattern.

CDLStickSandwhich(): int

Stick Sandwich.

CDLTakuri(): int

Takuri (Dragonfly Doji with very long lower shadow).

CDLTasukiGap(): int

Tasuki Gap.

CDLThrusting(): int

Thrusting Pattern.

CDLTristar(): int

Tristar Pattern.

CDLUnique3River(): int

Unique 3 River.

CDLUpsideGap2Crows(): int

Upside Gap Two Crows.

CDLXSideGap3Methods(): int

Upside/Downside Gap Three Methods.
 

Returns:

-100 for a bearish pattern, +100 for a bullish pattern, and 0 for no pattern match at the current bar.

Remarks:

Example:

function run()
{
  set(PLOTNOW);
MaxBars = 500;
PlotScale = 8;
// mark patterns with triangles on the chart if(CDLDoji())
plot("Doji",1.002*priceHigh(),TRIANGLE4,BLUE);
if(CDLHikkake() > 0)
plot("Hikkake+",0.998*priceLow(),TRIANGLE,GREEN);
if(CDLHikkake() < 0)
plot("Hikkake-",1.002*priceHigh(),TRIANGLE4,RED); // go long 3 bars on a bullish Hikkake, short on a bearish Hikkake LifeTime = 3; if(CDLHikkake() > 0) enterLong(); else if(CDLHikkake() < 0) enterShort(); }

See also:

Indicators, curve form detection, pattern analyzer.

► latest version online ccyStrength

Currency strength functions

ccySet(var Strength)

Adds the given Strength value to the currency and subtracts it from the counter currency of the current Forex pair. The Strength value can be any indicator or other price function that indicates a "strength" or "weakness" of a currrency, for instance ROC or RSI. 

ccyReset()

Resets the stored strengths of all currencies. Usually called at the begin of every bar before the strengths are summed up by ccySet.

ccyStrength(string Currency): var

Returns the average strength of the given Currency (3 letters, f.i. "USD"). The average strength is the strength sum of all Forex pairs beginning with Currency, minus the strength sum of all Forex pairs ending with Currency, divided by the number of Forex pairs. If a currency pair is given (7 letters, f.i. "EUR/USD"), the function returns the difference of both average strengths.

ccyMax(): string

Returns the currency pair with the strongest currency and the weakest counter currency.

ccyMin(): string

Returns the currency pair with the weakest currency and the strongest counter currency.

Parameters:

Strength Strength value, for instance the price rate of change. Must be in the same range for all currency pairs.
Currency Either a single currency ("USD") or a currency pair ("EUR/USD").

Remarks:

Example:

// Currency Strength Strategy /////////////////////
// Exploits price shocks f.i. by CHF cap and Brexit

function run()
{
  BarPeriod = 60;
  ccyReset();	// reset strengths at begin of any bar
  string Name;
  while(Name = (loop(Assets)))
  {
    if(assetType(Name) != FOREX) 
      continue; // Currency pairs only
    asset(Name);
    vars Prices = series(priceClose());
    ccySet(ROC(Prices,1)); // store price change as strength
  }
  
// get currency pairs with highest and lowest strength difference
  string Best = ccyMax(), Worst = ccyMin();
  var Threshold = 1.0;

  static char OldBest[8], OldWorst[8];	// static for keeping contents between runs
  if(*OldBest && !strstr(Best,OldBest)) { // new strongest asset?
    asset(OldBest);
    exitLong();
    if(ccyStrength(Best) > Threshold) {
      asset(Best);
      enterLong();
    }
  } 
  if(*OldWorst && !strstr(Worst,OldWorst)) { // new weakest asset?
    asset(OldWorst);
    exitShort();
    if(ccyStrength(Worst) < -Threshold) {
      asset(Worst);
      enterShort();
    }
  }

// store previous strongest and weakest asset names  
  strcpy(OldBest,Best);
  strcpy(OldWorst,Worst);
}

See also:

asset, ROC

► latest version online cdf, qnorm

cdf(var x): var

Cumulative distribution function; the probability that a Gaussian (normal) distributed variable takes a value less than or equal to x. Often used to compress the variable to the 0..1 range. Sometimes also referred to as pnorm(x).

erf(var x): var

Error function; the probability that a Gaussian (normal) distributed variable falls in the interval [-x, x].

qnorm(var x): var

The inverse cdf function; returns the value whose cumulative distribution matches the probability x.

dnorm(var x, var mu, var sigma): var

Gaussian probability; returns the probability of the value x in a normal distribution with mean mu and variance sigma

Parameters:

x - any var.

Example:

x = cdf(1); // x is now 0.84 
x = cdf(-1); // x is now 0.16

See also:

sign, abs, randomize

► latest version online

filter

filter (var* Data, int Length, var Kernel[6]) : var*

Applies a 5-element convolution filter with the given Kernel to the Data array. Each Data element is added to its local neighbors, weighted by the Kernel, using this formula: Data[i] = Kernel[0]*Data[i-2] + Kernel[1]*Data[i-1] + Kernel[2]*Data[i] + Kernel[3]*Data[i+1] + Kernel[4]*Data[i+2] + Kernel[5].  This function can be used for data smoothing, summing up, subtraction, sharpening, edge enhancement, or similar data array manipulation operations.

Parameters:

Data Array or series to be filtered.
Length Number of elements to be filtered.
Kernel Vector of 5 weights plus constant to be added.

Returns

Modified Data
 

renorm (var* Data, int Length, var Sum) : var*

Modifies the Data array by multiplying all elements with a factor so that they sum up to Sum. This function can be used to normalize a list of weights to a certain total.

Parameters:

Data Array or series to be normalized.
Length Number of elements to be normalized.
Sum Resulting sum of elements.

Returns

Modified Data
 

Remarks:

Identity { 0, 0, 1, 0, 0, 0 } 
Sharpen { -1, -1, 4, -1, -1, 0 }  
Smooth { 0.2, 0.2, 0.2, 0.2, 0.2, 0 }
Gaussian { 0.1, 0.2, 0.4, 0.2, 0.1, 0 }
Shift to the right { 0, 1, 0, 0, 0, 0 }
Mean subtraction { 0, 0, 1, 0, 0, -mean } 
Fill with constant { 0, 0, 0, 0, 0, constant } 

Example:

var* Filter = { 1,2,3,2,1,0 };
filter(Data,Length,renorm(Filter,5,1));

See also:

predict, advise, polyfit, distribute

► latest version online Chart Viewer and Debugger | The Zorro Project

Chart Viewer and Visual Debugger

A click on the [Result] button after a test run opens the chart viewer and debugger. It allows to zoom and scroll from an overview of the whole simulation down to a single day or hour, and can replay or single step through the strategy for debugging purposes. Charts can be exported and stored as images, or already generated as images..

The chart runs from the begin of the first test cycle (not identical to the StartDate) to the end of the last test cycle. By default it displays equity and drawdown profiles (blue and red), the price curve (black), and the trades of the asset selected with the [Asset] scrollbox. For displaying a different asset, select it with the scrollbox and click [Result] again.

The chart uses the same time zone as the historical data, normally UTC. The left axis is the asset price, the right axis is the profit or loss in account currency units. Any axis runs from the minimum to the maximum value within the chart. Trade entry and exit points are connected with green or red lines for winning or losing trades. A falling green line or a rising red line indicates a short position. For option trades the lines connect the strike prices with underlying exit prices.

Equity and drawdown profiles are summed up from the whole portfolio and averaged over all oversampling cycles. If Capital is invested, it adds to the equity curve dependent on PlotMode settings. In BALANCE mode the blue profile displays the balance rather than the equity, in PIPRETURN mode it's the volume-independent profit in pips. The red "underwater profile" is normally the drawdown, the equity distance from a preceding balance peak. In MAECAPITAL mode it's the adverse excursion from a preceding equity peak and thus 'mirrors' the top of the equity curve. In PL_BENCHMARK mode the equity curve is plotted as a blue line rather than bars, for comparison with buy-and-hold or market profit lines. Additional curves appear either on or below the main chart, depending on plot settings.

Candles are displayed when zooming in; otherwise the price curve is a black line. In the chart below, a short trade was opened on 16 at market open and closed intraday on Aug 8, probably by a trailing stop. A close at bar end by an exit command would have placed the end dot next to the close of the candle.

Chart elements can be switched off either with the buttons on the left side, or by setting their Color to 0.
 

Chart Buttons

Date
Jump to a particular date in the chart
Move
Move mode. Drag the chart with the mouse.
Zoom
Zoom mode. Click on a position in the chart for zooming in.
Total Zoom out to the total view.
Equity

Toggle the equity and underwater curves.

Trades

Toggle the trades.

Candles

Toggle the price curve.

Export
Export the current chart to a .png image.

Debugger Buttons

Step
Restart the script in single step mode. Step forward 1 bar.
Skip Restart the script in single step mode. Jump to the begin of the next trade.
Replay Replay the script, one bar per second. Gives some sort of live trading experience in fast-forward mode.

The price curve with the displayed trades can be selected with the [Assets] scrollbox; afterwards click [Result] again for refreshing the chart. The equity or balance curve results from the sum of all trades of the portfolio. The underlying datasets of a chart can be exported and further evaluated with third party charting software, f.i. an R data analysis package.

In PL_FILE mode, the chart is initally generated as a .png image in the Log folder and displayed with the ZView image viewer. Elements can be removed from the chart image by setting their corresponding Color parameters to 0. The size, scale, and resolution of the chart image can be set up with plot parameters. The number of bars per chart can be limited with the PlotBars variable.

Remarks

Debugging a strategy

You can replay or single step through a strategy for examining the trade behavior in detail. Use the [Asset] scrollbox to select the asset to be observed, then click [Step] or [Replay] for stepping through the backtest or replaying it in reduced speed. [Step] moves one bar forward, [Skip] moves to the next bar at which a trade opens or closes. The two buttons also appear on the Zorro main window.

Debugging opens the chart window at the current position, and another window with a list of open trades:

Single Step Debugging

The trade list has the same format as on the live status page.

The stepwise change of variables and indicators can be visualized either with a watch statement, or by plotting them in the chart. For debugging single loops or function calls, place watch ("!...", ...) statements inside. [Step] will then not proceed to the next bar, but to the next watch statement. Stepwise debugging normally begins at the end of the LookBack period. For beginning at a certain date or bar number, call watch dependent on a condition, f.i. if(date() >= 20150401) watch("!...", ...);. Set the STEPWISE flag for starting the backtest already in debugging mode.

See also:

Testing, Colors, plot, watch, Troubleshooting, Performance Report

► latest version online

clamp

clamp(int x, int lower, int upper): int

clamp(var x, var lower, var upper): var

Returns a variable limited to a lower and upper border.

Parameters:

x, lower, upper - any var or int.

Returns:

x > upper upper
lower <= x <= upper x
x < lower lower

Example:

x = clamp(x,0.,1.); // limit x to 0..1

See also:

sqrt, abs, sign, between

► latest version online

Command, Define

Command[0] .. Command[3]

Contains the numbers passed with the -i option on the command line.

Type:

int 

Define

The name given with the -d option on the command line

Type:

string

Remarks:

Example:

function run()
{
  switch(Command[0]) {
    case 1: doThis(); break;
    case 2: doThat(); break;
  }
  ...
}

See also:

Command line

 

► latest version online color

color (var Value, int Color1, int Color2, int Color3, int Color4) : int

Returns the color corresponding to a position within a range of up to 4 colors; used for plotting heatmaps or multiple curves.

Parameters:

Value The color position within the range in percent; from 0 for color1 until 100 for color4.
Color1..Color4 The color range, starting with color1 and ending with color4. For 2-color or 3-color ranges, color3 and color4 can be omitted. See Colors for the color format.

Returns

Color within the range

 

colorScale (int Color, var Scale) : int

Returns the color multiplied with a scaling factor.

Parameters:

Color The color to be scaled.
Scale The scaling factor in percent, < 100 for reducing the brightness and > 100 for increasing it.

Returns

Color * Factor.

Remarks:

Example:

for(i=0; i<N; i++)
for(j=0; j<N; j++)
plotGraph("Heatmap",j+1,-i-1,SQUARE,color(Correlations[i][j]*100,BLUE,RED));

See also:

plot, Colors

► latest version online Colors

Colors

ColorUp

ColorDn

Colors for the white and black candles (default: ColorUp = 0x00CCCCCC = bright grey, ColorDn = 0x00222222 = dark grey). If both colors are 0, no price candles are plotted in the main chart.

ColorEquity

Color for the equity bars (default: 0x00666699 = blue-grey). If at 0, no equity curve is plotted in the main chart.

ColorDD

Color for the "underwater equity" bars (default: 0x00FF3333 = red). If at 0, no underwater equity is plotted in the main chart.

ColorWin

ColorLoss

Colors for the winning and losing trades (default: ColorWin = 0x0033FF33 = green, ColorLoss = 0x00FF3333 = red). If both colors are 0, no trades are plotted in the main chart.

ColorBars[3]

Colors for the bars on parameter charts. Default: ColorBars[0] = objective (red), ColorBars[1] = wins (blue), ColorBars[2] = losses (blue-grey).

ColorPanel[6]

Default colors for the cells on a control panel. ColorPanel[0] = text background (light grey), ColorPanel[1] = number background (reddish), ColorPanel[2] = editable (white), ColorPanel[3] = button (blue), ColorPanel[4] = highlighted text (red), ColorPanel[5] = grayed out text (grey).

Range:

0..0xFFFFFFFF

Type:

long

Remarks:

Example:

function run()
{
  ColorEquity = 0x0000FF00; // green equity bars
  ColorDD = 0; // no underwater equity
  ...
}

See also:

plot, color, plotScale, performance

 

► latest version online Option Combos

Option Combos

The functions below can be used for handling combinations of options, such as Spreads, Straddles, Strangles, Condors, Butterflies, etc. Option combos are mainly used for limiting risk. For an introduction to options trading, see Financial Hacker. The image below shows the profit or loss of an Iron Condor combo dependent of the underlying price, at expiration (blue) and halfway to expiration (red). 


Iron Condor payoff diagram (Payoff.c)

 

combo (CONTRACT* C1, int N1, CONTRACT* C2, int N2, CONTRACT* C3, int N3, CONTRACT* C4, int N4): int

Combines 2, 3, or 4 option contracts C1..C4 to a combo. The number and trade direction of contracts are given by N1..N4; use negative numbers for selling and positive numbers for buying, f.i. -2 for short selling two contracts. C3/N3 and C4/N4 can be 0 for combos with only 2 or 3 legs. Returns the number of combo legs, i.e. the number of different contracts of the combo. If all numbers N are 0, or if any CONTRACT* pointer is zero while its associated number N is nonzero, the combo is disabled and the function returns 0. Source code in contract.c.

comboAdd (CONTRACT* C, int N): int

Adds a copy of the option contract C to the current combo. Amount and trade direction of the contract is given by N; use a negative number for selling and a positive number for buying. Returns the number of the current combo leg. If C is 0, the function deletes all contracts from the combo and returns 0.

comboLegs (): int

Returns the number of legs of the current combo that was defined before with combo() or comboAdd() calls.

comboContract (int Leg): CONTRACT*

Pointer to the CONTRACT struct of the combo leg with the given number (1..4). Macro defined in contract.c.  

comboLeg (int Leg): int

Selects the contract of the combo leg with the given number (1..4), and returns the number assigned to that contract as defined in the combo() function. The number is negative for selling and positive for buying, so it can be directly used for the Lots parameter passed to enterLong

comboStrike (int Leg): var

Returns the strike value of the given combo Leg (1..4). Source code in contract.c.

comboProfit (var Price, int Sign): var

Returns the profit or loss of the current bought (Sign == 1) or sold (Sign == -1) combo when expiring at the given Price. Type, fAsk, and fBid of all contracts of the combo must be set, so contractPrice should have been called before; the other contract parameters don't matter. Multiplier and transaction costs are not included. Source code in contract.c.

comboRisk (int Sign): var

Returns the maximum possible loss of the current bought (Sign == 1) or sold (Sign == -1) combo. For combos with unlimited loss, returns the loss when the price drops to zero or ends up at twice the strike, whichever is worse. Type, fAsk, fBid, and fStrike of all contracts of the combo must be set, so contractPrice must have been called before; the other contract parameters don't matter. Multiplier and transaction costs are not included. Source code in contract.c.

comboPremium (int Sign): var

Returns the premium of the current bought (Sign == 1) or sold (Sign == -1) combo, also referred to as the combo's ask and bid price. Positive premium is earned, negative premium is paid. If the combo consists of long and short positions, its ask price can be lower than its bid price. fAsk and fBid of all contracts of the combo must be set, so contractPrice should have been called before; the other contract parameters don't matter. Multiplier and Transaction costs are not included. Source code in contract.c.

comboType (): int

Returns the combo type: 1 for a single put, 2 for a single call, 6 for a call spread, 7 for a strangle (call and put), 8 for a put spread, 13 for a call butterfly (3 calls), 14 for a put butterfly (3 puts), and 20 for a 4-contract combo such as a condor (2 calls and 2 puts). Source code in contract.c.

comboMargin (int Sign, int AssetType): var

Calculates the margin cost of the current bought (Sign == 1) or sold (Sign == -1) combo with the given AssetType. Uses the margin formula on the Interactive Broker's US Options Margin Requirements page. Supports single calls and puts, call spread, put spread, strangle (call and put), Butterfly with 3 calls, Butterfly with 3 puts, and Condor (2 calls and 2 puts). Source code in contract.c.

Parameters:

C1..C4 CONTRACT* pointer for leg 1..4, or 0 when the leg is not used in the combo.
N1..N4 Number of contracts for leg 1..4, negative for selling and positive for buying contracts. 0 when the leg is not used in the combo.
Leg Number of the combo leg, 1..4, starting with 1 for the first contract.
Sign Position sign, -1 for short and 1 for long.
AssetType 1 for a currency, 2 for an index, 3 for stocks and ETFs. The margin for futures must be individually calculated.

 

comboPrint (int To)

Prints the parameters of the current combo contracts to the destination To, as in print(To,...).  

plotCombo (var HistVol, var Min, var Max, int Days, int Modus)

Plots the payoff diagram of the current combo with volatility HistVol from price Min to price Max. Modus 0 initializes the plot and plots a zero line, Modus 1 plots the diagram at expiration, Modus 2 at the given Days into the lifetime (R and RQuantLib required), and Modus 3 plots the Delta. Type, strike, ask, bid, and expiry in days must be set in all contracts of the combo. Two different expiration periods are supported. Source code in contract.c; usage example in PayOff.c.
 

Remarks:

Example (see also Workshop 8 and Payoff.c):

if(combo(
  contract(CALL,Days,Strike),1, 
  contract(PUT,Days,Strike),1,
  0,0,0,0)) // Strangle combo
{
   enterLong(comboLeg(1));
   enterLong(comboLeg(2));
}

See also:

contract, contract variables, Workshop 8

 

► latest version online Zorro command line

Zorro command line

Zorro can be started directly from an external program, a shortcut, a batch file, the Windows command shell, or a PHP exec call on a Windows server. The command line looks like this:

"C:\Users\YourName\Zorro\Zorro.exe" [filename] [options]

For starting it manually with command line options, use the Windows command prompt, navigate to your Zorro folder (cd command), type in a command line, for instance Zorro -run MyScript, and hit the [Enter] key.

If a script name is given, Zorro will open and start it. This allows to automatically run scripts or display historical data by clicking on the file. filename (without blanks or special characters) must be either an existing script in the StrategyFolder given in Zorro.ini, or a historical data file in .t1, .t6, or .t8 format. If a historical data file is given, the Chart script is started with that file. While the script is running, the COMMAND status flag is set to indicate that it's run from the command line. Several Zorro functions use this way to start other Zorro processes, f.i. for multicore training, retraining, or retesting. External tools, like the genetic optimizer, also use the command line.

You can give a command line option either directly in the Windows command prompt, or with the Windows [Run] function, or by editing the properties of a Zorro shortcut on the Windows desktop. For this, right click the shortcut icon and select Properties (for icons in the task bar you need to hold the [Shift] key). Under the shortcut tab you'll see the Target field containing the exact location of Zorro.exe within quotation marks. Add command line options, such as '-diag', after the last quotation mark, and save the modified shortcut with [Apply].

Besides the file name, the following command line options can be given (Zorro S only):

-run

Run the script in [Test] mode, and exit afterwards.

-train

Run the script in [Train] mode, and exit afterwards.

-trade

Run the script in [Trade] mode.

-edit

Select the script and open it in the editor.

-h

In combination with -run, -train, or -trade: run with minimized Zorro window.

-w offset

Shift the Zorro window by the given number of pixels to the right.

-stay

In combination with -run, -train, or -trade: don't close Zorro afterwards.

-a assetname

Select the given asset from the [Assets] scrollbox.

-c accountname

Selects the account with the given name from a user defined account list.

-d definename

Passes a #define statement with the given definename to the script, and stores the definename also in the Define string (Zorro S only). This way, a script started with the command line can behave or compile differently. Only a single #define can be set through the command line.

-u string

Allows to enter strings or other content that can be parsed from the command line with the report funcrion.

-i number

Passes an integer number to the script (Zorro S only) that can be read through the Command variable. Up to 4 numbers can be transferred to the script, each preceded by "-i". This way, the same script started with the command line can behave in different ways.

-x 

Compiles the selected script to an executable, like the EXE flag, and exit afterwards (Zorro S only).

-diag

Starts Zorro in 'black box' mode, as if the DIAG flag was set. Used for debugging the startup behavior. A message "diagnostics mode" will appear in the message window at startup, and the DIAG flag is automatically set in subsequent scripts. Startup events are recorded to a file "diag.txt" in the Zorro main folder. Black box recording strongly reduces the program speed, so do not use this feature unnecessarily.

-quiet

Don't open a message box when Zorro encounters a fatal error; print the error in the message window instead. Don't wait 3 seconds after a command line run before closing the Zorro window.


The command line can be evaluated with the report function, so arbitrary user-defined command line options can be added. Use the ExitCode variable for returning with a script-defined exit code or error level.
   

Examples

Start a re-training run with the Z3 strategy.

Zorro.exe -train Z3

From outside the Zorro folder, run the script pricedownload.c with the selected asset "USD/CAD" in test mode.

"c:\Users\MyName\Zorro\Zorro.exe" -run pricedownload -a USD/CAD

Start Zorro in diagnostics mode. A file diag.txt is generated in the Zorro folder.

Zorro.exe -diag

A .bat file in the Zorro folder that trains 3 scripts when clicked on.

Zorro -train MyStrategy1
Zorro -train MyStrategy2
Zorro -train MyStrategy3

A .bat file that runs a Zorro script 50 times in 2 nested loops and passes the loop numbers to Command[0] and Command[1].

for /l %%x in (1, 1, 5) do (
for /l %%y in (1, 1, 10) do (
  @echo Loop %%x %%y
  Zorro -run MyScript -i %%x -i %%y
)
)
pause

 

See also:

Testing, Training, Trading, Zorro.ini, ExitCode, report, Command

► latest version online

Comparisons

Comparisons

A comparison is a special type of expression that delivers either true (nonzero) or false (zero) as a result. There are special comparison operators for comparing variables or expressions:

== True if the expressions left and right of the operator are equal.
!= True if the expressions left and right of the operator are not equal.
> True if the expression left of the operator is greater than the expression right of the operator.
>= True if the expression left of the operator is greater than or equal to the expression right of the operator.
< True if the expression right of the operator is greater than the expression left of the operator.
<= True if the expression right of the operator is greater than or equal to the expression left of the operator.
and, && True if the expressions left and right of the operator are both true.
or, || True if any one of the expressions left and right of the operator is true.
not, ! True if the expression right of the operator is not true.
() Brackets, for defining the priority of comparisions. Always use brackets when priority matters!

Remarks:

Examples:

10 < x // true if x is greater than 10
(10 <= x) and (15 => x) // true if x is between 10 and 15
!((10 <= x) and (15 => x)) // true if x is less than 10 or greater than 15 (lite-C only)

See also:

 Functions, Variables, Pointers, Expressions

 

► latest version online Content | Zorro Algo Trading Manual

Zorro Algo Trading Manual - Content Overview

Open the local Zorro manual with the [Help] button on the Zorro panel or with [Shift-F1] in the editor. The online Zorro manual is available on https://zorro-project.com/docs.php. The manuals are not identical: The online manual can contain new features that are currently in beta test and not yet available in the Zorro release. If in doubt, use the local manual. For searching items, click the [Search] tab in the local manual. For finding a function or variable, click the [Index] tab. For a comprehensive introduction to working with Zorro, get the Black Book.

This manual covers:

When looking through the manual the first time, you will be probably overwhelmed by the tons of functions, variables, and modules that affect test, training, trading, debugging, and analyzing. They result from the many different trading methods and algorithms developed for clients over the years. But don't worry. Zorro works well with default settings, and whichever market you're trading or algorithm you're using, you'll only need a small subset of the available functions.

See also

Zorro Home, What's new?, Get started ► latest version online Futures and options

Options, Futures, FOPs

The following functions can be used for selecting, analyzing, and trading options, futures, and options on futures (FOPs). Since they have additional parameters, namely type, strike, and expiration, options trading is a bit more complex than trading the underlying. For a brief introduction to options trading, read the Black Book chapter 8, or visit Financial Hacker.

contractUpdate (string Name, int Handle, int Mode): int

Loads a new contract chain of type Mode (see below) for the current underlying and the current day. The contract chain contains all options, futures, or FOP contracts available at a certain date. It is required for all the subsequent contract functions and for entering or exiting contract positions.
  Contract chains are only loaded when the LookBack period is over and real trading begins. They are loaded individually per asset and can be directly accessed with the Contracts pointer. Chains are automatically sorted at loading: first by expiration date, then by strike. This allows fast finding a certain option or combo. NumContracts is set to the number of contracts in the chain, which is also returned. If no contract chain for the current or given time is found, 0 is returned.
  In [Test] or [Train] mode the chain is either read out of a contract chain file given by Name.t8 at or before the current time, or from a previously loaded dataset of contracts when its Handle is nonzero. Name can contain month or day numbers for monthly or daily historical contract files. If Name is 0, the current asset name is used. If the global Now variable is nonzero in [Test] or [Train] mode, it is used for filtering contracts instead of the current time. ContractRow is set to the row of the first found contract in the dataset, which allows to load extra data, such as greeks, from a second parallel dataset.
  In [Trade] mode the chain for the symbol Name is either downloaded from the broker API, or - when Handle is nonzero - filled from a previoulsy loaded dataset with no date/time filtering. If Name is 0, the current asset name is used. For contracts that are no US stocks or traded on multiple exchanges, Name should be a symbol with exchange, currency, and for futures the expiration date (e.g. "N225-FUT-20220609-225-OSE.JPN-JPY"). For limiting the chain to a particular trading class, use the SET_CLASS command. 

contractRecord (string Name, var MinStrike, var MaxStrike, int MinDays, int MaxDays)

Appends the current contract chain to the begin of the .t8 contract history file in [Trade] mode. This allows recording live contract chains for re-training or other purposes. The prices of all recorded contracts are loaded from the broker API. Because loading contract prices can take a long time (up to 10 seconds per option contract with the IB API), the strike range and life time can be limited with the MinStrike, MaxStrike, MinDays, MaxDays parameters. Only contracts that fall into the limits and have a nonzero price are recorded. If the limits are 0, prices for the whole chain are loaded. Example: contractRecord(0,priceClose()-50,priceClose()+50,1,70); appends all contracts within a +/-50 strike range and up to 70 days expiration at the begin of the .t8 file of the current asset.

contract (int Type, int Expiry, var Strike): CONTRACT*

Selects the option or future contract from the current option chain that matches exactly the given Type (including FUTURE or EUROPEAN), Expiry date, and Strike value. Returns a CONTRACT pointer to the found contract, or 0 when no such contract was found. If a contract is selected, enterLong and enterShort will buy or write (sell short) the contract instead of the underlying asset. The function sets up ContractStrike, ContractExpiry and other contract variables, and sets ContractRow to the row of the selected contract in the historical dataset or in the options chain. Calling asset(), contractUpdate(), or contract(0,0,0) deselects the contract and allows again trading with the underlying.

contract (int Type, int Days, var Strike): CONTRACT*

As above, but selects the option or future contract that has an expiration of at least the given minimum life time in Days and strike closest to the given Strike value. Returns the CONTRACT pointer, or 0 when no contract with matching Type, at least Days life time, and a strike closer than 10% to Strike was found. 

contract (int N): CONTRACT*

As above, but selects the Nth contract from the current contract chain, where N = 1..NumContracts. If N = 0, the currently selected contract is deselected in order to allow trading with the underlying.

contract (CONTRACT*): CONTRACT*

As above, but selects directly the given option or future contract, and does not alter ContractRow.

contract (TRADE*): CONTRACT*

As above, but selects the contract from a contract trade. Returns 0 if no contract for this trade was found, which can happen at the expiration day or when the chain was not updated. Otherwise trade variables and contract variables are available for the trade and contract. 

contractNext (int Type, int Expiry, var Strike): CONTRACT*

As above, but selects and returns the option or future contract with the Type and Expiry, but the next-higher strike than the given Strike value. If Strike is negative, selects and returns the contract with the next-lower strike value. 

contractFind (int Type, int Days, var Find, int N): CONTRACT*

contractFind (int Type, int Days, var Find, int N, var MinStrike, var MaxStrike): CONTRACT*

contractFind (int Type, int Expiry, var Find, int N): CONTRACT*

contractFind (int Type, int Expiry, var Find, int N, var MinStrike, var MaxStrike): CONTRACT*

As above, but selects the option or future contract with the given Type, closest to the given minimum life time in Days, and a parameter closest to the given Find value. The parameter can be selected with N: 1 = ask, 2 = bid, 3 = fVal, 4 = fVol, 5 = fUnl, 6 = strike, 7 = strike distance from the underlying. The strike range can be optionally limited to MinStrike and MaxStrike for speeding up the function. Returns a CONTRACT pointer, or 0 when no contract was found. If a selected parameter is not yet available, such as ask, bid, or underlying price in live trading, it is automatically retrieved from the broker API. Depending on the number of contracts that match the expiration and strike range, this function can then take a long time (up to 10 seconds per contract) on the first call in live trading after loading a new contract chain. Subsequent calls are faster.

contractDays (CONTRACT*): var

contractDays (TRADE*): var

Returns the fractional number of calendar days until contract or trade expiration (see ExpiryTime). Returns a negative number when the expiration time is in the past. Note that the exact time to expiration depends on the time zone and market close time of the exchange where the contract is traded, so adapt ExpiryTime to your exchange when the exact life time in hours and minutes is needed. Note that many exchanges have restrictions to trading contracts at their expiration day.

contractPrice (CONTRACT*): var

contractPrice (TRADE*): var

Selects the contract and updates the current ask, bid, and underlying price, fill amount, trade profit, either from the broker API, or from historical data by the last contractUpdate call. Only valid as long as the contract is not expired. Returns the bid/ask mid price per underlying unit. Updating contract prices can be slow, depending on broker, Some broker plugins, such as IB, support a 'fast mode' by brokerCommand(SET_PRICETYPE,8); this can remarkably speed up the price update. For retrieving the underlying price in live trading, the GET_UNDERLYING command is used; for updating the fill amount, the BrokerTrade function is used and must be supported by the API. If the contract is not traded or the market data not subscribed, the function returns 0. Contract prices of open positions are automatically updated at any bar. If the underlying price is not available, it is updated from the current asset price. The underlying price is automatically copied to all contracts of the current chain with the same expiration date.  

contractUnderlying (): var

Returns the unadjusted underlying price, in [Trade] mode from the current contract, otherwise from the fUnl element of the first contract of the chain. If the underlying price is not available, returns the price of the currently selected asset.Make sure with contractUpdate and contractPrice that the contract is up to date. Note that FOPs have no common underlying price, but fUnl depends on the expiration and can be different for any contract. Source code in contract.c.

contractPosition (TRADE*): int

Returns the current number of open lots or open contracts of the given trade (negative values for a short position). Can be used for determining if a certain contract was expired or exercised by the broker. In live trading the GET_POSITION command must be supported by the broker API, and no other trades with the same contract type, strike, and expiry must be opened.

contractCheck (TRADE*, int Mode): int

Checks the option position on the broker account in live [Trade] mode. If it is still open, returns the total number of open contracts (negative for a short position). Otherwise it checks for an open position of the underlying, and returns the position size. If Mode is at 1 and no open contract was found, the trade is automatically closed with a "Lapsed" message. If Mode is at 3 and an underlying position was found, it is sold at market. Mode = 3 thus replicates the backtest behavior of expired options. The function can be called regularly in a for(open_trades) loop for keeping the positions between broker and strategy in sync. The GET_POSITION command must be supported by the broker API, and no other trades with the same contract type, strike, expiry, and underlying must be opened.

contractRoll (TRADE*, int Days, var Strike, function TMF): TRADE*

Rolls a closed option or future contract by opening a duplicate with the same type, volume, stop loss and profit distances, the given number of Days until expiration, the given Strike (0 for using the previous strike) and an optional trade management function. Returns a pointer to the new trade, or 0 if the contract could not be rolled. Source code in contract.c.

contractSellUnderlying (): int

Checks in [Trade] mode if any underlying position of the current asset is open, and if so, sells it at market. Returns the number of lots, or 0 when no underlying position was found. Source code in contract.c.

contractExercise (TRADE*)

Exercises an option contract. In the backtest, the option is closed at its intrinsic price (see remarks). In live trading, an order to exercise the option is sent to the broker. It is normally not immediately executed, but pending. Use contractCheck() for closing the trade and selling the underlying as soon as the order was executed. Don't call this function for European options before expiration, or for options that are not in the money.
 

contractVal (CONTRACT*, var Price, var HistVol, var Dividend, var RiskFree, var* Delta, var* Gamma, var* Vega, var* Theta, var* Rho): var

Returns the theoretical value and optionally the greeks of the given option contract at the given unadjusted Price of the underlying. Type, fStrike, and Expiry of the contract must be set; the other parameters don't matter. Expiry can be either an expiration date in the YYYYMMDD format, or alternatively the number of calendar days until expiration. HistVol is the underlying annual volatility of the log returns, f.i. by Volatility or VolatilityOV (usually over 20 days). Dividend is the continuous annual dividend yield per year of the underlying, as a fraction in the 0..1 range. RiskFree is the annual risk-free yield as a fraction in the 0..1 range, f.i. by yield()/100.
  Delta is the impact of an underlying price change on the option value, in the 0..1 range for calls and 0..-1 for puts. Gamma is the impact on Delta. Vega is the impact of a change of the underlying volatility, Theta is the impact of a change of the expiration time, and Rho is the impact of a change of the risk-free yield.
  The function uses R and the RQuantLib package; both must be installed (see remarks below). Source code in contract.c.

contractVol (CONTRACT*, var Price, var HistVol, var Value, var Dividend, var RiskFree): var

Returns the implied volatility (IV) of the given option contract with the given Value. For the parameters, see contractVal. The function uses R and the RQuantLib package; both must be installed on the trading PC (see remarks below). Source code in contract.c. The implied volatility is an estimate of the future volatility of the underlying, based on the current option parameters. If the contract has no value or if the strike price is too far or on the wrong side of the asset price, the function returns 0

contractDelta (int Type, int Days, var Price, var HistVol, var RiskFree, var Strike): var

Returns the theoretical delta of a contract of given Type with the given Days until expiration, unadjusted underlying Price, volatility HistVol, RiskFree yield rate, and Strike. Uses the formula Delta = cdf((ln(Price/Strike) + (r + (sigma^2)/2) * T) /(sigma * sqrt(T))). Source code in contract.c. Delta is the first derivative of the option value with respect to the underlying price, and can be used as a proxy of the probability to expire in the money. Note that some platforms and brokers use individual methods for calculating HistVol and thus produce different Delta values for the same contracts.

contractStrike (int Type, int Days, var Price, var HistVol, var RiskFree, var Delta): var

Returns the theoretical strike value of a contract of given Type with the given Days until expiration, unadjusted underlying Price, volatility HistVol, RiskFree yield rate, and Delta. Uses the formula Strike = Price * exp(-qnorm(Delta) * sigma * sqrt(T) + (r + (sigma^2)/2) * T). If a positive Delta is entered for a put, it is converted to the equivalent negative delta, so the same Delta value can be used for selecting put and call contracts. Source code in contract.c.

contractIntrinsic (CONTRACT*, var Price): var

contractIntrinsic (TRADE*, var Price): var

Returns the intrinsic value of the option contract per underlying unit at the given Price of the underlying. The intrinsic value of a call option is the difference between price and strike; for put options it's the difference between strike and price. A positive difference means that the option is in the money, a negative difference means it's out of the money. Type and fStrike of the contract or trade must be set; the other parameters don't matter. Source code in contract.c.

contractProfit (CONTRACT*, var Price, var Premium): var

Returns the profit per underlying unit of a contract sold or purchased at the given Premium when expiring at the given underlying Price. Premium is positive for sold, negative for bought contracts. Type and fStrike of the contract must be set; the other parameters don't matter. Trading costs are not included. The calculation of profit or loss is described in the remarks below. Source code in contract.c.

contractMargin (CONTRACT*, int AssetType): var

Calculates the margin cost of the given non-covered short put/call contract for the given AssetType (the margin of long contracts is simply their ask price). Uses the margin formula on the Interactive Broker's US Options Margin Requirements page. Source code in contract.c.

contractLetter (int AssetMonth): string

Returns the letter assigned to the given expiration month (F Jan, G Feb, H Mar, J Apr, K May, M Jun, N Jul, Q Aug, U Sep, V Oct, X Nov, Z Dec). Source code in contract.c.
  

contractPrint ()

Exports the current contract chain to a Data\*.csv file, for diagnostics purposes,

contractPrint (CONTRACT*)

Prints the given contract parameters to the log file for diagnostics purposes, in the order Date, Type, Expiry, Strike, Underlying, Ask, Bid, fVal, fVol. 

plotContract (int N, CONTRACT*, var HistVol, var Min, var Max, int Days, int Modus)

Plots the payoff diagram of N contracts (negative for selling) with volatility HistVol from price Min to price Max. Modus 0 initializes the plot and plots a zero line, Modus 1 plots the diagram at expiration, Modus 2 at the given Days into the lifetime (RQuantLib required), and Modus 3 plots the Delta. Type, strike, ask, bid, and expiry in days must be set in the CONTRACT struct. Source code in contract.c; usage example in PayOff.c.

 

yield(): var

Helper function, returns the current yield rate of 3-months US treasury bills in percent. Can be used as a proxy of the risk-free interest for calculating the values of option contracts (divide it by 100 for using it in contractVal or contractVol). Works in backtest as well as live trading. Uses the dataFromQuandl function. Zorro S required for Quandl data access; enter your Quandl key in the QuandlKey field in Zorro.ini. Source code in contract.c.

yieldCSV(): var

As above, but gets the current yield rate from an already-downloaded treasure history in History\FRED-DTB3.csv.

initRQL(): int

Helper function that initalizes RQuantLib. Call this in the INITRUN when contractVal or contractVol are used.

 

Parameters:

Name The name of the underlying, f.i. "ES" or "SPY", or the symbol - if different to the name - in live trading, or 0 for using the name or symbol of the current asset. For contract chains in backtest mode, use the name of the current .t8 file for loading monthly or daily files.
Handle A number from 1...800 that identifies a previously loaded dataset containing a CONTRACT list with historical options data; or 0 for automatically loading content from a historical file or from the broker API.
Mode PUT|+CALL for options, FUTURE for futures, PUT+CALL+FUTURE for options on futures.
Type The exact contract type to match, a combination of PUT, CALL, FUTURE, EUROPEAN, BINARY (f.i. PUT+EUROPEAN), plus the following optional flags:
+ONLYW3 - select only contracts that expire at the 3rd Friday, i.e. between the 16th and the 21th of the month.
+ONLYMATCH - select only contracts that exactly match the expiration date.
AssetType 1 for a currency, 2 for an index, 3 for stocks and ETFs. The margin for futures must be individually calculated.
Days The minimum number of calendar days until expiration. The closest expiration date will be selected.
Expiry The expiration date in the YYYYMMDD format.
Strike Option strike price, or 0 for futures.
Delta First derivative of the option value with respect to the underlying price. Determines the impact of an underlying price change on the option value, and can serve as a proxy of the probability to expire in the money. 0..1 for calls and 0..-1 for puts.
HistVol The historical annualized volatility of the underlying asset, as a fraction, f.i. 0.2 for 20%. Normally calculated with the Volatility or VolatilityOV indicators from the last 20 days. For the calculation of greeks, implied volatility is often used instead of historical volatility.
Dividend The annual dividend yield of the underlying, as a fraction, f.i. 0.02 for 2% dividend yield.
RiskFree The risk-free interest rate, as a fraction, f.i. yield()/100.
Value The value of the option for determining the implied volatility.
TRADE* The pointer of an option or future trade. Use ThisTrade for the trade pointer in a TMF or trade enumeration loop.

Remarks:

Examples:

// Sell call and put options at 1 stddev of the underlying
// Buy them back 2 days before expiration #include <contract.c> void run() { BarPeriod = 1440; BarZone = EST; BarOffset = 15*60; // trade at 3 pm Eastern time LookBack = 20; if(Init) { assetList("AssetsIB"); assetHistory("SPY",FROM_AV|UNADJUSTED); brokerCommand(SET_CLASS,"SPYW"); } asset("SPY"); Multiplier = 100; if(!NumOpenTotal && !is(LOOKBACK)) { contractUpdate(Asset,0,PUT|CALL); vars Close = seriesC(); int DTE = 6*7; // look for 6-week contracts var Strangle = StdDev(Close,20); CONTRACT* Call = contract(CALL,DTE,Close[0] + Strangle); CONTRACT* Put = contract(PUT,DTE,Close[0] - Strangle); if(Call && Put) { // open single positions, no combo contract(Call); enterShort(); contract(Put); enterShort(); } } // Check expiration and buy them back when in the money for(open_trades) { if(contractDays(ThisTrade) <= 2 && contractIntrinsic(ThisTrade,Close[0]) > 0) exitTrade(ThisTrade); } }
// Load high res contract chains from daily files like "SPY_20210505.t8"
if(day(0) != day(1)) { // new day?
  if(Live) // load a new contract chain without prices
    contractUpdate(0,1,CALL|PUT);
  else // load a new t8 file with prices 
    dataLoad(1,strf("History\\%s_%04d%02d%02d.t8",Asset,year(0),month(0),day(0)),9);
}
if(!Live) // in the backtest, update prices at any bar
  contractUpdate(0,1,CALL|PUT);
// Example script for converting EOD options data to .t8:
// Format: underlying symbol, exchange, date MMDDYYYY, adj close, option symbol, expiry MMDDYYYY, strike, Call/Put, American/European, ask, bid, volume, open interest, close
// Sample: "TLT,NYSEArca,04/10/2015,129.62,TLT   150410C00112500,04/10/2015,112.5,C,A,17.3,16.2,0,0,129.62"
string Format = ",,%m/%d/%Y,,,i,f,s,s,f,f,f,f,f";

void main() 
{
// first step: parse the CSV file into a dataset
  int Records = dataParse(1,Format,FILENAME);
  printf("\n%d Records parsed",Records);
// second step: convert the raw data to the final CONTRACT format
  for(i=0; i<Records; i++) 
  {
    CONTRACT* C = dataAppendRow(2,9);
    C->time = dataVar(1,i,0);
    string PC = dataStr(1,i,3);
    string EA = dataStr(1,i,4);
    C->Type = ifelse(*PC == 'P',PUT,CALL) + ifelse(*EA == 'E',EUROPEAN,0);
    int Expiry = dataInt(1,i,1); 
    C->Expiry = 10000*(Expiry%10000) + Expiry/10000; // MMDDYYYY -> YYYYMMDD
    C->fStrike = dataVar(1,i,2);
    C->fAsk = dataVar(1,i,5);
    C->fBid = dataVar(1,i,6);
    C->fVol = dataVar(1,i,7);
    C->fVal = dataVar(1,i,8); // open interest
    C->fUnl = dataVar(1,i,9);
    if(!progress(100*i/Records,0)) break; // show a progress bar
  }
  dataSort(2);
  dataSave(2,"History\\MyOptions.t8");
}

See also:

enterLong/Short, exitLong/Short, Quandl bridge, date functions, contract variables, contractCPD, combo, workshop 8

 

► latest version online Market Sentiment: The Price Probability Distribution

Market Sentiment: What's the price next month?

Suppose a stock trades at $250. You want to know its price in 30 days. Or rather, which price is expected in 30 days by most market participants. For this purpose, Zorro analyzes the chain of 30-day options contracts and calculates a price probability distribution. The result is the probability of any future price between $200 and $300 at the option expiration day, based on the expectations of option buyers and sellers. The most likely price is at the top of the distribution. The underlying algorithm and usage examples are described on financial-hacker.com/the-mechanical-turk.
 
The following functions estimate a future price from the current ask, bid, and strike prices of all option contracts at a determined expiration date. They generate a distribution of the cumulative price probability at their expiration. The height of a bar in the image below is the probability that the underlying price will end up at or below the given price level on the x axis, in the implied opinion of option traders.


SPY January 2018, cumulative price probability distribution 

contractCPD (int Days): int

Generates a cumulative probabililty distribution of the current asset price at the given number of days in the future, and stores it internally. Returns the number of option contracts used for the distribution. The contractUpdate function must be called before.  

cpd(var Price): var

Returns the cumulative probability of the given price in 0..100 range. The future price will be at or below the given value with the returned probability in percent. The contractCPD function must be called before.

cpdv(var Percentile): var

Returns the price at the given percentile of the distribution. F.i. cpdv(50) returns the median of the price distribution, with equal probability of a higher or lower price. The contractCPD function must be called before.

Parameters:

Days Minimum number of calendar days for which the price distribution is estimated. Determines the expiration date of the used options.
Price Underlying asset price to be estimated.
Percentile Probability that the future price is at or below the returned value.

Remarks:

Example:

void main() 
{
  StartDate = 20170101;
  BarPeriod = 1440;
  PlotScale = 10;

  assetList("AssetsIB");
  assetHistory("SPY",FROM_STOOQ|UNADJUSTED);
  asset("SPY");
  Multiplier = 100;

// load today's contract chain
  contractUpdate(Stock,0,CALL|PUT);
  var PriceCurrent = Contracts->fUnl;
  printf("\nCurrent price %.2f",PriceCurrent);

// what's the SPY price in 45 days?
  contractCPD(45);
  var Price = 0.;
  int i;
  for(i = 0; i < 150; i++) {
    Price += 0.01*PriceCurrent;
    plotBar("CPD",i,Price,cpd(Price),BARS|LBL2,RED);
  }
  printf(", projected price %.2f",cpdv(50));
}

See also:

contract, cdf ► latest version online Contract Variables

Variables for futures and options

The following variables are valid after loading a contract chain and selecting a contract.

ContractType

ContractExpiry

Type and expiration date (YYYYMMDD format) of the selected contract, or 0 when no valid contract was selected.

Type:

int, read/only

ContractAsk

ContractBid

ContractUnl

ContractStrike

ContractVol

ContractVal

Current ask, bid, underlying, and strike price, and trade volume or open interest of the selected contract. 0 when no contract chain was loaded, or no valid contract was selected, or when the parameter is not or not yet available in the selected contract. In historical data, ContractAsk or ContractBid can be 0 with illiquid or worthless contracts; such contracts should not be traded. ContractVal includes the multiplier retrieved from the broker API in [Trade] mode. For retrieving the ask and bid prices, the underlying, and the volume in [Trade] mode, contractPrice must be called before.

Type:

var, read/only

ThisContract

The currently selected contract by the last contract call. In [Trade] mode, (string)ThisContract - the first element of the CONTRACT struct - contains the trading class retrieved from the broker API.

Contracts

The current option or future chain loaded by the last contractUpdate call. A list of CONTRACT structs of the size NumContracts, sorted first by ascending expiration, second by ascending strike. Can be used for enumerating all contracts.

Type:

CONTRACT*

NumContracts

The number of contracts in the chain, up to 20000, or 0 when no chain was loaded..

ContractRow

The row number of the selected contract in the historical .t8 dataset  that holds the contract chain history of the current asset in Test and Train mode; the row number of the selected contract in the options chain (Contracts pointer) in Trade mode. Can be used to retrieve further data belonging to the contract from additional datasets.

ContractHandle

The handle of the historical .t8 dataset that holds the contract chain history of the current asset in Test and Train mode. Can be set to a specific dataset before calling contractUpdate; otherwise it is automatically set.

Type:

int, asset specific.

 

The following variables are used to set up asset specific contract parameters:

Multiplier

Number of underlying units per option / future contract, for calculating the trade volume, selecting a contract, and filtering the options chain (default 0 = download all options). Asset specific; set it after selecting the asset and before buying contracts. If at 0, contractUpdate will in [Trade] mode automatically set this variable to the multiplier of the first contract if available via broker API.

Centage

Combination of flags that determine which option or futures prices in historical data or live data are given in cents instead of dollars (default 0 = all prices in dollars). Prices are then automatically converted to dollars when loading history or retrieving data from the broker API. Centage flags can be combined by adding them up. Asset specific; to be set after selecting the asset and before buying contracts.

1 -  strike in cents, in live contract data
2 -  strike in cents, in contract history
4 -  underlying in cents, in live contract data
8 -  underlying in cents, in contract history
16 - ask/bid in cents, in live contract data
32 - ask/bid in cents, in contract history
64 - underlying prices and spreads in cents, in live and historical price data (similar to HedgeRatio = 0.01).

Example: Centage = 59 (= 32+16+8+2+1) for ask/bid always in cents, underlying history in cents but live in dollars, and strike always in cents.

Type:

int, asset specific

ExpiryTime

The hour in HHMM format at which contracts expire; default 1200 (UTC). Used for contractDays. Added to the TradeExitDate variable when a trade is opened. At that time on its expiration day, the trade will be removed from the open trade list, and its profit or loss will be booked to the account. Set this to a higher value for checking contract expiration with the contractCheck function in live trading. Note that some exchanges have restrictions to trading or exercising contracts at their expiration day.

Type:

int, global for all assets

Exchange

The exchange for the contracts in live trading. Set the exchange symbol manually through this variable when the exchange is required by the broker. The list of valid exchange symbols can be obtained from the broker website.

Type:

string, asset specific.
 

Remarks:

Examples: see Options and Futures

See also:

enterLong/Short, Contract, Combo

 

► latest version online Migrating Algorithmic Trading Code | Zorro Project

Converting EasyLanguage, MQL4, C#, AFL, Equilla, PineScript... to C/C++

The ideal languages for algorithmic trading are C or C++, due to their speed and their access to large functions libraries and Python or R packages. But before starting serious algotrading with C/C++ and Zorro, you have probably used a traditional automated trading platform. So you want to migrate your trading strategies, "Expert Advisors", algorithms, or indicators to C or C++. Although the Zorro C/C++platform can directly run R and Python functions or Windows DLLs, it won't understand scripts from other platforms without adaption. Even when the syntax is also C or C# based, their trading functions are normally too different for an automated conversion. It's still a programmer's task. You can either hire our algo conversion service or do it yourself. Below you'll find some useful hints and examples.

Generally, Zorro can replicate any indicator and any strategy or trading algorithm from any platform. The conversion will normally result in shorter, much faster, better readable, and more robust code. Here are some examples of algorithmic trading scripts in C/C++. Many more examples for code conversion from Amibroker, TradeStation, MetaStock, or other platforms can be found in Petra Volkova's articles on Financial Hacker.

Comparing backtests and charts between different platforms

Even perfectly converted algo trading strategies can produce very different backtests and charts on different platforms. Especially MT4/MT5 backtests are often not reproducible with other platforms. When platform A produces a positve and platform B a negative backtest result of the same strategy - which one should you trust? Here's what to check for determining the reason of backtest and chart differences:

TradeStation™, MultiCharts™, TradeSignal™

TradeStation was the first platform that supported automated trading. Its EasyLanguage™, also used by MultiCharts and in a variant named 'Equilla' by TradeSignal, has a similar design philosophy as Zorro's lite-C. Although the EasyLanguage syntax is a mix of C and Pascal, conversion to C is relatively straightforward. EasyLanguage Vars are equivalent to C data series. The first element of a series has no index, thus MyData in EasyLanguage is the same as MyData[0] in C. Keywords are case insensitive and local variables are preserved between function calls. Array indices start with 0 as in C, but in many EasyLanguage code examples you find them starting with 1. So the arrays are probably allocated with 1 extra element. Trigonometric functions (Sine, Cosine etc) expect angles in degrees (0..360), while in C and in most other languages angles are in radians (0..2*PI). Log is the logarithm base e as in C.

At the time of this writing, EasyLanguage did still not support functions, which is a strong limitation to complex projects. But separate scripts can be called like functions. The execution of an EasyLanguage script is similar to a lite-C script, with a lookback period determined by the MaxBarsBack variable. Aside from the function definitions, EasyLanguage strategies and lite-C strategies look very similar.

{ Easylanguage version }
{ Choppy Market Index Function by George Pruitt }
Inputs: periodLength(Numeric); Vars: num(0),denom(1);
if(periodLength <> 0) then
begin
denom = Highest(High,periodLength) – Lowest(Low,periodLength);
num = Close[periodLength-1] – Close;
ChoppyMarketIndex = 0.0;
if(denom <> 0) then ChoppyMarketIndex = AbsValue(num/demon)*100;
end;
// Zorro version (lite-C)
// Choppy Market Index Function by George Pruitt
var ChoppyMarketIndex(int periodLength) { if(periodLength != 0) {
var denom = HH(periodLength) – LL(periodLength);
var num = priceClose(periodLength-1) – priceClose(0);
if(denom != 0) return abs(num/denom)*100; } return 0;
}
{ Easylanguage version }
{ enter a trade when the RSI12 crosses over 75 or under 25 }
Inputs:  
    Price(Close),LengthLE(20),LengthSE(20),
    OverSold(25),OverBought(75),StoplossPips(100),ProfitTargetPips(100);
 
variables:  
    var0(0),var1(0);
 
{ get the RSI series }
var0 = RSI( Price, LengthLE );
var1 = RSI( Price, LengthSE );
 
{ if rsi crosses over buy level, exit short and enter long }
condition1 = Currentbar > 1 and var0 crosses over OverBought ;
if condition1 then                                                                    
    Buy( "RsiLE" ) next bar at market;
 
{ if rsi crosses below sell level, exit long and enter short }
condition2 = Currentbar > 1 and var1 crosses under OverSold ;
if condition2 then                                                                    
    Sell Short( "RsiSE" ) next bar at market;
 
{ set up stop / profit levels }
SetStoploss(StoplossPips);
SetProfitTarget(ProfitTargetPips);
// Zorro version (lite-C)
// enter a trade when the RSI12 crosses over 75 or under 25
function run()
{
  BarPeriod = 60;
// get the RSI series
  vars RSIs = series(RSI(seriesC(),20));
 
// set stop / profit levels and trade duration
  Stop = 100*PIP;
  TakeProfit = 100*PIP;
  LifeTime = 24;
 
// if rsi crosses over buy level, exit short and enter long
  if(crossOver(RSIs,75))
    enterLong();
// if rsi crosses below sell level, exit long and enter short
  if(crossUnder(RSIs,25))
    enterShort();
}

"Meta"-Trader 4 and 5

MT4™ and MT5™ are popular platform for retail traders and provided by many brokers. The MQL4 / MQL5 script language of their "Expert Advisors" (EAs) is based on C or C++, which would theoretically allow easy conversion to Zorro. Unfortunately, MQL4 has some issues that make "Expert Advisors" more complex and difficult to convert than scripts of other platforms. The successor MQL5 has even more issues, and consequently has never managed to replace the older MQL4.
  MQL4 and MQL5 trades require a lot of managing by script. Series are not natively supported, but must be emulated with loops and functions. Some indicators - for instance, ATR - produce different results in MQL4 than in other platforms because they are not based on the standard algorithms, but on special MQL4 variants. Candles are based on local server time, and account parameters are not normalized, so any EA must be individually adapted to the country, broker, and account. To complicate matters further, MQL4 and MQL5 do not use common trade units such as lots and pips, but calculates with "standard lots" and "points" that need to be multiplied with account-dependent conversion factors. Most code in an EA is therefore not used for the trade logic, but for patching all those issues. This results in the long and complex 'spaghetti code' that is typical for MT4 and MT4 EAs.
  For conversion, first remove the MQL4/MQL5 specific code that is not needed in lite-C, such as trade management loops, broker dependent pip and trade size calculations, and array loops that emulate series. Then the rest can be converted by replacing the MQL4 indicators and trade commands by their lite-C equivalents. Replace OnTick either with tick or run, dependent on whether the code is bar or tick based. Bar indexes are normally shifted by 1, since they refer to the new (incomplete) bar, not to the last (complete) bar. Different indicator algorithms have to be converted or replaced.

// MQL4 version of the RSI strategy (see above)
// enter a trade when the RSI12 crosses over 75 or under 25
int start()
{
// get the previous and current RSI values
   double current_rsi = iRSI(Symbol(), Period(), 12, 
     PRICE_CLOSE, 1); // mind the '1' - candle '0' is incomplete!!
   double previous_rsi = iRSI(Symbol(), Period(), 12, PRICE_CLOSE, 2);
 
// set up stop / profit levels
   double stop = 200*Point;
   double takeprofit = 200*Point;

// correction for prices with 3, 5, or 6 digits
   int digits = MarketInfo(Symbol(), MODE_DIGITS);
if (digits == 5 || digits == 3) {
stop *= 10; takeprofit *= 10; } else if (digits == 6) {
stop *= 100; takeprofit *= 100; } // find the number of trades int num_long_trades = 0; int num_short_trades = 0; int magic_number = 12345;
// exit all trades in opposite direction for(int i = 0; i < OrdersTotal(); i++) { // use OrderSelect to get the info for each trade if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue; // Trades not belonging to our EA are also found, so it's necessary to // compare the EA magic_number with the order's magic number if(magic_number != OrderMagicNumber()) continue; if(OrderType() == OP_BUY) { // if rsi crosses below sell level, exit long trades if((current_rsi < 25.0) && (previous_rsi >= 25.0)) OrderClose(OrderTicket(),OrderLots(),Bid,3,Green); else // otherwise count the trades num_long_trades++; } if(OrderType() == OP_SELL) { // if rsi crosses over buy level, exit short trades if((current_rsi > 75.0) && (previous_rsi <= 75.0)) OrderClose(OrderTicket(),OrderLots(), Ask,3,Green); else // otherwise count the trades num_short_trades++; } } // if rsi crosses over buy level, enter long if((current_rsi > 75.0) && (previous_rsi <= 75.0) && (num_long_trades == 0)) { OrderSend(Symbol(),OP_BUY,1.0,Ask,3,Ask-stop,Bid+takeprofit,"",magic_number,0,Green); } // if rsi crosses below sell level, enter short if((current_rsi < 25.0) && (previous_rsi >= 25.0) && (num_short_trades == 0)) { OrderSend(Symbol(),OP_SELL,1.0,Bid,3,Bid+stop,Ask-takeprofit,"", magic_number,0,Green); } return(0); }
Under Tips & Tricks you can find an example how to replicate MQ4-style indicator parameters with Zorro. The above mentioned issues also apply to MQL5, the "Meta"-Trader 5 script language. It has some similarities to MQL4, but offers C++ language elements and requires even more complex code for opening and managing trades.
 

NinjaTrader™

NinjaScript™ is based on C# and thus similar in syntax to Zorro's lite-C. NinjaScript also supports data series in the same way as lite-C, and its basic function list is very similar; this makes script migration rather easy. One major difference is that all NinjaTrader indicator functions return data series, while Zorro indicators return single values. Use the series function (f.i. series(indicator(..))) for making Zorro indicators also return series.

// NinjaTrader version
// Trade when a fast SMA crosses over a slow SMA
protected override void Initialize()
{ // Run OnBarUpdate on the close of each bar
CalculateOnBarClose = true; // Set stop loss and profit target at $5 and $10 SetStopLoss(CalculationMode.Ticks,5); SetProfitTarget(CalculationMode.Ticks,10); } protected override void OnBarUpdate()
{ // don't trade during the LookBack period if(CurrentBar < 20)
return; double Fast = 10; double Slow = 20; // Exit short and go long if 10 SMA crosses over 20 SMA
if(CrossAbove(SMA(Close,Fast),SMA(Close,Slow),1)) { ExitShort();
EnterLong(); } // Exit long and go short if 10 SMA crosses under 20 SMA
else if(CrossBelow(SMA(Close,Fast),SMA(Close,Slow),1)) { ExitLong();
EnterShort(); } }
// Zorro version
// Trade when a fast SMA crosses over a slow SMA
void run()
{ // Set stop loss and profit target at $5 and $10 Stop = 5; TakeProfit = 10; vars SMAFast = series(SMA(seriesC(),10)); vars SMASlow = series(SMA(seriesC(),20)); // Exit short and go long if 10 SMA crosses over 20 SMA
if(crossOver(SMAFast,SMASlow))
enterLong(); // Exit long and go short if 10 SMA crosses under 20 SMA
else if(crossUnder(SMAFast,SMASlow)) enterShort(); }
 

Amibroker™

Amibroker's AFL™ language is a C dialect, with similar syntax as lite-C. But the script structure is different. Amibroker uses "Buy" and "Sell" variables for entering trades, instead of a function call. And Amibroker calls functions for setting system parameters, instead of using variables. So it's just the opposite of what you would expect. This unusual concept has its reason in a sort of vectorized approach to a trading system, where the code mainly initializes parameters and signal conditions. Variables need not be declared, and they are usually series, as in EasyLanguage. Functions are often very similar to lite-C - for instance, Plot() or Optimize(). Others can be easily converted, f.i. Amibroker's "Explore" feature is equivalent to Zorro's print(TO_CSV).

// Amibroker version of the SMA crossing system (see above)
// Trade when a fast SMA crosses over a slow SMA
// Set stop loss and profit target at $5 and $10 ApplyStop(stopTypeLoss,stopModePoint,5,0,True,0,0,-1); ApplyStop(stopTypeProfit,stopModePoint,10,0,True,0,0,-1); SMAFast = MA(C,10); SMASlow = MA(C,20); // Buy if 10 SMA crosses over 20 SMA
Buy = Cross(SMAFast,SMASlow); // Sell if 10 SMA crosses under 20 SMA
Sell = Cross(SMASlow,SMAFast);
 

TradingView™

TradingView is a charting tool with a proprietary language named PineScript™ for defining indicators. Variables are declared by assigning a value to them, and language blocks are defined by indentation, as in Python. Functions are defined with '=>'. Data series are in reverse order compared to other platforms: the [0] element is the oldest. Aside from that, conversion to C is normally pretty straightforward. Example for a Simple Moving Average:

// PineScript version
// SMA definition
study("My sma")
my_sma(Prices, Length) =>
  Sum = Prices
  for i = 1 to Length-1
    Sum := Sum + Prices[i]
  Sum / Length
// C version
// SMA definition
var my_sma(vars Prices,int Length) { var Sum = Prices[0]; for(i = 1; i < Length; i++) Sum += Prices[i]; return Sum/Length; }
 

Neuroshell Trader™

Neuroshell Trader is a platform specialized in employing a linear neural network for automated trading. Neuroshell indicators are functions added through DLLs. They take an input array, an output array, the array size, and additional parameters. Many other trade platforms use similar DLL based indicators. Such indicators are often based on normal C, thus conversion to Zorro is very easy - especially when you don't have to convert it at all and can call the DLL function directly.
   When using an indicator made for a different platform, the array order convention must be take care of. Neuroshell stores time series in ascending order (contrary to most other platforms that store them in reverse order) and passes the end of the array, not its begin, to the indicator function. Neuroshell indicators normally return an output series instead of a single value. Below both methods of indicator conversion are shown.

// Neuroshell version - Entropy indicator
// published by ForeTrade Technologies (www.foretrade.com/entropy.htm)
#include "math.h"
#include "stdlib.h"

__declspec(dllexport) void Entropy (double *price, double *entropy, long int size, long int numbars)
{
  double *in, *out, P, G;
  long int i,j;
  double sumx = 0.0;
  double sumx2 = 0.0;
  double avgx = 0.0;
  double rmsx = 0.0;

  in=price;
  out=entropy;

  for (i=0; i<size; i++)
  {
    if (i < numbars+1) *out = 3.4e38;
    else 
    {
      sumx = sumx2 = avgx = rmsx = 0.0;
      for (j=0;j<numbars+1;j++)
      {
        sumx += log(*(in-j) / *(in-j-1)) ;
        sumx2 += log(*(in-j) / *(in-j-1)) * log(*(in-j) / *(in-j-1));
      }
      if (numbars==0) 
      {
        avgx = *in;
        rmsx = 0.0;
      }
      else 
      {
        avgx = sumx / numbars;
        rmsx = sqrt(sumx2/numbars);
      }
      P = ((avgx/rmsx)+1)/2.0;
      G = P * log(1+rmsx) + (1-P) * log(1-rmsx);
      *out=G;
    }
    in++; out++;
  }
}
// Zorro version - Entropy indicator
// Method 1 - calling the DLL function directly
// Copy the Entropy DLL into the Zorro folder
int Entropy(double *price, double *entropy, long size, long numbars); // function prototype
API(Entropy,entropy) // use the Entropy function from entropy.dll

var EntropyZ(vars Data,int Period)
{
  Period = clamp(Period,1,LookBack-1); // prevent exceeding the Data size 
  double E; // single element "array" for receiving the output value 
  Entropy(
    Data+Period+1, // end of the array (element order does not matter here)
    &E, 1,         // pointer to the output "array" with size 1
    Period); 
  return E;
}
// Zorro version - Entropy indicator
// Method 2 - converting the DLL code to lite-C
var EntropyZ(vars Data,int Period)
{
  Period = clamp(Period,1,LookBack-1); // prevent exceeding the Data size 
  var sumx = 0., sumx2 = 0.;
  int j;
  for (j=0; j<Period; j++) {
    sumx += log(Data[j]/Data[j+1]);
    sumx2 += log(Data[j]/Data[j+1]) * log(Data[j]/Data[j+1]);
  }
  var avgx = sumx/Period;
  var rmsx = sqrt(sumx2/Period);
  var P = ((avgx/rmsx)+1)/2.;
  return P * log(1+rmsx) + (1-P) * log(1-rmsx);
}

 

MatLab™

MatLab is a commercial computing environment and interactive programming language by MathWorks, Inc. It has some similarity to R, but is not specialized on data analysis and machine learning. It allows symbolic and numerical computing in all fields of mathematics. With interpreted code and accordingly slow execution, it is not suited for backtesting trading strategies (unless they are converted to a vectorized structure, which is however an awkward process). Converting MatLab code to C is also a lot of work due to the very different language syntax. Fortunately there is a better solution: MatLab has an integrated compiler that compiles a MatLab trading algorithm to a C/C++ DLL. The DLL function can then be directly called from a lite-C script.
 

See also:

Introduction to lite-C, Pointers, Structs, Functions, DLLsMT4 bridge, R bridge, Python bridge, C++ to lite-C, lite-C to C++

► latest version online

COT

Commitment Of Traders Report

The Commitments of Traders (COT) is a market report by the Commodity Futures Trading Commission (CFTC), listing the holdings of participants in futures of financial instruments, metals, and other commodities. It is believed by some traders to give insight into the upcoming development of those markets. The CFTC releases a new report every Friday at 3:30 p.m. Eastern Time, and the report reflects the commitments of traders on the prior Tuesday.

The following functions return selected parts of the COT report, and work likewise in backtest and live trading:

COT (int Handle, string Code, int Field): var

Downloads the COT report with the given Quandl Code and stores it in the dataset with the given Handle. Returns the position determined by the Field number for the time of the current bar. If the dataset already exists, the data is not loaded again, but only the position returned.

COT_CommercialPos (int Handle, string Code): int

As before, but returns the net commercials position, i.e. the difference producer + swap dealer longs - producer + swap dealer shorts.

COT_CommercialIndex (int Handle, string Code, int TimePeriod): var

As before, but returns the commercials net position normalized over the given TimePeriod and scaled to 0..100.

COT_OpenInterest (int Handle, string Code): var

As before, but returns the open interest.

Returns:

Positions, net positions, or index.

Parameters:

Handle Dataset handle for storing the report. Every report needs a dedicated handle.
Code The Quandl code number of the report; normally 6 digits or letters. Can be looked up under https://www.quandl.com/data/CFTC-Commodity-Futures-Trading-Commission-Reports. The "CTFC/" prefix and "_F_ALL" suffix of the Quandl database are automatically added.
Field The field of the report: 1 = open interest, 2 = producer long positions, 3 = producer short positions, 4 = swap dealer long positions, 5 = swap dealer short positions, 6 = noncommercials long positions, 7 = noncommercials short positions. 
TimePeriod The number of bars for normalizing the index.

 

Remarks:

Example:

#include <contract.c>

// Database symbols and codes below are just for illustration.
// Current available symbols can be found on the Quandl / NASDAQ website.
function run()
{
  StartDate = 2018;
  EndDate = 2023;
  BarPeriod = 1440;
  LookBack = 12*20;
  set(PLOTNOW);
  
  assetAdd("GOLD","YAHOO:GC=F");
  asset("GOLD");
  string COT_Code = "088691";
  var Ind = COT_CommercialIndex(1,COT_Code,6*20); // gold COT report
  plot("Fast Index",Ind,NEW,RED);
  var Ind2 = COT_CommercialIndex(1,COT_Code,12*20);
  plot("Slow Index",Ind2,0,BLUE);
  var Ind3 = COT_OpenInterest(1,COT_Code);
  plot("Open Interest",Ind3,NEW,BLACK);
}

See also:

asset, season, dataFromQuandl

► latest version online crossOver, crossUnder

crossOver (vars Data1, vars Data2) : bool

crossUnder (vars Data1, vars Data2) : bool

crossOver (vars Data, var Border) : bool

crossUnder (vars Data, var Border) : bool

Determines if the Data1 series crosses over or under the Data2 series, or over or under a fixed Border value between the previous and the current bar. Often used for trade signals.

crossOver (vars Data1, vars Data2, int TimePeriod) : int

crossUnder (vars Data1, vars Data2, int TimePeriod) : int

crossOver (vars Data, var Border, int TimePeriod) : int

crossUnder (vars Data, var Border, int TimePeriod) : int

Returns the bar offset of the last crossing (1 = between the previous and the current bar), or 0 when no crossing occurred within the TimePeriod

touch (vars Data1, vars Data2) : bool

touch (vars Data, var Border) : bool

Determines if the Data1 series touches or crosses the Data2 series or a fixed Border value from any direction.

Parameters:

Data1 First time series.
Data2 Second time series
Border Border value
TimePeriod Number of series elements to test

Returns:

true or bar offset if the first series crosses the second, false or 0 otherwise.

Modifies

rMomentum - Data movement in percent per time frame at the time of the crossing; indicates the 'speed' of the crossover.

Algorithms:

bool crossOver(var* Data1,var* Data2) { return (Data1[0] > Data2[0]) && (Data1[1] <= Data2[1]); }
bool crossUnder(var* Data1,var* Data2) { return (Data1[0] < Data2[0]) && (Data1[1] >= Data2[1]); }
bool crossOver(var* Data,var Border) { return (Data[0] > Border) && (Data[1] <= Border); }
bool crossUnder(var* Data,var Border) { return (Data[0] < Border) && (Data[1] >= Border); }

Remarks:

Example:

function run()
{
  vars Price = series(priceClose());
  vars SMA100 = series(SMA(Price,100));
  vars SMA30 = series(SMA(Price,30));

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

See also:

series, rising/falling, peak/valley, crossOverF/UnderF, predict

► latest version online dataParse, dataFind, dataGet

Dataset handling

The following functions can be used for downloading and parsing data from various sources, and storing it in binary datasets. A dataset is a list of records, normally in time descending order. Any record begins with an 8-byte timestamp field that can also hold other 8-byte data in special cases. The subsequent fields have a size of 4 bytes and can contain floats, ints, or strings. The size of a record in bytes is therefore 4+fields*4. The total number of records must not exceed the int range.
  Datasets can store option chains, order books, asset names, reports, earnings, interest rates, or any other data organized in rows and columns. Text strings can have any size and can occupy several adjacent fields. A dataset can be saved, loaded, imported or exported, searched, sorted, merged, split, resized, or used as indicator in backtests. The .t1, .t2, .t6, and .t8 historical data files are in fact datasets with 1, 2, 6, or 8 data fields plus 1 timestamp field.

The following functions are used to create or load a dataset:

dataNew (int Handle, int Records, int Fields): void*

Deletes the given dataset (if any), frees the memory, and creates a new dataset with the given number of Records and Fields. If they are 0, the dataset is deleted, but no new dataset is created. Returns a pointer to the begin of the first record, or 0 when no new dataset was created.

dataLoad (int Handle, string Filename, int Fields): int

Reads a dataset from a binary file. Fields is the number of fields per record, including the timestamp field at the begin of any record. Thus, a .t1 historical data file has 2 fields and a .t8 file has 9 fields. The function returns the number of records read, or 0 when the file can not be read or has a wrong size.

dataCompress (int Handle, string Filename, int Fields, var Resolution): int

Like dataLoad, but reads only records that differ in at least one value other than the timestamp from the previous record, and are at least Resolution milliseconds apart. On files with 2 fields, positive and negative column 1 values are compared separately for dealing with ask and bid quotes of .t1 files. Can be used to compress price history files by changing the resolution and eliminating records with no price change.

dataDownload (string Code, int Mode, int Period): int

Downloads the dataset with the given Code from Quandl™ or other price sources, and stores it in CSV format in the History folder. Returns the number of data records. Data is only downloaded when it is more recent than the last downloaded data plus the given Period in minutes (at 0 the data is always downloaded). Zorro S is required for loading Quandl datasets.

dataParse (int Handle, string Format, string Filename, int Start, int Num): int

Parses a part or all data records from the CSV file Filename and appends them at the start of the dataset with the given Handle number. For beginning a new dataset when content was already parsed, call dataNew(Handle,0,0) before. Num records are parsed, beginning with the record Start. If both parameters are omitted or zero, the whole CSV file is parsed.
  Records can have time/date, floating point, integer, and text fields. CSV headers are skipped. Several CSV files can be appended to the same dataset when their record format is identical. The CSV file can be in ascending or descending chronological order, but the resulting dataset should normally be in descending order, i.e. the newest records are at the begin. Any record in the dataset begins with a time stamp field in DATE format; the other fields can be in arbitrary order determined by the Format string (see Parameters). If the CSV file does not contain time stamps, the first field in the record is filled with zero.
  The function returns the number of records read, or 0 when the file can not be read or has a wrong format. Please see below under Remarks how to build a correct format string and how to debug the parsing.

dataParse (int Handle, string Format, string Filename, string Filter): int

As before, but parses only lines that contain the string Filter. The string is case sensitive and can include delimiters, so it can cover several adjacent fields. Use delimiters at begin and end for matching a whole field. Use '\n' as first filter character for matching the first field. This way only lines with a certain asset name, year number, or other content are parsed.

dataParseJSON (int Handle, string Format, string Filename): int

dataParseString (int Handle, string Format, string Content): int

As before, but parses timestamp, prices, and volumes from the JSON file Filename (with ".json" extension) or from the given JSON string to the dataset with the given Handle number. The file or string is supposed to contain OHLC candles or BBO quotes as JSON objects in winged brackets {..}. Depending on Format (see Parameters), the dataset is created either in T6 record format with 7 fields, or in T2 format with 3 fields. The field names are given with the Format string in a fixed order. Data fields in the JSON file must contain valid numbers; they must not be empty or contain text like "NaN" or "null". Content is modified by the parsing process.The function returns the number of records read, or 0 when the file can not be read or has a wrong format.
 

Storing datasets

dataSave (int Handle, string Filename, int Start, int Num)

Stores the dataset with the given Handle as a binary file. Num records are stored, beginning with the record Start. If both parameters are omitted or zero, the whole dataset is stored. Make sure that the number of records does not exceed the 2,147,483,647 limit and the resulting file size does not exceed 5 GB, which is the Windows limit for file operations.

dataSaveCSV (int Handle, string Format, string Filename, int Start, int Num)

The opposite to dataParse; stores a part or all of the dataset with the given Handle number in a CSV file with the given FileName. The type and order of the CSV fields can be defined by the Format string in the same way as for dataParse, except that no header line is stored. Usage example in CSVfromHistory.c.
 

Manipulating datasets

dataMerge (int Handle1, int Handle2): int

Merges dataset Handle2 into dataset Handle1. Both datasets must be sorted in descending time stamp order, and the first dataset must begin with an earlier timestamp than the second. When timestamps overlap, the content from the second dataset replaces content from the first. This function can be used to stitch datasets together. Returns the total number of records, or 0 when the datasets could not be merged

dataAppend (int Handle1, int Handle2, int Start, int Num): int

Appends dataset Handle2 partially or completely at the end of the dataset Handle1. The Handle1 dataset must be either empty or have the same number of columns as Handle2. The number of rows may be different. Num records from Handle2 are stored, beginning with the record Start. If both parameters are omitted or zero, the whole dataset is appended. Returns the total number of records, or 0 when the datasets could not be appended.

dataAppendRow (int Handle, int Fields): void*

Appends a new record at the end of the given dataset, and returns a temporary pointer to the begin of the new record. If the dataset didn't exist, it is created with the given number of fields. The returned pointer remains valid until the next dataAppendRow call. Use dataRow (see below) for converting the pointer to a record number. 

dataDelete (int Handle, int Record1, int Record2): int

Deletes all records from Record1 up to Record2 from the dataset. Returns its new number of records.

dataClip (int Handle, int Records): int

Truncates the dataset to the given number of records.

dataSort (int Handle)

Sorts the dataset with the given Handle in descending order of the first column (usually the time stamp). If Handle is negative, sorts in ascending order. Returns the number of records.

dataCompressSelf (int Handle, var MinTime)

Like dataCompress, but compresses the dataset with the given Handle.
 

Plotting datasets

Datasets can be plotted to a histogram or statistics chart with the plot commands. For plotting them in special ways, the following commands have been added:

dataChart (int Handle, string Filename, CONTOUR, NULL)

Generates a contour chart of a 3-field dataset. The resulting image is stored under Filename when PL_FILE is set, otherwise displayed on the interactive chart. The value in the first field of a record is displayed as a contour color ranging from red to green. The second field is the x coordinate and the third field the y coordinate (see example). CONTOUR|DOT plots a cross at every xy coordinate. This function can be used to display parameter contour charts from CSV files exported by genetic or brute force training.

dataChart (int Handle, string Filename, HEATMAP, NULL)

Generates a heatmap from a 2D dataset. The resulting image is stored under Filename when PL_FILE is set, otherwise displayed on the interactive chart. The dataset contains heat values in a column x row matrix that are displayed in colors ranging from blue to red. This function can be used to display correlation or weight heatmaps.

 

Accessing data

dataFind (int Handle, var Date): int

Returns the number of the first record at or before the given Date in wdate format. Returns -1 when no matching record was found or when no dataset with the given Handle exists. Returns the number of records when Date is 0. The dataset must be in descending time stamp order. Decrease the returned record number to retreive records with later dates; increase it to get records with earlier dates or with the same date. Subtract an offset from Date for avoiding future peeking; f.i. for EOD datasets with timestamps from the begin and data from the end of the day, subtract 16 hours (16./24) to adjust the timestamps to 16:00 market close time.

dataRow (int Handle, void *Record): int

Returns the row number of the record with the given pointer. Reverse function of dataStr(Handle,Row,0).

dataSize (int Handle, int *Rows, int *Columns): int

Returns the number of elements in the given dataset, and set the Rows and Columns pointers, when nonzero, to the number of records and fields. 

dataSet (int Handle, int Row, int Column, var Value)

dataSet (int Handle, int Row, int Column, int Value)

Stores the Value in the floating point or integer field Column of the record Row. Can be used for modifying datasets f.i. for removing outliers or adding parameters. Since the target field format depends on whether Value is int or var, make sure to use the correct type, especially when entering constants, and typecast it with (int) or (var) if in doubt. When modifying the time stamp field of the record (Column = 0), make also sure to keep descending order of dates in the array.

dataVar (int Handle, int Row, int Column): var

Returns the value of the floating point field Column from the record Row. If Column is 0, the time stamp of the record is returned in wdate format. If Row is negative, the record is taken from the end of the dataset, i.e. Row = -1 accesses the oldest record. If the dataset is empty or if Row or Column exceed the number of records and fields, 0 is returned.

dataInt (int Handle, int Row, int Column): int

As before, but returns the value of the integer field Column from the record Row.

dataStr (int Handle, int Row, int Column): string

As before, but returns a pointer to the field Column from the record Row. It it's a text field, the text string of up to 3, 7, or 11 characters is returned. If Column is 0, it returns a pointer to the timestamp field, i.e. the start of the record. For getting a pointer to the first record of the dataset, call dataStr(Handle,0,0). For modifying a text field in a dataset, return its string pointer and then modify the string.

dataCol (int Handle, var* Data, int Column): int

Sets the predefined rMin, rMax, rMinIdx and rMaxIdx variables to the minimum and maximum values of the given Column. If Data is nonzero, fills it with all float or double elements from the column. The Data array must have at least the same number of elements than the number of records in the dataset. Returns the number of records.

dataCopy (int Handle, var* Data): int

Fills the Data array with the content of the dataset, while all float elements are converted to double. Sets the rMin and rMax variables to the minimum and maximum value. Returns the number of elements. The Data array must have at least the same number of elements as the dataset. 
 

Helper functions

dataFromQuandl (int Handle, string Format, string Code, int Column): var

Helper function for generating an indicator based on a Quandl™ EOD time series. Works in live trading as well as in backtest mode, and returns the content of the field Column from the dataset Code in the given Format. This function is often used for getting extra market data, such as the yield rate or the Commitment of Traders (COT) report of particular assets. Timestamps are automatically adjusted by 16 hours so that the indicator changes when the US market opens. If this is not desired, remove the term -16./24 from the function source code in contract.c (which must be included for using this function). Zorro S is required for accessing Quandl data. 

dataFromCSV (int Handle, string Format, string Filename, int Column,int Offset): var

Helper function for generating an indicator based on a downloaded CSV file; for backtesting only. Returns the content of the field Column from the file Filename.csv in the given Format. Offset is the time stamp adjustment in minutes, f.i. to 16:00 for avoiding future peeking with EOD data. Source code in contract.c, which must be included for using this function. 
 
 

Parameters:

Code The Google or Quandl code, f.i. "NYSE:AMZN" or "WIKI/AAPL". For selecting a ticker from a Quandl data table, add a colon and the ticker symbol, f.i. "ZACKS/ES:AAPL". The file is stored in the History folder under the Code name with ": /' characters replaced with "- _", plus "1" when only the most recent record was downloaded, plus ".csv".
Mode FROM_GOOGLE for downloading a time series from Google™
FROM_GOOGLE|1 for downloading only the last records, for live trading
FROM_QUANDL for downloading a time series from Quandl™ (Zorro S and Quandl key required).
FROM_QUANDL|1 for downloading only the most recent record, for live trading
FROM_QTABLE for downloading a Quandl™ data table
Period Minimum time in minutes to keep the last downloaded file until a newer file is downloaded, or 0 for always downloading the file.
Handle A number from 1...1000 that identifies the dataset. Handles above 1000 are reserved for Zorro functions.
FileName Name of the file, with relative or absolute path.
Records Number of records in the dataset.
Fields Number of fields per record, including the date field.
Date Timestamp in Windows DATE format. Days are represented by whole number increments starting with 30 December 1899, midnight UTC. The time of the day is represented in the fractional part of the number.
Start, Num The first record and the number of records to be stored.
Row, Column The record and field number, starting with 0. The date is always the first field of the record. If Row is negative, the record is taken from the end of the file, i.e. Row = -1 accesses the oldest record.
Value New value of the addressed field.
 
Filter Filter string to select lines to be parsed. The line must contain the string. Use delimiters for matching a complete field. Use '\n' as first character when the line must begin with the filter string.
Format

CSV format string for parsing records with mixed content from a CSV file to a dataset, or for storing a dataset to a file in the CSV format. Fields in the format string are separated with the same delimiter as in the CSV file, either a comma, a semicolon, or '|' for a tab. A field can be either empty, or contain a placeholder that determines the field content. Fields with no placeholder are skipped and don't appear in the dataset. A record of a CSV time series should contain at least one date/time field; if there are more, f.i. separate fields for date and time, they are summed up.

Codes at the begin of the format string:

+ - ascending date order; reverse the records and append parsed data to the end of the dataset. Otherwise descending date order is assumed and data is appended to the begin.
0,1,2 - number of header lines in the .csv file to be skipped (default: 1).
u01..u99 - skip all lines that are up to the nth character (n = 00..99) identical to the previous line. Used for storing only records with unique date/time fields.
n - skip all lines that contain the string "NaN" or "null" or have an invalid date/time field.

The following placeholders can be used to parse field content. If the format string is empty or contains no placeholders, the CSV file is parsed as if all fields contained floating point numbers.

f - for a floating point field, f.i. 123.456. If the delimiter is a semicolon, the decimal point can be a comma.
i - for an integer field. Nonnumerical characters are skipped, f.i. "07/21/16" is parsed to 72116.
s - for a 3 characters text field.
ss... - for an extended text field of size 4n-1, where n is the number of 's'. Occupies n adjacent fields in the dataset, while field 0 counts as 2 fields.
%t - for a date/time field in Unix format, either seconds or milliseconds since January 1,1970.
%w - for a date/time field in Windows DATE format, number of days since December 30,1899.
%... - for a date/time field with DATE format codes, f.i. "%Y-%m-%dT%H:%M:%S" for the ISO 8601 time format. Trailing seconds can have decimals and are parsed with 1 microsecond precision.

Example: A profit-and-loss curve generated by a backtest is a list of CSV records with a date and the associated equity, like "2016-06-01,123.45", in ascending date order with a single header line. The format string "+%Y-%m-%d,f" parses it to a dataset in T1 format.

Date/time fields are parsed into field 0 of the dataset and can be retrieved with dataVar(..,..,0) in the Windows DATE format. The number of fields in the dataset is the date field plus the sum of all f, i, s characters in the format string. It can be different to the number of fields in the CSV record. The f, i, s placeholders are normally followed by a field number in the resulting dataset. Example: "+%Y%m%d %H%M%S,f3,f1,f2,f4,f6" parses Histdata™ CSV files into a dataset in T6 format. If the field number is omitted, the fields are parsed in ascending order, starting with field 0. Use f1 for starting with field 1. Skipped fields are filled with 0.
  

Format JSON format string containing the token names of a OHLC candle or  ask/bid (BBO) quote in a JSON file. OHLC candles are stored in T6 records, ask or bid quotes are stored in T2 records.
T6: "Start,Time,Timeformat,High,Low,Open,Close,AdjClose,Volume"
T2: "Start,Time,Timeformat,Ask,AskSize,Bid,BidSize"

Start
- token name or start string of the whole price structure. Determines from where to parse.
Timeformat - format of the date/time field with DATE format codes, as in the CSV format string.
Time - token name of the date/time field.
High,Low,Open,Close - token names of the price fields.
AdjClose - token name of the adjusted close field, or empty if the file contains no such field.
Volume - toke name of the volume field, or empty if the file contains no volume data.
Ask - token name of the best ask quote field.
AskSize - token name of the ask size field.
Bid - token name of the best bid quote field, or empty if the file contains no bid quotes.
BidSize - token name of the bid size field, or empty.

If the file already begins with the '[' character, use it for the start token.
Example: "[,date,%t,high,low,open,close,,".

Remarks:

Examples (see also import, scripts, and contract):

// COT report for S&P500
var CFTC_SP(int Column) { 
  return dataFromQuandl(802,"%Y-%m-%d,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f","CFTC/TIFF_CME_SP_ALL",Column); 
}

// convert some .csv formats to .t6
string Format = "%Y-%m-%d,f3,f1,f2,f4,,,f6,f5"; // Quandl futures format to .t6, f.i. "CHRIS/CME_CL1"
dataParse(1,Format,"History\\CL1.csv");
dataSave(1,"History\\CL1.t6");

string Format = "%Y-%m-%d,f3,f1,f2,f4,f6,f5"; // Yahoo data format to unadjusted .t6, with adjusted close stored in fVal
dataParse(1,Format,"History\\history.csv");
dataSave(1,"History\\AAPL.t6");

// read a time series out of field 1 of dataset H
vars MyData = series(dataVar(H,dataFind(H,wdate(0)),1));

// Coinbase Bitcoin/EUR price to .t1
void main()
{
  string Format = "+0,,,%t,f,f,s";
  int Records = dataParse(1,Format,"History\\BTCEUR.csv");
  printf("\n%d records read",Records);
// now convert it to t1 and change sign for sell quotes
  for(i=0; i<Records; i++) {
    T1* t1 = dataAppendRow(2,2);
    t1->time = dataVar(1,i,0);
    t1->fVal = dataVar(1,i,1);
    string sell = dataStr(1,i,3);
    if(sell[0] == 't') t1->fVal = -t1->fVal;
// display progress bar and check [Stop] button
    if(!progress(100*i/Records,0)) break;
  }
  if(Records) dataSave(2,"History\\BTCEUR.t1");
}

// evaluate an extra parameter stored with time stamps in a dataset
...
if(Init) dataLoad(1,"History\\MyParameters.dta",2);
int Row = dataFind(1,wdate(0));
MyExtraParameter = dataVar(1,Row,1);
...

// Download earnings data from AlphaVantage 
// and store earnings surprises in a dataset
int loadEarnings()
{
  string URL = strf("https://www.alphavantage.co/query?function=EARNINGS&symbol=%s&apikey=%s",
    Asset,report(33));  // 33 = AlphaVantage API key
  string Content = http_transfer(URL,0); // JSON format
  if(!Content) return 0; 
  string Pos = strstr(Content,"reportedDate");  
  if(!Pos) return 0;
  dataNew(1,0,0);
  while(Pos) {
    var Date = wdatef("%Y-%m-%d",Pos+12+4);
    if(Date == 0) break;
    Pos = strstr(Pos,"surprisePercentage");
    if(!Pos) break;
    int Row = dataRow(1,dataAppendRow(1,2));
    dataSet(1,Row,0,Date); // earnings date in field 0
    dataSet(1,Row,1,atof(Pos+18+4)); // surprise in field 1
    printf("\n%.4f %.4f",dataVar(1,Row,0),dataVar(1,Row,1));
    Pos = strstr(Pos,"reportedDate"); // next JSON record
  }
  return 1;
}

// plot a heatmap
void main()
{
  dataNew(1,8,10);
  int i,j;
  for(i=0; i<8; i++) 
    for(j=0; j<10; j++)
      dataSet(1,i,j,(var)(i+j));
  dataChart(1,"Test",HEATMAP,NULL);
}

// plot a contour map
void main()
{
  dataNew(1,100,3);
  int i,j;
  for(i=0; i<10; i++) 
    for(j=0; j<10; j++) {
      dataSet(1,i*10+j,2,(var)i);
      dataSet(1,i*10+j,1,(var)j);
      dataSet(1,i*10+j,0,(var)(i+j));
   }
   dataChart(1,"Test",CONTOUR|DOT,NULL);
}

// plot the 2nd field of a dataset as a red line
int Records = dataLoad(1,"MyDataset.dta");
int N;
for(N=0; N<Records; N++)
  plotBar("2nd_Field",N,N,dataVar(1,N,1),LINE,RED);

See also:

file, strvar, sortData, price history, contract

► latest version online DataSlope, DataSkip, DataSplit

DataSplit

Splits the WFO simulation in a training period (given in percent) and a following test period. F.i. when set at 60, the training period has a length of 60% and the test period has a length of 40%. This works with WFO (NumWFOCycles > 1) or without WFO (NumWFOCycles == 1) and ensures that the test uses out-of-sample data.

Typical range:

50..90 (default = 0 = no separate training / test period).

Type:

int

DataSkip

Gives the number of bars to skip with the SKIP1...SKIP3 flags (default: number of bars corresponding to one week).

Type:

int 

DataHorizon

Prevents trading for the given number of bars at the begin of a WFO test phase (default: 0). This avoids test bias by future-peeking training f.i. with a machine learning algorithm. To avoid artificial triggers by parameter changes, set DataHorizon = 2; to avoid peeking bias when training uses prices from 5 bars in the future, set DataHorizon = 5. See also RECALCULATE.

Type:

int

DataSlope

Applies a moving weight factor to the trade results in the training period. F.i. at DataSlope = 2 the last trades have twice the weight than the first trades. This generates parameters that are better fitted to the most recent part of the price curve, and thus takes care of slow market changes.

Typical range:

1..3 (default = 1 = equal weight for all trades)

Type:

var
 

Remarks:

Example:

function run()
{
  DataSlope = 2;
  DataSplit = 80;
  NumWFOCycles = -10; // anchored WFO
  ...
}

See also:

Mode, WFO

 

► latest version online StartDate, EndDate

Time/date periods

StartDate

Start of the simulation; determines the begin of the backtest or of the preceding training period. The variable can be set in different ways:
- A 4-digit year number (f.i. 2006) gives the number of the historical data file with which the simulation begins (f.i. EURUSD_2006.t6). If the data file has no year number (f.i. MSFT.t6), the simulation starts with the earliest record in the file that matches the year number.
- A date in yyyymmdd format starts the simulation at a certain date (f.i. 20090401 = April 1st, 2009). The LookBack period is added in front of the date and begins accordingly earlier. Due to the lookback period, StartDate = 2006 is not the same as StartDate = 20060101.
- StartDate = NOW sets the start date to the current day, and executes the run function in [Trade] mode immediately after the lookback period, regardless of the current time. Useful for strategies that do not run permanently, but are only started for modifying a portfolio.
- StartDate = 0 (default) starts the simulation with NumYears before the current year.

EndDate

End of the simulation, either 4 digits for determining the number of the last historical price data file (similar to StartDate), or a date in yyyymmdd format for ending the backtest at that date (f.i. 20091231 = December 31, 2009), or NOW for the current day. If at 0 (default), the simulation runs until the end of the available price history. In January it runs until the end of the price history of the previous year.

NumYears

Number of years of the simulation if no StartDate or EndDate is given (default: 6 years; max 32 years). The current year counts as one full year. Set NumYears to -1 for not loading any prices by assetHistory.

MaxBars

Maximum number of bars of the simulation (default: 0 = no limit). The simulation ends either at EndDate or after the given number of bars (including the LookBack period), whichever happens earlier.
 

UpdateDays

Interval in days for automatically downloading price data from the selected broker (default: 0 = don't automatically download price data). The download process starts when calling asset() the first time in a [Test] or [Train] cycle and the price history is older than the given number of days. Zorro will then log in to the broker, download recent price data in M1 or T1 format dependent on History, and add it to the price history. This variable can be used for getting fresh data in a retraining or retesting process. It has no effect in [Trade] mode. Set UpdateDays to -1 for loading prices even when the history is up to date.

ReTrainDays

Interval in days for automatically retraining a live trading system (Zorro S required; default: 0 = no automatic retraining). Set this to the duration of the WFO test period for keeping a WFO trained system in sync with the market when it trades unsupervised for a long time. The retraining interval starts with the session start.

GapDays

Maximum allowed gap in days in the historical prices and in downloaded price data (default: 0 = no gap checking). Set this to 2 or above in order to check the price curve for gaps and inconsistencies, and give an Error 047 message if any are detected. Weekends and Holidays are except from gap checking. Gaps of 1 day are normal in historical prices when market holidays are not set up in the Holidays array (see below).
 

StartWeek

EndWeek

Start and end of the business week in dhhmm local time in the BarZone, where d = day number (1 = Monday .. 7 = Sunday) hh = hour and mm = minute. Default: Start 72300 (Sunday 23:00), end 52000 (Friday 20:00). Used to determine the weekend for BarMode flags. 

StartMarket

EndMarket

Daily global market opening and closing time in hhmm local time in the BarZone, hh = hour and mm = minute. Default: 0930. Used for local time functions (day, market, etc.) and for trading and sampling of bars, dependent on BarMode. Automatically converted from AssetMarketStart/End (see below) when BR_LOCAL is set. 

AssetMarketStart

AssetMarketEnd

Local market opening and closing time of the selected asset, in hhmm local time in the AssetMarketZone. Initially read from the Market field in the asset list; can also be set by script. Used for intraday trading in BR_LOCAL mode. 

Type:

int
 

Holidays

Pointer to an int array of holiday dates either in yyyymmdd or in mmdd format, ending with 0. Default: { 0101, 1225, 0 }. Can be set to a 0-terminated array for defining local stock market holidays. The yyyymmdd format specifies a holiday only in a certain year, mmdd for all years.

Type:

int*
 

Now

Date/time variable in DATE format for passing a certain point in time to the contractUpdate function or to the NOW argument of date/time functions. When at 0 (default), the current PC date and time is used for NOW.

DayOffset

Time period in DATE format to be added to the current time in [Trade] mode for special purposes, such as a quick test of the live behavior at a particular day, or at weekend or market closure (see also Troubleshooting). Set it to 1 or adding a day, or to 1./24 for adding one hour to the current time, or increase it by 1./24 at any 1-hour bar for trading a system in double speed. Affects also the lookback period and the time of a connected server; does not affect timestamps of historical data.

Type:

var
 

Remarks:

Example:

StartDate = 20150901; // start the simulation in September 2015
EndDate = 20160901; // and simulate one year. static int USHolidays[10] = { 0101, 0218, 0419, 0704, 0527, 0902, 1128, 1225, 0 }; Holidays = USHolidays; // set up US holidays

See also:

bar, BarPeriod, LookBack, Detrend, time/date functions, PlotDate

 

► latest version online dayOpen, Close, High, Low, Pivot

dayOpen (int zone, int day) : var

dayClose (int zone, int day) : var

dayHigh (int zone, int day) : var

dayLow (int zone, int day) : var

dayPivot (int zone, int day) : var

Returns the open, close, high, low, and pivot point within the market hours (by default, 9:30 am until 16:00 pm) of a given working day and time zone. Can be used for seasonal trading, gap trading, or daily price action. The pivot point is defined as (High+Low+Close)/3.

Parameters:

zone UTC for UTC time or EST for New York time, or a number giving the zone offset in hours to UTC. Daylight saving time is considered.
day Working day offset, i.e. 0 = today (see remarks), 1 = yesterday, 2 = day before yesterday, and so on. Weekends are skipped, f.i. if today is Monday, 1 refers to last Friday.

Returns

Price.

Usage:

dayClose(ET,1) returns yesterday's closing price of the current asset at the New York Stock Exchange.

Remarks:

Example:

// simple gap trading system, 
// based on a publication by David Bean 
function run()
{
  BarPeriod = 10;
  LookBack = 100*24*60/BarPeriod; // 100 days lookback 

  asset("SPX500");  
  Stop = 100*PIP;
  TakeProfit = 40*PIP;

  vars Prices = series(price());  
  var High = dayHigh(ET,1);
  var Low = dayLow(ET,1); 
  var Close = dayClose(ET,1);

// enter a trade when the NYSE opens 
  if(High > 0 && Low > 0 && Close > 0 
    && timeOffset(ET,0,9,30) == 0)
  {
    var Avg = SMA(Prices,LookBack);
    if(*Prices > Close 
      && *Prices < High
      && *Prices < Avg)
      enterShort();
          
    if(*Prices < Close 
      && *Prices > Low
      && *Prices > Avg)
      enterLong();
  }
}

See also:

price, timeOffset, market, BarMode, Pivot, Support/Resistance

► latest version online

CBI, CBIScale, ReturnCBI

Cold Blood Index

It is essential for risk control to permanently observe automated trading systems and detect as early as possible if a market change rendered the algorithm unprofitable. The Cold Blood Index (CBI) can distinguish between normal drawdowns that are common to algo trading systems, and loss of profitability due to due to a substantial market change.
  For this purpose, the live trading profit/loss curve is daily compared with the backtest profit-loss curve. The drawdown probability is calculated based on the algorithm published on Financial Hacker, and displayed in the trade log and on the status page. A drawdown of low probability indicates a deviation from the backtest due to a a market change. This information can be used for deciding whether to stop the trading algorithm, or to continue it in cold blood.

The drawdown probability is calculated with the following function:

CBI (var* Data, int Length, int Shuffle, var Depth, int Width, int Horizon): var

Returns the probability in percent (0..100) of encountering a drawdown of the given Depth and Width within a given time Horizon. For this, the drawdown is compared with the Data array that is supposed to contain a profit/loss curve from a backtest or a previous trading session.

Returns:

Probability in percent (0..100). A low probability (less than 2%) indicates a deviation from the backtest. 

Parameters:

Data Data array with profit/loss values, f.i. from the pnl.dbl curve stored in a backtest.
Length Length of the data array, positive for an array in time descending order and negative for ascending order.
Shuffle Number of shuffled profit/loss curves to evaluate, or 1 for only evaluating the original curve. The returned probability is calculated from the original and the shuffled curves.
Depth Drawdown depth, the difference of an equity or balance peak with the subsequent equity or balance valley, in the same scale as the values in the Data array.
Width Drawdown duration, the time distance between the highest peak and the subsequent deepest valley, in the same units as the time distance of the points in the Data array, usually bar periods or days.
Horizon Live trading time in in the same units as the Data array, usually bar periods or days. Must be equal or above Width and smaller than Length.

 

CBIScale

Drawdown scale for the automatic CBI calculation in [Trade] mode (default = 1 = no scaling). The drawdown depth is divided by this scale factor for calculating the CBI.

Type:

var

ReturnCBI

Current CBI value in  [Trade] mode (see remarks).

Type:

var, read/only
 

Remarks:

Examples:

//scale drawdowns for CBI according to invested capital
var Invest = slider(1,5000,0,10000,"Investment","Invested capital");
static var InitialInvest = 0;
if(is(INITRUN)) InitialInvest = Invest; // store the initial investment that was also used in the backtest 
if(InitalInvest > 0) CBIScale = Invest/InitialInvest;
//Check the Cold Blood Index at a Z12 Drawdown of $800 in 
//$500 in the last 60 of 100 trading days
void main()
{
  var* PnLs = file_content("Data\\Z12_pnl.dbl");
  int Length =  file_length("Data\\Z12_pnl.dbl")/sizeof(var);
  var P = CBI(PnLs,-Length,1,500,60,100);
  printf("\nCBI = %.0f%%",P);
}

See also:

Trading, Cold Blood Index, randomize, ScholzBrake

 

► latest version online Deep Learning for Market Prediction | Zorro Project

Deep learning for market prediction

Machine learning algorithms can be used for market prediction with Zorro's advise functions. Due to the low signal-to-noise ratio and to ever-changing market conditions of price series, analyzing the market is an ambitious task for machine learning. But since the price curves are not completely random, even relatively simple machine learning methods, such as in the DeepLearn script, can predict the next price movement with a better than 50% success rate. If the success rate is high enough to overcome transactions costs - at or above 55% accuracy - we can expect a steadily rising profit curve.

Compared with other machine learning algorithms, such as Random Forests or Support Vector Machines, deep learning systems combine a high success rate with litle programming effort. A linear neural network with 8 inputs driven by indicators and 4 outputs for buying and selling has a structure like this:

Deep learning uses linear or special neural network structures (convolution layers, LSTM) with a large number of neurons and hidden layers. Some parameters common for most neural networks:

Here's a short description of installation and usage of 5 popular R or Python based deep learning packages: Deepnet, Torch, Keras/Tensorflow, H2O, and MxNet. Each comes with an short example of a (not really deep) linear neural net with one hidden layer.

Deepnet (R)

Deepnet is a lightweight and straightforward neural net library supporting a stacked autoencoder and a Boltzmann machine. Produces good results when the feature set is not too complex. Note that the current version does not support more than 3 hidden layers. The basic R script for using a deepnet autoencoder in a Zorro strategy:
library('deepnet')

neural.train = function(model,XY)
{
  XY <- as.matrix(XY)
  X <- XY[,-ncol(XY)]
  Y <- XY[,ncol(XY)]
  Y <- ifelse(Y > 0,1,0)
  Models[[model]] <<- sae.dnn.train(X,Y,
  hidden = c(30),
  learningrate = 0.5,
  momentum = 0.5,
  learningrate_scale = 1.0,
  output = "sigm",
  sae_output = "linear",
  numepochs = 100,
  batchsize = 100)
}

neural.predict = function(model,X)
{
  if(is.vector(X)) X <- t(X)
  return(nn.predict(Models[[model]],X))
}

neural.save = function(name)
{
  save(Models,file=name) 
}

neural.init = function()
{
  set.seed(365)
  Models <<- vector("list")
}

Torch (Python)

Torch is an open-source machine learning library and a scientific computing framework, created by the Idiap Research Institute. Torch development moved in 2017 to PyTorch, a port of the library to Python.

The step by step installation: 

You need to write a .cpp script and run it with the 64-bit Zorro version. Include pynet.cpp, which contains the neural function for Python. Then write the PyTorch script, with the same name as your strategy, but extension .py. Example:

import torch
from torch import nn
import math
import numpy as np

Path = "Data/Signals.csv"
Device = "cpu"
NumSignals = 8
Batch = 15
Split = 90
Epochs = 30
Neurons = 256
Rate = 0.001
Models = []

def neural_init():
    global Device
    Device = (
        "cuda"
        if torch.cuda.is_available()
        else "mps"
        if torch.backends.mps.is_available()
        else "cpu"
    )
    
def network():  
    model = nn.Sequential(
        nn.Linear(NumSignals,Neurons),
        nn.ReLU(),
        nn.Linear(Neurons,Neurons),
        nn.ReLU(),
        nn.Linear(Neurons,1),
        nn.Sigmoid()
    ).to(Device)
    loss = nn.BCELoss()
    optimizer = torch.optim.Adam(model.parameters(),lr=Rate)
    return model,loss,optimizer 

def data(Path):
    global NumSignals
    Xy = np.loadtxt(Path,delimiter=',')
    NumSignals = len(Xy[0,:])-1
    return Xy

def split(Xy):
    X = torch.tensor(Xy[:,0:NumSignals],dtype=torch.float32,device=Device)
    y = torch.tensor(Xy[:,NumSignals].reshape(-1,1),dtype=torch.float32,device=Device)
    y = torch.where(y > 0.,1.,0.)
    return X,y

def train(N,Xy):
    global Models
    model,loss,optimizer = network()
    X,y = split(Xy)
    for j in range(Epochs):
        for i in range(0,len(X),Batch):
            y_pred = model(X[i:i+Batch])
            Loss = loss(y_pred,y[i:i+Batch])
            optimizer.zero_grad()
            Loss.backward()
            optimizer.step()
        print(f"Epoch {j}, Loss {Loss.item():>5f}")
    Models.append(model)
    return Loss.item()*100

def neural_train(N,Path):
    Xy = data(Path)
    return train(N,Xy)

def neural_predict(N,X):
    if N >= len(Models):
        print(f"Error: Predict {N} >= {len(Models)}")
        return 0
    model = Models[N]
    X = torch.tensor(X,dtype=torch.float32,device=Device)
    with torch.no_grad():
        y_pred = model(X)
    return y_pred

def neural_save(Path):
    global Models
    torch.save(Models,Path)
    print(f"{len(Models)} models saved")

def neural_load(Path):
    global Models
    Models.clear()
    Models = torch.load(Path)
    for i in range(len(Models)):
        Models[i].eval()
    print(f"{len(Models)} models loaded")

Keras / Tensorflow (R)

Tensorflow is a neural network kit by Google (although they lately seem to have abanoned it). It supports CPU and GPU and comes with all needed modules for tensor arithmetics, activation and loss functions, covolution kernels, and backpropagation algorithms. So you can build your own neural net structure. Keras is a neural network shell that offers a simple interface for that, and is well described in a book.

Keras is available as a R library, but installing it with Tensorflow requires also a Python environment such as Anaconda or Miniconda. The step by step installation for the CPU version: 

Installing the GPU version is more complex, since Tensorflow supports Windows only in CPU mode. So you need to either run Zorro on Linux, or run Keras and Tensorflow under WSL for GPU-based training. Multiple cores are only available on a CPU, not on a GPU. 

Example to use Keras in your strategy:

library('keras')

neural.train = function(model,XY)
{
  X <- data.matrix(XY[,-ncol(XY)])
  Y <- XY[,ncol(XY)]
  Y <- ifelse(Y > 0,1,0)
  Model <- keras_model_sequential()
  Model %>%
    layer_dense(units=30,activation='relu',input_shape = c(ncol(X))) %>%
    layer_dropout(rate = 0.2) %>%
    layer_dense(units = 1, activation = 'sigmoid')

  Model %>% compile(
    loss = 'binary_crossentropy',
    optimizer = optimizer_rmsprop(),
    metrics = c('accuracy'))

  Model %>% fit(X, Y,
    epochs = 20, batch_size = 20,
    validation_split = 0, shuffle = FALSE)

  Models[[model]] <<- Model
}

neural.predict = function(model,X)
{
  if(is.vector(X)) X <- t(X)
  X <- as.matrix(X)
  Y <- Models[[model]] %>% predict_proba(X)
  return(ifelse(Y > 0.5,1,0))
}

neural.save = function(name)
{
  for(i in c(1:length(Models)))
    Models[[i]] <<- serialize_model(Models[[i]])
  save(Models,file=name) 
}

neural.load = function(name)
{
  load(name,.GlobalEnv)
  for(i in c(1:length(Models)))
    Models[[i]] <<- unserialize_model(Models[[i]])
}

neural.init = function()
{
  set.seed(365)
  Models <<- vector("list")
}

MxNet (R)

MxNet is Amazon’s answer on Google’s Tensorflow. It offers also tensor arithmetics and neural net building blocks on CPU and GPU, as well as high level network functions similar to Keras; a future Keras version will also support MxNet. Just as with Tensorflow, CUDA is supported, but not (yet) OpenCL, so you’ll need a Nvidia graphics card to enjoy GPU support. The code for installing the MxNet CPU version:
cran <- getOption("repos")
cran["dmlc"] <- "https://s3-us-west-2.amazonaws.com/apache-mxnet/R/CRAN/"
options(repos = cran)
install.packages('mxnet')
The MxNet R script:
library('mxnet')

neural.train = function(model,XY)
{
  X <- data.matrix(XY[,-ncol(XY)])
  Y <- XY[,ncol(XY)]
  Y <- ifelse(Y > 0,1,0)
  Models[[model]] <<- mx.mlp(X,Y,
    hidden_node = c(30),
    out_node = 2,
    activation = "sigmoid",
    out_activation = "softmax",
    num.round = 20,
    array.batch.size = 20,
    learning.rate = 0.05,
    momentum = 0.9,
    eval.metric = mx.metric.accuracy)
}

neural.predict = function(model,X)
{
  if(is.vector(X)) X <- t(X)
  X <- data.matrix(X)
  Y <- predict(Models[[model]],X)
  return(ifelse(Y[1,] > Y[2,],0,1))
}

neural.save = function(name)
{
  save(Models,file=name) 
}

neural.init = function()
{
  mx.set.seed(365)
  Models <<- vector("list")
}

Remarks:

Read on:

Training, Strategies, advise 

► latest version online

#define

#define name

Defines the name as a condition for later including or excluding lines (see #ifdef), or for setting other special conditions during compilation.

Example:

#define TEST
...
#ifdef TEST
printf("This is a test!");
#endif

#define name value

Every time the name appears in the script below the #define, it will be replaced by the value, which can be another name, a number, or a simple arithmetic expression. Replacing names makes functions more 'readable', for instance by giving general purpose variables some meaningful names.

Examples:

#define PI 3.14159
#define HEALTH skill17
#define WINAPI __stdcall
...
x = 2.0*PI;
my.HEALTH -= 50;

long WINAPI MessageBox(HWND,char *,char *,long);

Remarks

#undef name

Undefines a previously defined name.

#define macro(parameter,..)  expression(parameter,..)

Defines a macro as a replacement or abbreviation for a numerical expression. Whenever the macro is encountered in the code, the expression is executed. Macros work rather like functions, but with some minor differences. Since macros are implemented as a textual substitution, there is no effect on program performance (as with functions), however they produce larger code than functions. They are normally only used for fairly small expressions.

Examples:

#define set(obj,flag) obj.flags |= (flag)
#define reset(obj,flag) obj.flags &= ~(flag)
#define toggle(obj,flag) obj.flags ^= (flag)
#define is(obj,flag) (obj.flags & (flag))
#define zero(ptr) memset((void*)&ptr,0,sizeof(ptr))

#define macro(parameter,..)  expression(parameter##token,..)

The merging operator ## adds the token to the parameter. Useful for redefining variable or functions names in a macro.

Example:

#define merge3(name) merge(name##1,name##2,name##3) // merge3(test) is evaluated to merge(test1,test2,test3) 

Remarks

 

Some special #defines:

#define PRAGMA_ZERO

Initializes all local variables to 0.

#define PRAGMA_API  FunctionName;ModuleName!ProcName

Loads the function prototype FunctionName from function ProcName in the DLL ModuleName (see Using DLLs). Example:
#define PRAGMA_API MessageBox;user32!MessageBoxA

See also:

#ifdef, #ifndef, #else, #endif

► latest version online

Frechet Distance

frechet (vars Data, int TimeFrame, var Scale, var* Pattern) : var

Calculates the Frechet distance between the recent part of a data series and a predefined curve. Returns a percent value equivalent to the similarity between the two curves. This function can be used for detecting cups, zigzags, or similar patterns in the price curve.

Parameters:

Data The series to be compared.
TimeFrame The number of bars in the series to be compared, or 0 for using the length of the pattern. Determines the horizontal size of the pattern.
Scale The vertical size of the pattern (f.i. 10*PIP for detecting a 10 pips tall pattern). Use a negative scale for inverting the pattern.
Pattern The pattern shape to be detected in the series, given by an array of positive values that starts with the oldest value and ends with 0 as an end mark.

Returns

Similarity between Data and Pattern in percent, normally in the 20..80 range.

Remarks:

Example:

//detect 10-pip 10-bar cup formations in the price curve
function run()
{
  vars Price = series(price());
  static var cup[10] = { 6,3,2,1,1,1,2,3,6,0 };
  plot("Cup Similarity",frechet(Price, 0, 10*PIP, cup),NEW,RED);
}

See also:

predict, advise, polyfit

► latest version online Detrend

Price Curve Detrending, Randomizing, Shaping

When developing, testing, or training systems, it is often very useful to modify price curves, randomize price data, or remove trend from trades, indicators, or trained parameters. This allows training or testing strategies under defined conditions, such as with no trend, with artificial trend, or with artificial price peaks or drops. Randomized price curves can be used for Monte Carlo evaluations or reality checks. Backtesting with detrended or inverted price curves is a quick check to verify the validity and robustness of a strategy. 


          Artificial price drop for testing the behavior of a grid trading system


          Shuffled price curves for a reality check algorithm

One variable does it all:

Detrend

Accepts the flags below for detrending or other manipulations of the price curves or of trades, indicators, or trained parameters. The flags can be set with Detrend = Flag; or setf(Detrend,Flag);. Flags can be combined with '+' or '|':

Range:


TRADES Detrend trade results. The trade return is corrected by a factor derived from the average price slope of the current WFO cycle. This removes trend bias from a WFO test while keeping the properties of the price curve.
PRICES Detrend trade results and price functions, as well as indicators derived from them. A corrrection factor as above is added to the returned prices. This ensures that any WFO cycle starts and ends at the same price, and detrends indicators and signals based on series generated with price() calls.
CURVE Detrend historical price data on loading. The curve is tilted so that the end and the start of the historical data are at the same level. This also affects the displayed price curve in the chart.
RECIPROCAL Replace historical prices with their reciprocal values. This it can be used for creating an artifical asset as the reciprocal of an existing forex or crypto pair (f.i. convert BTC/USD to USD/BTC).
INVERT Similar to RECIPROCAL, but keep the price range while inverting the price curve. This reverses all trends in the curve, and can be used for a reality check of a system that is symmetric in long and short positions.
SHAPE Bend the price curve to a predefined shape for testing strategy behavior under certain conditions such as sudden price drops or trend reverals, even when they don't appear in the original curve.The desired shape can be set up with the Shape array (see below).
NOPRICE Do not process the historical data by detecting gaps or fixing invalid prices or outliers on loading. For data files that contain no prices, but other types of data.
SHUFFLE Randomize the price curve by shuffling the price ticks without replacement. Keeps its overall trend from start to end, but removes any short-term trends and correlations between the prices. Used for reality checks. Requires historical data in .t6 format.
SHUFFLE+PEAK
SHUFFLE+VALLEY
Randomize the price curve as above, but generate a curve that does not exceed the highest peak or/and the lowest valley of the original price curve. Use this for keeping the original price range. Can be slow on curves with large price ranges.
BOOTSTRAP Randomize the price curve by shuffling the price ticks with replacement. Keeps its overall trend from start to end, but removes any short-term trends and correlations between the prices. Used for reality checks.
BOOTSTRAP+PEAK
BOOTSTRAP+VALLEY
Randomize the price curve as above, but generate a curve that does not exceed twice the maximum of the orginal price curve, or/and does not fall below zero. Can be slow on curves with large price ranges.
RANDOMWALK Generate a random walk price curve by moving the price in random steps that depend on original volatility. Removes any market inefficiency out of the price curve.
RANDOMWALK+PEAK
RANDOMWALK+VALLEY
Generate a random walk price curve that does not exceed twice the maximum of the orginal price curve, or/and does not fall below zero. Can be slow on curves with large volatility.

Type:

int
 

Shape

Pointer to a 0-terminated array of price values for applying a shape to the price curve when Detrend = SHAPE is set. All prices of the array are placed in equal distances, and the price curve is bent so that it touches any of them. The more prices in the array, the closer is the curve adapted to the given shape. The last array element must be 0 for indicating the end (see example).

Type:

var*
   

Remarks:

Example (see also Detrend.c):

// detrend the price curve for training only
if(is(TRAINMODE)) Detrend = CURVE; 

// apply a rectangular shape to the curve
var SuddenDrop[50] = { 1.5, 1.5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.5, 1.5, 0 };
Shape = SuddenDrop;
Detrend = SHAPE;

See also:

mode, WFO, DataSlope, randomize, setf

 

► latest version online diff

diff (var x, int Period): var

Returns the difference of x to its value from Period bars before.

Parameters:

x Variable for difference calculation.

Returns

x - previous x

Remarks

Example:

// generate a price difference serie
vars Changes = series(diff(priceClose(),1));

See also:

series, ROCP

► latest version online Developing Algo Trading Systems with VC++

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. Scripts of supported types appear in the [Script] scrollbox and run automatically when the [Test], [Train], or [Trade] button is clicked. Zorro S compiles .x and .dll files from c. and .cpp sources automatically. 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.

The Visual Studio debugger can display 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. it 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 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 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++ 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 intptr_t 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++ setup for a DLL project

There are severat cases when you need to set up a VC++ project, rather than just compiling the C++ script on the fly. You need a project for using the Visual Studio debugger, for using C++ with the free Zorro version, for compiling a DLL from several separate .cpp files, for writing a broker plugin, or for special purposes such as adding external C++ libraries to your script. The dialogs shown below are slightly different for any Visual Studio version, but the settings are the same. Here's the setup for Visual C++ 2017 Community; for VC++ 2019 and 2022 it's 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

email

email (string To, string From, string Subject, string Body, string Server, string User, string Password): int

Sends an email to a given recipient. Useful for receiving emails when Zorro opens or closes a trade, or for status reports.

Parameters:

To - Recipient address as required by the server. Usually in angular brackets like "<me@myself.com>".
From - Sender address, usually in angular brackets.
Subject - Subject line of the email, and optionally content types.
Body - Text content of the email, max 16k.
Server - SMTP/SMTPS server with optional port address, f.i. "smtp://smtp.1und1.de" or "smtps://smtp.gmail.com:465".
User - Login name for the SMTP server, often identical to the sender address.
Passwort - Login password for the SMTP server.

Returns:

0 if the email could not be sent, otherwise nonzero.

Remarks:

Example:

void main()
{
  string To = "<me@myself.org>";
  string From = "<zorro_alert@gmail.com>";
  string Subject = "Zorro Message";
  string Body = "Zorro has sent you a message!";
  string Server = "smtps://smtp.gmail.com:465";
  string User = "zorro_alert@gmail.com";
  string Password = "zorros_password";
  email(To,From,Subject,Body,Server,User,Password);
}

See also:

HTTP functions, FTP functions ► latest version online Zorro Integration

Zorro processes. Integration in 3rd party software.

Zorro can be used for trading, backtests, optimizations, or other computations under control from either a VC++ project, a software application, or a Zorro 'master' instance. For this purpose, a ZorroControl DLL provides functions to start, stop, and exchange information with one or several Zorro processes (up to 72). A Zorro process is a single Zorro.exe instance running a given script and returning a result. In this way Zorro scripts can be run from other trading platforms, user interfaces, or software tools. 



Your
 Application




 
Zorro
 Control







 Zorro
 EXE






 Zorro
 Script

 



 Zorro
 EXE




 
Master
 Script






 Zorro
 Processes





 Zorro
 Scripts

The following functions are available in the ZorroControl DLL as well as in the Zorro API. They can be called from other programs, or directly from a Zorro 'master' script:

zInit (int Num, int Size): int

Initializes the interprocess communication and determines the maximum number of processes and the size of the data area per process. Must be called first from the ZorroControl DLL and from all Zorro processes. Returns 0 when no communication channel could be established, otherwiee nonzero.

zOpen (int Id, const char* Commandline): HANDLE

Starts a single Zorro process with the given identifier and command line. Must be called from the ZorroControl DLL or the master instance. Returns the process handle, or 0 when no process could be started due to an error in the command line, no Zorro S license, no Zorro.exe in the same folder, or a failed zInit call. For starting the process with no window, use the -h command line option.

zStatus (int Id): int

Returns 1 when the process is still running, 0 otherwise. Must be called from the ZorroControl DLL or master instance. 

zClose (int Id)

Closes the Zorro process with the given Id. Id == 0 closes all Zorro processes. Must be called from the ZorroControl DLL or the master instance.

zData (int Id): void*

Returns a pointer to a memory area of the previously given Size for exchanging data and information with the Zorro process with the given Id. Every process has its own data area, but can also read or write to other processes' data areas. For the data area, a byte-aligned struct can be defined that contains the variables to be exchanged (see example). 

Parameters:

Num Total number of processes to handle, 2..72. The master instance is always process 1.
Size Size of the data area for information exchange per process, in bytes. 8 bytes per var, 4 bytes per int.
Id Process identifier number, 2..Num, or 1 for the master instance.
Commandline Command line parameters for the Zorro process. If no script name is given, the script of the master instance is used.

Returns:

Nonzero when successful, 0 otherwise.

Remarks:

Example (see also the Process.c script):

// data struct that is exchanged between 2 Zorros
typedef struct INFO { 
  var Position; 
} INFO;

INFO *Own, *Other;

void main()
{
// initialize IPC
  if(!zInit(2,sizeof(INFO))) {
    printf("Can't initialize process!"); 
    return;
  } 
// if main process, start second Zorro
  if(Core <= 1) zOpen(2,"-w 320"); // shift window to the side
// get pointers to own and other data struct
  Own = zData(Core);
  Other = zData(3-Core); // Core is 1 or 2
// define sliders 
  slider(1,1,1,100,"This",0);
  slider(2,1,1,100,"Other",0);
// permanently replicate the 'Other' slider to the other Zorro
  while(wait(1)) {
    slider(1,Own->Position);
    Other->Position = slider(2);
  }
  zClose(2);
}

See also:

Licenses, Core, command line ► latest version onlin Error Messages

Error messages

Below you'll find a list of possible errors that can occur during compilation or at runtime, together with suggestions for finding the reason of the error. Messages related to opening and closing positions are listed under Log; known issues with brokers are commented on the related page (FXCM, IB, Oanda, MT4, etc...) in this manual.

Error messages must never be ignored. Even when your script runs and produces a result, an error message always indicates that something is seriously wrong and must be fixed. Warning messages can be ignored in some cases, but only when you know and understood their reason. You can encounter three types of warning or error messages:

For dealing with error messages by script, two functions are available:

 

error (string Message)

User-supplied function that is called on any runtime error message or any print/printf call that contains the word "Error", "Warning", or "Info". This function can be used to sound an alert or send an email about a potential problem. It is not in sync with other functions or I/O operations, so use the call scheduler when modifying global variables or accessing non-reentrant parts of the broker API.

allow (int Number)

Call this function for not generating messages on runtime errors or warnings with the given number. For instance, allow(73) suppresses all Error 073 messages. Not all errors can be suppressed; fatal errors that terminate the script will still generate a message.  

 

Compiler errors

Error in line ...

Wrong or missing token in the script code. Sometimes the line in question is ok, but some command in the preceding code was incomplete. Examples are an #ifdef without an #endif, or a missing semicolon in the previous line, or an orphaned { } bracket in the previous function. Another not-obvious error is declaring a variable or function with the same name as a predefined system variable, such as Stop or Lots.

Wrong type ...

Wrong variable type. You've used a numeric operation with wrong parameter types, for instance an AND or OR operation with a float or var type. Or you're using a variable with the same name as a predefined variable - look them up in variables.h. Or you've called a function with a var although it needs a series, or vice versa. Example: "Wrong type POINTER::DOUBLE" means that the code expects a double (or var) and you gave it a pointer (or series) instead.

Pointer expected ...

Function call with a wrong parameter type, for instance with a var when a series or an array is needed.

Undeclared identifier ...

The given name or type is unknown. A common reason is an #include statement without including <default.c> before. default.c contains all language definitions, but is only automatically included when nothing else is included.

Can not translate ...

Numerical operation with wrong parameter types, for instance an AND or OR operation with a float or var type. In that case, typecast the expression to the correct type.
 

Runtime errors

For suppressing a non-critical runtime error or warning, call ignore(ErrorNumber). Some warnings are only displayed on a higher Verbose level.

Error 010: Invalid trade parameter

A trade was entered with an invalid Stop, TakeProfit, Trail, Margin, or asset parameter that's unusually high, low, or at the wrong side of the current price. Or with an option or futures contract was entered with a wrong or missing Multiplier, strike, or expiration date. This trade cannot be executed. Wrong price limits can be caused by a bug in the script or by wrong margin cost, pip cost, or leverage parameters in the asset list. If it happens during live trading, an alert box will pop up dependent on Verbose settings.

Warning 010: Invalid trade parameter / no price

A trade was entered with an unknown parameter; for instance an option contract with no price in the historical or live data (call contractPrice before entering a trade). In live trading the position will be entered nevertheless, but this message is just to inform that it is not advisable to trade at unknown prices or other parameters.

 Error 011: xxx called with invalid parameters

A function was called with wrong parameters - for instance a price of zero, or a parameter outside its valid range. This is usually caused by a bug in the preceding script code. The function name is included in the message.

Error / Warning 012: Bad time / bad date / outdated

A date/time function was called with bad data, or the bar with the given time could not be found, or a contract chain had not the current date.

Error 013: Invalid expression (...)

An invalid value or a wrong variable format caused an exception in a standard C function or expression. For instance a division by zero, the square root of a negative number, an error in a string, or a wrong placeholder in a format statement. The reason is normally a bug in the script, but can also be a history or .csv file that is not found or has a wrong format, or a function returning a NaN number. This error is raised by the Windows invalid parameter handler.

Warning 014: Bar nnn clipped

Market hours, weekend, holidays, bar offset, time zone, and BarMode parameters contradict each other, causing a wrong start or end time at the given bar. This usually happens when the bar offset enforces bars outside market hours and the BR_MARKET flag suppresses such bars at the same tiome. It can also be caused by large gaps in the historical data. The bar is then clipped at the bar period. When using BarZone with daily bars, consider that the bar period is one hour smaller or longer when daylight saving changes - this can cause bars to end sometimes inside, sometimes outside market hours.

Error 015: Invalid xxx data

Object or function xxx got invalid data, f.i. a wrong parameter for a pointer or string, or data from a non initialized array, or from an invalid mathematical operation such as a division by zero. 

Error 016: Invalid date

A YYYYMMDD date has been given in a wrong format.

Error 017: Bad return value

A script function returned an invalid value.

Error 018: Bad name

A name was too long, too short, or contained spaces or other invalid characters.

Error 019: Empty function

A function prototype was defined, but not set to a function body.

Warning/Error 030: Check dates / price history / order of settings

The lookback period, simulation period, or price history was not set up correctly. Parameters that affect the bars generation (StartDate, BarPeriod, BarOffset, LookBack, TICKS, LEAN, etc.) were either inconsistent (f.i. a too short LookBack period for a subsequent indicator call), or changed after the bars were generated, or did not match the historical data. Make sure to set up all parameters and a sufficient lookback period before calling asset. Use the PRELOAD flag for live trading with extremely long lookback periods.

Error 031: Price history missing

The simulation could not be finished because historical data was missing.

Error 032: Constant parameter changed

A variable that should remain constant - such as NumWFOCyles - was changed after the initial run. Make sure not to change those parameters at runtime.

Warning 033: Asset missing in INITRUN

The given asset was not selected in the initial run of the strategy. The initial asset call was either missing, or skipped, or failed due to insufficient historical data. The asset parameters are filled with default data. Make sure that all assets are loaded and initialized before they are needed.

Warning 034: No asset data for ...

The given asset is missing in the asset list (note that asset names are case sensitive). The simulation will still run when historical price data is found, but asset parameters such as spread, margin, lot size, trade costs, trade profit etc. are made up and do not reflect the true values. The asset can not be traded.

Warning/Error 035: Price gap / invalid ticks / outliers detected

Prices are missing, outliers, or have invalid time stamps. The broker API did not provide the required historical data, or did not send the current price during the last bar. The missing prices will be automatically replaced with the last valid price. If the warning is triggered by heavy price fluctuations in downloaded data, reduce the sensitivity of the Outlier variable.

Error 036: Index exceeded

A function wrote past the maximum index of an array or a series.

Error 037: Invalid value

A plot function was called with an invalid value, caused by a division by zero, the square root of a negative number, or similar errors in the script. See Troubleshooting for working around such errors.

Error 038: Too many elements

The plotGraph with the given name had more elements that bars exist on the chart. If you need this many, use another name for the rest of the elements.

Error 039: Expression too complex

The rule generated by a machine learning function is too complex for conversion to a C expression. In case of candlestick patterns, use the FAST pattern detection mode that generates simpler rules, or reduce the number of patterns.

Error 040: Skipped optimize calls

Number or order of optimized parameters differ from run to run. Make sure that optimize is always called in the same order, the number of optimize calls is the always same as the number of optimized parameters, and the PARAMETERS flag is not changed between optimize calls.

Error 041: series size / number / usage

Something was wrong with a series, loop, or matrix call in your script. They are special functions that cannot be arbitrarily called. Series might have been called in wrong order or with different size, or loop might have been called outside the run function. Check all such calls and all indicators or functions that are described to create series, such as Volatility, ATR, LowPass, etc. Any run must have the same series with the same size in the same order. Don't skip series calls with if statements, don't create them in event-triggered functions such as tmf, tick, or tock, and don't chnage the size of a series. The error message tells you at which bar the problem occured first. If you need an extremely large number of series, increase TradesPerBar until the error message disappears.

Error 042: No parameters

Error 043: Irregular optimize calls

The optimize function was called too often per component, or the produced parameters were not found or are inconsisted with the current strategy. Make sure that the optimize calls or the loop parameters were not changed after training. For asset-dependent parameters, make sure that all assets and algos are selected with loop calls and their optimize calls are inside the inner loop. For asset-independent parameters in a portfolio system, make sure that always the same asset is selected before the optimize call.

Error 044: No rule ... / ... not trained

The advise or optimize function could not find a setup or a trained rule, model, or parameter for the current asset/algo combination. Either it did not enter any trades, or something was wrong with the setup order in your script. Make sure that prior to any advise or optimize call, the asset and algo (if any) was selected, the PARAMETERS or RULES flag was set, and TrainMode was set up. Then [Train] again.

Error 045: Negative price offset

A price that lies in the future was requested by a price or day function. For the simulation you can suppress this message by setting the PEEK flag. PEEK is not available for real trading though - Zorro is good in predicting prices, but not this good.

Error 046: LookBack exceeded

A price, series, or TA function required a higher lookback period than reserved through LookBack (default = 80). When no asset is selected and time periods don't change at runtime, Lookback is automatically adapted to the longest indicator time period of the initial run. Otherwise it should be manually set to the longest possible time period. Setting LookBack to 0, or calling ignore(46) allows series longer than the lookback period and suppresses this error message at user's risk.

Warning / Error 047: Not enough bars

Historical price data was insufficient for covering the test period or the LookBack period. Possible reasons:

Make sure that used assets are selected in the first run, and that historical data - when needed - is sufficient for the backtest plus lookback period and is of the same type for all assets. Don't use a mix of .t1, .t2, and .t6 data, or a part of the data split into years and another part not. You can download price data from online sources with the Download script. Frequently used data is also available on the Zorro Download page. If price data for a certain year is not available, create a .t6 file of 0 bytes size with the name of the asset and year (f.i. SPX500_2017.t6). Zorro will then skip that period in the simulation and not display the error message.

Error 048: Endless loop or recursion

Trades are possibly generated in an endless loop or recursion, f.i. by a TMF that enters trades who themselves enter new trades through their TMF. Or a for(trades) loop is nested inside another for(trades) loop.

Error 049: Too many trades

The script entered more than one trade per bar and asset, which is normally a sign of a script bug - therefore the error message. If you really need a huge number of trades, for instance for unlimited grid trading or for training several advise functions at the same time, set the TradesPerBar variable to the required number of trades per bar and asset.

Warning 050: Trade closed / not found

The trade from the previous session cannot be resumed. It was either opened with a different algo version, or on a different account, or closed on the broker side, for instance due to liquidation by insufficient margin. It's also possible that the trade got 'orphaned' due to an ID change or a glitch on the broker server. You can normally continue the trading session, but check the trade in your broker's platform. You can identify it from the last 5 digits of the trade ID. If it is still open, close it manually. If it was a pool trade in virtual hedging mode, it will be automatically re-opened at the next trade entry.

Error 051: Can't continue trades

A trading session was stopped without closing the open trades, then resumed with a different Zorro version or script version. Zorro can only continue trades that the same version has opened. Use the broker platform for closing the trades manually.

Error 052: Broker interface busy

Zorro attempted to log in to the broker, but another Zorro instance on the same PC is already connected. For multiple trading sessions and accounts, Zorro S is required.

Error 053: ... prices unavailable / invalid asset

The script failed to retrieve prices. The selected asset does not exist, or is not available, or had a wrong symbol, or had no prices due to market closure, an offline server, no market subscription, an unsupported price type, or for other reasons. A checklist:

Warning / Error 054: asset parameter

One or several parameters in the asset list do not match the values returned from the broker API. If it happens in [Train] or [Test] mode, it's an invalid value, and you need to edit the asset list and fix it. If it happens in [Trade] mode, the asset parameter returned from the broker API differed by more than 50% from their values in the asset list, of from values previously received. If you are sure that the asset parameters in your list are correct, but the broker API got them wrong, you can override the API with the SET_PATCH command or BrokerPatch setting. MT4/MT5 servers can temporarily return zero values for previously unused assets (see remarks about MT4 issues). In this case, simply start the script again. If the displayed value is substantially different to the value in the asset list, backtests won't reflect the connected account, so check and correct the asset list.

Error 056: ... can't download / no data

Historical price data could not be downloaded from the broker's price server or from an online source. The server can be offline or does not offer price data for the given asset and time period. For brokers that require market data subscriptions, check if you've subscribed the assets to be downloaded. Errors from online sources are usually due to an unsupported symbol or to exceeding a download limit. The full response can be found in the downloaded History\history.csv or history.json file.

Error 057: inconsistent history format

Check if the historical data is consistent (no mix of different .t1, .t6, or .t8 files) and has similar resolution for all used assets. Second or tick resolution is needed for testing with bar periods less than one minute.

Error 058: ... can't parse

A CSV dataset can not be parsed due to an invalid file format. If the file was recently downloaded (f.i. history.csv), open it with a text editor - it might contain no CSV data, but an error message from the data provider. Otherwise compare the CSV file content with your conversion format string. If in doubt, set Verbose to 7 for checking the source and parse result of the first 2 lines - this normally reveals the format error. The error details:

Bad code - your format string contained an invalid field placeholder.
Bad date - the date code in your format string does not match the date format in the file.
Bad field number - your format string contained invalid field numbers.

Error 059: ... invalid format

An asset list, account list, or panel definition could not be read due to an invalid parameter or format. Check if it has valid CSV format and that no field is empty or contains invalid content. Every line in a .csv file must have the same number of delimiters - all commas or all semicolons - and end with a 'new line' character.

Error 060: Out of memory / Memory fragmented / limit exceeded

Available memory is insufficient for your script. Windows can assign 3 GB memory to a 32-bit process, and all free memory to a 64-bit process. If that size is exceeded, or if the memory became too fragmented by previous simulation or training runs, the memory required by the script cannot be allocated. Some methods to overcome the problem or to reduce the memory requirement:

A formula for calculating the memory requirement per asset can be found under asset. Usually, memory on a Windows PC is sufficient for several years tick-based backtests even with large history files.

Error 061: Incompatible / Can't compile ...

The executable script could not be compiled, or was compiled with an incompatible version, like 32 bit vs 64 bit, or is missing a VC++ library on your PC. Delete the outdated .x or .dll file (if any), make sure that you entered the correct VC++ compiler path in Zorro.ini, and compile it again. For running compiled C++ code in 64 bit mode, install the VC++ 64-bit redistributable package that is available either from Microsoft or from our server.

Error 062: Can't open file ...

A file was not found, could not be opened, or had wrong format. Make sure that the file exists, that it has the correct name, that the Zorro process has access rights to it, and that it is not opened with exclusive access in another application (f.i. a .csv file in Excel). If a Windows error number is given, f.i. (wb:13), the reason of the error can be found in the list below. 
   

2

No such file or directory

9

Bad file number

11, 12

Not enough memory or resources

13

No access rights, or file opened in another application

16

Device or resource busy

23, 24

Too many files open in system

28

No space left on device

30

Read-only file system

38

Filename too long


If the missing file is a .par, .c, .fac, or .ml file, either the script was not yet trained, or the file had a wrong name. The usual reason is selecting a different asset, or no asset at all, before an optimize or advise call. Parameter and rule files will then get inconsistent names in test and training and therefore cannot be read.

Error 063: ... not available

The function is not available in this script because it requires a different setup, an external plugin, or Zorro S. Make sure that you installed your personal Zorro S key or token.

Warning 070: Potential orphan

Zorro sent a trade order to the broker API, but received no response. This can happen due to a server glitch or connection issue right at or after sending the order. It is then unknown whether the position was opened or not. Zorro will assume that the order was rejected. But if it was in fact executed, the trade is orphaned and not anymore under Zorro control.
  Check in the broker platform if the position was opened. If so, close it manually. If orphaned trades occur frequently, check your Internet connection and/or connect - if possible - to a different server of your broker. Some Forex brokers, such as FXCM, are known for occasional orphaned trades.

Error 071: Trade not found ...

Zorro closed the trade with the given ID, but the trade was not found in the broker's trade lists. Possibly it was never opened, or was already closed manually or by a margin call or similar event.

Error 072: Login failed ...

A broker connection could not be established. The broker server could be offline or the broker API nonfunctional.

Warning 073: Can't close nn lots

An exitShort/exitLong command for partial closing could not be fully executed because not enough trades were open to close the given number of lots. When partial closing, make sure not to close more lots than are open.

Error 074: Balance limit exceeded

You need Zorro S when your annual profit or your balance exceeds the real account limits of the free version. There are no restrictions on demo or paper accounts.

Warning 075: Can't open / can't close / trade removed...

A trade order was not executed by the broker API during a live trading session, and subsequently cancelled. The reason is normally printed either to the Zorro log, or to the MT4/MT5 'Experts' log. Valid reasons for not excuting orders are: not enough funds, not enough free margin, no permission to trade that asset, a too distant or too close stop or order limit, a wrong asset symbol, a wrong or missing exchange symbol, a closed market, a broker server issue, no liquidity or no taker, FIFO violation, NFA rules violation, unsupported hedging (f.i. Oanda), or unsupported partial closing (f.i. some MT4 / MT5 brokers). All this, except for funds, liquidity, and permissions, can often be solved or worked around in the script, the asset list, or in case of a Z system, in the Z.ini configuration.
  If a close order is not executed, it will be automatically repeated in increasing intervals until the position is closed. If it is still not closed after 3 working days, the trade will be removed and no further close orders will be sent. If an open order is rejected, it is not automatically repeated, so it's up to the script to either ignore or handle it.

Error 075: Check NFA flag

You're trading with a NFA broker, but the NFA flag in your script or account list is wrong or missing. Stopping or closing trades is prohibited on the broker side due to NFA compliance.

Error 080: Python / R code

Python or R code could not be executed due to a syntax error or for other reasons.

Error 111: Crash in ...

A function in the script crashed due to a wrong operation. Examples are a division by zero, a wrong type or missing variable in a print/printf call, a wrong array index, or exceeding the stack size by declaring huge local arrays. The current run is aborted, possibly causing subsequent errors, f.i. inconsistent series calls. The name of the faulty function is normally displayed. See Troubleshooting about how to fix bugs in your script functions or deal with other sorts of crashes.

 

Broker errors and messages

!...

All messages beginning with an exclamation mark are sent from the broker API in a live trading session, so their content depends on the broker. On high Verbose settings, some diagnostics can be printed on events such as session start. Other messages can indicate that the connection was temporarily interrupted or a buy/sell order was rejected (see enter/exit). This happens when assets are traded outside their market hours, such as an UK stock index when the London stock exchange is closed. A "Can't close trade" message can also indicate that the NFA flag was not set on a NFA compliant account, or vice versa. Read the comments on the broker related page (FXCM, IB, Oanda, MTR4, ...) in this manual. When trading with the MT4/5 bridge, details and reason of error message is printed under the [Experts] and [Journal] tabs of the MTR4 platform.
  If a trade cannot be opened, Zorro will not attempt to open it again unless either enforced by the script, or when it's a pool trade. Pool trades will be rebalanced at any bar until they match the virtual trades. If a trade cannot be closed, Zorro will attempt to close it again in increasing intervals. If it is still not closed after 4 days, Zorro will assume that it was manually closed, and make no further attempts.

 

 

 

► latest version online evaluate

evaluate ()

User-supplied function that is called at the end of a [Test] run, or when [Result] is clicked, or in [Trade] mode when the status page is updated. Can be used for analyzing the performance or storing it for further evaluation in a user specified way.

Parameters:

perf - optional pointer to a PERFORMANCE struct containing the result of the test; defined in include\trading.h.

cleanup ()

User-supplied function that is called at the end of a simulation or session. Can be used to copy files, free memory, or release other allocated resources.

Remarks:

Examples:

function evaluate()
{
  printf("\nR2 = %.2f Mean = %.3f",ReturnR2,ReturnMean);
}
// plot a parameter histogram in [Test] mode - profit vs. BarZone
function run()
{
  ...
  NumTotalCycles = 12;
  BarZone = 2*TotalCycle;
  ...
}

void evaluate()
{
  PlotScale = 10;
  plotBar("Objective",TotalCycle,BarZone,Balance,BARS,RED);	
}

See also:

Performance, trade statistics, user supplied functions

► latest version online exec

exec(string Program, string Options, int Flags)

Execute an external program, open a document or URL, or run a script or batch file.

Parameters:

Program - file name of the exe, batch file, document, URL to be opened. Use "Editor" for opening the script editor, "Zorro" for another Zorro instance.
Options - command line parameter string to be passed to the program, or 0 for no command line options.
Flags - 1 for waiting until the external program was terminated, 2 for hiding its window, otherwise 0.

Returns:

0 if the program was not found or could not be started, otherwise the return code of the program in mode 1, or the process ID in mode 0.

Remarks:

Examples (see also Python):

exec("notepad","test.txt",0); // open notepad
exec("Editor",strf("Log\\%s.csv",Script),0); // open a .csv file in the Log folder with the editor
exec("c:\\program files\\internet explorer\\iexplore.exe","https://zorro-project.com",0); // open an URL with Internet Explorer
exec("https://zorro-project.com",0,0); // open an URL with the standard browser
exec("Zorro","-run Myscript.c",1); // run a Zorro script in another instance
exec("Zorro",strf("-train %s",Script),0); // train current script in another Zorro instance

See also:

window, keys ► latest version online ExitCode

ExitCode

The exit code of the Zorro application. It not set, Zorro terminates with exit code 0.

Type:

int

Remarks:

Example

Script Test.c:
void main() { ExitCode = 777; }

Cmd Shell:
start /wait Zorro -run Test
echo %ERRORLEVEL%

See also:

Command line, quit, HWnd

 

► latest version online Data export / import

Exported Files

The log

Zorro records script printouts and trade or other events in a .log file when the LOGFILE flag is set. The log contains the currrent equity and asset price at every bar, plus events such as opening or closing a trade, adjusting a trailing stop, or a printf message by the script. If Verbose is at a higher level, it also records the daily state of all open trades, the daily profit or loss, and the current drawdown depth. Event logs are normal text files and can be opened with any text editor. Their content looks like this (the messages are explained under Log):

--- Monday 03.08. - daily profit +363$ ---
[GER30:EA:L7407] +684.97 / +685.0 pips
[GER30:HU:L7408] +684.97 / +685.0 pips
[UK100:EA:L7409] +580.55 / +464.4 pips
[USD/CAD:LP:S7612] +136.29 / +851.8 pips
[USD/CAD:LS:S7613] +68.15 / +851.8 pips
[AUD/USD:LP:L8412] +115.92 / +483.0 pips
[AUD/USD:MA:L1511] +42.50 / +265.6 pips
[USD/CAD:HU:S5412] +14.63 / +182.9 pips
[AUD/USD:HU:L5413] +28.05 / +175.3 pips
[GER30:EA:L7407] Trail 1@4746 Stop 4907
 
[5770: 04.08. 04:00]  6: 9289p 143/299
[GER30:EA:L7407] Trail 1@4746 Stop 4914
[AUD/USD:MA:L1511] Exit after 56 bars
[AUD/USD:MA:L1511] Exit 2@0.8406: +38.71 07:36
 
[5771: 04.08. 08:00]  6: 9773p 143/299
[GER30:EA:L7407] Trail 1@4746 Stop 4920
 
[5772: 04.08. 12:00]  6: 9773p 143/299
[GER30:EA:L7407] Trail 1@4746 Stop 4927
[USD/JPY:LP:S7308] Short 1@94.71 Risk 8
[USD/JPY:LS:S7309] Short 2@94.71 Risk 29
[USD/JPY:HU:S7310] Short 1@94.71 Risk 13

The name of the log file is the script name with appended .log extension. If the script contains no asset call or if the first printf message precedes it, the name of the selected asset from the scrollbox is added to the log file name. Optionally a number (LogNumber) can be added to the file name, for comparing log files generated with different script versions. New log files overwrite old log files of the same name. For preventing that the Log folder gets cluttered with thousands of log files, Zorro can be set up in Zorro.ini to automatically delete old files.

Trade lists

The exported trade lists - *_trd.csv, demotrades.csv, trades.csv - contain a description of every finished trade in comma separated format for import in Excel™ or other spreadsheet or database programs. They can be used for evaluating trade statistics or for the tax declaration. *_trd.csv is exported in [Test] mode when LOGFILE is set. demotrades.csv and trades.csv are exported in in [Trade] mode, dependent on whether a demo or real account was selected. The latter two are perpetual files, meaning that they are never overwritten, but their content is preserved and any new trade sent to the broker is just added at the end. So they will grow longer and longer until they are deleted manually or moved to a different folder. Depending on the Comma setting, numbers are exported with either a decimal comma or point, and separated with either a semicolon or a comma; this is because German spreadsheet programs require CSV data to be separated with semicolon. A trade spreadsheet (generated with Comma) looks like this:

Meaning of the fields:

Name Algo identifier (see algo), or the script name when no identifier is used.
Type Trade type, Long or Short. For options, also Put or Call.
Asset Traded asset.
ID Trade identifier number, also used in the broker's records.
Lots Number of lots. Multiply this with the asset's lot size (see above) to get the number of contracts.
Open Timestamp when the trade was opened, in the format Day.Month.Year Hour:Minute.
Close Timestamp when the trade was closed., in the format Day.Month.Year Hour:Minute.
Entry Trade fill price. If the fill price is not returned from the broker API, the ask or bid (dependent on Type) market price at trade entry. Not identical to TradePriceOpen, which is always the ask price.
Exit Trade exit price. If the exit fill price is not returned from the broker API, the ask or bid (dependent on Type) market price at trade exit. For in the money expired or exercised options, it's the executed underlying price. 0 for out of the money expired options.
Profit Profit or loss in units of the account currency, as returned from the broker API. Includes spread, commission, and slippage.
Rollover Interest received from or paid to the broker for keeping the trade open overnight, in units of the account currency.
ExitType Sold (by exitTrade), Reverse (by enterTrade), Stop (stop loss), Target (profit target), Time (ExitTime), Expired (options), Exit (by a TMF that returned 1), Cancelled (cancelTrade) or Closed (externally closed in the broker platform).

Spreadsheets

The print(TO_CSV,...) function offers an easy way to export backtest data or indicator values to a .csv spreadsheet in the Log folder. Under Tips & Tricks some more examples can be found for exporting data to .csv files or for importing data from an external text file, for instance to set up strategy parameters.
 

P&L curves

When the LOGFILE flag is set in [Test] or [Trade] mode, the daily profit and loss curve is exported at the end of the session to a _pnl.csv file in the Log folder, and a _pnl.dbl file in the Data folder. For this the session must have run for more than one day and at least one trade must have been executed. The files contain the daily balance or equity values in ascending date order, in text format in the .csv file, and as a double array in the .dbl file. This file is used for calculating the Cold Blood Index in a subsequent live trading session. Note that the array samples the values at the end of the day, so its last value is not identical to the end profit when the simulation ended before the end of the day.

When the EXPORT flag is set in [Train] mode or for a multi-cycle test,  the P&L curves generated from Parameter optimization and from any cycle are exported separately for any asset and algo to a _cyc.dbl file in the Data folder. The curves can be evaluated, for instance with White's Reality Check. The file contains multiple double arrays, one for any curve. The size of any curve can be determined from the size of the _pnl.dbl file.

Training data

Rules, parameters, factors, and machine learning models are exported to .c, .par, .fac, and .ml files in the Data folder. With the exception of factors, any WFO cycle exports a different set of files. The .c files contain the trained rules in C code, the .par and .fac are plain text files files containing the parameter values and factors separately for any component of the system. The .ml format is binary and depends on the used machine learning algorithm. The name of these files is affected by the Script string.

Brute Force optimization exports all parameter combinations and the resulting objectives to a *par.csv file in the Log folder. The first column contains the objective returns, the subsequent columns the parameter velues in the order of their appearance in the script. The exported spreadsheet can be evaluated by a script for generatiung heatmaps of any two parameters, or by Excel to generate a 3-d surface. Note that Excel requires re-arranging the objective column in a 2-d matrix for generating the surface.

Remarks

See also:

Bars, file, asset parameters, assetHistory

► latest version online

Expressions

Expressions

An expression is an arithmetical operation that delivers a result, which can then be assigned to a variable or object parameter. The arithmetic expression may be composed of any numbers, further variables or object parameters, function calls, brackets, and arithmetic operators.

The following operators are available in expressions:
= Assigns the result right of the '=' to the variable left of the '='.
+-*/ The usual mathematical operators. * and / have a higher priority than + and -.
% Modulo operator, the integer remainder of a division.
| Bitwise OR, can be used to set certains bits in a variable.
^ Bitwise exclusive OR, can be used to toggle certain bits in a variable.
~ Bitwise invert, toggles all bits of a variable.
& Bitwise AND, can be used to reset certains bits in a variable.
>> Bitwise right shift, can be used to divide a positive integer value by 2.
<< Bitwise left shift, can be used to multiply a positive integer value by 2.
() Parentheses for defining the priority of mathematical operations.

Examples:

x = (a + 1) * b / c;
z = 10;
x = x >> 2; // divides x by 4 (integer only)
x = x << 3; // multiplies x by 8  (integer only)
x = fraction(x) << 10; // copies the fractional part of x (10 bits) into the integer part

Assignment operators

The "="-character can be combined with the basic operators:

+= Adds the result right of the operator to the variable left of the operator.
-= Subtracts the result right of the operator from the variable left of the operator.
*= Multiplies the variable left of the operator by the result right of the operator.
/= Divides the variable left of the operator by the result right of the operator.
%= Sets the variable left of the operator to the remainder of the division by the result right of the operator.
|= Bitwise OR's the the result right of the operator and the variable left of the operator.
&= Bitwise AND's the the result right of the operator and the variable left of the operator.
^= Bitwise exclusive OR's the the result right of the operator and the variable left of the operator.
>>= Bitwise right shift the variable left of the operator by the result right of the operator.
<<= Bitwise left shift the variable left of the operator by the result right of the operator.

Increment and decrement operators

Variables can be counted up or down by attaching '++' or '--' before or after a variable.
 
x++ Increments x by 1; the result is the previous value of x (before incrementing).
++x Increments x by 1; the result is the current (incremented) value of x. This is slightly faster than x++.
x-- Decrements x by 1; the result is the previous value of x (before decrementing).
--x Decrements x by 1; the result is the current (decremented) value of x. This is slightly faster than x--.

Examples:

x = x + 1; // add 1 to x
x += 1; // add 1 to x
++x; // add 1 to x

Remarks:

See also:

Functions, Variables, Pointers, Comparisons

 

► latest version online File functions

File functions

The following functions can be used to read, write, or otherwise handle files:

file_copy (string Dest, string Src)

Copies the file with the name Src to the file with the name Dest. If the Dest file already exists, it is overwritten. If Dest is a folder ending with a backslash '\', the file is copied into the destination folder. This function can also be used to rename files by storing them under a different name.

Parameters:

Dest - destination folder or file path (either absolute, or relative to the Zorro folder, e.g. "Log\\MyStrategy.txt").
Src - source file path

file_delete (string name)

Deletes a file.

Parameters:

name - file path
 

file_length (string name): size_t

Checks if a file with the given name exists, and returns its length in bytes. If the file was not found, 0 is returned.

Parameters:

name - file path

file_date (string name): long

Checks if a file with the given name exists, and returns its last modification date in UTC as the number of seconds since January 1, 1970. The mtu function can convert it to the DATE format. If the file was not found, 0 is returned.

Parameters:

name - file path
 

file_select (string path, string filter): string

Opens a file dialog box at a given directory that lets the user select a file name to open or save. Returns the selected file name including path. The returned string keeps its content until the next file_select call.

Parameters:

path - initial directory to open, f.i. "Data", or 0 for opening the Zorro directory.
filter - list of pairs of null-terminated filter strings, or 0 for selecting all files. If the first filter string begins with '#', it generates a save dialog, otherwise an open dialog. The last string must be terminated by two null characters ("\0"). The first string in each pair describes the filter (f.i. "Parameter Files"), and the second string specifies the filter pattern (f.i. "*.par"). Example: "CSV Files\0*.csv\0\0". Multiple filter patterns can be separated with a semicolon (f.i. "*.par;*.fac;*.c"). The pattern must not contain spaces. Example of a filter list with three pairs: "All files (*.*)\0*.*\0Par, Fac\0*.par;*.fac\0Text files\0*.txt\0\0".
 

file_next (string path): string

Enumerate files in a directory. Returns the first file name (without directory path) that matches the given path. Returns the next match when path is 0 or an empty string. Returns 0 after the last matching file. 

Parameters:

path - file path with wildcards ("Data\\MyStrategy*.par") for the first file; 0 for subsequent files.
 

file_content (string name): string (temporary)

Returns a null-terminated temporary string with the content of the file, or 0 when the file was not found. The string keeps its content only until the next file_content call. For multiple files to be read at the same time, allocate buffers and use file_read.

Parameters:

name - file path

file_read (string name, string content, size_t length): size_t

Reads the content of a file into a string or array, and appends 0 at the end. Returns the number of read bytes.

Parameters:

name - file path
content - string or array of any type to be filled with the content of the file; must have a size of at least length+1.
length - maximum number of characters or bytes to read, or 0 for reading the whole file into a buffer of sufficient size. 

file_write (string name, string content, size_t length)

Stores the content of a string, series, or other data array in a file.

Parameters:

name - file path
content - string or other data to be written.
length - number of bytes to be written, or 0 for writing the complete content of a string.

file_append (string name, string content, size_t length)

file_appendfront (string name, string content, size_t length)

Opens a file and appends text or other data either at the end (file_append) or at the begin (file_appendfront). If the file does not exist, it is created.

Parameters:

name - file name with path.
content - text or other data to be appended at the end or begin of the file.
length - number of bytes to be written, or 0 for writing the content of a string.

Remarks:

Examples:

//script for merging all WFO .par and .fac files
//from two strategies to build a combined strategy
 
string src1 = "Z1";    // first strategy, can be identical to dest
string src2 = "Z1add"; // second strategy, must be different to dest
string dest = "Z1";    // combined strategy

int file_merge(int n,string ext)
{
  char name1[40],name2[40],name3[40];
  if(n) {
    sprintf(name1,"Data\\%s_%i.%s",src1,n,ext);
    sprintf(name2,"Data\\%s_%i.%s",src2,n,ext);
    sprintf(name3,"Data\\%s_%i.%s",dest,n,ext);
  } else {
    sprintf(name1,"Data\\%s.%s",src1,ext);
    sprintf(name2,"Data\\%s.%s",src2,ext);
    sprintf(name3,"Data\\%s.%s",dest,ext);  
  }
  if(!file_date(name1)) 
    return 0; // file does not exist
  if(0 != strcmp(name3,name1))
    if(!file_copy(name3,name1))
      return 0;
  if(!file_append(name3,file_content(name2))) 
    return 0;
  return 1;
}

function main()
{
  int cycles = 1;
  for(; cycles < 100; cycles++)
    if(!file_merge(cycles,"par")) 
      break;
  
  cycles += file_merge(0,"par");
  cycles += file_merge(0,"fac");
  
  if(cycles > 3) 
    printf("%i files combined!",cycles);
  else
    printf("Error!");
}
// set up strategy parameters from a .ini file
function run()
{
  static var Parameter1 = 0, Parameter2 = 0;
  if(is(INITRUN)) { // read the parameters only in the first run
    string setup = file_content("Strategy\\mysetup.ini");
    Parameter1 = strvar(setup,"Parameter1");
    Parameter2 = strvar(setup,"Parameter2");
  }
}
 
// mysetup.ini is a plain text file that contains
// the parameter values in a format like this:
Parameter1 = 123
Parameter2 = 456

See also:

keys, sprintf, string functions, http functions, ftp functions, putvar

► latest version online Fill

Order Filling and HFT Simulation

The Fill variable supports several modes for simulating naive or realistic order filling, as well as a HFT simulation mode. HFT (High Frequency Trading) is a trading method that exploits inefficiencies on the millisecond or microsecond time scale. It is very different to normal algorithmic trading. Usual trading accessories such as candles or indicators, are not used. The deciding factor is speed. HFT systems do normally not run on a trading platform - even Zorro would be not fast enough - but are directly programmed in a high speed language, such as C, Pentium assembler, shader language (HLSL) or hardware design language (VHDL).

Zorro can be used for testing HFT systems in Fill mode 8. In this mode, only direct trading, receiving price quotes, and plotting data are supported; indicators, series, or the run function are not used. Use the main function for initializing the system and for looping through bars. The maximum number of bars must be set up with MaxBars. Trades are opened and closed only with enter and exit commands; TMFs, limits, stops or targets are not used in HFT mode. Hedge mode must be 2. Price quotes are entered to the system with the priceQuote function. Any price quote must include the exchange time stamp, since the system is synchronized to it. The two-way latency between exchange and PC location is set up with OrderDelay and determines the price quote at which the enter or exit order will be filled.

An introduction into HFT testing with Zorro, and an example of a HFT system can be found on Financial Hacker.

For determining the latency of your system, make sure that your PC clock is exactly in sync with the clock used at the exchange, then compare the exchange time stamp of incoming live price quotes with the arrival time stamp. Double this time for getting the roundtrip latency and add the reaction time of your hardware and software. As a rule of thumb, the time stamp difference is about 1 ms per any 300 km distance to the exchange (light travels at 300.000 km/sec), plus about 1..2 ms additional delay due to signal routing. OrderDelay can be set up with a precision of 1 microsecond. Price quote data by data vendors has usually a precision of 1 ms.

Fill

Determines how order fills are simulated in [Test] and [Train] mode and how orders entries affect other open trades.

Range:

0 Naive order filling. Trades open or close at the current market price or at their Entry, Stop, or TakeProfit limits, regardless of latency, slippage, or intrabar price movement. Accidental 'cheating' - generating unrealistic profits - is possible by modifying stop or profit limits in a TMF. This mode can be used for special purposes, such as comparing Zorro results with 'naive' results by other platforms.
1 Realistic order filling (default). Trades open or close at the most recent price quote plus Slippage or Penalty. When Slippage and Penalty are 0, this mode simulates a latency-free connection to the market that immediately reacts on any price quote. If an entry or exit limit is hit and no Penalty is set, the position is filled at a price replicating live trading as accurate as possible with a slight pessimistic bias (see details below). 'Cheating' is not possible. With daily bars, this mode simulates trading right before the market closes.
3 Delayed order filling. Trades open or close at the next price quote plus Slippage or Penalty. With daily bars, this mode simulates trading at the market open price of the next day.
8 HFT fill mode. Trades open or close at the entered priceQuote whose time stamp is most recent to the current time plus OrderDelay. This mode can be used to simulate the effect of a script-defined latency.

Type:

int

Remarks:

Example:

function run()
{
  BarPeriod = 1440;
  Fill = 3; // enter trades at next day's open 
  ...
}

HFT simulation framework:

// Simulate a stock trading HFT system

#define LATENCY  2.5   // simulate 2.5 milliseconds latency

typedef struct QUOTE {
  char  Name[24];
  var  Time,Price,Size;
} QUOTE; // Quote struct by the NxCore plugin

int callback(QUOTE *Quote) // called by the plugin
{
  static int Counter = 0; // quote counter
  if(0 == (++Counter % 1000)) { // every 1000 quotes...
    if(!wait(0)) return 0; // check if [Stop] was clicked
  }
    
  asset(Quote->Name+1); // NxCore adds "e" to the asset name
  priceQuote(Quote->Time,Quote->Price);
  ...
// trade algorithm here, generate TradeSignalLong/Short
  ...
  if(!(NumOpenLong+NumPendingLong) && TradeSignalLong) {
    exitShort();
    enterLong();
  } else if(!(NumOpenShort+NumPendingShort) && TradeSignalShort) {
    exitLong();
    enterShort();
  }
  return 1;
}

function main()
{
  if(Broker != "NxCore") {
    quit("Please select NxCore plugin!");
    return;
  }

  StartDate = 20170103;
  EndDate = 20170131;
  LookBack = 0;
  set(LOGFILE);
  assetList("AssetsHFT");
  OrderDelay = LATENCY/1000.;
  Hedge = 2;
  Fill = 8;
  Lots = 1;
  Verbose = 3;

// process the NxCore history tape files, one for each day
  login(1);	// for initilizing the plugin
  int NxDate;
  for(NxDate = StartDate; NxDate = EndDate; NxDate++) {
    string NxHistory = strf("History\\NxTape%8d.nx2",NxDate);
    printf("\nProcessing tape %s..",NxHistory);
    brokerCommand(SET_HISTORY,NxHistory);
    if(!wait(0)) break; // check if [Stop] was clicked
  }
}

See also:

enter, exit, Hedge, Slippage, Penalty, mode, priceQuote

 

► latest version online Signal Processing Functions

Spectral filters and frequency analysis

The following functions retrieve frequency spectra from a data series, or apply spectral filters. They are based on signal processing theory and generate faster and more accurate trading signals than traditional indicators. Most filters below are inspired from concepts and ideas by John Ehlers (see book list).

AGC(vars Data, int TimePeriod): var

AGC(vars Data, var alpha): var

Automatic gain control of the Data series, by John Ehlers. Transforms the Data series to the -1 .. +1 range with a fast-attack, slow-decay algorithm. TimePeriod determines the time for reducing the gain by 1.5 db; alternatively, alpha determines the reduction factor per bar period (< 1). Adjustment to higher values happens immediately. The minimum length of the Data series is 1; a single var can be passed with the & operator, f.i. AGC(&MyVariable,10). The function internally creates series (see remarks). Source available in indicators.c. See also scale.

Amplitude(vars Data, int TimePeriod): var

Returns the amplitude - the highest high minus the lowest low - of the Data series, smoothed over the TimePeriod with an EMA so that the more recent fluctuations become more weight.

BandPass(vars Data, int TimePeriod, var Delta): var

Bandpass filter; lets only pass the cycles close to the given TimePeriod. The Delta value (0.05 .. 1) determines the filter width; at Delta = 0.1, cycles outside a 30% range centered at TimePeriod are attenuated with > 3 db. A bandpass filter is useful for retrieving a certain cycle from the data, while ignoring trend and cycles with different periods. The filter can be made steeper by connecting two or more bandpass filters, f.i. BandPass (series (BandPass (Data, TimePeriod, Delta)), TimePeriod, Delta). The minimum length of the Data series is 3. The function internally creates series (see remarks).

Butterworth(vars Data, int CutoffPeriod): var

3-pole Butterworth lowpass filter that suppresses all cycles below the cutoff period. The suppression at the CutoffPeriod is 3 dB. Often used for super-smoothing data. The minimum length of the Data series is 1; a single var can be passed with the & operator, f.i. Butterworth(&MyVariable,10). The function internally creates series (see remarks). Source available in indicators.c.

DominantPeriod(vars Data, int Period): var

Time period of the current dominant cycle in the Data series, in bars; valid range = 10..60 bars, lag = ~10 bars. The dominant cycle is the main periodicity in the data; its period is calculated with a Hilbert transform of the Data series. This function is useful to detect seasonal behavior or to set up moving averages, bandpass or highpass filters so that they adapt to the current market cycle. Period gives the bar period of maximum sensibility. If Data is omitted, the current asset price series is used. The function requires a LookBack period of at least 100 bars. It internally creates series (see remarks).

DominantPhase(vars Data, int Period): var

Current phase angle of the dominant cycle in the Data series, lag corrected; range 0..2*PI. The phase is normally increasing, but can stop or even go backwards when the dominant period is undefined or changes suddenly. The dominant cycle can be synthesized from the data by passing the phase to a sin function (f.i. sin(DominantPhase(Data,30))). This allows determining the maxima and minima of the current market cycle. By adding a constant to the phase (f.i. PI/4), a leading cycle can be generated for detecting the maxima and minima in advance. Period gives the bar period of maximum cycle sensibility. This function also calculates the dominant period; results in rDominantPeriod, rDominantPhase (returned). If Data is omitted, the current asset price series is used. The function requires a LookBack period of at least 100 bars. It internally creates series (see remarks).

FIR3(vars Data): var

FIR4(vars Data): var

FIR6(vars Data): var

Simple finite impulse response lowpass filters with 3, 4, and 6 taps. Often used in more complex filters and indicators for removing noise and smoothing the data. The lag is 1, 1.5, and 2.5 bars; the minimum length of the Data series is equal to the number of taps. Source available in indicators.c

HighPass(vars Data, int CutoffPeriod): var

Wide band highpass filter for separating the cycles in the Data curve from the underlying trend. Attenuates cycles longer than the cutoff period. Often used as an oscillator for identifying price turning points. Compared to traditional oscillators, such as RSI or Stoch, this filter generates much smoother signals with less lag. The minimum length of the Data series is 3, minimum lookback period is 7 bars. The function internally creates series (see remarks).

HighPass1(vars Data, int CutoffPeriod): var

1-pole digital highpass filter, attenuates cycles longer than the cutoff period and returns a curve consisting only of the high frequency part of the data. Often used by other filters for retrieving the cycle part from the data. The minimum length of the Data series is 8. The function internally creates series (see remarks).

HighPass2(vars Data, int CutoffPeriod): var

2-pole digital highpass filte, attenuates cycles longer than the cutoff period and returns a curve consisting only of the high frequency part of the data. The minimum length of the Data series is 3. The function internally creates series (see remarks). Source available in indicators.c.

Laguerre(vars Data, var alpha): var

Laguerre(vars Data, int Period): var

4-element Laguerre lowpass filter. Used for smoothing data similar to an EMA, but with less lag and a wide tuning range given by the smoothing factor alpha (0..1) or alternatively a smoothing period. The low frequency components are delayed much more than the high frequency components, which enables very smooth filters with only a short amount of data. The minimum length of the Data series is 1, the minimum lookback period is 4. The function internally creates series (see remarks). Source available in indicators.c

LowPass(vars Data, int CutoffPeriod): var

2-pole digital lowpass filter; suppresses high frequencies in the Data series, and thus attenuates all cycles that are shorter than the cutoff period. Compared to an EMA (which is in fact a 1-pole lowpass filter), cycles longer than the cutoff period are unaffected and passed without lag. This allows the LowPass function to react much faster and generate signals earlier than moving averages. Replacing moving averages by lowpass filters improves most algorithms and often makes the difference between a losing and a winning system. The minimum length of the Data series is 3. The function internally creates series (see remarks).

LowPass(var *Buffer, var Value, int CutoffPeriod): var

2-pole digital lowpass filter as above. It does not use series, but keeps its intermediate data in the given Buffer of length 5. The buffer can be an algo-specific array, a static series, or 5 consecutive variables. For instance, LowPass(AssetVar+3,Price,100) uses AssetVar[3]..AssetVar[7] for the buffer. The begin of the buffer is a 3-element series of the LowPass return values and can be used for functions like crossOver, peak, or rising.

Spectrum(vars Data, int TimePeriod, int SamplePeriod): var

Spectral analysis; returns the relative amplitude of the spectral component given by TimePeriod (~5..200). Can be used to analyze the frequency spectrum of a price curve over the given SamplePeriod. The minimum length of the Data series is SamplePeriod; set SamplePeriod (default = 2*TimePeriod) to a multiple of the TimePeriod in order to compensate for spectral dilation. TimePeriod must keep its value from bar to bar, but Spectrum can be called multiple times with different time periods and sample periods during the same bar, for generating a spectrum. The function internally creates series (see remarks). See the Spectrum script and the Strategy chapter for an example.
 

genNoise(): var

genSine(var Period1, var Period1): var

genSquare(var Period1, var Period2): var

Noise, sine, and square wave chirp generators for testing filters and algorithms. The noise generator produces random noise with 1.0 amplitude. The wave generators generate a hyperbolic chirp with 1.0 amplitude and a wave period changing linearly from Period1 to Period2. For a constant wave period, set both periods to the same value. Source available in indicators.c. See Filter script and example below.

Output of some spectral filters applied to a sine chirp with a period from 5..60 bars (generated by the Filter test script); top to bottom: Data, Leading Cycle, DominantPhase, DominantPeriod, BandPass, HighPass, HighPass1, HighPass2, LowPass, Butterworth.
 

Standard parameters:

TimePeriod Number of bars for the time period of the function.
CutoffPeriod Number of bars for the period that determines the filter frequency.
Data A data series, often directly derived from the price functions price(), priceClose() etc.. Alternatively a user created series or any other double float array with the given minimum length can be used. If not mentioned otherwise, the minimum length of the Data series is TimePeriod.

Returns:

Filtered value from the Data series.

Remarks:

Examples:

// plot some filters  
function run()
{
  set(PLOTNOW);
  vars Price = series(price());

  plot("LowPass",LowPass(Price,20),0,BLUE);
  plot("HighPass",HighPass(Price,50),NEW,RED);
}
// test the dominant cycle detection with a sine chirp
function run()
{
  set(PLOTNOW);
  MaxBars = 2000;
  ColorUp = ColorDn = 0; // don't plot a price curve
  asset(""); // dummy asset
  
  vars Sine = series(genSine(5,60));
  plot("Sine",Sine[0],0,BLACK);
  plot("Period",DominantPeriod(Sine,50),NEW,BLUE);
}
// plot the frequency spectrum of a price curve
function run()
{
  set(PLOTNOW);
  BarPeriod = 60;
  StartDate = 20130101;
  EndDate = 20130201;
  LookBack = 300; // 2 x maximum Cycle

  vars Price = series(price());
  int Cycle;
  for(Cycle = 5; Cycle < 150; Cycle += 1)
    plotBar("Spectrum",Cycle,Cycle,Spectrum(Price,Cycle),BARS+AVG+LBL2,RED);
}

See also:

traditional indicators, transformations, tutorial

► latest version online floor, ceil

floor(var x): var

Rounds x down to the largest integer that is not greater than x; .

ceil(var x): var

Rounds x up to the smallest integer that is not smaller than x.

Parameters:

x - var to be rounded up or down.

Returns:

x rounded up or down.

Remarks:

Example:

int a = floor(b); 

See also:

round, modf

► latest version online

fmod

fmod(var x, var y): var

Returns the fractional remainder of x divided by y, similar to the integer % operator.

Parameters:

x - dividend
y
- divisor.

Returns:

Remainder of x/y.

Speed:

Fast

Example:

var x = fmod(1.255,0.250); // x = 0.005

See also:

floor, ceil, modf, %

► latest version online

for

for (initialization; comparison; continuation) { ... }

Performs the initialization, then evaluates the comparison and repeats all instructions between the winged brackets as long as the comparison is true or non-zero. The continuation statement will be executed after the instructions and before the next repetition. This repetition of instructions is called a loop. Initialization and continuation can be any expression or function call. A for loop is often used to increment a counter for a fixed number of repetitions.

Remarks:

Example:

int i;
for(i=0; i<5; i++) // repeat 5 times
  x += i; 

See also:

if, while, break, continue, comparisons, for(open_trades)

► latest version online

Format codes and tables

Format Codes - C Programmer's Cheat Sheet

DATE format codes, for wdate, strdate, dataParse, etc:

%a Abbreviated weekday name
%A Full weekday name
%b Abbreviated month name
%B Full month name
%d Day of month as decimal number (01..31)
%H Hour in 24-hour format (00..23)
%j Day of year as decimal number (001..366)
%m Month as decimal number (01..12)
%M Minute as decimal number (00..59)
%S Second (00..59) or second with decimals (dataParse, wdatef, 00.000000 .. 59.999999)
%S. Second with milliseconds (strdate only)
%t Unix time stamp in seconds, milliseconds, or microseconds since 1/1/1970 (dataParse, wdatef)
%U Week of year as decimal number, with Sunday as first day of week (00..53)
%W Week of year as decimal number, with Monday as first day of week (00..53)
%y Year without century, as decimal number (00..99)
%Y Year with century, as decimal number (1900..2999)
%% Percent sign

The DATE format is equivalent to a double / var variable and counts the number of days since 12-31-1899 with a resolution of 1 µsec.

Variable / string format codes, for printf, print, strf, etc:

Format specifiers:
%c ANSI character (int, char)
%d Decimal number (int, char)
%e Exponential floating-point number (var, double)
%f Floating-point number (var, double)
%g Either %e or %f, whichever is more compact
%i Decimal number (int, char)
%o Octal number (int, char)
%p Hexadecimal pointer (void*)
%s Null-terminated text (string, char*, char[])
%u Unsigned decimal number (int, char)
%x Hexadecimal number (int, char), lowercase
%X Hexadecimal number (int, char), uppercase
%% Percent sign
   
Format modifiers (optional, after the %) :
n Field width, minimum number of digits.
.m Precision, maximum number of digits after the decimal.
-

Left align the result within the given field width.

+

Prefix the output value with a sign (+ or –).

0 Add zeros until the number of digits is reached.
' ' Add a blank ' ' if the output value is signed and positive.
   
Special characters
\a Audible alert
\b Backspace
\f Form feed
\n New line, or linefeed
\r Carriage return
\t Tab
\v Vertical tab
\\ Backslash
#... Print to log only

Regular expressions, for the Npp search functions

Code Matches Example

*

Preceding item zero or more times; like {0,}.

zo* matches "z" and "zoo".

+

Preceding item one or more times; like {1,}.

zo+ matches "zo" and "zoo", but not "z".

?

Preceding item zero or one time; like {0,1}.

zo? matches "z" and "zo", but not "zoo".

^

The position at string start.

^\d{3} matches 3 numeric digits at the start of the searched string.

$

The position at string end.

\d{3}$ matches 3 numeric digits at the end of the searched string.

.

Any single character except newline \n.

a.c matches "abc", "a1c", and "a-c".

|

Choice between two or more items.

z|food matches "z" or "food". (z|f)ood matches "zood" or "food".

\* The * character; similar with \+, \?, \., \[, \(, etc.  

\b

Word boundary.

er\b matches the "er" in "never" but not the "er" in "verb".

\B

Word non-boundary.

er\B matches the "er" in "verb" but not the "er" in "never".

\d

Digit character; equivalent to [0-9].

In "12 345", \d{2} matches "12" and "34". \d matches "1", 2", "3", "4", "5".

\D

Nondigit character; equivalent to [^0-9].

\D+ matches "abc" and " def" in "abc123 def".

\s Any white-space character; like [ \f\n\r\t\v].  
\S Any non-white-space character; like [^ \f\n\r\t\v].  

\w

A-Z, a-z, 0-9, and underscore, like [A-Za-z0-9_].

In "The quick brown fox…", \w+ matches "The", "quick", "brown", "fox".

\W

Any character except A-Z, a-z, 0-9, underscore.

In "The quick brown fox…", \W+ matches "…" and all of the spaces.

()

Start and end of a subexpression.

A(\d) matches "A0" to "A9". (f|b)*oo matches foo, boo, and oo.

[abc]

A character set; matches any specified character.

[abc] matches the "a" in "plain".

[^abc]

Negative character set; any not specified character.

[^abc] matches the "p", "l", "i", and "n" in "plain".

[a-z]

A range of characters; any character in the range.

[a-z] matches any lowercase character in the range "a" through "z".

[^a-z]

Negative range; any character not in the range.

[^a-z] matches any character that is not in the range "a" through "z".

[a,b]

All items separated with commas.

[a-z,-] matches all characters in "my-file".

{n}

Preceding item exactly n times. 

o{2} does not match the "o" in "Bob", but the two "o"s in "food".

{n,}

Preceding item at least n times. 

o{2,} does not match the "o" in "Bob" but all the "o"s in "foooood".

{n,m}

Preceding item at least n and at most m times. 

In "1234567", \d{1,3} matches "123", "456", and "7".

\cx

The control character indicated by x.

\cM matches a CTRL+M or carriage return character.

\xnn

A 2 digits hexadecimal character.

\x41 matches "A". (\x041 is wrong!)

\unnnn

A 4 digits Unicode character.

\u00A9 matches the copyright symbol (©).

ANSI table (ISO 8859), for special characters in strings

32 [ ] 33 ! 34 " 35 # 36 $ 37 % 38 & 39 '
40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 /
48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7
56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ?
64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G
72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O
80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W
88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _
96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g
104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o
112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w
120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127  
128 € 129 • 130 ‚ 131 ƒ 132 „ 133 … 134 † 135 ‡
136 ˆ 137 ‰ 138 Å  139 ‹ 140 Å’ 141   142 Å¢ 143  
144 • 145 Ř 146 ř 147 Ŗ 148 ŗ 149 • 150 Ŕ 151 ŕ
152 ˜ 153 ™ 154 š 155 › 156 œ 157 • 158 ţ 159 Ÿ
160   161 ¡ 162 ¢ 163 £ 164 ¤ 165 Â¥ 166 ¦ 167 §
168 ¨ 169 © 170 ª 171 « 172 ¬ 173   174 ® 175 ¯
176 ° 177 ± 178 ² 179 ³ 180 ´ 181 µ 182 ¶ 183 ·
184 ¸ 185 ¹ 186 º 187 » 188 ¼ 189 ½ 190 ¾ 191 ¿
192 À 193   194 Â 195 Ã 196 Ä 197 Ã… 198 Æ 199 Ç
200 È 201 É 202 Ê 203 Ë 204 ÃŒ 205   206 ÃŽ 207  
208 �? 209 Ñ 210 Ò 211 Ó 212 Ô 213 Õ 214 Ö 215 ×
216 Ø 217 Ù 218 Ú 219 Û 220 Ü 221   222 Þ 223 ß
224 à 225 á 226 â 227 ã 228 ä 229 å 230 æ 231 ç
232 è 233 é 234 ê 235 ë 236 ì 237 í 238 î 239 ï
240 ð 241 ñ 242 ò 243 ó 244 ô 245 õ 246 ö 247 ÷
248 ø 249 ù 250 ú 251 û 252 ü 253 ý 254 þ 255 ÿ

 

Zorro Manual

► latest version online

for(current_trades)

Enumeration loops

for(current_trades) { ... }

A for loop that cycles through all open and pending trades with the current asset and algo, starting with the earliest trade; useful f.i. for closing trades in a FIFO compliant way.

for(last_trades) { ... }

A for loop that cycles backwards through the internal list of open, pending, closed, and cancelled trades with the current asset and algo, starting with the last trade.

for(open_trades) { ... }

A for loop that cycles through all open and pending trades of all assets and algos, starting with the earliest trade.

for(closed_trades) { ... }

A for loop that cycles backwards through all closed trades of all assets and algos, starting with the latest trade.

for(all_trades) { ... }

A for loop that cycles through the internal list of all trades; useful for a detailed trade statistic after the simulation.

for(past_trades) { ... }

A for loop that cycles backwards through the internal list of all trades, starting with the latest trade; useful for equity curve trading.

for(listed_assets) { ... }

A for loop that cycles through all assets from the asset list, in the same order as in the list. This is basically the same as for(i=0; Assets[i]; i++) { Asset = Assets[i]; ... } .

for(used_assets) { ... }

A for loop that cycles through all assets used in the script in alphabetical order, by selecting one after the other.

for(all_algos) { ... }

A for loop that cycles through all asset/algo combinations used in the script, by selecting one component after the other, 

break_trades

break_assets

break_algos

Special break macros for aborting a trade, asset, or algo enumeration loop. Use return_trades for returning from inside a trade loop. All macros are defined in variables.h.

Type:

Macro

Remarks:

Example:

// find all pending trades with the current asset and all algos
int numPending(bool isShort)
{
  int num = 0;
  for(open_trades)
    if(0 == strcmp(Asset,AssetPrev) 
      && TradeIsPending && TradeIsShort == isShort)
        num++;
  return num;
}
 
// sum up the profit/loss of all open trades with the current asset
var val = 0;
for(open_trades)
  if(0 == strcmp(Asset,AssetPrev) && TradeIsOpen && !TradeIsPhantom)
    val += TradeProfit;

// increase the stop of all winning trades slowly over time
for(open_trades) {
if(TradeIsOpen && TradeProfit > 0 && !TradeIsPool)
TradeStopLimit -= 0.02 * TradeStopDiff;
}
// lock 80% profit of all winning trades for(open_trades) { if(TradeIsOpen && !TradeIsPool && TradeProfit > 0) { TradeTrailLock = 0.80; if(TradeIsShort) TradeTrailLimit = max(TradeTrailLimit,TradePriceClose); else TradeTrailLimit = min(TradeTrailLimit,TradePriceClose); } } // sum up open profits of all assets and the current algo var ProfitSum = 0; for(used_assets) { asset(Asset); // select the current component ProfitSum += ProfitOpen; }

See also:

bar, enterLong/Short, trade variables, results, loop

 

► latest version online frame, frameSync

frame(int Offset): bool

Returns true when the current TimeFrame ends at the given bar, and false when inside a time frame.

frameSync(int Period): int

Returns a synchronized TimeFrame that is aligned to the begin of an hour, a 4 hours period, a day, or a week. Returns 0 inside the time frame, and the negative number of skipped bars at the end of the time frame. Periods that are higher than one week or are no divisor of a full hour are not synchronized. FrameOffset can be used to shift the synchronized time frame to a particular time or workday.

Parameters:

Offset Bar distance to the current bar. Must be 0 when TimeFrame is synchronized to a fixed time period or to an external event.
Period Time period to be synchronized, in bars; an integer fraction of a hour, a 4 hours period, a day, or 7 days for a week.

sync(vars Data, int Type, int Period) : var

Selects a value of the Data series using a synchronized time frame given by Period. Can be used to generate higher time frames of price series or indicators. Dependent on Type, the first, maximum, minimum, or last element  within the time frame, or the previous value is selected. The function internally creates series (see remarks). Source code in indicators.c.

Parameters:

Data Data series, for instance prices or indicator values.
Type 1 first, 2 highest, 3 lowest, 4 last element, +8 for the previous value.
Period Synchronized time period, in bars; an integer fraction of a hour, a 4 hours period, a day, or 7 days.

Remarks:

Examples:

// let a time frame change daily at 3:00
BarPeriod = 60;
FrameOffset = 3;
TimeFrame = frameSync(24);
...

// shift a series every 24 hours
BarPeriod = 60;
if(0 == frameSync(24)) set(NOSHIFT);
vars MySeries = series(MyValue);
set(NOSHIFT|OFF);
...

// return OHLC prices from synchronized higher time frames
var priceSyncOpen(int Period) { return sync(seriesO(),1,Period); }
var priceSyncClose(int Period) { return sync(seriesC(),4,Period); }
var priceSyncHigh(int Period) { return sync(seriesH(),2,Period); }
var priceSyncLow(int Period) { return sync(seriesL(),3,Period); }

See also:

Bar, TimeFrame, BarZone, BarMode, suspended, series

► latest version online FTP functions

FTP functions

The FTP functions can be used for uploading or downloading files from a web server.

ftp_download(string url, string path, string username, string password)

Starts a file download from a FTP server. Returns 0 if an error occured, nonzero otherwise. Use ftp_status to check when the transfer was complete.

ftp_upload(string url, string path, string username, string password)

Starts a a file upload to a FTP server. Returns 0 if an error occured, nonzero otherwise. Use ftp_status to check when the transfer was complete. 

ftp_getdate(string url, string username, string password)

Starts a file access for getting the timestamp and size of a file stored on a FTP server. Returns 0 if an error occured, nonzero otherwise. Use ftp_status to check when the file access was complete. 

ftp_stop()

Stops the currently running FTP transfer.

ftp_size(): long

Returns the total file size of the current/last file in bytes.

ftp_sent(): long

Returns the amount of sent data of the current/last file in bytes.

ftp_timestamp(): long

Returns the timestamp of the current/last file, after ftp_getdate() was executed successfully.

ftp_status()

Returns the status of a currently running or the last FTP transfer:

-1 if the last FTP transfer was stopped because of an error
0 if the FTP transfer is still running
1 if the last FTP transfer was successful

ftp_log (var mode)

Enables/disables the logging of FTP transfers. The logfile is named "ftp_log.txt".

Parameters:

url - URL of the file to be downloaded, or destination URL for a file upload (e.g.: "ftp://www.testhoster.com/downloads/test.txt").
path - local path + filename (e.g.: "testdir/test.txt")
username - FTP username
password - FTP password
mode - 1 to enable, 0 to disable FTP logging

Remarks:

Example:

function main() 
{ 
//Downloads the file "myfile.txt" and saves it in the Data folder 
  ftp_download("ftp://www.testhost.com/files/myfile.txt","Data/myfile.txt","username","password"); 
  while(!ftp_status()) //as long as the download is running
    wait(50);
  if (ftp_status() == 1) 
    printf("\nDownload successful!"); 
  else 
    printf("\nDownload failed!"); 
}

See also:

http functions, file functions, email

► latest version online

Algorithmic trading functions | Zorro Project

Zorro algo trading functions by category

Time series analyis

Open Source
AC Accelerator Oscillator
ADO Accumulation/Distribution Oscillator
AGC Automatic gain control
ADX Average Directional Movement Index
ADXR Average Directional Movement Rating
Alligator Alligator 3-line indicator
ALMA Arnaud Legoux Moving Average
Amplitude Amplitude of series
AO Awesome Oscillator
APO Absolute Price Oscillator
Aroon Aroon Indicator
AroonOsc Aroon Oscillator
ATR Average True Range, original
ATRS Average True Range, simple MA
AvgPrice Average Price
BandPass Bandpass filter  
BBands Bollinger Bands
BBOsc Bollinger Bands oscillator
Beta Beta value
BOP Balance Of Power
Butterworth Butterworth filter
CBI Cold Blood Index  
CCI Commodity Channel Index
CCYI Correlation Cycle Indicator
CCYIR Correlation Cycle Indicator rate of change
CCYIState Correlation Cycle Market State
ccyMax Strongest Forex pair
ccyMin Weakest Forex pair
ccyReset Initialize currency strength
ccySet Define currency strength
ccyStrength Get currency strength
CDL... 60 traditional candle patterns
CGOsc Center Of Gravity oscillator
ChandelierLong Chandelier exit long
ChandelierShort Chandelier exit short
Chikou Ichimoku Chikou line
CI Choppiness Index
CMO Chande Momentum Oscillator
concave Curve concavity  
ConnorsRSI Connors RSI indicator
Coral Coral indicator
Correlation Pearson correlation coefficient
COT Commitment Of Traders report
COT_CommercialPos COT commercials net position
COT_CommercialIndex COT index
COT_OpenInterest COT open interest
Covariance Covariance coefficient
crossOver Curve cross over  
crossOverF Fuzzy cross over  
crossUnder Curve cross under  
crossUnderF Fuzzy cross under  
CTI Correlation Trend Indicator
dayClose Day close
dayHigh Day high
dayLow Day low
dayOpen Day open
dayPivot Day pivot
DChannel Donchian Channel
DCOsc Donchian Channel Oscillator
Decycle Ehlers' Decycler
DEMA Double Exponential Moving Average
Divergence Pricve / Oscillator peak line divergence
DominantPeriod Fundamental price oscillation  
DominantPhase Fundamental price phase  
DPO Detrended Price Oscillator
DX Directional Movement Index
EMA Exponential Moving Average
ER Efficiency Ratio
FIR3 Finite Impulse Response filter, 3 taps  
FIR4 Finite Impulse Response filter, 4 taps  
FIR6 Finite Impulse Response filter, 6 taps  
falling Curve falling  
fallingF Curve falling, fuzzy  
findIdx Find element  
Fisher Fisher transform
FisherInv Inverse Fisher transform
FisherN Fisher transform with normalization
FractalDimension Fractal Dimension
FractalHigh High Fractal indicator
FractalLow Low Fractal indicator
frechet Frechet pattern detection  
Gauss Gauss filter  
HAClose Haiken Ashi Close
HAHigh Haiken Ashi High
HALow Haiken Ashi Low
HAOpen Haiken Ashi Open
HH Highest High
HMA Hull Moving Average
HighPass Wide highpass filter  
HighPass1 1-pole highpass filter  
HighPass2 2-pole highpass filter
HTDcPeriod Hilbert transform cycle period
HTDcPhase Hilbert transform cycle phase
HTPhasor Hilbert transform phasor components
HTSine Hilbert transform sine wave
HTTrendline Hilbert transform instantaneous trendline
HTTrendMode Hilbert transform trend indicator
Hurst Hurst exponent
IBS Internal Bar Strength
Ichimoku Ichimoku indicator
KAMA Kaufman Adaptive Moving Average
KAMA2 KAMA with individual settings
Keltner Keltner channel
Laguerre Laguerre lowpass filter
line Line position at a given bar
LinearReg Linear regression
LinearRegAngle Linear regression angle
LinearRegIntercept Linear regression intercept
LinearRegSlope Linear regression slope
LL Lowest Low
LowPass Lowpass filter  
LSMA Least Squares Moving Average
MACD Moving Average Convergence/Divergence
MACDExt MACD with various MA types
MACDFix MACD with standard parameters
MAMA MESA Adaptive Moving Average
MovingAverage Moving Average with given type
MovingAverage
VariablePeriod
Moving Average with variable period
MatchIndex Index of best match
MaxVal Highest value
MaxIndex Index of highest value
Median Median filter  
MedPrice Center price of candle
MidPoint Center value of period
MidPrice Center price of period
MinVal Lowest value
MinIndex Index of lowest value
MinMax Lowest and highest values
MinMaxIndex Indexes of lowest and highest values
MMI Market Meanness Index
MinusDI Minus Directional Indicator
MinusDM Minus Directional Movement
Mode Most frequent value
Mom Momentum
Moment Mean, variance, skew, kurtosis
MovingAverage Moving Average with various MA types
NATR Normalized Average True Range
Normalize Normalize to -1 .. +1
NumInRange Count ranges in interval
NumDn Count of falling elements
NumRiseFall Length of streak
NumUp Count of rising elements
NumWhiteBlack Difference of white and black candles
OBV On Balance Volume
peak Curve peak  
peakF Curve peak, fuzzy  
Percentile Percentile  
PercentRank Percent rank  
Pivot Pivot point
PlusDI Plus Directional Indicator
PlusDM Plus Directional Movement
polyfit Polynomial regression  
polynom Regression polynomial  
PPO Percentage Price Oscillator
predict Curve peak / crossover prediction  
predictMove Predict price move by statistics
predictSeason Predict price move by seasonal analysis
ProfitFactor Ratio of positive to negative returns
QLSMA Quadratic Least Squares Moving Average
R2 Determination coefficient  
Resistance Resistance line
RET Rate of change between two points
rising Curve rising  
risingF Curve rising, fuzzy  
ROC Rate of change
ROCP Rate of change percentage
ROCR Rate of change ratio
ROCL Logarithmic return
ROCR100 Rate of change ratio, 100 scale
Roof Ehlers' roofing filter
RSI Relative Strength Index, original
RSIS Relative Strength Index, simple MA
RVI Ehlers' Relative Vigor Index
SAR Parabolic SAR
SemiMoment Mean, downside variance, skew, kurtosis
SentimentLW Williams' Market Sentiment
SentimentG Genesis Sentiment Index
ShannonEntropy Randomness metric
ShannonGain Expected gain rate
Sharpe Sharpe ratio
SIROC Smoothed Rate of Change
slope Line through minima or maxima
SMA Simple Moving Average
SMAP Average of positive values
SMom Smoothed Momentum
Smooth Ehlers' super-smoother
Sortino Sortino ratio
Spearman Spearman's rank correlation coefficient
Spectrum Spectral analysis  
StdDev Standard deviation
Stoch Stochastic oscillator
StochEhlers Ehlers' predictive stochastic
StochF Stochastic Fast
StochRSI Stochastic RSI
Sum Sum of elements
SumDn Sum of falling elements
SumUp Sum of rising elements
Support Support line
T3 Triple smoothed MA
TEMA Triple EMA
touch Curve touches another  
Trima Triangular Moving Average
Trix TEMA rate of change
TrueRange True range
TSF Time Series Forecast
TSI Trend Strength Index  
TypPrice Typical price
UltOsc Ultimate Oscillator
UO Universal Oscillator
Variance Variance
valley Curve valley  
valleyF Curve valley, fuzzy  
Volatility Annualized volatility
VolatilityC Chaikin Volatility indicator
VolatilityMM Min/Max volatility
VolatilityOV Empirical volatility
WCLPrice Weighted Close Price
WillR Williams' Percent Range
WMA Weighted Moving Average
yield Riskfree interest rate
ZigZag ZigZag indicator
ZMA Zero-lag Moving Average
     

Markets & trading

 
adviseLong/Short Machine learning indicator  
algo Select algorithm  
asset Select asset  
assetAdd Add asset to list  
assetHistory Download price history  
assetList Select asset list  
assetSource Set up online data source  
assetType Type of asset  
assign Convert portfolio weights  
brokerAccount Retrieve account status  
brokerAsset Download asset parameters  
brokerCommand Send special command to broker
brokerRequest Send a REST API request to broker  
brokerTrades Download list of open positons  
cancelTrade Undo trade  
combo Combine options to a combo
comboAdd Add option to a combo  
comboContract Return contract of a combo leg
comboLeg Select a combo leg  
comboLegs Return number of combo legs  
comboMargin Calculate the margin cost of a combo
comboStrike Return strike of given combo leg
comboPremium Calculate combo premium
comboProfit Calculate combo profit
comboRisk Calculate maximum possible loss
comboType Detect type of a combo
contract Select option/future contract  
contractCheck Check for termination  
contractCPD Price probability analysis  
contractDays Contract duration  
contractDelta Delta value from strike
contractExercise Exercise option  
contractFind Find contract by parameter  
contractIntrinsic Intrinsic value
contractMargin Calculate contract margin
contractNext Next contract in chain  
contractPosition Get current position size  
contractPrice Get current value  
contractProfit Get current profit/loss  
contractRecord Save contract chain to history
contractRoll Roll expired contract forward
contractSellUnderlying Sell assigned stock from exercised contracts
contractStrike Strike value from Delta
contractUnderlying Unadjusted underlying price
contractUpdate Load contract chain  
contractVal Calculate option value
contractVol Calculate implied volatiltiy
cpd Predicted price probability  
cpdv Price at given probability  
dataDownload Market data from online source  
dataFromCSV Market data from CSV file  
dataFromQuandl Market reports from Quandl™
distribute Calculate portfolio weights  
enterLong/Short Open position  
enterTrade Add position from template  
exitLong/Short Close one or several positions  
exitTrade Close selected position  
for(trades...) Enumerate trades, assets, algos  
frame Timeframe state  
frameSync Timeframe synchronization  
knapsack Optimize asset amounts  
loadStatus Load system status  
login Connect to broker  
loop Loop through assets/algos  
marketVal Bar value (spread)  
marketVol Bar value (volume)  
markowitz Mean-variance optimization  
markowitzReturn Max return for given variance  
markowitzVariance Min variance for given return  
optimize Optimal parameter value  
orderCVD Order flow analysis  
orderUpdate Read order book  
price Mean price at bar  
priceClose Close at bar  
priceHigh High at bar  
priceLow Low at bar  
priceOpen Open at bar  
priceQuote Enter current price  
priceRecord Save price to history  
priceSet Modify price at bar  
results Statistics of trade results  
saveStatus Save system status  
seriesO/H/L/C Price series  
suspended Trading permission  
tradeUpdate Resize or update trade parameters  
tradeUpdatePool Synchronize virtual and pool trades  
 

Math & misc

 
abs Magnitude  
aboveF Fuzzy >=  
andF Fuzzy && (and)  
asin Arc sine  
acos Arc cosine  
atan Arc tangent  
atan2 Arc tangent, high precision  
belowF Fuzzy <=  
between Range check  
betweenF Fuzzy range check  
cdf Gaussian cumulative distribution  
ceil Round up  
center Center about median  
changed Change of value
clamp Limits  
compress Scale to +/-100  
cos Cosine  
cosh Hyperbolic cosine
cum Accumulation function
diff Change since last bar  
dnorm Gaussian probability  
equalF Fuzzy ==  
erf Statistical error function  
exp Exponential  
filter 1D covolution filter  
fix0 Safe division
floor Round down  
fmod Fractional modulo  
fuzzy Defuzzyfication  
genNoise Random noise
genSine Sine wave chirp
genSquare Square wave chirp
hmac Cryptographic hash  
ifelse Conditional assignment  
invalid Invalid value  
log Logarithm  
max Maximum  
min Minimum  
modf Fractional part  
normalize Normalize to +/-100  
notF Fuzzy ! (not)  
once First occurrence  
orF Fuzzy || (or)  
pow Nth power, Nth root  
qnorm Gaussian distribution value  
random Random number generator  
ref Value at bar
renorm Multiply and normalize  
round Round to next integer  
roundto Round to next step  
scale Center and scale to +/-100  
seed Random number initialization  
sign Sign  
sin Sine  
sinh Hyperbolic sine  
sqrt Square root  
tan Tangent  
tanh Hyperbolic tangent  
valuewhen Conditional array access
zscore Z-Score  
     

Time / date

 
at Event at time of day
barssince Bars since condition became true
day Day of month  
dmy YYYYMMDD to OLE time/date
dom Days in month  
dow Day of week  
dst Daylight saving time  
hour UTC hour  
ldow Day of week at time zone  
lhour Hour at time zone  
ltod HHMM at time zone  
market Market open time  
minute Minute  
minutesAgo Bar distance in minutes  
minutesWithin Bar length in minutes
month Month at given bar  
mtu OLE time/date from Unix time  
ndow N-th weekday of month  
nthDay Date of n-th weekday of month
second Second with microseconds  
tdm Trading day of month  
timeOffset Bar at given time  
tom Trading days in month  
tod HHMM time of day  
tow DHHMM time of week  
ltow DHHMM at time zone  
utm Unix time  
wdate OLE time/date  
wdateBar OLE time/date at bar  
wdatef OLE time/date from string  
week Week number  
workday Workday or holiday  
year Year at given bar  
ymd OLE time/date to YYYYMMDD
     

Data structures

 
atof String to var  
atoi String to int  
conv Convert array or time series  
dataAppend Extend dataset  
dataAppendRow Extend dataset by row  
dataChart Plot dataset content  
dataClip Remove records  
dataCol Extract data column  
dataCompress Remove duplicates  
dataFind Find date/time in dataset  
dataLoad Load dataset  
dataInt Get integer from field  
dataMerge Merge two datasets  
dataNew Create dataset  
dataParse Create dataset from CSV  
dataParseJSON Create OHLC dataset from JSON  
dataSave Save dataset  
dataSaveCSV Save dataset to CSV  
dataSet Set dataset field  
dataSize Get dataset format  
dataSort Sort dataset by date  
dataStr Get string from field  
dataVar Get variable from field  
filter Convolution filter  
matrix Matrix / vector creation  
me Matrix element  
matAdd Matrix addition  
matMul Matrix multiplication  
matScale Matrix scaling  
matSet Matrix copy  
matSub Matrix subtraction  
matTrans Matrix transpose  
randomize Shuffle array or time series  
renorm Normalize array  
ref Reference past value  
rev Reverse array or time series  
series Create time series  
sftoa Convert number to string  
shift Shift array or time series  
sortData Sort array  
sortIdx Create sort index  
sortRank Create ranking list  
strcat Append string  
strcmp Compare strings  
strcpy Copy string  
strlen String length  
strstr Find substring  
strchr Find character  
strrchr Find character from end  
strtok Tokenize string  
strvar Variable from ini string  
strtext Text from ini string  
strdate Time/date to string  
strf Variables to string  
strx Replace substrings  
strxc Replace characters  
strmid Strip string  
strcount Count characters  
strw Wide string  
stridx String to index  
strxid Index to string  
strtr Trade ID string  
strcon Contract ID string  
sprintf Print into string  
sscanf Parse string  
ve Vector element  
     

Input / output

 
color Define color range  
colorScale Brighten / darken color  
dataParse Create dataset from CSV  
dataParseJSON Create OHLC dataset from JSON  
email Send email  
file_append Append data to end of file  
file_appendfront Append data to begin of file  
file_content Read content of file  
file_copy Copy file  
file_date File date  
file_delete Delete file  
file_length File size  
file_next Read directory  
file_read Read file to string  
file_select Open file dialog box  
file_write Write string to file  
ftp_download Download file from FTP server  
ftp_upload Upload file to FTP server  
ftp_getdate Get file date and size from FTP server  
ftp_stop Stop the current FTP transfer  
ftp_size Size of the received file  
ftp_sent Size of the sent file  
ftp_timestamp Get file timestamp  
ftp_status FTP transfer status  
ftp_log Enables FTP logging  
getvar Get system-wide variable  
http_transfer Load data from website  
http_request Send HTTP request  
http_post Start HTTP POST transfer  
http_proxy Define a proxy server  
http_status HTTP transfer status  
http_result Retrieve received file  
http_free Stop current HTTP transfer  
keys Send keystrokes to window  
mouse Mouse position  
msg Message box  
panel Create user panel from spreadsheet  
panelFix Determine panel scroll area  
panelGet Get data from user panel  
panelLoad Load panel state  
panelMerge Merge cells on panel  
panelSave Save panel state  
panelSet Update user panel or action scrollbox  
plot Plot curve  
plotBar Plot histogram bar  
plotBuyHold Plot a buy-and-hold benchmark
plotChart Update histogram  
plotContract Plot option payoff diagram
plotCorrelogram Plot autocorrelation histogram
plotData Get plot data for export  
plotDay Daily seasonal analysis
plotDayProfit Daily profit histogram
plotGraph Plot symbol  
plotHeatmap Plot heatmap matrix
plotHistogram Plot a general histogram
plotMAEGraph Max adverse excursions histogram
plotMAEPercentGraph Max adverse excursions in percent
plotMFEGraph Max favorable excursion histogram
plotMFEPercentGraph Max favorable excursions in percent
plotMonth Monthly seasonal analysis
plotMonthProfit Monthly profit histogram
plotPriceProfile Price difference histogram
plotQuarterProfit Quarterly profit histogram
plotTradeProfile Profit distribution histogram
plotWeek Weekly seasonal analysis
plotWeekProfit Weekly profit histogram
plotWFOCycle WFO cycle analysis
plotWFOProfit Per-cycle profit histogram
plotYear Annual seasonal analysis
printf Print message  
print Print to target  
progress Progress bar  
putvar Set system-wide variable  
pyInt Integer from Python variable  
pySet Send variables to Python  
pyStart Start Python session  
pyVar Double float from Python variable  
pyVec Array from Python variable  
pyX Execute Python code  
Rd Double float from R expression  
Ri Integer from R expression  
Rrun R status  
Rset Send variables to R  
Rstart Start R session  
Rv Array from R expression  
Rx Execute R expression  
report Generate performance report  
slider Slider input  
sound Play WAV file  
window Find active window  
     

System

 
call Run function at event  
exec Run external program  
free Free memory area  
GetProcAddress Get DLL function  
ignore Suppress error message  
ifelse Conditional assignment  
is System flag state  
isf Flag state of a variable  
LoadLibrary Open DLL  
lock Protect code against interruption  
malloc Allocate memory area  
memcmp Compare memory  
memcpy Copy memory area  
memory Get memory allocation  
memset Fill memory area  
of Enumerate elements in a loop  
once First condition change  
quit Terminate simulation  
realloc Change memory area  
require Require software version  
resf Reset flag of a variable
set Set or reset system flag  
setf Set flag of a variable  
timer Performance timer  
unlock Unlock code  
version Software version  
wait Pause  
watch Debugging info  
Win32 API Windows API functions  
zalloc Allocate temporary memory space  
zInit Initialize multiprocess communication  
zClose Stop process  
zData Exchange process information  
zOpen Start process  
zStatus Get process status  
     

Optional user-supplied functions

 
bar Special bar definition (Renko, Kagi, etc)  
click Button click function  
callback Callback for broker API and messages  
cleanup Run once at the end  
error Run at any error  
evaluate Evaluate strategy results  
manage Trade micromanagement  
main Run once at the begin.  
neural External machine learning and prediction  
objective Parameter optimization target  
order Special order transmission  
parameters Parameter optimization setup  
run Run at any bar  
tick Run at any incoming price  
tock Run at fixed time intervals  
     

 

 

 

Zorro Home, Zorro Manual

► latest version online

Functions

Functions

A function is simply a list of commands. When the function is called, the commands are executed one after another. Such a command can assign a value to a variable, or call another user-defined or pre-defined function. A function is defined this way:

functiontype name (Parameter1, Parameter2, ...) { ... code ... }

functiontype is the variable type (such as var, int...) that the function returns to the caller. If the function returns nothing, write just void for the type. The often used type function can return either nothing, or an int.

name is the name of the function. It must follow the same rules as variable names.

(Parameter1, Parameter2, ...) is a list of variable types and names. The function gets these variables from the caller. For each variable that is passed to the function, the parameter list must contain one entry. This entry specifies the type and name with which the variable is referred to inside the function. The parameter list is enclosed in parentheses. If the function expects no variables, leave the parentheses empty.

{ ... code ... } is the function body, enclosed in braces. It's a list of commands that do the real work. When a function is called, execution begins at the start of the function body and terminates (returns to the calling program) when a return statement is encountered or when execution reaches the closing brace.

A simple function definition without parameters looks like this:

function alert() 
{ 
  printf("Warning!");
  sound("beep.wav");
} 

This function is executed whenever the program calls the command alert(). The next example is a function that takes two var variables as parameters:

function alert2(var value, var limit)
{
  if (value > limit)
    printf("Warning - value above limit!");
}

If a global variable or another object of the same name as the parameter exists, all references within the function refer to the 'local' parameter and not to the 'global' variable. Still, to avoid confusion, don't use the same names for global and local variables. The function may change any of its parameters, as 'value' in the example, but the original variable in the calling function remains unchanged.

Return values

A function can return a value to its caller, through the return statement. Example:
var average(var a, var b) 
{ 
  return (a+b)/2; 
} 


// this can be called this way:
...
x = average(123,456); // calculate the average of 123 and 456, and assign it to x
For returning multiple values, their pointers can be given in the parameter list. Or the function can return a pointer to a static struct or array.

Function prototypes and function pointers

Like all other objects, functions must be defined before they can be called by another function. Sometimes this is inconvenient, for instance if two functions call each other. In that case, a function can be predefined through a prototype. The prototype consists of the function title with the parameters, followed by a semicolon, but without the command list in {...}. In the argument list of a prototype, the variable names can be omitted, only the variable types are necessary.Example:
var greater_of(var,var); // prototype of function greater_of

var greater_of(var a, var b) // real function greater_of
{
  if (a > b) return a;
  else return b;
}

In lite-C a function prototype can also be used as a function pointer. Example:

long MessageBox(HWND,char*,char*,long); // prototype of function MessageBox
...
MessageBox = DefineApi("user32!MessageBoxA"); // set the MessageBox pointer to a function in the user32.dll

Recursive functions

A function can call itself - this is named a recursive function. Recursive functions are useful for solving certain mathematical or AI problems. Example:
int factorial(int x)
{
  if (x <= 1) return 1;  
  if (x >= 10) return(0); // number becomes too big...
  return x*factorial(x-1);
}

Recursive functions must be used with care. If you make a mistake, like a function that infinitely calls itself, you can easily produce a 'stack overflow' which crashes the computer. The stack size allows a recursion depth of around 1000 nested function calls, dependent on number and size of local variables inside the function.

Overloaded functions

In lite-C, functions can be overloaded, which means you can define several functions with the same name, but different parameter lists. The compiler knows which function to use depending on the types and numbers of parameters passed. Example:
var square(var x) 
{ 
  return pow(x,2); 
}

int square(int x) 
{ 
  return(x*x); 
}
...
var x = square(3.0); // calls the first square function
int i = square(3);   // calls the second square function

Variable number of parameters

Functions can be called with less or more than given number of parameters when the parameter list has a "..." for the last parameter. The function can then be called with less parameters; the missing parameters at the end are replaced with 0. Functions in an external DLL or library, such as printf, can also be called with more parameters. C specific macros such as va_start and va_end then retrieve the additional parameters. Example:
int test(int a,int b,int c,...)
{
  return a+b+c; 
}

void main()
{
  printf("\n%d",test(1,2,3)); // 6
  printf("\n%d",test(1,2));   // 3
  printf("\n%d",test(1));     // 1
  printf("\n%d",test());      // 0
}

Calling modifiers

In lite-C, C,or C++ the __stdcall calling modifier is used to call Win32 API functions. The called function cleans the stack. The __cdecl calling modifier is the default calling convention in C-Script, lite-C, C and C++ programs. Because the stack is cleaned up by the caller, it can do variable argument (vararg) functions, like printf(...).

Special functions

If a function named main() exists in the script (see above example), it is automatically called at the start of the program. The program terminates when the main() function returns. This is for using a script for other purposes than for trading.

It a function named run() exists in the script, it is automatically called at the start of the program and also after every bar. The program terminates when there are no more bars, or when [Stop] is clicked. More functions that are automatically called at certain events are listed here.

See also:

Variables, Pointers, Structs► latest version online Fuzzy Logic

Fuzzy logic functions

Fuzzy logic is a form of many-valued logic. In contrast with traditional binary logic that deals only with true or false, fuzzy logic functions have a truth value that ranges in degree between 1 and 0, from 'completely true' over 'almost true' to 'completely false'. Trade signals derived with fuzzy logic are often less susceptible to random noise in the price data, especially with complex logic expressions.

The following functions return a fuzzy truth value:

equalF (var v1, var v2): var

aboveF (var val, var border): var

belowF (var val, var border): var

Fuzzy comparison, equivalent to the binary ==, > and < operators.

betweenF (var val, var lower, var upper): var

Fuzzy between function.

peakF (vars Data): var

valleyF (vars Data): var

Fuzzy peak and valley functions.

risingF (vars Data): var

fallingF (vars Data): var

Fuzzy rising and falling functions.

crossOverF (vars Data1, vars Data2): var

crossUnderF (vars Data1, vars Data2): var

crossOverF (vars Data, var border): var

crossUnderF (vars Data, var border): var

Fuzzy crossOver and crossUnder functions.

andF (var a, var b): var

orF (var a, var b): var

notF (var a, var b): var

Fuzzy logic functions, equivalent to the &&, ||, and ! operators.

Parameters:

a, b Fuzzy truth values, 0.0 .. 1.0
Data, Data1, Data2 Data series.
val, v1, v2 Values to compare.
lower, upper, border Comparison borders.

Returns

Fuzzy truth, from 0.0 (completely false) to 1.0 (completely true)
 

fuzzy (var a): bool

Defuzzyfication, converts a fuzzy truth value to binary true or false. Use this function to convert the result of a fuzzy logic expression back to binary logic.

eq (var v1, var v2): bool

Fuzzy comparison, returns true when the parameters differ less than FuzzyRange, otherwise false.

 

Parameters:

a Fuzzy truth value, 0.0 .. 1.0

Returns

Boolean true or false
 

The following system variables affect the fuzzy logic calculation:

FuzzyRange

Determines the 'fuzziness' range of the input data (default: 0 = no fuzziness). When comparing two variables, the truth value goes from 0 to 1 within that range. Set this to a small fraction of the price volatility, or f.i. to 0.1*PIP for comparing moving averages, or to 0.1 for comparing percentage values. The smaller this value, the 'less fuzzy' is the logic. At the default value 0 the logic is binary. The FuzzyRange variable is also used for classifying signal patterns for price action trading.

FuzzyLevel

Determines the level above which fuzzy true becomes binary true (default: 0.5); used by the fuzzy function.
 

Remarks:

Example:

// Fuzzy version of a Workshop 4 variant ///////////////////
function run()
{
  vars Price = series(price());
  vars Trend = series(LowPass(Price,1000));
  vars Signals = series(0);
 
  Stop = 4*ATR(100);
  FuzzyRange = 0.001*ATR(100);

  var Valley = valleyF(Trend),
    Peak = peakF(Trend);
  Signals[0] = orF(Valley,Peak); // store the signal
  if(fuzzy(Valley))
    exitShort();
  if(fuzzy(andF(Valley,notF(Sum(Signals+1,3)))))
    enterLong();
  if(fuzzy(Peak))
    exitLong();
  if(fuzzy(andF(Peak,notF(Sum(Signals+1,3)))))
    enterShort();  
}

// Binary version for comparison  
function run()
{
  vars Price = series(price());
  vars Trend = series(LowPass(Price,1000));
  vars Signals = series(0);
 
  Stop = 4*ATR(100);
  bool Valley = valley(Trend),
    Peak = peak(Trend);
  if(Valley or Peak)
    Signals[0] = 1; // store the signal
  if(Valley)
    exitShort();
  if(Valley and Sum(Signals+1,3) == 0)
    enterLong();
  if(Peak)
    exitLong();
  if(Peak and Sum(Signals+1,3) == 0)
    enterShort();  
}

See also:

crossOver, crossUnder, rising, falling, peak, valley, comparisons

 

► latest version online Coinbase Plugin

Coinbase Pro Plugin (deprecated)

Coinbase alias GDAX is an insurance backed cryptocurrency exchange. There are two Zorro plugins that use the Coinbase Pro API, one developed by a Zorro user and available on Github, one included in the Zorro S distribution (Zorro 2.41 or above).

For using the plugin, first generate an API Key on the Coinbase Pro website. In Zorro, select CoinbasePro from the scrollbox, enter the API key and the passphrase (with a space in between) in the User ID input box, and the secret in the Password input box.

User Key Passphrase
Password Secret

Please note: Coinbase no longer supports their CoinbasePro API, so the plugins are deprecated.

Supported broker commands

The Coinbase Pro plugin supports the brokerCommand function with the following standard commands:

Some additional commands have been implemented:

Remarks

See also:

Links, order, brokers, broker plugin, Binance

► latest version online

Hedge

Hedging, Virtual Hedging, FIFO Compliance

Many strategies, such as grid traders or systems with multiple algorithms, often hold opposite positions of the same asset. This would normally violate the NFA and FIFO compliance required by international brokers and cause rejection of orders. Even if orders are accepted, holding long and short positions simultaneously increases risk, margin, and trading costs. It is always preferable to close an open position, rather than opening a new position in opposite direction.
 
Therefore scripts must take care of NFA and FIFO compliant trading, i.e. close first opened positions first, and avoid concurrent long and short positions. This can require lengthy and awkward coding when several algorithms trade with the same assets. The virtual hedging mechanism of Zorro S guarantees FIFO compliant closing of trades and prevents positions in opposite directions, even with complex portfolio systems. This happens in a completely transparent way; the script needs no special code and can arbitrarily open and close positions.

The virtual hedging mechanism uses two layers of trades, phantom trades and pool trades. The pool trades hold the net amount of the phantom trades. The strategy script only handles the phantom trades, while the broker only receives orders for the pool trades. Pool trades are opened or closed when phantom trades open or close, but not necessarily in that order. Phantom trades can be open in both directions at the same time, but the resulting pool trades are only open in one direction per asset, either short or long.
 


Strategy
Script

 

 

Phantom
Trades


 

Pool
Trades


 

Market
Orders


 

Broker
API

Systems with virtual hedging are almost always* superior to 'real hedging' systems that hold opposite positions. Aside from NFA and FIFO compliance, virtual hedging systems achieve higher profit due to reduced transaction costs, need less capital due to lower margin requirements, and have lower risk because trades are closed earlier and less exposed to the market. Some brokers, such as Oanda, apply virtual hedging automatically to all trades. For brokers that don't, Zorro can activate virtual hedging with the Hedge variable (see below). All trades are then entered in phantom mode. When the net amount - the difference of long and short open lots - changes, Zorro automatically opens or closes pool trades in a way that FIFO order is observed and market exposure is minimized.

Example: several long positions are open with a total amount of 100 lots. Now a short trade of 40 lots is entered. The net amount is now 60 lots (100 long lots minus 40 short lots). Zorro closes the oldest long pool trades fully or partially until the sum of open positions is at 60 lots. If partial closing is not supported, the oldest long pool trades are fully closed until the remaining position is at or below 60 lots. If it's less than 60 lots, a new long pool trade is opened at the difference. In both cases the net amount ends up at exactly 60 lots.

Virtual hedging affects performance parameters. The equity curves of a system with or without virtual hedging are rather similar, but the number of trades, the profit factor, the win rate, and the average trade duration can be very different. Here's an example of the same grid trading system without and with virtual hedging:


EUR/CHF grid trader, real hedging, 402 trades, avg duration 14 weeks, win rate 95%, profit factor 10, total profit $15900


EUR/CHF grid trader, virtual hedging, 261 trades, avg duration 2 weeks, win rate 65%, profit factor 3, total profit $16300

* Exception: Systems that exploit rollover / swap arbitrage or use special order types cannot use virtual hedging.


Hedge

Hedging behavior; determines how simultaneous long and short positions with the same asset are handled.

Range:

No hedging; automatically closes opposite positions with the same asset when a new position is opened (NFA compliant; default for NFA accounts).
1 Hedging across algos; automatically closes opposite positions with the same algo when a new position is opened (not NFA compliant; default for non-NFA accounts).
2 Full hedging; long and short positions even with the same algo can be open at the same time (not NFA compliant). Entering a trade will not automatically close opposite positions.
4 Virtual hedging without partial closing (NFA compliant). Long and short positions can be open simultaneously, but only the net position is open in the broker account. Phantom trades immediately trigger the opening or closing of corresponding pool positions.
5 Virtual hedging with partial closing and pooling (NFA and FIFO compliant). Phantom trades in the run function are collected and result in a single pool trade. Intrabar phantom trades trigger pool trades immediately. Open pool positions are partially closed to match the net amount.
6 Script-synchronized virtual hedging with partial closing and pooling (NFA and FIFO compliant). Like Hedge mode 5, but pool trades are not automatically snychronized at any bar, but only when the tradeUpdate function is called.

Type:

int

Remarks:

Example:

if(Live)
  Hedge = 5;  // virtual hedging in trade mode
else
  Hedge = 2;  // full hedging in test mode

See also:

NFA, enterLong/Short, LotsPool, Phantom trades, tradeUpdate

 

► latest version online Historical data

Historical data (and how to get it)

Historical price data is used in [Test] and [Train] mode, and can also be used in [Trade] mode for pre-loading the LookBack period. The needed data period is determined with StartDate and EndDate. If historical prices from the desired period are missing, they can be downloaded with the assetHistory function or the Download script (see below). All downloaded prices are usually aligned to UTC time. The broker's time zone does not matter. Therefore it's normally no problem to use price data from different sources for the simulation and for trading.

Zorro stores historical price data in files the History folder. The file name begins with the asset name, and the file extension indicates the content:

Any historical data file is simply a list of structs. Any struct represents a candle, quote, contract, or order book entry. The struct size - the number of elements besides the time stamp - is indicated by the number after the "t" extension. Thus, a .t6 file consists of T6 structs (see below) with a time stamp and 6 data elements. For avoiding extremely large files that are awkward to handle, price history can be split in multiple years. The file name has then the year number attached; for instance, "AAPL_2010.t2" contains AAPL order book data from 2010. Multi-year files are automatically detected and loaded. Some files, such as high resolution options data, may be too large even for year splitting. They can then be split in months or days, and loaded by script code at the begin of any month or day in the backtest. If additional data is required, it can be separately imported from arbitrary user-specific formats in a dataset. The .t6 price data files included with Zorro are normally split in years and contain a list of 1-minute candles in T6 format. The data resolution is format independent, but should be at least as high or higher than the bar resolution in backtests.

The same asset can have different price histories, for instance EOD data for strategies with daily bars, M1 data for day trading and T1 data for scalping. The historical data files can be distinguished with a postfix character to the file name, for instance "AAPLeod.t6" for EOD data with no year number, "AAPL_2018m1.t6" for M1 candle data split into years and "AAPL_2018.t1" for tick data. The script can then select the right price history through the History variable. For instance, "History = *eod.t6" selects the EOD data, "History = *m1.t6" the M1 data and "History = *.t1" the tick data. The default is *.t6 for bars of one minute or more, and *.t1 for bars of less than one minute.

Historical data files contain either T1, T2, T6, or CONTRACT structs in descending timestamp order (newest price first).The structs are defined in the trading.h header:

typedef struct T1
{
DATE time; // UTC timestamp of the tick in DATE format
float fPrice; // price data, positive for ask and negative for bid
} T1; typedef struct T2 { DATE time; // UTC timestamp in DATE format float fPrice; // price, ask or bid float fVol; // trade volume or ask/bid size, negative for bid } T2; typedef struct T6 { DATE time; // UTC timestamp of the close, DATE format float fHigh,fLow; float fOpen,fClose; float fVal; // additional data, usually ask-bid spread float fVol; // additional data, usually volume, negative for bid } T6; typedef struct CONTRACT { DATE time; // UTC timestamp in DATE format float fAsk,fBid; // premium without multiplier float fVal,fVol; // additional data, like delta, open interest, etc. float fUnl; // underlying price (unadjusted) float fStrike; // strike price long Expiry; // YYYYMMDD format long Type; // PUT, CALL, FUTURE, EUROPEAN, BINARY } CONTRACT;

All years and assets should have the same historical file format; mixing different formats would produce an error message. Data in .t2 order book format or .t8 options format can be used additionally to the historical price data, since they are loaded with different functions (orderUpdate and contractUpdate). Underlying prices (fUnl in the CONTRACT struct) and order book data is normally unadjusted, while historical prices in .t1 or .t6 files are usually split and dividend adjusted. Some strategies require both types of prices.

The structs are stored in descending time order, i.e. the most recent tick comes first. Timestamps are in OLE DATE format, a double float that counts days since midnight 30 December 1899. Hours, minutes, seconds, and milliseconds are simply fractions of days. If the tick covers a time period, its timestamp is the UTC time of its last price, i.e. the time of the Close. If you have price data based on local time, convert it to UTC (you can find some example code here). For daily (D1) price history with timestamps containing the date only - i.e. no time of day - a bar time offset is automatically added. If the original price data contains bid prices, you can convert them to ask prices by adding the spread. If the price data contains time stamps not from the close of a tick, but from the open or middle price, correct them with the TickFix variable if needed for the strategy. Normally it has little effect on backtest results if ticks contain ask, bid, or trade prices, or if time stamps are from the tick open or the close.  

The fOpen, fClose, fHigh and fLow data streams can be separately accessed with price functions. The fVal and fVol data can be used to store additional data, such as spread, quote frequency, trade volume, implied volatility, delta, swap, etc, and accessed with market functions. All data streams are automatically synchronized to the streams of the first asset call in the script; so it does not matter if some data is only available in daily or weekly 'ticks'.

Outliers in historical data are automatically detected and removed. If this is not desired, for instance with high volatility assets such as cryptocurrencies or penny stocks, disable outlier detection or raise the detection level.

Import and conversion

Data can be downloaded from brokers, free online source, data vendors, or the Zorro download page. You can get it in different resolutions, and - with stocks - either split and dividend adjusted, or unadjusted. You normally use adjusted data for backtests. Forex M1 data and stock D1 data is free, but data in higher resolution or for other assets is not. M1 history for US Stocks and D1 options history can be purchased from us in .t6 and .t8 format, or from data vendors in CSV or JSON formats. When downloading data from a broker, make sure to select the 'last trade' price type if available, and adjust stock prices to avoid artifacts by splits and dividends. Purchased data usually contains adjusted trade prices.

For using data files with a resolution of less than one minute (such as .t1 or .t2 files), Zorro S is required. For importing price or options data files from external sources, convert them to a list of T1, T2, T6, or CONTRACT structs, optionally divide them into years or months, and store them in binary files with the format and name described above.

Data from online sources or vendors in CSV or JSON formats can be converted to .t1, .t2, .t6, or .t8 with the dataParse function. Examples can be found under Import. The Strategy folder also contains several small scripts for download and conversion from popular formats. Download.c (see below) downloads historical data in .t6 or .t1 format from Yahoo, IB, FXCM, or Oanda. CSVtoHistory converts data from popular .csv formats, f.i. from HistData, Quandl, or Yahoo, to the Zorro .t6 format. Example code for automatically downloading .csv price data from the Internet or from a provider API can be found on the http page. A script for generating artificial .t8 historical data for options from the yield rate, dividends, and underlying price history can be found here. Example scripts for converting history files from complex .csv or .nxc formats to the .t1 format can be found here.

For looking into .t8, .t6, .t2, or .t1 files and displaying their content, use the History or Chart scripts.

The Download script

For downloading free data from brokers or online price sources, you can use Download.c, a small script for retrieving or updating prices and asset parameters. You can download either the history of a single asset, or do a bulk download of all assets of a given asset list. The script opens a control panel with a few toggle buttons (blue) and entry fields (yellow):

History  
AAPL Either asset symbol, or asset list file name
2020-2024 Time period to download
M1 Data resolution: M1, Tick, or D1
Ask/Bid Data type: Ask/Bid, Trades, Unadjusted, Parameters
Broker Data source: Broker, Stooq, Yahoo, AlphaVantage
Download! Click here to start the download.

Asset or Asset List

Enter here the symbol of the asset in the form needed by the broker or price source, f.i. "AAPL.US" for downloading AAPL data from Stooq. If the name contains the word "Assets" or ends with ".csv" (f.i. "AssetsFXCM.csv"), all assets of the asset list with the given name are downloaded.

Data resolution

Data type

Data source

Not all combinations make sense. For instance, you can normally not download M1 data or asset parameters from online data sources. Check with your broker which assets are available. It is recommended to download forex data from the same broker that you use for trading, since it's broker dependent. If a price history file of the same asset already exists, the new prices are appended at the end. If the file was already updated on the same or previous day, no data is downloaded. If you tried to download nonexisting data from a data source, you'll normally get a file containing an error message as response. Zorro won't interpret that file, but you can find it in your History folder as the most recent file, open it with a text editor, and see what's wrong.

 After all is downloaded, click [Stop] to end the session.

See also:

Bars, file, dataset, data import, asset parameters, assetHistory

► latest version online

hmac

hmac(string Content, int Size, string Secret, int Mode): string

Generates a cryptographic hash from the string Content with an optional Secret for authentication purposes.

Parameters:

Content - Message or data to be hashed.
Size - Content size in bytes, or 0 for using the string length of Content.
Secret - Secret string for generating a cryptographic hash, or 0 for a simple SHA512 or SHA256 hash.
Mode - 512 for a HMAC512 hash, 256 for a HMAC256 hash. +64 when Secret is base64 encoded.

Returns:

Hash string of the given Content, or 0 when something was wrong.

Remarks:

Example:

char Header[1024]M
strcpy(Header,"Content-Type:application/json");
strcat(Header,"\nAccept:application/json");
strcat(Header,"\nApi-Key: ");
strcat(Header,MyKey);
strcat(Header,"\nApi-Signature: ");
strcat(Header,hmac(MyContent,0,MySecret,512));

See also:

HTTP functions, FTP functions ► latest version online http functions

HTTP functions

The HTTP functions can retreive or scrape web content - prices, signals, news, or indicators - and use them in the trading strategy. They can also execute PHP scripts and can be used by a broker plugin for communicating with a REST or FIX API.

http_transfer (string URL, string Data): string

Transfers data to or from the web address given by URL. The function waits until the web page content or the PHP script response is transferred back to Zorro. The data string can contain arguments to be passed to a PHP script (such as the content of an email). The function returns a temporary string containing the web page content for further evaluation; the content remains valid until the next http_transfer call. The number of bytes received can be retrieved with http_status(0).

http_request (string Url, string Data, string Header, string Method): int

Sends a HTTP request to the given URL, and returns a identifier number (id) for controlling the data transfer with http_status. The Data string can contain arguments to be passed to a PHP script, such as the content of an email. The request type, such as "GET", "POST", "PATCH" etc. is given in Method. If Data and Method are 0, the request type defaults to GET. The optional Header string can contain custom headers (f.i. for authorization) separated by line feeds ("\n"). After the transfer is finished, the identifier number must be freed with http_free

http_proxy (string proxy, int port)

Sets up a HTTP proxy server for the connection to the Internet. If no proxy is used, or if you don't know what a HTTP proxy is, you won't need this function.

http_status (int id): long

Returns the number of bytes received with the HTTP data transfer with the identifier id, or the number of bytes received with the last http_transfer call when id == 0, or the transfer status info: 0  - transfer is still in progress; -1 - transfer failed; -2 - id is invalid; -3 - website did not respond; -4 - host could not be resolved..

http_result (int id, string Content, long Size): long

Stores the received data of a HTTP data transfer in the content string, up to size bytes, and returns the received number of bytes. A string terminator (0) is added at the end of the received data.

http_free (int id)

FFrees the identifier number of a HTTP data transfer and stops the transfer. Must be called at the end of each HTTP data transfer. For stopping the most recent transfer, set id to 0.

Parameters:

proxy - proxy server name (example: "proxy-host.com").
port - port of the proxy server (example: 8080)).
Url - URL of the web page (examples: "http://opserver.de/scratch/ip.php" or "https://www.google.com/finance")). 
Data - string (or char array) containing the data to be sent, for instance a JSON record.
Content - string (or char array) to receive the web page content or the result data of a HTTP POST operation.
Header - one or several header strings separated by line feeds ('\n'):
Size - maximum number of characters to be received, or 0 for using the actual content string length.
id - identifier number, returned by http_post().

RRemarks:

Examples:

// access the CFTC website, read the current Commitment Of Traders Report,
// and store it in a file.
function main()
{ file_delete("cot.txt"); // delete previous report file_append("cot.txt", http_transfer("http://www.cftc.gov/dea/futures/deacmesf.htm",0)); }
// get current date and time from the Internet
function main()
{ string Response = http_transfer("http://worldtimeapi.org/api/ip",0); string Date = strtext(Response,"\"datetime\":","0"); printf("\n%s",Date); }
// Download historical EOD price data from Google
function main()
{
  string Code = "SPY";
  string URL = strf("https://www.google.com/finance/historical?q=%s&startdate=01-Jan-2000&output=csv",Code);
  string Content = http_transfer(URL,0);
  if(!Content) continue;
  file_write("History\\history.csv",Content,0);
  dataNew(1,0,7);
  if(dataParse(1,"%d-%b-%y,f3,f1,f2,f4,f6","History\\history.csv"))
    dataSave(1,strf("History\\%s.t6",Code));
}
// Download earnings data from AlphaVantage 
// and store earnings surprises in a dataset
int loadEarnings()
{
  string URL = strf("https://www.alphavantage.co/query?function=EARNINGS&symbol=%s&apikey=%s",
    Asset,report(33));  // 33 = AlphaVantage API key
  string Content = http_transfer(URL,0); // JSON format
  if(!Content) return 0; 
  string Pos = strstr(Content,"reportedDate");  
  if(!Pos) return 0;
  dataNew(1,0,0);
  while(Pos) {
    var Date = wdatef("%Y-%m-%d",Pos+12+4);
    if(Date == 0) break;
    Pos = strstr(Pos,"surprisePercentage");
    if(!Pos) break;
    int Row = dataRow(1,dataAppendRow(1,2));
    dataSet(1,Row,0,Date); // earnings date in field 0
    var Surprise = atof(Pos+18+4);
    dataSet(1,Row,1,Surprise); // surprise in fields 1
    printf("\n%.4f %.4f",dataVar(1,Row,0),dataVar(1,Row,1));
    Pos = strstr(Pos,"reportedDate");
  }
  return 1;
}
// start the script "ip.php" on a remote server, and 
// print dots until Zorro's IP address is returned
function main() { char ip_str[100]; // just a long empty string int id = http_request("http://myserver.de/php/ip.php",0,0,0); if(!id) return; while(!http_status(id)) { if(!wait(100)) return; // wait for the server to reply printf("."); }
if(http_status(id) > 0) { //transfer successful? http_result(id,ip_str,100); //get the replied IP printf("\n%s",ip_str); } else printf("\nError during transfer!"); http_free(id); //always clean up the id! } ip.php: <?php
echo "Your IP address: " . $_SERVER['REMOTE_ADDR'];
?>
// send an email when a trade is entered
function sendEmailAboutTrade()
{
// compose the message
  string Content = strf("content=Zorro has entered a trade!\n%d Lots of %s",
    Lots,Asset);
  http_transfer("http://www.myserver.com/zorromail.php",Content);
}

zorromail.php:
<?php
$to = "me@myself.com";
$subject = "Message from Zorro!";
$body = $_POST['content'];
mail($to,$subject,$body);
?>
// write text to a file on a server
function sendEmailAboutTrade()
{
// compose the message
  string Content = "content=This is my file content");
  string echo = http_transfer("http://www.myserver.com/store.php",Content);
  printf("\nResponse: $s",echo);
}

store.php:
<?php
$file = 'file.txt'; $data = $_POST['content']; file_put_contents($file,$data); echo "Stored in " . $file; ?>

See also:

ftp functions, file functions, email

► latest version online

HWnd

HWnd

The window handle of the Zorro window.

Type:

int

Remarks:

Example (run 2 Zorros with this script):

#include <windows.h>
#include <default.c>

long Handle1 = 0;

int click()
{
  if(Handle1)
    PostMessage(Handle1,WM_APP+2,0,0); // WM_APP+2 = trigger callback function
  else
    PostMessage(FindWindow(0,"Msg Test 2"),WM_APP+2,0,0);
}

void callback(void *Message)
{
   printf("\nMessage received!");
}

void main()
{
// use [Result] button for senging
  panelSet(-1,0,"Send",0,0,0);
  Handle1 = FindWindow(0,"Msg Test 1");
  if(1Handle1)
    print(TO_TITLE,"Msg Test 1");
  else
    print(TO_TITLE,"Msg Test 2");
  while(wait(1));
}

See also:

Command line, callback, window ► latest version online IB Bridge

Interactive Brokers Bridge

Interactive Brokers™ offers access to most major exchanges, a huge number of supported assets including options and futures, and low trading costs. The IB bridge gives Zorro algo trading strategies direct access the IB API, either through IB's Trader Workstation (TWS), or through the IB Gateway. The TWS is preferable for testing a script, the Gateway for automated trading. The TWS, a bloated java program, normally stops every 24 hours and interrupts the connection. The Gateway lasts a bit longer and usually disconnects every weekend. Thus, long-term algo trading with the IB API requires supervising the trading system and restarting the Gateway every Monday. When downloading TWS or Gateway from the IB website, get the release (stable) version, not the beta (unstable) version.

For trading with the TWS, open File/Global Configuration/API/Settings (see screenshot below) and select Enable ActiveX and Socket Clients. Make sure that the socket port is set to 7496. Deselect Read-Only API when you want to send orders. Make sure that Allow connections from localhost only is checked. Enter an individual number from 101..108 in Zorro's User field for connecting to the TWS via socket port 7496. The Password field can be left empty.

For trading with Gateway, select IB API (not FIX CTCI). Open Configuration/API/Settings and make sure that the socket port is set to 4002. Enter an individual number from 1..8 in Zorro's User field for connecting to the Gateway via socket port 4002. The Password field can be left empty.

Other socket port numbers than 7496 or 4002 can be set up in the Server field of the account list (see below). Up to 8 Zorros can connect to the same Gateway or to the same TWS, but each connection needs a separate IB.dll. Use DLL copies as described under Broker Arbitrage. Note that only a single TWS or a single Gateway per user is supported by IB. The connection will break down when a second Gateway or TWS instance with the same user is opened, even on a different PC.

Zorro login fields for IB:

User: 1 .. 8 for the Gateway, 101 .. 108 for the TWS
Password: Empty

Asset list example: AssetsIB.csv

Accounts.csv example entries:

Name Server Account User Pass Assets CCY Real NFA Plugin
IB-TWS-Demo   D4567890 101 0 AssetsIB USD 0 14 IB
IB-TWS-Real   U1234567 101 0 AssetsIB USD 1 14 IB
IB-Gateway 4002 U1234567 1 0 AssetsIB USD 1 14 IB
 

Trading with IB. Rebalancing portfolios

The IB plugin included with the free Zorro version supports paper trading accounts that begin with the letter "D". A plugin for real accounts is included in Zorro S. For opening an account with IB, the normal choice is a RegT Margin account. If you own Zorro S and trade high volumes, consider a Portfolio Margin account with its lower margin costs. At the time of this writing, IB paper trading accounts were only available after opening a real account.

Due to the high margin and lot size requirements, the Forex/CFD trading Z systems are not really suited for IB. They would require a large Capital setup for not skipping too many trades, and even then achieve less annual return due to the low leverage. The main advantage of IB is support of many exchanges, so that a large range of financial assets can be traded. This allows many new trade systems that exploit specific inefficiencies, f.i. volatility, or seasonal effects of particular stocks or treasuries.

Unadjusted historical price data for backtests can be downloaded from IB, with limits to the data resolution and maximum number of years. In second resolution only a few days of data are normally available. Unadjusted and adjusted EOD data is also available from many other Internet sources, f.i. from Yahoo, Stooq, or Quandl by using the Download script or the assetHistory or dataDownload functions. Assets symbols can be defined in a way that live data is loaded from IB and historical EOD data from another source. 

For rebalancing portfolios, the script can either adjust positions directly through enter/exit commands, or generate a CSV file containing the desired asset percentages. This file can then be manually imported in the TWS window Portfolio Tools / Rebalance Portfolio and will produce orders for adjusting the positions. The required CSV format changes from time to time, so export first an example file from the TWS and use it as template for the file generated by your script. An example file that rebalances a Z9 portfolio:

DES,XLU,STK,SMART/AMEX,,,,,,0.0
DES,SMH,STK,SMART/AMEX,,,,,,12.5
DES,XBI,STK,SMART/AMEX,,,,,,17.0
DES,XLI,STK,SMART/AMEX,,,,,,0.0
DES,XLV,STK,SMART/AMEX,,,,,,11.4
DES,VGK,STK,SMART/AMEX,,,,,,0.0
DES,VOO,STK,SMART/AMEX,,,,,,0.0
DES,UUP,STK,SMART/AMEX,,,,,,0.0
DES,VCSH,STK,SMART/AMEX,,,,,,0.0
DES,HYG,STK,SMART/AMEX,,,,,,0.0
DES,TLT,STK,SMART/AMEX,,,,,,20.0
DES,IAU,STK,SMART/AMEX,,,,,,19.0
DES,AGG,STK,SMART/AMEX,,,,,,0.0
DES,SPY,STK,SMART/AMEX,,,,,,0.0

For trading an asset, some prerequisites must be fulfilled:

Not all assets can be traded all the time, and not all price types are available for all assets. Index (IND) assets are read only, but you can trade indices with ETFs, Futures, or CFDs. Some assets, such as specific CFDs, are not available to US citizens, and require an UK or other account outside the US. European traders are not allowed, thanks to EU regulations, to trade US ETFs. The default minimum sizes for currency pairs are in the 20,000 contracts range, but entering a lower minimum size in the asset list is also possible. Orders are then marked as 'odd lot' and cause higher transaction costs. Some stocks (STK) and some other assets are not shortable, either permanently or dependent on market liquidity. Short trades for closing a long position are normally always accepted.

For setting up the IB asset list and for simulating IB trading in the backtest, margin requirements can be found on https://www.interactivebrokers.com/en/index.php?f=24176. The maintenance or end-of-day margin is often higher than the initial margin.

IB symbols

Any asset you want to trade must be represented by a line in the asset list. The asset symbols (Symbol column) are converted to IB contracts in the following way:

A example file AssetsIB.csv with currencies, CFDs, and stocks is included; we do however not guarantee the accuracy and correctness, so use it at your own risk.

Additional data and commands

The IB bridge supports the following additional data streams:

Multiple IB accounts are supported. The account can be set up in the account list.

The IB bridge supports the brokerCommand function with the following commands (extra commands can be implemented on user request):

Remarks

Typical IB API error messages

See also:

Brokers, Broker plugin, MT4 bridge

► latest version online

if .. else

if (comparison) { ... }

else { ... }

If the comparison or expression between the round brackets is true or non-zero, all commands between the first pair of winged brackets are executed. Otherwise all commands between the second pair of winged brackets following else will be executed. The else part with the second set of commands can be omitted. The winged brackets can be omitted when only one instruction is to be executed dependent on the comparison.

Example:

if (((x+3)<9) or (y==0))   // set z to 10 if x+3 is below 9, or if y is equal to 0
  z = 10;
else z = 5;// set z to 5 in all other cases

See also:

comparisons, while, switch, ifelse

► latest version online

#ifdef, #ifndef, #else, #endif

#ifdef name

#ifndef name

#else

#endif

Defined names can be used to to skip certain script lines dependent on previous #defines. All script lines between #ifdef and #endif are skipped if name was not defined. Likewise, all lines between #ifndef and #endif are skipped if name was #defined. The #else statement reverses the line skipping or non-skipping.

Example:

#define SCALPING
...
#ifdef SCALPING
aut.nBarMinutes = 0;
#endif

See also:

#define ► latest version online ifdelse

Helper functions

The following functions are implemented for convenience and for easier converting code from other platforms.

ifelse(bool c, var x, var y): var

ifelse(bool c, int x, int y): int

ifelse(bool c, string x, string y): string

Returns x when the expression or condition c is true or nonzero, otherwise y.

Parameters:

c - any integer or boolean expression.
x,y - variable, constant, or string, both of the same type.

Returns:

x when c is true, otherwise y.
 

once(bool c): int

Helper function; returns nonzero when its c parameter became true or nonzero the first time in a session or backtest. Afterwards it returns 0 regardless of c. Can only be called once per bar.

Parameters:

c - any integer or boolean expression.

Returns:

1 when called the first time with a nonzero c, otherwise 0.
 

changed(var V): int

Helper function; returns 1 when its V value has increased since the last bar, -1 when it has decreased, 0 otherwise. Can be called multiple times per bar. Creates an internal series (see remarks). Source code in indicators.c

Parameters:

V - any variable.

Returns:

1 or -1 when V has changed since the last bar, otherwise 0.
 

barssince(bool c): int

Returns the number of bars or time frames since the expression or condition c was true or nonzero the last time, or -1 when it was never true. This function internally creates series (see remarks). Source code in indicators.c

Parameters:

c - any integer or boolean expression.

Returns:

Number of bars, 0 when true on the current bar, -1 when never true.
  

cum(var Inc): var

Accumulates Inc at any bar, and returns the sum. This function internally creates a series (see remarks). Source code in indicators.c

Parameters:

Inc - variable to be added.

Returns:

Sum of all Inc values so far.
 

valuewhen(bool c, vars Data, int n): var

Returns the Data value at which the expression or condition c was true or nonzero on the n-th most recent occurrence. This function internally creates series (see remarks). Source code in indicators.c.

Parameters:

c - any integer or boolean expression.
Data - data series of LookBack elements.
n - number of occurrences, 1 = most recent, 2 = second most recent etc.

Returns:

Data[i] when c was true the n-th time i bars ago, otherwise 0
 

Remarks:

Examples:

var MaxOfXY = ifelse(X > Y,X,Y);         
var CloseAtLastCross = valuewhen(crossOver(Data1,Data2),seriesC(),1); 
if(once(!is(LOOKBACK))) printf("\nEnd of lookback reached!");

See also:

abs, min, max, between, sign, clamp, if, fix0

► latest version online

Historical Data Import and Conversion | Zorro Project

Importing Historical Data - Sample Scripts

When purchasing historical data from a data vendor, you'll usually get it in CSV formats, JSON formats, or proprietary formats such as NX2. Since most data vendors change their data formats all the time, there are innumerable format variants around. Most formats can be directly converted to the Zorro data format with the dataParse or dataParseJSON functions and a supplied format string. Sometimes a two-step process is needed for modifying, splitting, or merging data fields that are very different to the target format. Some data has poor quality and requires detecting and removing bad records. If the data file contains multiple symbols, they must be parsed out in a loop and stored in multiple history files.

Below you'll find a collection of example scripts for non-trivial data conversion tasks. Example records of the source format are displayed in the initial comments. If you don't want to write a conversion script yourself, contact info@opgroup.de for hiring our data conversion service.

Price data (t1, t2, t6)

// BitMex crypto to t1 
//id, exchange, market, tradeString, price, quantity, total, time_local, type 
//12311611 BMEX XBT/USD 14838153 957.57 940 900115.8 2016-12-31 00:01:29 BUY

string Format = "+0||||f|||%Y-%m-%d %H:%M:%S|s";
string InName = "CSV\\BMEX_XBTUSD_20170101_20171231.txt"; 
string OutName = "XBTUSD_2017.t1";

void main()
{
  dataNew(1,0,0);
  int Records = dataParse(1,Format,InName);
  printf("\n%d records read",Records);
  for(i=0; i<Records; i++) {
    T1* t1 = dataAppendRow(2,2);
    t1->time = dataVar(1,i,0);
    t1->fVal = dataVar(1,i,1);
    string buysell = dataStr(1,i,2);
    if(buysell[0] != 'B') t1->fVal = -t1->fVal; // negative bid quote
// display progress bar and check [Stop] button
    if(!progress(100*i/Records,0)) break;
  }
  if(Records) dataSave(2,OutName);
}
// Bittrex multiple cryptos to M1 t6 
//id,exchange,symbol,unix date,price,amount,sell
//1667839,BT,SLSBTC,1475314100533,0.00087836,0.85360253,false

string Format = "+0,,,%t,f,f,s";

// merge ask + bid to a single T6 record with spread
void addToT6(T6* Tick,DATE Time,float Price,float Vol)
{
  static var Bid = 0, Ask = 0;
  if(Price < 0) { // bid? convert to ask & spread
    Bid = Price; 
    Price = ifelse(Ask > 0,Ask,-Bid);
  } else
    Ask = Price;
  if(Tick->time == 0.) { // new record
    Tick->time = Time;
    Tick->fOpen = Tick->fHigh = Tick->fLow = Price;
  }
  Tick->fClose = Price;
  Tick->fLow = min(Tick->fLow,Price);
  Tick->fHigh = max(Tick->fHigh,Price);
  if(Bid < 0) Tick->fVal = max(0.,Price + Bid);
  Tick->fVol += Vol;
}

void main()
{
  var N = assetList("AssetsBittrex");
  string Name;
  while(Name = loop(Assets)) 
  {
    string InName = strf("CSV\\%s.csv",Name);
    string OutName = strf("History\\%s.t6",Name);
    dataNew(1,0,0);
    int Records = dataParse(1,Format,InName);
    printf("\n%s: %d records read",Name,Records);
    dataNew(2,0,0);
    T6* Tick = dataAppendRow(2,7);
    int Minutes = 0;
    for(i=0; i<Records-1; i++) {
      var Time = dataVar(1,i,0),
      var Price = dataVar(1,i,1),
      var Vol = dataVar(1,i,2);
      string Sell = dataStr(1,i,3);
      if(Sell[0] == 't') Price = -Price; // bid quote
      if(Tick->time - 1./1440 > Time) { // 1 min distance
        Tick = dataAppendRow(2,7);
        Minutes++;
      }
      addToT6(Tick,Time,Price,Vol);
    }
    printf(", %d minutes",Minutes);
    if(Minutes) dataSave(2,OutName);
  }
}
// Tickdata .csv to .t1, ask/bid 
//DateTime,Ask,Bid,Ask Volume,Bid Volume
//04.05.2003 21:00:00.914,1.12354,1.12284,0,0

string Format = "+%d.%m.%Y %H:%M:%S,f1,f2";
string InName = "CSV\\tick_mxnusd.csv"; 
string OutName = "History\\MXNUSD_%.t1";

void main()
{
  int i,Year;
  for(Year = 2010; Year <= 2022; Year++) {
    dataNew(1,0,0);
// read only records from current year
    int Records = dataParse(1,Format,InName,strf(".%i",Year));
    printf("\n%i - %d rows read",Year,Records);
    if(!Records) return;
    dataNew(2,0,0);
    for(i=0; i<Records; i++) {
      var Time = dataVar(1,i,0);
      var Price = dataVar(1,i,2); // bid price
      T2* Quote = dataAppendRow(2,2);
      Quote->time = Time;
      Quote->fVal = -Price;
      Price = dataVar(1,i,1); // ask price
      Quote = dataAppendRow(2,2);
      Quote->time = Time;
      Quote->fVal = Price;
    }
    dataSave(2,strf(OutName,Year)); // store year dataset
    printf("\n%s saved",strf(OutName,Year));
  }
}
// t2 order book data to t1 tick data

string InName = "History\\BTCUSD_%i.t2";
string OutName = "History\\BTCUSD_%i.t1";
int StartYear = 2017, EndYear = 2022;

void main() 
{
  int Year;
  for(Year = EndYear; Year >= StartYear; Year--) {
    int Records = dataLoad(1,strf(InName,j),3);
    printf("\n%i records",Records);
    if(!Records) return;
    T2* Quotes = dataStr(1,0,0);
    var LowAsk = 100000;
    var HighBid = -100000;
    var CurrentTime = 0;
    int i,N=0;
    for(i=0; i<Records; i++,Quotes++)
    {
      if(CurrentTime > Quotes->time) { 
        T1* t1 = dataAppendRow(2,2);
        t1->time = CurrentTime;
        t1->fVal = HighBid;
        T1* t1 = dataAppendRow(2,2);
        t1->time = CurrentTime;
        t1->fVal = LowAsk;
        N+=2;
        LowAsk = 100000;
        HighBid = -100000;
      }
      CurrentTime = Quotes->time;
      if (Quotes->fVal > 0) // ask
        LowAsk= min(Quotes->fVal,LowAsk);
      else if (Quotes->fVal < 0) // bid
        HighBid = max(Quotes->fVal,HighBid);
    }
    dataSave(2,strf(OutName,j),0,N);
    printf("=> %i bars",N);
  }
}

 

Options data (t8)

// Append OptionsDX data to t8
//[QUOTE_UNIXTIME], [QUOTE_READTIME], [QUOTE_DATE], [QUOTE_TIME_HOURS], [UNDERLYING_LAST], [EXPIRE_DATE], [EXPIRE_UNIX], [DTE], [C_DELTA], [C_GAMMA], [C_VEGA], [C_THETA], [C_RHO], [C_IV], [C_VOLUME], [C_LAST], [C_SIZE], [C_BID], [C_ASK], [STRIKE], [P_BID], [P_ASK], [P_SIZE], [P_LAST], [P_DELTA], [P_GAMMA], [P_VEGA], [P_THETA], [P_RHO], [P_IV], [P_VOLUME], [STRIKE_DISTANCE], [STRIKE_DISTANCE_PCT]
//1638392400, 2021-12-01 16:00, 2021-12-01, 16.000000, 450.440000, 2021-12-01, 1638392400, 0.000000, 0.985740, 0.000470, 0.007560, -0.415280, 0.006070, 3.964130, 0.000000, 147.870000, 300 x 300, 139.610000, 142.100000, 310.000000, 0.000000, 0.010000, 0 x 4522, 0.030000, 0.000000, 0.000030, 0.000430, -0.004940, 0.000000, 2.521350, 0.000000, 140.400000, 0.312000

string Format = "+,%Y-%m-%d %H:%M,,,f5,i7,,,,,,,,,f4,,,f2,f1,f6,f8,f9,,,,,,,,,f10,,";
string InName = "CSV\\spy_eod_%d%02d.txt";
string OutName = "History\\SPY_2022.t8"; // file to append to

void parse(int Year,int Month)
{
  int Records = dataParse(2,Format,strf(InName,Year,Month));
  if(!Records) return;
  printf("\n%d new records",Records); 
  for(i=0; i<Records; i++) { 
// any record holds a call and a put contract, so split it in two
    CONTRACT* C = dataAppendRow(1,9);
    C->Type = CALL;
    C->time = dataVar(2,i,0);
    C->fAsk = dataVar(2,i,1);
    C->fBid = dataVar(2,i,2);
    C->fVol = dataVar(2,i,4);
    C->fUnl = dataVar(2,i,5);
    C->fStrike = dataVar(2,i,6);
    C->Expiry = dataInt(2,i,7);
    C = dataAppendRow(1,9);
    C->Type = PUT;
    C->time = dataVar(2,i,0);
    C->fAsk = dataVar(2,i,9);
    C->fBid = dataVar(2,i,8);
    C->fVol = dataVar(2,i,10);
    C->fUnl = dataVar(2,i,5);
    C->fStrike = dataVar(2,i,6);
    C->Expiry = dataInt(2,i,7);
  }
}

int Year = 2022,Month1 = 4,Month2 = 9;

void main() 
{
  dataNew(1,0,0);
  for(j=Month1; j<=Month2; j++)
    parse(Year,j);
  dataSort(1);
  int N = dataLoad(2,OutName,9);
  printf("\n%i previous records",N); 
  N = dataMerge(2,1);
  printf("\n%i total records",N); 
  if(N) {
    dataSave(2,OutName,0,0);
    printf("\nOk!");
  }
}
// Append HistoricalOptionsData to t8
//underlying, underlying_last, exchange, optionroot, optionext, type, expiration, quotedate,strike,last,bid,ask,volume,openinterest,impliedvol,delta,gamma,theta,vega,optionalias,IVBid,IVAsk
//SPY,290.13,*,SPY190621C00295000,,call,06/21/2019,04/12/2019,295,3.59,3.59,3.62,8192,34153,0.102,0.4048,0.03,-16.1222,49.0036,,0.1017,0.1023

string Format = "+,f5,,,,s8,i7,%m/%d/%Y,f6,,f2,f1,f4,f3";
string InName = "CSV\\SPY_2021.csv";
string OutName = "History\\SPY_2021.t8";

void parse()
{
  int Records = dataParse(1,Format,InName);
  if(!Records) return;
  printf("\n%d new records",Records); 
  for(i=0; i<Records; i++) 
  {
    string PC = dataStr(1,i,8);
    dataSet(1,i,8,(int)ifelse(*PC=='p',PUT,CALL));
// fix expiry that is stored in reverse order
    int Expiry = dataInt(1,i,7);
    int MMDD = Expiry/10000;
    int YYYY = Expiry-MMDD*10000;
    dataSet(1,i,7,YYYY*10000+MMDD);
  }
}

void main() 
{
  dataNew(1,0,0);
  parse();
  dataSort(1);
  int N = dataLoad(2,OutName,9);
  printf("\n%d old SPY records",N); 
  N = dataMerge(2,1);
  printf("\n%d total SPY records",N); 
  if(N) {
    dataSave(2,OutName,0,0);
    printf("\nOk!");
  }
}
 // iVolatility H1 to t8
//ReceivingDate ReceivingTime,ID,Underl Symbol,Expiry,Strike,C/P,A/E,Symbol,Bid,Ask,Bid Time,Ask Time, Bid Size,Ask Size,Bid Exchange,Ask Exchange,Volume,?,Underlying,
//2014-06-24 9:30, 23083, AMZN7, 2014-06-27 0:00, 250, C, A, AMZN7, 140627C00250000, 74.85, 79.45, 2014-06-24 16:02, 2014-06-24 16:02, 0, 0, *, *, 0, 0.263868, 327.24

string Name = "AMZN";
string Format = "+%Y-%m-%d %H:%M:%S,,,i7,f6,s8,,,f2,f1,,,,,,,f4,,f5,";
string InName = "CSV\\options_%d-%02d_000.csv";
string OutName = "History\\%s_%d.t8";
int StartMonth = 1, EndMonth = 12, Year = 2014;

void main()
{
  int i,j,k;
  for(Year = 2014; Year <= 2019; Year++) {
    dataNew(2,0,0);
    for(i = EndMonth; i >= StartMonth; i--) 
    {
      string FileName = strf(InName,Year,i);
      if(!file_date(FileName))
        continue;
      dataNew(1,0,0);
      int Records = dataParse(1,Format,FileName); 
      printf("\n%d records",Records);
      var LastValidPrice = dataVar(1,0,5);
      int Dropped = 0;
      for(k = 0; k < Records; k++)
      { 
        CONTRACT* C = dataStr(1,k,0);
        if(C->fUnl < 0.7*LastValidPrice) {
          Dropped++; continue; // remove outlier
        }
        LastValidPrice = C->fUnl;
        C->Type = ifelse(dataStr(1,k,8) == "C",CALL,PUT);
        C->Expiry /= 10; // remove the '0' from the time
        dataAppend(2,1,k,1); // append dataset
      }
      printf(" - %d dropped",Dropped);
      if(!progress(100*(EndMonth-i)/(EndMonth-StartMonth+1),0)) return;
   }
   dataSort(2);
   dataSave(2,strf(OutName,Name,Year)); 
}

 

Data manipulation

// Remove outlier spikes from .t1 data
int isOutlier(var Price,var Prev,var Next)
{
  Price = abs(Price); // could be negative bid
  if(Price > abs(Prev)/Outlier && Price < abs(Prev)*Outlier)
    return 0;
  else if(Price > abs(Next)/Outlier && Price < abs(Next)*Outlier)
    return 0;
  else {
    printf("\nOutlier %i: %.2f-%.2f-%.2f",
      ++NumOutliers,Prev,Price,Next);
    return 1;
  }
}

void main()
{
  Outlier = 1.1; // detect outliers above 10%
  string Name = file_select("History","T1 History\0*.t1\0");
  if(!Name) return;
  int Records = dataLoad(1,Name,2);
  printf("\n %i records",Records);
  if(!Records) return;
  T1 *Tick = (T1*)dataStr(1,1,0);
  for(i=1; i<Records-1; i++) {
    if(isOutlier(Tick->fVal,(Tick-1)->fVal,(Tick+1)->fVal)) {
      Records = dataDelete(1,i,i);
      i--;
    } else
      Tick++;
  }
  if(NumOutliers) {
     file_copy(strx(Name,".","o."),Name); // keep a copy of the original
     dataSave(1,Name);
  }
}
// Compress hi-res t6 files by removing records with similar timestamps
void main()
{
  string Name = "GER30";
  var Similarity = 500; // milliseconds, min distance 
  int Year = 2015; // first year
  for(; Year <= 2022; Year++) {
    int Records = dataCompress(1,strf("History\\%s_%4d.t6",Name,Year),7,Similarity);
    printf("\n Records = %i",Records);
    if(Records)
      dataSave(1,strf("History\\%s_%4d.t6",Name,Year));
  }
}
// merge two historical data files
void main()
{
  int Rec1 = dataLoad(1,"History\\EURUSD_2016_1.t1"),2); // earlier
  int Rec2 = dataLoad(2,"History\\EURUSD_2016_2.t1"),2); // later
  printf("\n%i + %i",Rec1,Rec2);
  int Rec3 = dataMerge(1,2);
  printf(" = %i",Rec3);
  dataSave(1,"History\\EURUSD_2016.t1");
}
// Split single .t6 in multi-year files
void main() 
{
  string Name = file_select("History","T6 History\0*.t6\0");
  if(!Name) return;
  int Records = dataLoad(1,Name,7);
  if(!Records) return;
  printf("\n%i records",Records);
  int i,j;
  for(i=0,j=0; i<Records; i++)
  {
    T6* Candle = dataStr(1,i,0);
    int Year = ymd(Candle->time)/10000,
      NextYear = 0;
    if(i < Records-1)
      NextYear = ymd((Candle+1)->time)/10000;
    if(Year != NextYear) {
      string OutName = strx(Name,".",strf("_%i.",Year));
      dataSave(1,OutName,j,i-j+1);
      j = i+1;
    }
    if(!progress(100*i/Records,0)) break;
  }
}


See also:

History, dataset, assetHistory

► latest version online

#include

#include "filename"

#include <filename>

Reads an additional script from the given file name and then continues compiling the original script file. This way a strategy can consist of an arbitrary number of scripts.

Remarks:

Example

#include <default.c> // default trading functions
#include <windows.h> // include the Windows API
#include "Strategy\common.c"

See also:

define, headers

► latest version online

Zorro.ini

Zorro.ini / ZorroFix.ini

The Zorro.ini or ZorroFix.ini files can be edited for setting up some Zorro properties when the program is started. For this, open it in the Zorro folder with the script editor or any other plain text editor, and edit or enter the following parameters. Enter text strings between the double quotes.

Line Default Description
Comma = 0 Decimal point. Set this to 1 for using a comma instead of a full stop for the decimal mark in exported spreadsheet files. For European Excel™ versions.
Mute = 0 Sound enabled. Set this to 1 for disabling the trading sounds. Useful when Zorro is trading from your bedroom.
Security = 0 Remember login data. Set this to 1 for forgetting the password, and to 2 for also forgetting the user name when Zorro is closed. Recommended when other people have access to your PC.
AutoConfirm = 0 Ask for closing trades. Set this to 1 for suppressing the messagebox "Close open trades?" at the end of a trading session, and don't close the open trades.
AutoCompile = 0 Compile always. Set this to 1 for compiling only modified scripts. Saves a few seconds when testing scripts. Note that static or global variables are then not reset on subsequent runs.
CleanLogs = 30 Delete logs after 30 days. Set this to 0 for never deleting old logs and charts, otherwise to the minimum number of days for keeping logs, charts, and history cache.
CleanData = 0 Never delete data files. Set this to the minimum number of days (f.i. 365) for keeping previously trained rule, factor, and parameter files.
WebFolder = "Log" Log Set this to the folder for storing the trade status page. For viewing live results via Internet, many Windows servers use C:\inetpub\wwwroot.
StrategyFolder = "Strategy" Zorro\Strategy Set this to the folder containing the .c, .cpp, .x, or .dll scripts.
HistoryFolder = "History" Zorro\History Set this to the folder containing historical data files (*.t6, *.t1, `.t2, *.t8). Useful when many Zorro installations share a common price history on a LAN server. Asset and account lists are still loaded from the local History folder.
HistoryScript = "Chart" Chart.c Set this to the script to be started when clicking on a historical data file (*.t6, *.t1, `.t2, *.t8). "Chart" plots a chart, "History" opens the data viewer (Zorro S required).
RTermPath = "" Empty Set this to the path to the RTerm.exe program for the R bridge and the NEURAL machine learning method.
PythonPath = "" Empty Set this to the 32-bit Python folder for the Python bridge of the 32-bit Zorro verison.
PythonPath64 = "" Empty Set this to the 64-bit Python folder for the Python bridge of the 64-bit Zorro version.
VCPath = "" Empty Set this to the Visual Studio™ Build folder that contains the vcvars32.bat and vcvars64.bat files. For directly running C++ scripts; Zorro S required.
EditorPath = "Notepad++\Notepad++.exe" Notepad++ Set this to the path to the text editor for scripts, logs, and performance reports.
ViewerPath = "ZView.exe" ZView Set this to the path to the .png image viewer for charts and histograms.
QuandlKey = "" Empty Set this to your Quandl API key. Keys for other data sources are also available, see assetHistory.
ZorroKey = "" Empty Set this to your Zorro S subscription token.
Action = "Name: Commandline" Some examples Assign a Zorro command line to the [Action] scollbox under the given Name. Put your favorite scripts here. The scrollbox accepts up to 20 lines. Some command line options are available in Zorro S only.
Action = "Name: Program!Parameters" Some examples Assign an external program to the [Action] scollbox under the given Name.

Remarks:

See also:

Testing, Trading, Command line

► latest version online

invalid

invalid(var V): int

Checks whether V is the result of an invalid expression.

Parameters:

V - var to be examined.

Returns:

0  - V is a valid number.
1  - V is invalid, for instance the square root of a negative number.
-1 - V is infinite, or instance the result of a division by zero.

fix0(var V): var

Convenience function that allows dividing by V even when it is zero or invalid.

Parameters:

V - var to be fixed.

Returns:

0.0001  when V is 0 or an invalid number, otherwise V.
 

Remarks:

Invalid numbers in an expression cause the result to become also invalid. 

Example:

if(invalid(Margin)) printf("\nCheck the Margin calculation!"); 

See also:

floor, ceil, abs

► latest version online

DTN IQFeed for Zorro

DTN IQFeed

DTN IQFeed™ is a tick-by-tick unfiltered datafeed for almost all available symbols, with direct connection to the exchanges. The IQFeed plugin (Zorro S required) connects to the IQFeed client application and can retrieve live and historical data.

Installing IQFeed:

User IQFeed login ID
Password IQFeed password

Accounts.csv example entry

Name Broker Account User Pass Assets CCY Real NFA Plugin
IQFeed IQFeed 0 123456 asdfghjk MyAssetsIQF 0 0 0 IQFeed.dll

Remarks

Supported broker commands

The plugin supports the brokerCommand function with the following commands:

See also:

Brokers, broker plugin, IB plugin, MT4 bridge

► latest version online

is

Status Flags

Zorro's current status can be checked through status flags that can be either on (active) or off (not active). The is() function is used for reading the state of a status flag. The following status flags are available:

TESTMODE

TRAINMODE

TRADEMODE

The script is running in [Test], [Train], or [Trade] mode.

DEMO

A demo account is selected through the [Account] scrollbox.

SPONSORED

The script is running on a Zorro S version. See also version().

CHANGED

The [Script] or [Asset] scrollboxes have been changed since the last script run, or the [Edit] button has been clicked. The sliders are then set back to their default positions. Use this flag when you want to initialize something only at the first run of the script.

AFFIRMED

The [Ok] button of a nonmodal message box has been clicked.
 

INITRUN

Initial run of the session or simulation before the price data is loaded and before the log file is opened. Can be used to initialize global and static variables. System variables that are not yet known - f.i. variables that depend on the asset and simulation period - are at 0 during the initial run.

FIRSTRUN

First run of the session or simulation with valid price data, parameters, factors, and system variables. Normally immediately after the INITRUN.

FIRSTINITRUN

First initial run of the script, in the case of multiple simulation runs due to training cycles or NumTotalCycles. While INITRUN and FIRSTRUN is set at the start of any simulation run, FIRSTINITRUN is only set at the first initial run. It is not set in any subsequent training or total cycles.

EXITRUN

Last run of the simulation. All trades are closed. Can be used to calculate results, aside from the evaluate or objective functions. If a live session or simulation is aborted with [Stop] or quit(0), no EXITRUN is performed. 

MARGINCALL

At least once in the simulation a trade was liquidated because the equity dropped below the maintenance margin.
 

RUNNING

The script is currently running in simulation or live trading. Script functions can also be executed outside a simulation run f.i. by clicking on [Result] or on a panel button.

BUSY

A broker API call or a main, run, tick, or tock function is currently executed. Check this flag to determine if an asychronous process, f.i. triggered by a click or callback function, is allowed to access the broker API or global variables. See also call()

SLIDERS

One of the sliders has been moved. Reset this flag with set(SLIDERS|OFF); for detecting the next slider movement.

LOOKBACK

The script is currently in the lookback period, either at the begin of the simulation, or due to a RECALCULATE run.

NEWDAY

The current bar is the first bar of the current day. 

PARCYCLE

The script is currently generating parameters in [Train] mode.

FACCYCLE

The script is currently generating capital allocation factors in [Train] mode.

RULCYCLE

The script is currently generating rules or machine learning models in [Train] mode.
  

COMMAND

The Zorro instance was started through the command line.

COMPILED

The script is an executable (*.x).
 

PORTFOLIO

The script called the loop function.

ASSETS

The script called the asset function.

PLOTSTATS

The script called commands that plot a histogram rather than a price chart.
 

Some macros for often-used flags have been defined for convenience:

Train

The same as is(TRAINMODE).

Test

The same as is(TESTMODE).

Live

The same as is(TRADEMODE).

ReTrain

Process for updating parameters or rules, started by clicking [Train] while live trading (Zorro S only).

ReTest

Process started by clicking [Test] while live trading, f.i. for comparing the live trading results with backtest results of the same period (Zorro S only).

Init

The same as is(INITRUN).
 

Example:

function run()
{
  if(is(TRAINMODE)) set(SKIP3);
  if(is(TESTMODE)) set(SKIP1,SKIP2,LOGFILE,TICKS); 
  ...
}

See also:

System flags, isf

 

► latest version online keys

keys(string format, ...)

Sends key strokes and mouse clicks to the active window or dialog. This way a script can 'remote control' other external programs, f.i. for placing orders in the browser window of a trade web platform.

Parameters:

format - string containing the key characters to be sent, in the same format as in printf. Within the string, the following tokens in square barackets have a special meaning:

[Ctrl-] - the following character is sent together with the [Ctrl]-key.
[Alt-]  - the following character is sent together with the [Alt]-key.
[Shift-] - the following character is sent together with the [Shift]-key.
[Win-] - the following character is sent together with the [Window]-key.
[Click x,y] - mouse click on the spot given by the x,y coordinates relative to the window's upper left corner.
[...]   - special function keys, such as [enter], [tab], [del], [f1] etc. are given in square brackets. Use [cur], [cul], [cuu], [cud] for the cursor keys.
[]], [[] - the right and left square bracket can be given in square brackets.

Remarks:

Example:

// Opens Notepad, writes something, saves it, and closes Notepad.
function main()
{
// start Notepad before 
   while(!window("Editor")) wait(100);  // wait until Notepad is open
   keys("Zorro is cool!"); // write something
   keys("[Ctrl-]s");               // open Save dialog (Ctrl-S) 
   while(!window(NULL)) wait(100);      // wait until the dialog window is open
   keys("cool.txt[Enter][Alt-][F4]"); // save as "cool.txt", and exit (Alt-F4)
}

See also:

exec, window, mouse

► latest version online Lecture 1: An introduction to R

Lecture 1 – An introduction to R

The R lectures are part of the FINC 621 (Financial Mathematics and Modeling) graduate level class at Loyola University in Chicago. The lectures give an introduction into R for the non-programmer, with a focus on quantitative finance and statistics. If you've done the C Tutorial, you should have no problem to follow the lectures and become familiar with R.

Harry Georgakopoulos, who teaches the FINC 621 class, graciously gave permission to include his lectures in the Zorro tutorial. Harry Georgakopoulos is a quantitative trader for a proprietary trading firm in Chicago. He holds a master’s degree in Electrical Engineering and a master’s degree in Financial Mathematics from the University of Chicago. He is also the author of Quantitative Trading with R.

Any errors and omissions in the following lectures are not the responsibility of the author. The lectures should only be used for educational purposes and not for live trading.

Let's get started!

What is R?

R is the main language and environment for statistical computing and machine learning, used by scientists all over the world. It includes an effective data handling and storage facility that provides a suite of operators for calculations on arrays, matrices, data-frames and lists. The base installation of R comes with a large collection of tools for data analysis and data visualization. The language itself supports conditional statements, loops, functions, classes and most of the other constructs that VBA and C++ users are familiar with. R supports the object-oriented, imperative and functional programming styles. The plethora of contributed packages, a solid user-base and a strong open-source community are some of the other key strengths of the R framework.

The R system is divided into 2 parts:

  1. The base package which is downloadable from CRAN.
  2. Everything else.

The base R package contains, among other things, the necessary code which is required to run R. Many useful functions and libraries are also part of this base installation. Some of these include: utils, stats, datasets, graphics, grDevices and methods. What this means for you, is that you can get a lot done with the plain vanilla R installation!

History of R

The S language (R is a dialect of the S-language) was developed by John Chambers and others at Bell Labs in 1976. It started off as a collection of Fortran libraries and was used for internal Bell Lab statistical analysis. The early versions of the language did not contain functions for statistical modeling. In 1988 the system was rewritten in C (version 3 of the language). In 1998, version 4 of the language was released. In 1993 Bell Labs gave StatSci (now Insightful Corp.) an exclusive license to develop and sell the S language. The S language itself has not changed dramatically since 1998. In 1991 Ross Ihaka and Robert Gentleman developed the R language. The first announcement of R to the public occurred in 1993. In 1995, Martin Machler convinced Ross and Robert to use the GNU General Public License to make R free software. In 1996 public mailing lists were created (R-help and R-devel). In 1997 the R Core Group was formed (containing some people associated with the S-PLUS framework). The core group currently controls the source code for R. The first version R 1.0.0 was released in 2000.

Installing R

The installation of the R environment on a Windows, Linux or Mac machine is fairly simple. Here are the steps to follow for the Windows version:

  1. Navigate to http://cran.r-project.org/
  2. Click on the appropriate link for your system.
  3. For a Windows machine, select and download the base installation.
  4. Select all the default options.
  5. A desktop icon will appear once the installation is successful.

The following display appears when you click on the R icon.

R prompt

R Console

Interacting with the Console

There are at least three ways to enter commands in R. The code can either be typed directly into the console, sourced from a .R file or pasted verbatim from a text file.

Customization

There are a number of customizations that can be performed on the R console itself. For font, color and other cosmetic changes, navigate to the GUI Preferences menu:
Edit -> GUI Preferences

Another useful modification is to enable the sdi option for child windows. Whenever you create a plot or bring up another window within the existing R console, the child-window is confined within the main-window. In order to disable this behavior, do the following:

  1. Right-click on the R shortcut icon on the desk- top and select Properties
  2. Change the Target directory text from “…\R-2.15.1\bin\Rgui.exe†to “…\R-2.15.1\bin\Rgui.exe†–sdi

The Basics

One can think of R as a glorified calculator. All the usual mathematical operations can be directly entered into the console. Operations such as addition, subtraction, multiplication, division and exponentiation are referenced by the usual symbols +, -, /, * and ^. More advanced mathematical operations can be performed by invoking specific functions within R.

Basic Math

 1+1
sqrt(2)
20 + (26.8 * 23.4)/2 + exp(1.34) * cos(1)
sin(1)
5^4
sqrt(-1 + 0i)

Advanced Math

 integrand <- function(x) 1 / ((x+1) * sqrt(x))
integrate(integrand, lower = 0, upper = Inf)

Variable Assignment

The assignment of a value to a variable in R is accomplished via the <- symbol

 x <- 3
x <- x + 1
z <- x ^ 2
z <- "hello XR"
y <- "a"
Z <- sqrt(2)

A few things to notice from the previous example:

Containers and Operations on Containers

In order to properly work with any raw data, we need to first place that data into a suitable container.
The 4 important data containers in R are:

Once we have successfully placed our data into suitable data structures, we can proceed to manipulate the data in various ways.

Vector

A vector is equivalent to an array in C. Vectors hold data of similar type. Only numbers, or only characters can be placed inside a vector. The following example creates three vectors of type numeric and character.

 firstVector  <- c(1,2,3,4,5,6)
secondVector <- c("a", "b", "hello")
thirdVector  <- c("a", 2, 23)

The concatenation operator c() is used to create a vector of numbers or strings. The third example mixes numbers with characters. R will convert the type of any numeric values into string characters. Typing the variable name into the R console reveals the contents of our newly created vectors.

 firstVector
thirdVector

The concatenation operator c() can also be used on existing vectors.

 newVector <- c(firstVector, 7, 8, 9)

The extraction of elements from within a vector can be accomplished through a call to the [] operator.

Operations on Vectors

The following examples illustrate various operations that can be performed on vectors.
The first example specifies a single index to use for extracting the data. The second example specifies two indexes. Notice how the c() operator is used to create a vector of indexes. These indexes are subsequently used to extract the elements from the initial vector. This method of “extracting†data elements from containers is very important and will be used over and over again.

 #extract the 4th element of a vector
example1 <- newVector[4]
#extract the 5th and the 8th elements
example2 <- newVector[ c(5, 8)]

The next few examples illustrate mathematical operations that can be performed on vectors.

 x  <- c(1, 5, 10, 15, 20)
x2 <- 2 * x
x3 <- x ^ 2
x4 <- x / x2
x5 <- round(x * (x / x2) ^ 3.5 + sqrt(x4), 3)
x6 <- round(c(c(x2[2:4], x3[1:2]), x5[4]), 2)

Here are a few conclusions we can draw from these examples:

Matrix

A matrix can be thought of as a 2-dimensional vector. Matrices also hold data of similar type. The following code defines a matrix with 2-rows and 3-columns. In R, matrices are stored in columnar format.

 myMatrix <- matrix(c(1, 2, 3, 4, 5, 6), nrow = 2, ncol = 3)

The default matrix() command assumes that the input data will be arranged in columnar format. In order to arrange the data in row format, we need to modify our previous example slightly:

 myMatrix <- matrix(c(1, 2, 3, 4, 5, 6), nrow = 2, ncol = 3, byrow = TRUE)

In subsequent sections we will cover attributes of containers and other R-objects. In a nutshell, an attribute is an extra layer of information that we can assign to our objects. For matrices, a useful attribute might be a list of names for the rows and columns. If we do not specify the names, R will assign default row and column numbers.

Operations on Matrices

The extraction of elements from a matrix can be accomplished via the use of the [,] operator. In order to extract the element located in row 1 and column 3 of a matrix, we need to issue the following command:

 ans <- myMatrix[1, 3]

Operations of matrices can also be vectorized

 newMatrix1 <- myMatrix * myMatrix
newMatrix2 <- sqrt(myMatrix)

Here are some examples that utilize vectorization and single-element operations.

 mat1 <- matrix(rnorm(1000), nrow = 100)
mat2 <- mat1[1:25, ] ^ 2
round(mat1[1:5, 2:6], 3)
head(round(mat2,0), 9)[, 1:7]

Data Frame

Think of a data frame object as a single spreadsheet. A data frame is a hybrid 2-dimensional container that can include both numeric, character and factor types. Whenever data is read into R from an external environment, it is likely that the resulting object in R will be a data frame. The following code creates a data frame.

 df <- data.frame(price  = c(89.2, 23.2, 21.2), symbol = c("MOT", "AAPL", "IBM"), action = c("Buy", "Sell", "Buy"))

A data frame accepts columns of data as input. Notice that these can be either numeric or of type character. Also notice that a name can be assigned to each column of data. In a data frame, as in a matrix, it is important to ensure that the number of rows is the same for all columns. The data need to be in “rectangular†format. If this is not the case, R will issue an error message.

 df2 <- data.frame(col1 = c(1, 2, 3), col2 = c(1, 2, 3, 4))


Error in data.frame(col1 = c(1, 2, 3), col2 = c(1, 2, 3, 4)) : arguments imply differing number of rows: 3, 4

Operations on data frames

Data frames can also be indexed via the [,] operator.

 price1 <- df[1, 1]

The $ operator can be used to extract data columns by “nameâ€.

 symbols <- df$symbol
class(symbols)

The “Levels†descriptor for the symbols variable signifies that the type of the variable is a factor. We can find out what type any object in R is by using the class keyword.

Factors are a convenient data-type that can assist in the categorization and analysis of data. We will not cover factors in this class. In order to disable the conversion of any character vector into a factor, simply use the stringsAsFactors = FALSE argument in the data.frame() call.

 df3 <-data.frame(price  = c(89.2, 23.2, 21.2), symbol = c("MOT", "AAPL", "IBM"), action = c("Buy", "Sell", "Buy"), stringsAsFactors = FALSE)
class(df3$symbol)

Some things to take away from the previous examples:

List

A list structure is probably the most general container. It can be used to store objects of different types and size. The following code creates a list object and populates it with three separate objects of varying size.

 myList <- list(a = c(1, 2, 3, 4, 5), b = matrix(1:10, nrow = 2, ncol = 5), c = data.frame(price = c(89.3, 98.2, 21.2), stock = c("MOT", "IBM", "CSCO")))
myList

The first component of the list is named “a†and it holds a numeric vector of length 5. The second component is a matrix and the third one, a data frame. Many functions in R use this list structure as a general container to hold the results of computations.

Operations on lists

Lists can be indexed either numerically or by the component name through the double bracket operator [[]].

 firstComp <- myList[[1]]
class(firstComp)

An alternate extraction method is the following:

 secondComp <- myList[["b"]]
class(secondComp)

By using the [[]] operator, we extract the object that is located within the list at the appropriate location. By using the single bracket operator [] we extract part of the list.

 partOfList <- myList[ c(1, 3)]
class(partOfList)

The size of the list can be determined with the length() keyword.

 sizeOfList <- length(myList)

Useful Functions

The base package of R includes all sorts of useful functions. Novice R users are sometimes faced with the problem of not knowing what functions are available for performing a specific task. The following examples contain a few functions that are worth memorizing.

Simple Operations

 #1.  Greate 1000 normally distributed random numbers of mean 0 and stdev 1
      x     <- rnorm(1000, mean = 0, sd = 1)
 
#2.  Find the length of the vector x.
     xL    <- length(x)

#3.  Compute the mean and standard deviation of those numbers
     xM    <- mean(x)
     xS    <- sd(x)
 
#4.  Find the sum of all the numbers in x vector x.
     xSum  <- sum(x)
 
#5.  Do a cumulative sum of the values in x
     xCSum <- cumsum(x)
 
#6.  Look at the first 3 elements a vector
     head(xCSum, 3)
 
#7.  Look at the last 3 elements of a vector
     tail(xCSum, 3)
 
#8.  Look at summary statistics
     summary(x)
 
#9.  Sort x from smallest to largest and from largest to smallest.
     xSIn  <- sort(x)
     xSDec <- sort(x, decreasing = TRUE)
 
#10. Compute the median value of the vector
     xMed  <- median(x)
 
#11. Compute the range of a variable
     xR  <- range(x)
 
#12. Compute the difference between elements
     y  <- c(100.1, 100.2, 100, 99, 99.9)
     yDiff <- diff(y)
 
#13. Create a sequence from 1 to 10
     s  <- 1:10
 
#14. A sequence from 1 to 10 in steps of 0.1
     z  <- seq(1, 10, 0.1)

Simple Graphing

R has the ability to produce some intricate graphics. Most of the advanced graphing functionality is exposed in other add-on libraries such as lattice, ggplot2, rgl, and quantmod. For the time being, the plot() command is adequate to address our graphing needs. This is what the default output for plot() looks like.

 #Create a vector of numbers x and plot it
 x <- c(1, 2, 3.2, 4, 3, 2.1, 9, 19)
 plot(x)

Simple plot in R

Simple plot in R



The following code converts the dot plot into a line plot.

 plot(x, type = "l")

Line plot in R

Line plot in R



One can think of a plot as a canvas. We first create the canvas (by calling the first plot() command) and then proceed to paste other lines, points and graphs on top of the existing canvas. The following example will demo the creation of a simple plot with a main title, axis labels, a basic grid and an appropriate color scheme. We will then superimpose a few vertical and horizontal lines onto the first plot.

 plot(rnorm(1000), col = "blue", main = "Yo!", xlab = "Time", ylab = "Returns")
 grid()
 abline(v = 400, lwd = 2, col = "red")
 abline(h = 2, lwd = 3, col = "cadetblue")

Scatter plot in R

Scatter plot in R



Another useful command is the par() function. This command can be used to query or set global graphical parameters. The following code-snippet splits the viewing window into a rectangular format with 2 rows and 2 columns. A plot() command can be issued for each one of the child-windows. We can superimpose multiple line plots, other graphs and text on each unique plot.

 #Create a 2-row/2-column format
 par(mfrow = c(2, 2))
 plot(rnorm(100), col = "red", main = "Graph 1")
 plot(rnorm(100), col = "blue", main = "Graph 2", type = "l")
 plot(rnorm(100), col = "brown", main = "Graph 3", type = "s")
 abline(v = 50, col = "red", lwd = 4)
 plot(rnorm(100) , col = "orange", main = "Graph 4")
 abline(a = -1, b = 0.1, lwd = 2, col = "purple")
 
 #Reset the plotting window
 par(mfrow = c(1, 1)

Multiple R plots

Multiple R plots


Next: R Lecture 2

References

Lecture 2: Functions in R

Lecture 2 – Functions in R

The first class served as an introduction to the R environment. The fundamental data containers c(), matrix(), data.frame(), list() were introduced and some useful functions were presented. This second class is going to cover user-defined functions. When dealing with any sort of data analysis project, it is important to be able to create simple functions that perform specific tasks. Functions are programming constructs that accept zero or more inputs and produce zero or more outputs. Before we jump into functions, we need to address the concepts of: conditional statements, loops and extraction of elements from containers via boolean operators.

Conditional Statements and Boolean Expressions

A conditional statement can be thought of as a feature of a programming language which performs different computations or actions depending on whether a programmer-specified boolean condition evaluates to true or false. We will be using conditional statements quite a bit in most of our functions in order to create logic that switches between blocks of code.

If-Else Statement

The if-else conditional construct is found in R just as in most of the other popular programming languages (VBA, C++, C#, etc).

 value <- 0
 if(value == 0) {
    value <- 4 
 }

The statement within the parenthesis after the if keyword is a boolean expression. It can either be TRUE or FALSE. If the value is TRUE, the code within the curly braces will be evaluated. If the statemenent is FALSE, the code within the curly braces will not be evaluated. If we want to provide an alternate evaluation branch, we can use the else keyword.

 value <- 0
 if(value == 0) {
    value <- 4
 } else {
    value <- 9999
 }

If the boolean expression within the if() is FALSE, then the code after the else will be evaluated. We can combine multiple if-else statements in order to create arbitrarily complex branching mechanisms.

 myIQ <- 86
 if(myIQ <= 10) {
    cat("Wow! Need improvement!")
 } else if(myIQ > 10 && myIQ <= 85) {
    cat("Now we're talking!") 
 } else {
    cat("You're hired!") 
 }

A couple of points to take away from the previous example:

Booleans

Let’s take a look at some boolean expressions:

 x <-5 
 y <-6
 bool1 <- x == y
 bool2 <- x != y
 bool3 <- x < y
 bool4 <- x > y
 bool5 <- ((x + y) > (y - x)) || (x < y) 
 bool6 <- (bool5 && bool2) || (x/y != 3)

Statements such as if() take booleans as input. If the boolean expression is a simple one, it is a good idea to place it within the parentesis of the if() or while() directly. If the boolean expression is more involved, it is probably a good idea to pre-compute the expression, assign it to a variable, and then pass the variable to the appropriate statement.

 #simple case x <-5
 y <-4 
 if(x > y) {
    cat("Success")
 }
 
 #complicated case
 x <-5
 y <-4
 boolN <- ((x > y) && (sqrt(y) < x)) ||
          ((x + y == 9) && (sqrt(y) < x))
 if(boolN) {
    cat("Good times...")
 } else {
    cat("Bad times...")
 }

Let’s take a look at a vectorized boolean comparison.

 x <-5
 w <- c(1, 2, 3, 4, 5, 6) 
 z <- c(1, 3, 3, 3, 5, 3) 
 boolV <- w == z
 boolV <- x > w

In order to evaluate a boolean expression between 2 variables or expressions, we should use the && and || operators. If we want to evaluate a collection of variables against a collection of a different set of variables, we should use the & and | operators. Here is a simple example:

 #using && and ||
 w <-1
 z <-2
 boolS <- (w < z) && ( z < 5)
 
 #using & and |
 x <-3
 w <- c(1, 2, 3, 4, 5, 6)
 z <- c(1, 2, 3, 7, 8, 2)
 boolV <- (w > x) & (x < z) 
 boolV <- (w > x) | (x < z)

Loops

The for() and while() structures are typically utilized when the user wants to perform a specific operation many times.

for()

Here’s how to fill a numeric vector with integers from 1 to 20 using a for() loop.

 myNumbers <- c()
 for(i in 1:20) {
     myNumbers[i] <- i
 }

In the previous example the iterator i took values between 1 and 20. Any variable name can be used as an iterator. This way of populating a vector is certainly possible in R. However, it is not the recommended method for populating a container with data. The following vectorized example accomplishes the same task and avoids the for() loop altogether. Having said this, keep in mind that vectorization might be difficult to implement for certain types of problems. For most of the examples we are going to encounter in this class vectorization works just fine.

 myNumbers <- 1:20

The iterator within the for() loop does not have to be sequential. A vector of possible iterators can be passed directly to the loop.

 seqIter <- c(2,4,6)
 myArray <- 1:10
 for(j in seqIter) {
     myArray[j] <- 999
 }

while()

Another popular looping structure is the while() loop. The loop will perform a certain calculation until the boolean expression provided to it returns a FALSE value.

 x <- 5
 while(x < 5) {
   x <- x + 1 
 }

There is no need to keep track of a counter within a while loop.

Memory Pre-Allocation

It is advisable to pre-allocate a data container before filling it up with values within any loop. The following example fills up a numeric vector with numbers between 1 to 100,000 without pre-allocating the size. The system.time() function is used to measure the elapsed time.

 emptyArray <- c()
 system.time(
   for(i in 1:100000) {
       emptyArray[i] <- i
   })
 
 #Output
 user  system elapsed
 10.36    0.00   10.47

It takes roughly 10 seconds for this operation to complete!
The next example pre-allocates the container up to the maximum-size prior to populating the array.

 fullArray         <- c(NA)
 length(fullArray) <- 100000
 system.time(
     for(i in 1:100000) {
         fullArray[i] <- i
     })
 
 #Output
 user  system elapsed
 0.25    0.00    0.25

The reason for the time discrepancy is due to the copying of the vector elements into a new vector that is large enough to hold the next entry in the for() loop. If the vector is pre-allocated, no copying of elements needs to occur and the time for vector insertions decreases substantially.

allocation of memory in R

Memory allocation in R

Indexing with Booleans

In the previous class we saw how to extract elements of a data-container by passing in the numeric index of interest. Another way of accomplishing the same task is to pass in a boolean vector as the index. A TRUE value will return the contents at that particular location, whereas a FALSE value will not return the contents at that index.

 v1    <- c(1,2,3,4,5,6,7,8)
 boolI <- c(TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE)
 v2    <- v1[boolI]

If the length of the boolean index is smaller than the array being indexed, the boolean index will be internally replicated as many times as necessary in order to match the array length.

 v1    <- c(1,2,3,4,5,6,7,8)
 boolI <- c(TRUE,FALSE)
 v2    <- v1[boolI]

Writing Functions

There exist hundreds of functions in the base-installation of R. Thousands more exist within 3rd party packages/libraries. In many cases, it is convenient to create our own custom functions to perform certain tasks.
Here is an example of a simple function that accepts one input and produces one output:

  MyFirstFunction <- function(a) {
   return(a)
  }

In this simplistic example, the variable a is simply returned by the function. In order to create a function, we need to use the function() keyword. The values within the parenthesis are called the arguments of the function. Everything within the curly braces { } is the fuction body. All variables that are declared within the body of the function are only known to the function. The only exception occurs if the variables are declared as global. It is good practice to avoid the use of global variables. The
return() keyword returns the computed result to the user.
The next few examples explore functions in more depth.

Example 1
 SumF  <- function(a, b) {
   return(a+b)
 }
 
 #Usage
 ans   <- SumF(1,3)
Example 2
 SqCol <- function(d) {
   return(sum(d[,2]^2))
 }
 
 #Usage 
 dF <- data.frame(a = c(1, 3, 3, 2), 
                  b = c(1, 3, 5, 6),
                  c = c(1, 2, 3, 4))
 ans <- SqCol(dF)
Example 3
 MatF  <- function(mat) {
  outList <- list()
  sizeMat <- dim(mat)
  for(i in 1:sizeMat[2]) {
      ind <- which(mat[,i] < 15)
      if(length(ind) > 0) {
         outList[[i]] <- mat[ind,i]
      } else {
         outList[[i]] <- "Empty"
      }
  }
  return(outList)
 }
 
 #Usage
 m1  <- matrix(sample(1:100, replace = TRUE),
               nrow = 10, ncol = 10)
 ans <- MatF(m1)

Example 3 needs some clarification. The input to the function is a matrix called mat. Within the function, we first create a list container to hold our final answers. The dim() function figures out what the size of mat is. The output of dim() is itself a 2-dimensional vector. We just need the number of columns. That is what the [2] argument in the for loop accomplishes. The iterator variable i loops through all the columns of mat and the which() function checks to see which row in the matrix satisfies a given condition. In this case mat[, i] < 15. The ind variable creates a vector of booleans that is subsequently passed back to mat in order to extract only those elements. The final results are stored in the outList variable.

Next: R Lecture 3

References

Lecture 3: Matrices and Libraries

Lecture 3 – Matrices and Libraries

Matrices in R

A matrix is a very useful mathematical construct. Matrices provide a mechanism for easily manipulating large collections of data. Matrix Mathematics is a vast topic and there exist numerous papers and publications that talk about all the possible uses of matrices. Suffice it to say that this class is only going to use a small subset of these theorems. In R, a matrix can be created in the following manner:

 #specify an empty marix with 3 rows and 3 columns
 emptyMat <- matrix(nrow = 3, ncol = 3)

Matrices are created column first. If you want to create the rows first, make sure to use the byrow = TRUE attribute.

 mat1 <- matrix(c(1,2,3,4,5,6), nrow = 2, ncol = 3, byrow = TRUE)

as opposed to:

 mat2 <- matrix(c(1,2,3,4,5,6), nrow = 2, ncol = 3)
Naming Convention for Matrices

Since a matrix is an object within R, one can change the name attribute of the matrix. Names are assigned to the rows and to the columns of a matrix. The following three snippets of code accomplish this.

 #Method 1
 mat3 <- matrix(rnorm(16,0,1), nrow = 4, ncol = 4)
 dimnames(mat3) <- list(c("Row1", "Row2", "Row3", "Row4"),
                        c("Col1", "Col2", "Col3", "Col4"))
 #Method 2
 mat4 <- matrix(rnorm(16,0,1), nrow = 4, ncol = 4, dimnames =
                list(c("Row1", "Row2", "Row3", "Row4"),
                     c("Col1", "Col2", "Col3", "Col4")))
 #Method 3
 myRowNames  <- c("r1", "r2", "r3", "r4")
 myColNames  <- c("c1", "c2", "c3", "c4")
 matrixNames <- list(myRowNames, myColNames)
 mat5        <- matrix(rnorm(16,0,1),nrow = 4, ncol = 4, dimnames = matrixNames)

Fun with Matrices

The following basic operations can be performed on matrices:

Addition

Provided that the number of rows and columns are the same for the matrices being added, once can do the following:

 m1 <- matrix(c(1,2,3,4), nrow = 2, ncol = 2)
 m2 <- matrix(c(5,4,3,2), nrow = 2, ncol = 2)
 m3 <- m1 + m2
Subtraction
 m1 <- matrix(c(7.8,2.4,3.3,4.0), nrow = 2, ncol = 2)
 m2 <- matrix(c(5,4,3,2), nrow = 2, ncol = 2)
 m3 <- m1 - m2
Multiplication

When multiplying together two matrices, make sure that the inner dimensions match. For example, it is fine to multiply a 2×3 with a 3×4 matrix. It is not ok to multiply together a 2×3 with a 4×4 matrix.

 m1 <- matrix(c(7.8,2.4,3.3,4.0), nrow = 2, ncol = 2)
 m2 <- matrix(c(5,4,3,2), nrow = 2, ncol = 2)
 m3 <- m1 %*% m2
Other

Matrix division is not defined. Rather, one can think of matrix division as multiplication by a matrix times the inverse of the second matrix. Remember also that, AB is not equal to BA in matrix land. Another operation that can be defined with matrices is that of exponentiation. This is a more involved topic and will not be covered in this class.

Determinant and Inverse

The determinant of a matrix A can be written as det(A) or |A|. The inverse of a matrix A can be written as inv(A) or A^-1.
The determinant and the inverse of a matrix in R can be computed with the following functions: det() and solve().

Sourcing Files

R code can be composed entirely within a simple text file. For more advanced editing capability, check out the following links:

As mentioned in Class 1, there are 3 ways to get code into R.
1. Write code directly into the R console
2. Write code into a text file and copy/paste it into the R console
3. Write code into a text file, save the text file as a .R file and then invoke the source() command to load the contents of that file into memory.

 #specify the path of the .R file
 fileName <- "c:/myCode.R"
 #load the code into memory
 source(fileName)

Finding Packages

One of the benefits of the R environment is the abundance of open-source code in the form of external libraries/packages. The vast majority of these add-ons can be found here:
http://cran.r-project.org. Libraries are typically organized by subject matter. For a useful breakdown, click on Packages -> CRAN Task Views.

Installing Packages

There are two ways to install packages in R. The first way is via the GUI, and the second way is by issuing the appropriate command in the console.

Installing via the GUI
Package loading in R

Package Loading in R

  1. Click on Packages -> Install package(s)
  2. Select a CRAN mirror site from the drop-down window.
  3. Select the appropriate package from the drop-down window and click OK.
  4. A diagnostic message will appear on the screen and the package will be loaded into the appropriate library folder.

There is a difference between installing a package and loading a package. The installation procedure will expose the new library/package to the R environment. This task only needs to occur once. In order to use the functions and classes within the newly installed package, the library() or require() commands need to be specified. This needs to occur every time the R workspace is re-loaded. The following command loads the newly installed package into memory.

 library(xts)

Here, we have made the assumption that the xts package was installed. If all goes well, nothing will appear on the screen. If the package has not been previously installed, R will issue an error message.

Installing via the Command Prompt

To install a package from the command prompt, simply issue the following command.

 install.packages("xts")

Like most functions, the install.packages() function takes multiple arguments. Various repositories and alternate file locations can be specified.

Useful Financial Packages

This class will briefly cover 2 packages. These are xts and quantmod. The xts package is a timeseries package and comes in very handy when dealing with ordered observations. The quantmod package allows for some extended graphing functionality and works well with xts.

xts()

Over the years, various practitioners and academics have written functions in R that deal with financial timeseries data. Given that the bulk of xts is written in C, it is ideal for fast lookups and indexing.
An xts timeseries obect is composed of an index and coredata. The index contains the time information and the coredata contains the raw data. The following examples illustrate the creation and manipulation of xts objects. The first example is taken directly from the ?xts help file.

 data(sample_matrix)
 myXts <- as.xts(sample_matrix, descr='my new xts object') 
 class(myXts)
 str(myXts)
 
 #attribute 'descr' hidden from view 
 head(myXts)
 attr(myXts,'descr')
 
 #sub-setting all of 2007
 myXts['2007']
 
 #March 2007 to the end of the data set
 myXts['2007-03::']
 
 #March 2007 to the end of 2007
 myXts['2007-03::2007'] #the whole data set
 myXts['::']
 
 #the beginning of the data through 2007
 myXts['::2007']
 
 #just the 3rd of January 2007
 myXts['2007-01-03']

The first line of the previous example invokes the data() command. Typically, external packages include both functions and supporting data. The included data is meant to assist the user in understanding the functionality of the package. The as.xts() command casts the matrix into an xts object. In this example, the row-names of the matrix are converted into an index object and the rest of the data into the coredata. The :: operator is used to extract specific data from the xts object.
The next example extracts the index and the coredata from myXts.

 timeInfo <- index(myXts)
 dataInfo <- coredata(myXts)

The timeInfo object should now only contain the time-information. The command class(timeInfo) reveals that we are dealing with a POSIXct object. It is good practice to convert any timestamps into POSIXct from now on.
Before we move on to more intricate timeseries examples, we need to address the conversion of strings into POSIXct objects. Typically, timestamps are formatted as strings initially when read in from Excel or other databases. Before we can convert the strings into POSIXct, we need to let R know what format we are dealing with. The next example illustrates this.

 #read in file from C: drive
 x <- read.csv("C:/Users/yourname/Desktop/pricesFile.txt", stringsAsFactors = FALSE)
 head(x)
 
 #convert the first column from a character into a POSIXct object so that we can use it
 #to create an xts object.
 
 timeI <- x$Date
 class(timeI)
 xtsIndex  <- as.POSIXct(timeI, format = "%m/%d/%Y")
 xtsPrices <- xts(x[,-1], xtsIndex)

After converting a regular timeseries into an xts object, it becomes fairly easy to perform sub-setting, indexing and merging operations.

 #indexing 
 xtsPrices['2006-07-11::2007-05-10']
 
 #create a dummy xts series
 xtsDummy <- 1.2 * xtsPrices[1:10,1] - xtsPrices[1:10,3]
 
 #merging
 xtsMerged <- merge(xtsPrices[,1], xtsPrices[,2])
quantmod()

After installing and loading quantmod, we can use the following functions to visualize financial timeseries data. The following link provides some useful information about quantmod. http://www.quantmod.com. The examples that follow are taken directly from the quantmod website.

 #install package
 install.packages("quantmod")
 
 #load package
 library(quantmod)
 
 #Goldman OHLC from yahoo 
 getSymbols("GS") 
 chartSeries(GS) 
 barChart(GS,theme='white.mono', bar.type='hlc')
 
 #how about some candles, this time with color
 candleChart(GS,multi.col=TRUE,theme='white')
 
 #and a line, with the default color scheme
 lineChart(GS,line.type='h',TA=NULL) 
 
 #(December '07 to last observation in '08)
 candleChart(GS,subset='2007-12::2008')
 
 #slightly different syntax - after the fact. 
 #also changing the x-axis labeling 
 candleChart(GS,theme='white',type='candles') 
 reChart(major.ticks='months',subset='first 16 weeks')
 
 #The TA argument is one way to specify the
 #indicators to be applied to the chart. NULL means don't draw any. 
 chartSeries(GS, TA=NULL)
 
 #Now with some indicators applied
 chartSeries(GS, theme="white", TA="addVo(); addBBands(); addCCI()")
 
 #The same result could be accomplished a bit more interactively:
 chartSeries(GS, theme="white")
 addVo()      #add volume
 addBBands()  #add Bollinger Bands
 addCCI()     #add Commodity Channel Index
 
 #Yahoo! OHLC from yahoo
 getSymbols("YHOO")
 chartSeries(YHOO, TA=NULL)
 addTA(OpCl(YHOO),col='blue', type='h')
 
 #With newTA it is possible to create a #generic TA function. Let's call it addOpCl 
 addOpCl <- newTA(OpCl,col='green',type='h') 
 addOpCl()
Next: R Lecture 4

References

Lecture 4: Regression and Pairs Trading

Lecture 4 – Regression and Pairs Trading

Regression Analysis

Regression is a very important topic. It is a widely used statistical tool in economics, ï¬nance and trading. R provides pre-written functions that perform linear regressions in a very straightforward manner. There exist multiple add-on packages that allow for more advanced functionality. In this class, we will only utilize the lm() function which is available in the base installation of R. The following example demonstrates the use of this function:

 x    <- rnorm(1000)
 y    <- (x - 2) + rnorm(1000)
 outR <- lm(y ~ x)
 summary(outR)

What we did above was creat an independent variable x and a dependent variable y. The call to the function lm() performed an OLS (Ordinary Least Square’s) ï¬t to the function: y = b0 + b1x + e, where e was distributed as N(mu, sigma^2)

The ~ is used to separate the independent from the dependent variables. The expression y ~ x is a formula that speciï¬es the linear model with one independent variable and an intercept. If we wanted to ï¬t the same model, but without the intercept, we would specify the formula as y ~ x – 1. This tells R to omit the intercept (force it to zero). The following graph illustrates the best ï¬t lines for a model with and without an intercept. If you know that your model should contain an intercept term, then include it. Otherwise, do not include the intercept. For the subsequent pairs trading example, we are going to assume that the intercept term is equal to 0.

Graph of regression in R

Whenever a regression is performed, it is very important to analyze the residuals (e) of the ï¬tted model. If everything goes according to plan, the residuals will be normally distributed with no visible pattern in the data, no auto-correlation and no heteroskedasticity. The residuals can be extracted from the regression object by using the residuals keyword.

 res <- outR$residuals
 par(mfrow = c(2,1))
 plot(res, col = "blue", main = "Residual Plot")
 acf(res)

Here is a graph of both the residuals themselves and their auto-correlation.
residual plot in R and autocorrelation

The summary keyword can be used to obtain the results of the linear regression model ï¬t.

 summary(outR)

The p-values and t-statistics can be used to evaluate the statistical signiï¬cance of the coefï¬cients. The smaller the p-value, the more certain we are that the coefï¬cient estimate is close to the actual population coefï¬cient. Notice how both the intercept and the independent variable coefï¬cient is signiï¬cant in this example. The extraction of the coefï¬cients can be accomplished via
the coefficients keyword.

 outR$coefficients

Pairs Trading

What follows is a really simple version of a pairs trade between two equities. The main motivation for the following example is to expose you to obtaining data via the quantmod package and in using the lm() function that we covered above.

We will explore a simple two-legged spread between AAPL and QQQ. Once we download and transform the timeseries for both stocks, we will define a simple trading rule and explore the trades that our signal generates. Various trade statistics and graphs will be presented.

Step 1: Obtain data via quantmod
 #Utilize quantmod to load the security symbols
 require(quantmod)
 symbols <- c("AAPL", "QQQ")
 getSymbols(symbols)

Now that our data frames for AAPL and QQQ are loaded into memory, let’s extract some prices.

Step 2: Extract prices and time ranges
 #define training set
 startT  <- "2007-01-01"
 endT    <- "2009-01-01"
 rangeT  <- paste(startT,"::",endT,sep ="")
 tAAPL   <- AAPL[,6][rangeT]
 tQQQ   <- QQQ[,6][rangeT]
 
 #define out of sample set
 startO  <- "2009-02-01"
 endO    <- "2010-12-01"
 rangeO  <- paste(startO,"::",endO,sep ="")
 oAAPL   <- AAPL[,6][rangeO]
 oQQQ   <- QQQ[,6][rangeO]

Notice how we defined and in-sample and out-of-sample range. We will use the in-sample data to compute a simple hedge ratio and then we will apply this hedge ratio to the out of sample data.

Step 3: Compute returns and find hedge ratio
 #compute price differences on in-sample data
 pdtAAPL <- diff(tAAPL)[-1]
 pdtQQQ <- diff(tQQQ)[-1]
 
 #build the model
 model  <- lm(pdtAAPL ~ pdtQQQ - 1)
 
 #extract the hedge ratio
 hr     <- as.numeric(model$coefficients[1])
Step 4: Construct the spread
 #spread price (in-sample)
 spreadT <- tAAPL - hr * tQQQ
 
 #compute statistics of the spread
 meanT    <- as.numeric(mean(spreadT,na.rm=TRUE))
 sdT      <- as.numeric(sd(spreadT,na.rm=TRUE))
 upperThr <- meanT + 1 * sdT
 lowerThr <- meanT - 1 * sdT
 
 #visualize the in-sample spread + stats
 plot(spreadT, main = "AAPL vs. QQQ spread (in-sample period)")
 abline(h = meanT, col = "red", lwd =2)
 abline(h = meanT + 1 * sdT, col = "blue", lwd=2)
 abline(h = meanT - 1 * sdT, col = "blue", lwd=2)

trading in R pairs

Let’s look at the distribution of the spread.

 hist(spreadT, col = "blue", breaks = 100, main = "Spread Histogram (AAPL vs. QQQ)")
 abline(v = meanT, col = "red", lwd = 2)

distribution of spread in R

Step 5: Define the trading rule

Once the spread exceeds our upper threshold, we sell AAPL and buy QQQ. Once the spread drops below our lower threshold, we buy AAPL and sell QQQ.

 indSell <- which(spreadT >= meanT + sdT)
 indBuy  <- which(spreadT <= meanT - sdT)
Step 6: Figure out the trades
 spreadL  <- length(spreadT)
 pricesB  <- c(rep(NA,spreadL))
 pricesS  <- c(rep(NA,spreadL))
 sp       <- as.numeric(spreadT)
 tradeQty <- 100
 totalP   <- 0
 
 for(i in 1:spreadL) {
     spTemp <- sp[i]
     if(spTemp < lowerThr) {
        if(totalP <= 0){
           totalP     <- totalP + tradeQty
           pricesB[i] <- spTemp
        }
     } else if(spTemp > upperThr) {
       if(totalP >= 0){
          totalP <- totalP - tradeQty
          pricesS[i] <- spTemp
       }
    }
 }
Step 7: Visualize trades
 plot(spreadT, main = "AAPL vs. QQQ spread (in-sample period)")
 abline(h = meanT, col = "red", lwd =2)
 abline(h = meanT + 1 * sdT, col = "blue", lwd = 2)
 abline(h = meanT - 1 * sdT, col = "blue", lwd = 2)
 points(xts(pricesB,index(spreadT)), col="green", cex=1.9, pch=19)
 points(xts(pricesS,index(spreadT)), col="red", cex=1.9, pch=19)

R spread prices

Next: R Lecture 5

Lecture 5: Applied Statistical Concepts

Lecture 5 – Applied Statistical Concepts

In this lecture we will discuss statistical estimators, investigate the law of large numbers, the central limit theorem and look at implementing all these concepts within R.

Population vs. Sample Statistics

Consider the set of numbers: 102, 103.2, 102, 101.2, 499, 103.2 101.23, 99.2.
Here are some questions we might want to ask about these numbers:

  1. Where do these numbers come from?
  2. Are these all the numbers, or should I expect more to arrive?
  3. Is there a sequence (pattern) present or are these random?

We notice something right off the bat. We need to make assumptions. If these are the only numbers in our set, then we call this set a population. If the numbers are a sample from a larger set, then we call this a sample.
R sample vs polulation

Motivation

We want to write down the height of every Loyola student and we want to ask the following questions:

  1. What is the average height of all the Loyola students?
  2. What is the variance from that average height?

What is the sample and what is the population in this example?

Law of Large Numbers

Use R to generate 100000 random numbers. This is our population. Compute the mean and the standard deviation of these numbers.

 set.seed(100)
 X <- rnorm(1000000, 2.33, 0.5)
 mu <- mean(X)
 sd <- sd(X)
 hist(X, col = "blue", breaks = 100)
 abline(v = mu, col = "red", lwd = 3)

law of large numbers in R

Next, let’s take samples from this population and compute the average of these samples for different sample sizes.

 sample5 <- sample(X, 5, replace = TRUE)
 sample10 <- sample(X, 10, replace = TRUE)
 sample50 <- sample(X, 50, replace = TRUE)
 
 mean(sample5)
 mean(sample10)
 mean(sample50)
 mean(sample(X,1000,replace=TRUE))
 mean(sample(X,10000,replace=TRUE))

Notice how the mean of the samples converges to the population mean as the number of samples increases. This is referred to as the Law of Large Numbers.

Central Limit Theorem

This example will build on the previous example. This time, we will take repeated measurements from X, but we will keep the sample size the same.

 meanList <- list()
 for(i in 1:10000) {
   meanList[[i]] <- mean(sample(X, 10, replace=TRUE))
 }
 hist(unlist(meanList), breaks = 500, main = "Repeated measurements of the
      average for n = 10", col = "cadetblue", xlab = "average of 10 samples from X")
 abline(v=mu,col="red",lwd=3)

The distribution of the sample average converges to a normal looking distribution! This property is referred to as the Central Limit Theorem.
central limit theorem in R

To see how powerful the Central Limit Theorem is, consider a population that is highly non-normal. We only draw a zero or a one repeatedly and look at the
distribution.

 population <- sample(c(0,1), 100000, replace=TRUE)
 hist(population, col="blue", main="Non-normal")
 abline(v=mean(population), lwd=3, col="red")

non normal distribution in R

By taking repeated samples of size 10 from this highly non-normal distribution, we still obtain a normal looking distribution for the sample mean!

 meanList <- list()
 for(i in 1:10000) {
   meanList[[i]] <- mean(sample(population, 10, replace = TRUE))
 }
 hist(unlist(meanList), main = "Repeated measurements of the average for n=10",
      col = "cadetblue", xlab = "average of 10 samples")
 abline(v = 0.5, col = "red", lwd = 3)

non normal central limit theorem in R

Unbiasedness and Efï¬ciency

In a nutshell, unbiasedness means that the expected value of the estimator equals the true value of the parameter. Efficiency means that the variance of the estimator is as small as possible.
bias and efficiency in R

Covariance Matrix

Before we can start talking about what a covariance matrix is, we need to address the concepts of volatility and portfolio risk. This discussion will only focus on equities. Similar arguments can be applied to other instrument classes (bonds, options, futures, etc).

Volatility

Volatility is a proxy for risk. The Greek letter sigma σ is used to denote volatility. Inherently, when a stock has more risk, we can say that the σ of the stock is higher. Volatility is closely related to the second moment of the return distribution of a particular stock. Volatility is also the square root of the variance.

Mathematical Properties of Volatility

The following mathematical operations apply to the variances of random variables X and Y

The Ï variable is the correlation between instruments X and Y. Think of X as the percentage returns of a particular stock named X. Similarly, think of Y as the percentage returns of stock Y. The correlation between X and Y signiï¬es the strength of linear relationship between the two return series. The following graphic depicts the relationship between the two random variables X and Y. The ï¬rst one is a linear relationship, whereas the second one is a non-linear relationship. Even though there exists a strong dependence between the random variables in the second graph, the correlation is very low.
linear and non-linear in R

Portfolio Risk

The idea of portfolio diversiï¬cation has been known for quite some time. Diversiï¬cation implies that one can create a portfolio of risky instruments where the aggregate risk is smaller than the sum of the individual risks. We can use formula 2 from above to understand this idea. The diversiï¬cation (risk reduction) actually comes from the covariance term of equation 2. As the number of instruments in a particular portfolio increases, so does the beneï¬t of diversiï¬cation via the mutual covariance. Formula 2 becomes cumbersome to write as the number of instruments increases. The formula below represents the variance of a portfolio of 3 instruments.

The risk of the portfolio σ is the square root of this formula. For many assets, writing down such a formula becomes a nightmare. Luckily, there is an easier way to express the portfolio risk.

Matrix Notation

The portfolio variance for three assets can be written in a quadratic form as follows:

The covariance matrix Σ plays a pivotal role in modern portfolio theory. All the important volatility and correlation information between instruments can be encapsulated within this matrix. One of the important properties of a covariance matrix is the notion of positive definiteness. Intuitively, we would
never expect the risk of a portfolio to be a negative number. Positive definiteness is exactly the property of the matrix that guarantees this fact. A covariance matrix is positive deï¬nite if the following holds:

  1. wΣw^t > 0

A covariance matrix can be produced within R by using the cov() command.

Next: R Lecture 6

Lecture 6: Stochastic Processes and Monte Carlo

Lecture 6 – Stochastic Processes and Monte Carlo

In Beginner R Tutorial, FINC 621, R Programming

The use of probability and statistics is ubiquitous in quantitative finance. All of the observable prices, volumes, order arrival rates, etc, are due to supply and demand imbalances. However, keeping track of all the supply and demand imbalances becomes cumbersome as the number of variables increases. Statistical tools are vital in explaining and modeling these effects. Stochastic processes and Monte Carlo analysis are some of the tools that are used in the fields of finance, economics and marketing.

Random Variables

The term random variable is somewhat of a misnomer. A random variable, in effect, is neither random nor a variable. It is a function that maps a particular sample space Ω onto the real number line R. We can express the above statement in mathematical notation as follows: X : S → R. For every event in S, X is a function that maps s to a real number.

random variable in R

The following examples will help illustrate the difference between the a) experiment, b) sample space and c) mapping to the real line.

Flipping 2 coins

Consider flipping a fair coin 2 times. In this case, the experiment is the act of tossing the fair coin 2 times. The sample space is all the possible outcomes of the experiment. In this simple example, the sample space consists of {Head, Head}, {Head, Tail}, {Tail, Head} and {Tail, Tail}. Each {} item is an event in the sample space S. A random variable X is simply the function that takes each {} and maps it into a number.

flip coins in R

Tossing 1 die

In this experiment, you toss 1 fair die and count the number of dots that appear face up. The sample space is S = {1,2,3,4,5,6}. We can define a random variable X that assigns the number of dots to the real numbers {1,2,3,4,5,6}

Stochastic Process

Another name for Stochastic Process is Random Process. Roughly speaking, one can think of such a process as a set of random variables indexed by time. Consider the case of a repeated coin toss. This process is called a Bernoulli Process. In effect, we have a sequence of random variables that can take on either the values 0 or 1. In this case, we are implicitly defining a random variable that maps the outcome of a head to 0 and the outcome of a tail to 1. More formally: A Bernoulli Process is a finite or infinite sequence of independent random variables X1, X2, X3, X4, … , such that:

  1. For each i,the value of Xi is either 0 or 1.
  2. For all values of i, the probability that Xi = 1 is the same number p.

Brownian Motion

Our end goal will be to write down a stochastic process for a particular stock or equity index. In order to do so, we need to explore the notion of a Wiener Process; otherwise known as Brownian Motion. This process forms the basic building block of more advanced models. The three properties of Brownian Motion are:

  1. W0 = 0.
  2. Wt is almost surely continuous.
  3. Wt has independent increments with Wt−Ws = N(0, t−s) for 0 < s < t.
Brownian Motion Example

Let’s take a look at an example of Brownian Motion.

 bm <- cumsum(rnorm(1000,0,1))
bm <- bm - bm[1]
plot(bm, main = "Brownian Motion", col = "blue", type = "l")

brownian motion in R

Next, let’s take a look whether any dependence exists between consecutive observations.

 acf(diff(bm), main = "Autocorrelation of Wt")

autocorrelation in R

We can even investigate whether the differences are normally distributed.

 par(mfrow = c(2,1))
 hist(diff(bm), col = "orange", breaks = 100, main = "Wt-s Distribution")
 qqnorm(diff(bm))
 qqline(diff(bm))

random walk distribution in R

Monte Carlo Analysis

Monte Carlo analysis is a practical technique that has a long history and a ton of theory behind it. Fermi, Ulam and Von Neumann used statistical sampling ideas back in the 1930’s and 1940’s. The origins of statistical sampling date back to Laplace in the early 1800’s. The name Monte Carlo Analysis was suggested by Metropolis in 1946. Monte Carlo was used on the ENIAC computer to do neutron transport calculations in th mid 1940’s. Today, Monte Carlo analysis is utilized in all fields of research. The main assumption of this approach is that a randomly chosen sample tends to exhibit the same properties as the population from which it as drawn. Before we apply this technique to modeling stock prices, let’s take a look at a simple example.

How many runs of 4 do we expect in a sequence of 1000 coin tosses?

In other words, we toss a coin 1000 times. How many times should we expect to see 4 heads or 4 tails in a row? This is a problem that can easily be solved by repeated sampling from a known distribution.

 run4 <- numeric(10000)
for(i in 1:10000) {
   run4[i] <- sum(rle(sample(c(-1, 1), 1000, TRUE))$lengths == 4)
}
hist(run4)
mean(run4)

histogram in R

Armed with our basic building block (Brownian Motion, we can go on to construct a plausible model for the behavior of actual stock prices. Before we proceed with constructing a model, let’s take a look at some of the stylized facts of actual stock prices.

Prices and Returns
 #load quantmod
library(quantmod)
getSymbols("AAPL")
price_AAPL <- AAPL[,6]
plot(price_AAPL, main = "The price of AAPL")

stock price in R

The first thing we notice is that this price series doesn’t appear to be stationary. In other words, there is no obvious mean price and it doesn’t make sense to talk about the standard deviation of the price. Working with such non-stationary timeseries is a hassle. If prices are not convenient to work with, then what should we use instead? Let’s take a look at the percentage returns of this stock.

 returns_AAPL <- diff(log(price_AAPL))
plot(returns_AAPL, main = "AAPL % returns", col = "navyblue")
hist(returns_AAPL, breaks = 100, col="brown")

stock return distribution in R

Apart from some clustering in the returns plot, it appears that the returns are distributed somewhat like a normal (Gaussian) distribution. This is an exciting fact since we already know how to work with normal distributions! How about independence? Are these returns independent of each other in time? Here’s a quick way to partially answer that question:

 acf(returns_AAPL[-1], main = "Autocorrelation plot of returns")

autocorrelation of returns in R
Notice that there doesn’t seem to be any autocorrelation between consecutive returns. What are the mean and standard deviation of these returns?

 mR  <- mean(returns_AAPL[-1])
sdR <- sd(returns_AAPL[-1])
> mR
 [1] 0.001369495
> sdR
 [1] 0.02572958
Leap of Faith

So, the typical argument goes as follows:

Now, remember what our end goal is. We want a way to simulate stock prices. In order to do so, we need to come up with a model of how the prices behave (are distributed.) If returns are normally distributed, then how are prices distributed? The answer to this question is straightforward. A little math shows us the answer: Rt = log(Pt/Pt−1). The logarithm of the price is normally distributed. This means that price has a lognormal distribution. A straightforward method to simulate a stock price is to draw a random normal number with a certain mean and standard deviation value, and then exponentiate this number. Based on the formula from above: Pt = Pt−1*e^Rt. To summarize:

  1. Draw a random number from N(μ, σ).
  2. Exponentiate that number and mulitply it by Pt−1.
  3. Repeat for t = 1…N.
 N     <- 1000
mu    <- 0.0010
sigma <- 0.025
p  <- c(100, rep(NA, N-1))
for(i in 2:N)
   p[i] <- p[i-1] * exp(rnorm(1, mu, sigma))
plot(p, type = "l", col = "brown", main = "Simulated Stock Price")

simulation of stock price in R

Portfolio Simulation

Next, we will take a look at a simple portfolio simulation example.

 require(MASS)
require(quantmod)
 
#load a few symbols into memory
getSymbols(c("AAPL", "QQQQ", "SPY", "GOOG", "CVX"))
 
#plot the prices of these stocks 
par(mfrow = c(3,2))
plot(AAPL[,6], main = "AAPL")
plot(QQQQ[,6], main = "QQQQ")
plot(SPY[,6], main = "SPY")
plot(GOOG[,6], main = "GOOG")
plot(CVX[,6], main = "CVX")
par(mfrow = c(1,1))
 
#compute price matrix
pM <- cbind(AAPL[,6], QQQQ[,6], SPY[,6], GOOG[,6], CVX[,6])
 
#compute returns matrix
rM <-  apply(pM,2,function(x) diff(log(x)))
 
#look at pairwise charts
pairs(coredata(rM))
 
#compute the covariance matrix
covR <- cov(rM)
 
#use this covariance matrix to simulate 
# normal random numbers that share a 
# similar correlation structure with actual data
meanV <- apply(rM, 2, mean)
rV    <- mvrnorm(n = nrow(rM), mu = meanV, Sigma = covR)
 
#simulate prices based on these correlated random variables
 
#calculate mean price
p0 <- apply(pM,2,mean)
sPL <- list()
for(i in 1:ncol(rM)){
   sPL[[i]] <-round(p0[i]*exp(cumsum(rV[,i])),2)
}
 
#plot simulated prices
par(mfrow = c(3,2)) 
plot(sPL[[1]],main="AAPLsim",type="l")
plot(sPL[[2]], main = "QQQQ sim",type = "l")
plot(sPL[[3]], main = "SPY sim", type = "l") 
plot(sPL[[4]], main = "GOOG sim",type = "l") 
plot(sPL[[5]], main = "CVX sim", type = "l")

In the prior example, we gather daily data for 5 stocks and we compute the covariance matrix of the returns, along with an average price for each security. Since the purpose of this exercise is to generate a realistic simulation of the portfolio, we use the function mvrnorm() to create a matrix of random normal variables that are correlated in a similar manner as the original data. The following graphs display the original stock prices, the pairwise plot of their returns and the simulated stock prices. One can also look at the correlation matrix of the actual returns and the simulated returns and verify that they are similar.

price graph in R

correlation of stocks in R

simulated stock prices in R

Next: R Lecture 7

References

  1. Stochastic Process
  2. Random Variable and Process
  3. Bernoulli Process
  4. Wiener Process
Lecture 7: Visualization and Reporting with R

Lecture 7 – Visualization and Reporting with R

In Advanced R Tutorial, FINC 621, R Programming

By now you should be familiar with most of the core functionality of the R programming language. We can perform simulations, graph results, create summary statistics, export our results to files and accomplish almost any programming feat by simply using the base installation of R. In this lecture I want to introduce a few tools (libraries) that extend R’s reach. Especially, in terms of producing robust and elegant visualizations and drafting reproducible reports.

Graphing

All of our graphing needs up to this point have been met by the plot(), lines() and abline() functions. We can do better than this.

Hadley Wickham has created a great library that takes graphing/plotting in R to a whole new level. The library is called ggplot2 and much more information about it can be found here: ggplot2 examples.

According to the ggplot2 website: “ggplot2 is a plotting system for R, based on the grammar of graphics, which tries to take the good parts of base and lattice graphics and none of the bad parts. It takes care of many of the fiddly details that make plotting a hassle (like drawing legends) as well as providing a powerful model of graphics that makes it easy to produce complex multi-layered graphics.â€

Let’s take a look at a few examples. (I will closely follow the examples presented here.)

qplot()

We will be utilizing the mtcars data frame that is already accessible through R. Here’s what it looks like:

cars dataframe in R

The qplot() function is similar to the plot() function in many respects.

 require(ggplot2)
 qplot(mpg, wt, data = mtcars)

ggplot in R

 # Add different colors based on the cylinder type 
 qplot(mpg, wt, data = mtcars, colour = cyl)

ggplot graph in R 2

 # Different styling based on the cylinder type
 qplot(mpg, wt, data = mtcars, size = cyl)

ggplot graph in R 3

 # One or two sided faceting formula
 qplot(mpg, wt, data = mtcars, facets = vs ~ am)

ggplot in R 4

Graphics grammar syntax

Let’s take a look at a more interesting example that leverages the graphics grammar paradigm. More information on what a graphics grammar is can be found here.

 stocks <- data.frame(signal = sample(c("Buy", "Sell"), 100, replace = TRUE), returns = rnorm(100, 0, 0.6))
 
obj <- ggplot(stocks, aes(x=returns))
obj <- obj + geom_histogram(aes(y=..density..), binwidth=.8, colour="red", fill="grey") 
obj <- obj + geom_density(alpha=.2, fill="purple")
obj

ggplot2 graph

Reporting

No matter how sophisticated or elegant your statistical analysis happens to be, if you can’t report your results in a meaningful and reproducible way to your management or end-users of your research, your conclusions will be lost. The following steps outline one easy way for weaving together your analytics with your reporting and presenting the end result in a clean and comprehensive manner.

Rstudio

Rstudio is currently one of the best IDE’s (Integrated Development Environment) available for the R language. It is 100% free, is cross-platform (meaning that it works on windows, linux and mac machines) and enables R programmers to become more productive by providing them with all the functionality they could possibly need within the IDE.

Rstudio can be downloaded from Rstudio.com. Rstudio bundles various third party libraries that we will be utilizing in this lecture. One of them is knitr. This library was created by Yihui Xie and allows one to create PDF reports that include embedded R code along with LaTeX formatting. To find more about what LaTeX is and how it can help you write better documents, click here: LaTeX details.

In order to use the knitr library, LaTeX needs to be installed on your computer. If you are using a windows machine, you can obtain LaTeX from here: LaTeX for Windows. If you are using a mac, you can get LaTeX from here: LaTeX for Mac. Once you have downloaded and installed Rstudio, there is no need to install knitr. It is already part of the Rstudio installation.

How to create a simple .pdf report with Rstudio

Here are some basics:

  1. Open Rstudio, click on File -> New -> R Sweave

    Rstudio Knitr document

  2. Let’s start off with a simple example that doesn’t include any R code yet. Type the following between the begin{document} and end{document} tags. Hello R world. This is my first LaTeX document.
  3. Now click on the Compile PDF button above and save the file to an appropriate location.
  4. This is what you will see. A complete .pdf document.

Knitr pdf document

Let’s take this a step further and add some R code and some formatting into the mix. Here’s what the complete document contents should look like:

 \documentclass{article}
 
 \begin{document}
 
   Hello R world. This is my first LaTeX document. 
 
   <<echo = TRUE, message = FALSE>>=
 
    x <- rnorm(1000)
    plot(x)
 
   @
 
 \end{document}

The echo = TRUE command tells knitr to output the code to the pdf file. Not only is the code echoed to the user, it is also executed and the result prints to the pdf document.

pdf with graph knitr

Here’s a slightly more complicated example that passes a variable calculated from R back to the LaTeX text. I have also added some formatting to the pdf document itself for adjusting the margins slightly. Notice the use of the Sexpr command in the example below:

 \documentclass{article}
 
  \usepackage[
   top    = 1.00cm,
   bottom = 1.00cm,
   left   = 2.00cm,
   right  = 1.50cm]{geometry}
 
 \begin{document}
 
   Hello R world. This is my first LaTeX document. 
 
   <<echo = TRUE, message = FALSE>>=
 
    x <- rnorm(1000)
    plot(x)
   @
 
   Now we will compute something else with R:
 
   <<echo = TRUE, message = FALSE>>=
 
   # Here's a coin toss simulation
   N        <- 100
   coinToss <- sample(c(-1, 1), 100, replace = TRUE)
 
   @
 
  The number of heads that occur while tossing 
  a fair coin \Sexpr{N} times is: \Sexpr{length(which(coinToss == 1))}
 
 \end{document}

Formatting a LaTeX document is something that we have not covered in this class. Here’s a cheat sheet that might come in handy when working with LaTeX syntax.

knitr with R output

API

Using DLLs and APIs

The operating system and its subsystems provide Application Programming Interfaces (API) for programs to use their functions. Lite-C can call API functions either based on external Dynamic Link Libraries (DLLs), or on the Component Object Model (COM). DLLs are modules that provide external functions and variables; they are loaded at runtime. When a DLL is loaded, it is mapped into the address space of the calling process.

DLLs can contain two kinds of functions: exported and internal. The exported functions can be called by other modules. Internal functions can only be called from within the DLL where they are defined. Although DLLs can also export variables, their variables are usually only used by their functions. DLLs provide a way to modularize applications so that functionality can be updated and reused more easily. They also help reduce memory overhead when several applications use the same functionality at the same time, because although each application gets its own copy of the data, they can share the code.

The Microsoft® Win32® application programming interface (API) is implemented as a set of dynamic-link libraries, so any process that uses the Win32 API uses dynamic linking.

Declaring a DLL or Windows API Function

Before an API function from an external DLL can be called, a function prototype must be declared, just as any other function. Example:
long WINAPI MessageBoxA(HWND,char *,char *,long);

The function prototype - which is in fact a function pointer - must then be initialized with the function address. There are three methods: static initialization in the api.def (for functions that are often used), static initialization by an API(FunctionName,ModuleName) macro (for functions only defined in a particular header), and dynamic initialization by DefineApi (for functions that are only used in a particular appliction).

The most common static API functions are defined in the api.def file. It's just a plain text file, so it can easily be modified. Open api.def in the Zorro folder, and add a line to it in the style (FunctionName;ModuleName!ProcName). FunctionName is your declared function, ModuleName is the name of the DLL without the ".dll" extension, and ProcName is the name of the function within that DLL (which needs not necessarily be identical to your function name). Example:

MessageBox;user32!MessageBoxA

For acessing a function in a Windows header file, the simplest way is using the API macro in the script. Example:

long WINAPI MessageBoxA(HWND,char *,char *,long);
API(MessageBoxA,user32)

WINAPI is defined as __stdcall, as required for calling a Windows API function. You can find examples of this way to declare external DLL functions in the include\windows.h header file. For implementing your own DLLs you will probably use the __cdecl calling convention, which is the default. So make sure that the functions are defined correctly. Also look under Tips & Tricks for using indicators from external DLLs.

For dynamically accessing an API function at runtime, either use the DefineApi call, or load the DLL and retrieve the function address through standard Windows functions. The function prototype can be used as a function pointer. Examples:

// Example1:
long __stdcall MessageBox(HWND,char *,char *,long);
MessageBox = DefineApi("user32!MessageBoxA");

// Example2:
long __stdcall MessageBox(HWND,char *,char *,long);
long h = LoadLibrary("user32");
MessageBox = GetProcAddress(h,"MessageBoxA");

By default, api.def contains a selection of C standard functions. The windows.h header contains the Windows API functions. If you need a certain function that is not included, you can add it easily as described under Converting C++ Code to lite-C.

For calling functions from a self-written VC++ DLL, put it in the Strategy folder, and access its functions in your script as shown below (assume its name is MyDll.dll and it exports a function double square(double)):

var square(var Arg);
API(square,Strategy\\MyDll)

void main()
{
  if(!square)
    printf("No square!"); // DLL or function not found
  else
    printf("Square: %.3f",square(3));
}
For giving your DLL access to all Zorro functions, let Zorro call a function from your Dll and pass the pointer to the g singleton struct. The g->Functions pointer is the address of a list of all Zorro functions in the same order as in include\func_list.h. The function prototypes are coded in include\functions.h. In this way you can access all Zorro functions as if from a strategy DLL or a broker plugin. An example can be found in Source\VC++\ZorroDll.cpp, which is automatically added to all strategy DLLs. 

Using C++ classes (

Lite-C can use classes and functions from COM DLLs; the most often used example is the DirectX DLL. Classes are like structs, but contain not only variables but also functions (methods). Any COM class contains three standard methods - QueryInterface(), AddRef(), and Release() - as well as any number of class specific methods. For example, here's the lite-C code for defining a COM class that contains two specific methods, Func1() and Func2():

typedef struct _IFooVtbl
{    
    HRESULT __stdcall QueryInterface(void* This,IID *riid,void** ppvObject);        
    DWORD   __stdcall AddRef(void* This);    
    DWORD   __stdcall Release(void* This);    
    HRESULT __stdcall Func1(void* This);    
    HRESULT __stdcall Func2(void* This,int);
} IFooVtbl;


typedef interface IFoo { IFooVtbl *lpVtbl; } IFoo;

Note that each of the methods has an additional parameter called "This". You have to pass the This pointer parameter explicitly in C, but it can be passed automatically in lite-C. Any additional parameters come after This, as above. The interface is then typedef'd as a structure that contains a pointer to the vtable. For calling methods on COM objects, you can use either a C++-style or 'C'-style syntax. Example:

 pIFoo->Func1(); // C++ style
pIFoo->lpVtbl->Func1(pIFoo); // C style

As lite-C does not support class inheritance, just add all inherited methods, if any, to the class. Example for a DirectX class:

typedef struct ID3DXMeshVtbl
{
// IUnknown methods
long __stdcall QueryInterface(void* This, REFIID iid, LPVOID *ppv);
long __stdcall AddRef(void* This);
long __stdcall Release(void* This); // methods inherited from ID3DXBaseMesh long __stdcall DrawSubset(void* This, long AttribId); long __stdcall GetNumFaces(void* This); long __stdcall GetNumVertices(void* This); // ID3DXMesh methods
long __stdcall LockAttributeBuffer(void* This, long Flags, long** ppData);
long __stdcall UnlockAttributeBuffer(void* This)
long __stdcall Optimize(void* This, long Flags, long* pAdjacencyIn, long* pAdjacencyOut,
long* pFaceRemap, LPD3DXBUFFER *ppVertexRemap,
void* ppOptMesh) } ID3DXMeshVtbl; typedef interface ID3DXMesh { ID3DXMeshVtbl * lpVtbl; } ID3DXMesh; ... ID3DXMesh* pMesh; ... pMesh->DrawSubSet(0); long num = pMesh->GetNumFaces(); ...

See also:

Pointers, structs, functions, Dlls, plugins ► latest version online C for programmers

Lite-C for C/C++ programmers

Zorro's lite-C compiler uses the familiar syntax of C based languages such as C, C++, C#, or Javascript. Although it has some differences to allow a simpler compiler structure and faster compiling, it is 'compatible enough' to access the Windows API and even use complex external C++ libraries such as DirectX or OpenGL. Therefore it can be used to write any normal computer program (see Mandelbrot).

The differences of lite-C to standard C/C++ are listed here:

No early if(A && B) abort

The && and || operators are aliased with and and or, but can of course still be used. In C/C++, comparisons are early aborted when a && is encountered and the expression so far evaluated to false, or when a || is encountered and the expression evaluated to true. Lite-C always calculates the expressions up to the end; the order of comparisons plays no role. This requires a different syntax for checking the value of a struct pointer and its element in the same expression:
if((ptr != NULL) && (ptr->element == ..)) .. // C/C++
if(ptr != NULL) if(ptr->element == ..) .. // lite-C 

Trinary operators

Lite-C does not support the comparison ? expression : expression; syntax. Use the ifelse statement instead.
x = (x<0 ? -1 : 1); // C/C++
x = ifelse(x<0,-1,1);  // lite-C 

Infinite loops

Lite-C requires the continuation statement in a for loop. So don't use for(;;) for infinite loops..
for(;;) ... // C/C++
while(1) ... // lite-C 

Multiple operations in one command

Lite-C does not support the comma for clustering multiple operations in one command.
x = 2, y = 2; // C/C++
x = y = 2; // lite-C x = 2; y = 2; // lite-C

Struct and array initialization

In C/C++ structs can be initialized just like arrays, by giving a list of member values. This is only supported for numerical arrays in lite-C. Lite-C can initialize structs by directly setting their elements (see structs).

Struct copying

In C++, structs can be copied into each other with the '=' operator. In lite-C, use memcpy for copying structs or arrays:
// C++:
D3DXVector3 vecA, vecB;
...
vecA = vecB;

// lite-C:
D3DXVector3 vecA, vecB;
...
memcpy(&vecA,&vecB,sizeof(D3DXVector3));

Struct returning

In C++, functions can return structs. In lite-C, return struct pointers instead.

sizeof

In C/C++, sizeof is a built-in function. In lite-C, it's a macro with some restrictions for structs and arrays (see structs).

Enums

Enums are not supported and must be replaced by defines:
enum RGB { RED=1; BLUE=2; GREEN=3 }; // C/C++

#define RED 1 // lite-C #define BLUE 2 #define GREEN 3

Unions

Union members of the same type can be substituted by a #define, and union members of different type can be treated as a different members of the struct. Example:
typedef struct S_UNION { 
   int data1;
   union { int data2; float data3; };
   union { int data4; int data5; };
} S_UNION; // C/C++


typedef struct S_UNION { int data1; int data2; float data3; int data4; } S_UNION; // lite-C #define data5 data4

If the struct size must not change, or if for some reason the program requires different variable types to occupy the same place in the struct, a special conversion function can be used to convert the type of a variable without converting the content:

typedef struct S_UNION { 
   int data1;
   union { int data2; float data3; };

} S_UNION; // C/C++
...
S_UNION s_union;
s_union.data3 = 3.14;

typedef struct S_UNION { int data1; int data2; } S_UNION; // lite-C #define data3 data2 ... int union_int_float(float x) { return *((int*)&x); } ... S_UNION s_union; s_union.data3 = union_int_float(3.14);

Function pointers

In C/C++, function pointers are declared like this: int (*foo)(int a, int b);. In lite-C there's no difference between function prototypes and function pointers: int foo(int a, int b);. See details under pointers.

Signed, unsigned, extern, const, register...

In lite-C, variables are only defined by their type; further qualifiers are not used. Float, double, var, long, int are generally signed, and pointers, char and short are generally unsigned, according to the normal way they are used. The include\litec.h file contains definitions for all usual unsigned variables like DWORD or WORD that are used in Windows functions. So using unsigned variables normally does not cause any problems. However you need to take care when variables exceed their range. For instance, subtracting 1 from (DWORD)0 results in -1 under lite-C, but in 0xFFFFFFFF in standard C/C++.

Relaxed variable and function type checking

Lite-C allows a lot of things where a C++ compiler would issue a warning, such as assigning an int to a pointer, or not returning a value from a function. This relaxed checking is heavily used in the predefined scripts for making the code shorter and less complicated, but it also allows the user to make more mistakes.

main() and run() functions

The main() function works like in C. The run() function indicates that the program is a strategy script. It is called once before the start of the strategy for initialization, and then once for each bar.

Using C library or Windows API functions

Lite-C already contains the most often used functions from the standard C/C++ libraries - they are listed in <litec.h>, which is automatically included in all scripts without explicit #include statement. So you normally won't need to include C headers such as stdio.h or math.h. If you need a function that is not contained in <litec.h>, add it as described under Using the Windows API. Here's a brief instruction of how to add an external function to lite-C:

If you need certain structs or variable types that are not yet contained in include\windows.h or in the other standard include files, just add them from their original file either into your script. If you think that a certain function, struct, or variable type is often needed, suggest its inclusion on the Zorro future forum.

See also:

Pointers, Structs, Functions, API

► latest version online

Headers

Header files

The following standard header files are used in Zorro strategy scripts and/or in C++ strategy DLLs:

#include <litec.h>

Standard header for lite-C, contains standard variable type definitions. Automatically included in default.c.

#include <trading.h>

Standard header for strategy scripts, with definitions of the structs GLOBALS, TRADE, ASSET, T6, etc. Automatically included in default.c and zorro.h.

#include <variables.h>

Standard header for strategy scripts; contains #defines for all predefined trading variables. This file 'translates' the struct members into easier to memorize variable names. Automatically included in default.c and zorro.h.  

#include <functions.h>

Standard header for strategy scripts, with definitions of all functions. Automatically included in default.c and zorro.h.  

#include <default.c>

Standard header for lite-C strategy scripts; includes all the headers above. Automatically included when no other #include statement is found in the main script. Otherwise it must be explicitly included.

#include <windows.h>

Optional lite-C header; contains common definitions and functions from the Windows API. Requires including <default.c> when trading functions are used.

#include <stdio.h>

Optional lite-C header; contains all usual file, directory, and sort/search functions from the standard C libraries io.h, stdio.h, stdlib.h, direct.h. Requires including <default.c> when trading functions are used.

#include <r.h>

Optional header for the R bridge. Can be included in lite-C scripts and/or in C++ strategy DLLs.

#include <contract.c>

Optional header for contract functions.Can be included in lite-C scripts and/or in C++ strategy DLLs. 

#include <profile.c>

Optional lite-C header for histogram plotting functions.

#include <zorro.h>

Header for C++ strategy DLLs. Not for lite-C.

#include <legacy.h>

Header for deprecated and outdated lite-C keywords, for backwards compatibility to very old Zorro versions. If your script contains a deprecated keyword, you can include this header. But better remove or replace it.

See also:

API, scripts

► latest version online

assetHistory

assetHistory(string Name, int Mode): int

Loads price history either from the currently selected broker or data feed, or from a specified online price source in CSV or JSON format. Stores the data in a dataset or in a .t6 or .t1 file in the History folder. Online price sources can be user defined; some popular sources such as Quandl, Stooq etc. are predefined.

Parameters:

Name

Asset symbol or code specifying the price source, or the asset name from the asset list, or 0 for the current asset symbol.

Mode

0 - download tick (T1) data from the selected broker (Zorro S required).
1
- download one-minute (M1) price data (all brokers, plus Bittrex, CryptoCompare)..
2 - download hourly (H1) data (Bittrex, CryptoCompare).
3 - download daily (D1) data (all online sources, and IB).
4 - download data in LookBackResolution (all brokers).
8 - download nothing, but get the data from History\history.csv (for test purposes).

+FROM_SOURCE - download data from a user defined online source (see assetSource; Zorro S required).
+FROM_GOOGLE
- download daily (D1) data from Google Finance (currently unavailable).
+FROM_QUANDL - download daily (D1) data from Quandl (Zorro S and Quandl key required).
+FROM_AV - download daily (D1) data from AlphaVantage.
+FROM_STOOQ - download daily (D1) data from Stooq.
+FROM_EOD - download daily (D1) data from EOD.
+FROM_YAHOO
- download daily (D1) data from Yahoo Finance.
+FROM_IEX - download daily (D1) data from IEX Cloud.
+FROM_BITTREX - download M1, H1, or D1 data from Bittrex (Zorro S required).
+FROM_CRYPTOC - download M1, H1, or D1 data from CryptoCompare (Zorro S required).
 (If no online source is set, the data is downloaded from the selected broker).

+UNADJUSTED - download unadjusted prices (some sources only; see remarks below).
+OVERRIDE - download price data even when history is up to date, i.e. not older than half the data period.
+IMMEDIATE - download at least one tick of recent price data; for getting a live price from an online source.
+VOLATILE - store the data in a temporary dataset whose handle is returned (only D1 data or online sources).
+PRECISE - load only the period given by LookBack, StartDate, and EndDate.
+FOR_ASSET - store the file under the name of the current asset selected with asset or assetAdd.

Returns:

0 when nothing was downloaded due to an error or because the data was up to date. Otherwise a nonzero value or the dataset handle.
 

assetSource(string Filename, string URL, string Header, string Body, string Format)

Sets up all parameters of a price source website or REST API for a subsequent assetHistory(...,FROM_SOURCE) call. See examples.

Parameters:

Filename

Name of the file for storing or appending the downloaded data, or 0 for using the default symbol.

URL

Target URL for the data request, including request parameters.

Header

Request header, or 0 if no header is required. Some online sources requre an API key or access troken in the header.

Body

Request body for a HTTP POST request, or 0 for a HTTP GET request.

Format

Format string for converting the received data, as described under dataParse. If the format begins with '[', JSON data format is assumed, otherwise CSV format. If 0 or empty, the data is not converted, but stored under history.csv or history.json.

Remarks:

Example:

// Update M1 price history of all assets from selected broker
function main()
{
  NumYears = 2;
  while(loop(Assets))
    assetHistory(Loop1,1);
}

// Download D1 prices from Quandl
string Name;
while(Name = loop("AAPL","MSFT","GOOGL"))
  assetHistory(strf("WIKI/%s",Name),FROM_QUANDL);

// Download and plot Bitcoin prices from Bitfinex
function run()
{
  BarPeriod = 1440;
  assetHistory("BITFINEX/BTCUSD",FROM_QUANDL);
  assetAdd("BTCUSD");
  asset("BTCUSD");
  set(PLOTNOW);
}

// Download unadjusted SPY data and store it in "SPYuna.t6"
History = "*una.t6";
assetHistory("SPY",FROM_STOOQ|UNADJUSTED);

// Download AAPL prices from Stooq, using assetSource
assetSource("AAPL.t6",
"https://stooq.pl/q/d/l/?s=AAPL.US&d1=20000101&d2=20201024&i=d",
0,0,"+%Y-%m-%d,f3,f1,f2,f4,f6");
assetHistory(0,FROM_SOURCE);

// Download Bitcoin OHLC prices from Coin.io
int Month, Year = 2019, MinutesPerMonth = 31*1440;
for(Month = 1; Month <= 12; Month++) {
  string URL = strf("https://rest.coinapi.io/v1/ohlcv/BTC/USD/history?period_id=1MIN&time_start=%04d-%02d-01T00:00:00&limit=%d",
    Year,Month,MinutesPerMonth);
  string Name = strf("BTCUSD_%04d.t6",Year);
  assetSource(Name,URL,
    "X-CoinAPI-Key: my-coin-api-key",0,
    "[,time_period_end,%Y-%m-%dT%H:%M:%SZ,price_high,price_low,price_open,price_close,,volume_traded");
  assetHistory(0,FROM_SOURCE);
}

// Download Bitcoin BBO ticks from Kraken via Coin.io
var From = dmy(20190101), To = dmy(20200101);
for(; From < To; From += 1.) {
  string Start = strdate("%Y-%m-%dT00:00:00",From),
    End = strdate("%Y-%m-%dT00:00:00",From+1.);
  assetSource("BTCUSD_2019.t2",
    strf("https://rest.coinapi.io/v1/quotes/KRAKEN_SPOT_BTC_USD/history?time_start=%s&time_end=%s&limit=100000",
      Start,End),
    "X-CoinAPI-Key: my-coin-api-key",0,
    "[,time_exchange,%Y-%m-%dT%H:%M:%SZ,ask_price,ask_size,bid_price,bid_size");
  assetHistory(0,FROM_SOURCE);
}

See also:

price, asset, UpdateDays, MaxTicks, loadStatus, price history, dataDownload

► latest version online loadStatus

saveStatus(string FileName)

loadStatus(string FileName)

Saves and loads the current trading status to a .trd file in the Data folder. The file contains the status of open real and phantom trades, the status of the sliders, and component specific variables (AlgoVar). This function can be used to resume trading at the last stored state in case of a server crash or reboot, or to let a fallback server take over the trading in case of a hardware failure.

Parameters:

FileName

Name and path of the file, or 0 for a default name like "Data\\scriptname.trd".


SaveMode

This variable determines by several flags what's saved or loaded in the .trd file in [Trade] mode or when calling the saveStatus/loadStatus functions.

Flags:

SV_SLIDERS  - save/load slider positions (default).
SV_ALGOVARS  - save/load all AlgoVars (default).
SV_ALGOVARS2 - save/load all AlgoVars2.
SV_TRADES  - save/load all open trades (default).
SV_STATS  - save/load statistics data, to be continued in the next session.
SV_BACKUP
  - after loading, save a backup of the .trd file (default).
SV_HTML  - after loading trades in [Test] mode, generate a HTML file with the list of open trades.

Type:

int

Remarks:

Example:

function run()
{
  if(Bar >= 1) { // default asset is selected after first run
    AlgoVar[0] = 123;
    SaveMode = SV_ALGOVARS+SV_ALGOVARS2; 
    saveStatus("Data\\Test.trd");
    AlgoVar[0] = 456;
    printf("\n %.0f",AlgoVar[0]);
    loadStatus("Data\\Test.trd");
    printf("\n %.0f",AlgoVar[0]);
    quit("ok!");
  }
}

See also:

AlgoVar, assetHistory, Trading

► latest version online lock

lock(): int

Synchronizes the behavior of several Zorro instances, f.i. for preventing that they write into the same file at the same time (Zorro S only). If another Zorro has called lock() before, this function waits until it either calls unlock() or the [Stop] key was pressed. In the latter case lock() returns 0, otherwise nonzero.

unlock()

Cancels a previous lock call and let other waiting Zorros continue.

Example (see also putvar/getvar):

lock(); // prevent that other Zorros write into same file
file_append(Name,Text1);
file_append(Name,Text2);
unlock();

See also:

timer, run, wait, suspended, quit, version

► latest version online

Log mesages

Log messages

All events, such as opening or closing a trade, are recorded in the .log file in the Log folder. This file can be opened with any text editor; it normally opens in Notepad++ when clicking [Result] after a backtest. The 'verbosity' of log entries can be set up with the Verbose variable. Additional messages can be inserted in the log by the strategy script using the print functions.

The LOGFILE flag must be set for recording a log. For comparing or combining logs from different sessions, append a number to the file name by setting the LogNumber variable right at begin of the main or run function. Then compare both logs with a tool like BeyondCompare™. For generating a different log file name on any day, set LogNumber = wdate(NOW). For logging a particular training cycle, use the LogTrainRun variable.

Any bar is recorded in the log with its number, UTC time stamp, current balance or equity, and the current price of the selected asset.

Any trade is identified by asset, algo, type, and a unique number, like this:

[AUD/USD:CY:S1234] - Short trade (S) with AUD/USD, CY algorithm identifier, and a trade ID number ending with 1234. The ID can be used to identify the trade in the broker's platform. If a trade is partially closed, the ID can change. Trade types are short S, long L, put P, or call C.

[SPY::LP0103] - Long put (LP) with SPY, no algorithm identifier, and ID ending with 0103.

{AUD/USD:CY:s1234} - Short phantom trade (lowercase s and winged brackets). Phantom trades are simulated, not executed.

(AUD/USD::L) - Long pending trade (round parentheses). Pending trades are not yet opened and thus have no trade number.

The decimals of prices or other numbers in the log vary. The higher the value, the less decimals are normally printed.

Startup / price download messages

The following messages are generated at start:

Load AUD/USD.. 1000 h

Zorro downloaded 1000 hours recent price history from the broker's server and used it for the LookBack period. If an asset is not available for trading - which can happen with assets that US residents are not allowed to trade, f.i. some CFDs - an error message is issued and the asset must be excluded from the strategy.

Load AUD/USD.. 910+111 h

The PRELOAD flag was set and historical price data was available. Zorro read 910 hours price data from its history files and downloaded additional 111 hours price history from the broker's server. Dependent on broker history, the number of downloaded hours can differ from asset to asset.

Load AUD/USD.. 1000 h, gap 36 h

There is a 36 hours gap between the end of the downlaoded price data and the current time. This happens when a the price download was restricted by the broker, or when the price server was not up to date due to a recent weekend or holiday, or when the market was closed at session start. Small gaps are usually not harmful; large gaps can, dependent on strategy and gap size.

Load AUD/USD.. 835 h, 65 bars added

The price server could not cover the full LookBack period in time; 65 bars had to be substituted by Zorro. This can happen with MT4 brokers or other brokers iwht klimited history. MT4 servers sometimes get the missing data after a waiting time; in that case the strategy just needs to be restarted after a minute. Added bars are normally not harmful, as missing prices are interpolated. But when a large number of bars has been added, indicators with long time periods can be less accurate at strategy start and cause reduced performance during the first time. Use the PRELOAD flag for overcoming price server limitations.

Load AUD/USD.. failed

The asset is available, but price data can not be downloaded. This can happen with MTR4 servers that need a long time for sending assets that have not been requested before (f.i. because no chart with that asset was opened in the MTR4 client). In such a case, start the strategy again after a minute - chances are that the MTR4 server meanwhile got its act together.

AUD/USD 2019 complete

The current historical data is already up to date. Its file date is more recent than the most recent price quote minus 1 day. No data is downloaded.

[AUD/USD:CY:L4401] - resumed

Zorro is resuming a previously closed or interrupted trading session and continues the trades that were opened in the previous session. Previous trades are not resumed when the new session opened new trades already in the first run.

[AUD/USD:CY:L4401] - (closed)

The trade cannot be resumed. Either an asset named "AUD/USD" was not available at session start (see Error 053), or the trade was externally closed through a 'safety net' stop loss or manually in the broker platform.

Session statistics resumed - 27 bars passed

The previous session was closed 27 bars ago. Session statistics have been saved with the SAV_STATS flag and is resumed with the current session. The end of the previous session was still within the lookback period of the current session.

Read Trend.par Trend.c Trend.fac

The parameters, rules, and capital allocation factors of the Trend strategy were loaded. This message is generated at start and again when the strategy was re-trained (see below).

LookBack 123 bars, 20-11-19 00:00..21-05-19 00:00

Start of the lookback period

Bar 123 continued until 20:00

Bar 123 started in the lookback period, but continues in the life period.

End of lookback period at 19:00

All data series are filled and trading is now enabled.

Bar messages

[21: Wed 11-01-19 00:00] (95.18)

Bar number and bar close date and time, either in UTC or in the time zone given by LogZone, rounded to one second. Optional ask price at bar end of the asset selected with the scrollbox. The number of decimals of the price depends on the PIP value of the asset.

[217: Wed 11-01-19 01:15:00] 91.41/96.02\91.35/95.90

Bar number, close time, and optional open/high\low/close ask prices of the asset selected with the scrollbox, on Verbose = 2. If none of the assets used in the script is selected with the scrollbox, no prices are printed.

[217: Wed 11-01-19 17:15:00c] 91.41/96.02\91.35/95.90 -0.15

As before, but with current ask/bid spread with Verbose = 3. A "c" or "w" next to the time indicates a bar outside market hours or at weekend, "f" indicates a flat bar where the price did not change, and "z" indicates local time by LogZone.The spread is displayed as a negative value to be subtracted from the ask prices for getting the bid prices.

[217: Wed 11-01-19 17:15:00c] 90.00/95.00\85.00/95.00 (94.90) -0.15

As before, but OHLC prices from a special bar, the current real price in parentheses, and the current ask/bid spread, with Verbose = 3.

[1385: Fri 12-06-29 00:00] 9038 +1438 9/7 (97.69)

Bar number, rounded bar end time, current balance or equity (dependent on BALANCE flag), current open value, number of open winning/losing positions, and current price (when trades have been opened). The decimals of the equity and open value numbers depend on the decimals setting in the account list.

Trade messages

The following messages are generated when trades are entered or exited. Trade symbols are in curly brackets { } for phantom and virtual trades, in square brackets [ ] for trades sent to the broker, and in parentheses ( ) for trades that were not opened or are still pending. Long or short trades are indicated by a capital S or L for virtual or pool trades, and a small s or l for phantom trades. Prices and profits displayed are based on the last price quote. The number of decimals depends on the amount and can be enforced by an account list entry. Prices in the log are generally displayed as ask prices, even though long positions open at the ask and close at the bid, and short positions open at the bid and close at the ask. Profits include estimated trading costs. In live trading, real fill prices and profits can slightly deviate due to slippage and differences in trading costs.

(AUD/USD:CY:S) Skipped - Lots/Margin 0

The trade was not entered because Lots or Margin was set to 0.

(AUD/USD:CY:S) Skipped - Margin 10, Min 45

The trade was not entered because MARGINLIMIT is set and the allocated Margin of 10 account currency units was too low for buying 1 lot of the asset, which would require margin 45. If the ACCUMULATE flag is set, the margin of those skipped trades will be accumulated until a trade is eventually entered.

(AUD/USD:CY:S) Skipped - Risk 40, Min 90

The trade was not entered because the allowed Risk of 40 (in account currency units) was too low to cover the trade risk of 90 (see RISKLIMIT).

(AUD/USD:CY:S) Skipped - Total Margin 450

The trade was not entered because MARGINLIMIT is set and the account has not enough free margin to cover the Risk of the trade in a safe distance from a margin call. Either reduce the trade volume, or close open trades, or add some money to the account. You can see the current free margin in the broker platform, f.i. in the Account status of the FXCM Trading Station.

(AUD/USD:CY:S) Skipped - Total Risk 900

The trade was not entered because there's not enough balance in the account to safely cover the total risk of all open trades (see RISKLIMIT).

(AUD/USD:CY:S) Skipped (not enough capital)

Only in a verbose log. The trade was not entered due to insufficient funds.

(AUD/USD:CY:S) Skipped (frame)

Only in a verbose log. The trade was not entered because trading is disabled inside a TimeFrame.

(AUD/USD:CY:S) Skipped (market hours)

Only in a verbose log. The trade was not entered because trading was disabled during weekends or holidays

(AUD/USD:CY:S) Skipped (Max / Amount)

Only in a verbose log. The trade was not entered because it would exceed the allowed number of open or pending trades of this type or algo.

(AUD/USD:CY:S) Skipped (in-sample)

Only in a verbose log. The trade was not entered because trading was disabled due to DataSkip, DataSplit, or DataHorizon.

(AUD/USD:CY:S) Skipped (no trading)

Only in a verbose log. The trade was not entered because there is no trading in the LookBack period. The period can be extended beyond the normal lookback end when a particular asset had otherwise not enough price history.

(AUD/USD:CY:S) Short 3@0.87400 Entry stop

A pending trade was opened, with an entry stop at 0.87400. If the price drops to the entry stop within EntryTime, 3 lots will be bought at 0.87400 plus slippage. Note that pending trades are not sent to the broker.

(AUD/USD:CY:S) Short 3@0.87600 Entry limit

A pending trade was opened, with an entry limit at 0.87600. If the ask price rises to the entry limit within EntryTime, 3 lots will be bought at 0.87600 plus slippage. Note that pending trades are not sent to the broker.

(AUD/USD:CY:S) Missed entry 0.87400 after 2 bars

The pending trade was not executed because the entry price was not met within the allowed time period (EntryTime). This message is not displayed in the window, but in the log file.

(AUD/USD:CY:S) Missed entry 0.87400 at cancellation

The pending trade was not executed because the entry price was not met until the next exit command. This message is not displayed in the window, but in the log file.

(AUD/USD:CY:S) Order expired after 48 hours

The trade was not executed because the order was entered just before the market was closed at a weekend or holiday, and EntryTime was not long enough to cover that period. This message is not displayed in the window, but in the log file.

(AUD/USD:CY:S) Balance limit exceeded

The trade was not executed because you're already too rich. The balance limit for trading with the free Zorro version on a real money account was exceeded. You can either get Zorro S, or withdraw your profits until your account balance is again below the limit.

[AUD/USD:CY:S4400] Short 1@0.87310 Bid 0.87305 Risk 116 ptlsx

A short trade was entered. The number 4400 are the last 4 digits of the trade ID that was assigned by the broker API. The trade volume was 1 lot, the current ask quote was 0.87310, the current bid quote was 0.87305.(displayed at Verbose = 2 or above)  The loss of this trade at the stop level would be approximately 116 account currency units. This can deviate from a set-up Risk value when only a few lots are opened or when the entry price is different from the current price. Special exit conditions or trade modes are displayed with lowercase letters: "p" for a profit target, "t" for trailing, "l" for a profit lock, "s" for a fixed trailing step, "x" for a fixed exit time, "o" for a limit order, and "g" for a GTC order. In the log file the entry time is also listed.

[AUD/USD:CY:S4400] Short %2@0.87310 of 5 Risk 116 g

A short trade was entered as above, with no exit conditions, but as a GTC order ("g"). Only 2 ("%2") of 5 lots were initially filled. The remaining 3 lots could be filled later.

{AUD/USD:CY:l4500} Long 3@0.87310 Risk 200

A long phantom trade - mind the winged brackets and the lowercase l - was entered at market. 3 imaginary lots were bought at the price 0.87310.

[AUD/USD:CY:l4500] Long 3@0.87310 Pool trade

A pool trade in virtual hedging mode was entered at market in reaction on the opening or closing of phantom trades. 3 lots were bought at price 0.87310.

[AUD/USD:CY:S4400] Cover 1@0.85934: +105 at 17:01

A short trade was closed by an exitShort call. 1 lot was covered at 17:01 UTC at current price 0.85934 and profit of 105 account currency units. Since displayed profit and exit price are based on the last price quote and estimated trading costs, the real profit or loss of the trade can slightly deviate from the displayed values, and is visible in the broker platform.

[AUD/USD:CY:L4501] Sell %1@0.85945: +33 at 17:01

A long trade was partially closed by an exitLong call. 1 lot was sold at 17:01 UTC at exit price 0.85945 and profit 33. The rest of the position remains open. The trade ID usually changes by partially closing, and the displayed ID and all further messages of that trade are for the remaining open position.

[AUD/USD:CY:L4401] Reverse 1@0.85945: +33 at 17:01

A long trade was closed by opening a short trade in opposite direction.

[AUD/USD:CY:L4451] Expired 1@0.85945: +33 at 17:01

A long trade was automatically closed because its LifeTime expired.

[AUD/USD:CY:L4444] Liquidated 50@0.8567: -1044 at 17:01

A long trade was liquidated due to a margin call by spending all Capital.

[AUD/USD:CY:S4400] Exit 1@0.85934: +105 at 17:01

A trade was closed by the trade management function. 1 lot was sold at the price 0.85934. The profit was 105.

[AUD/USD:CY:S2210] Closed 1@0.80201: +20.5 at 17:01

The trade was manually closed in the broker platform. 1 lot was sold at the price 0.80201. The profit was 20.5.

[AUD/USD:CY:L1200] Stop 1@0.78713: -49.30 at 17:25

The trade was stopped out at 17:25 UTC. 1 lot was sold at the price 0.78713. The loss was 49.30.

[AUD/USD:CY:L1200] TP 1@0.7971: +55.70 at 17:25

The trade hit the profit target at 17:25 UTC. 1 lot was sold at the price 0.7971. The profit was 55.70.

[AUD/USD:CY:S4400] Trail 1@0.79414 Stop 0.80012

The stop loss moved to a different position, either diretly by script, or by trailing, or by setting an exit price limit, or by updating open trades when entering at a MaxLong/Short position limit. The new stop is 0.80012. In TICKS mode and in life trading, a trade can trail several times within a bar. A trail message is only printed when the stop loss has moved by more than 2 pips, therefore the real stop limit at the end of the bar be different than the last printed value. Smaller PIP values produce more trail messages.

Trade messages - futures, options, and combos

The following messages are generated when option or future contracts are entered, closed, or exercised. Note that displayed underlying prices are taken from the options history when available, and thus can sometimes slightly deviate from the unadjusted asset prices taken from the price history. Displayed premiums, unlike underlying prices that are always the ask price, do include the ask-bid spread.

[SPY:COT:LP0103] Buy 1 Put 20170118 210.0 100@1.16 Val 10 at 20:00

Algo COT buys a SPY put option with expiration at January 18, 2017 and strike price 210 at $116 premium (100 * 1.16). The Val element of the contract was 10.

[SPY:COT:SC0104] Write 1 Call 20170118 215.0 100@1.09 at 20:00

Algo COT sells short a SPY call option with expiration at January 18, 2017 and strike price 215 for $109 premium.

[SPY::SC0104] Place 1 Call 20170118 215.0 100@1.09 at 20:00

A SPY combo order places a short call (SC) with expiration at January 18, 2017 and strike price 215 for $109 premium. Only the last leg of the combo order really opens the position.

[SPY::LC0104] Buy 1 Call 20170118 215.0 %0@1.09 at 20:00

A SPY long call (LC) with expiration at January 18, 2017 and strike price 215 for $109 premium was ordered. The position was initially not filled (%0), but can be filled later when it was a GTC order.

[SPY::LP6633] Exercise 1 Put 212.0 100@209.84: +124$ at 19:00

Exercise a SPY put option with a strike price 212 at an underlying ask price of 209.84, and sell the underlying at market for a total win of $124.

[SPY::LC6634] Sell 1 Call 210.0 100@1.46: -22.00 at 19:00

Sell back a long SPY call option at $1.46, for a total loss of $22.

[SPY::SC6634] Cover 1 Call 210.0 100@1.46: +22.00 at 19:00

Buy back a short SPY call option at $1.46, for a total win of $22.

[SPY::LP5727] Expired OTM 1 Put 207.0 100@0.0: -190$ at 19:00

A long SPY put option expired out of the money. The $190 premium was lost.

[SPY::LC5131] Lapsed 1 Call 207.0 100@150.00: +50$ at 19:00

A long SPY call option position was found by contractCheck() to be externally closed on the account, by expiration, manual closing, being exercised, or similar events. A profit of $50 was assumed, based on the premium and the current underlying price 150.

[SPY::LC5728] Expired ITM 1 Call 207.0 100@209.50: +112$ at 19:00

A long SPY call option expired in the money at current underlying ask price 209.50. The underlying was sold at market. The total profit, minus the paid premium, was $112.

Other messages

Thursday 24.1.2012 --- Profit $880 ---

The daily profit or loss in relation to the previous day (if Verbose >= 2).

[AUD/USD:CY:S4400] +116$ s0.8733 c0.8729 e0.8731

Daily list of all currently open trades (if Verbose >= 3); also displayed on the status page. The trade made 116$ profit, based on a stop price of 0.8733, current price of 0.8729, and entry price of 0.8731.

Current DD: 2100$ (65%) in 11 days, CBI 23%

If Verbose >= 2: Depth and duration of the last or current equity drawdown in percent of the preceding equity peak; also displayed on the status page. CBI is the probability of the current drawdown based on the PnL curve from the last backtest. Note that the CBI is only valid when the backtest used the same trade volume as the live trading session, and no trades from a previous session have been resumed.

Market closed on 02.01.2017 23:00

It's weekend or holiday, either set up by the script or detected by the broker plugin. Zorro takes a break and suspends trading until the market opens again, which triggers a Market open... message at bar start. Weekends usually end on Sunday night GMT when the Sydney stock market opens. See BarMode, StartWeek, EndWeek.

Liquidation / Margin call in 2016 (Equity 12800$ Margin 13500$)

The current margin requirement exceeds the available account capital in the backtest (when the Capital variable is set). The trade is liquidated at market and the MARGINCALL status flag is set.

!ZDAS Exception - there is no tradeable price
[AUD/USD:CY:L] Can't open 1@0.82345 at 21:05:30

Messages that begin with an exclamation mark ("!") are information, errors, warnings, diagnostics, or other messages from the broker API (see Error Messages). A frequent message is about not being able to open or close a trade because no price quote is currently available or trading is temporarily suspended ("Instrument trading halted", "There is no tradeable price" etc.). This happens especially when assets are traded outside business hours. If a trade can not be closed, Zorro will attempt in regular intervals to close it again.

Error XXX ....
Warning XXX ...

Error messages normally indicate a bug in the script or a problem with the historical data. Warning messages indicate a potential problem. For details see Error Messages.

See also:

Bar, testing, trading, file formats, error messages, performance report

 

► latest version online login

login (int mode): int

Logs in or out to the broker API..

Parameters:

mode - 0 for checking the login state, 1 for logging in, 2 for logging out, 4 for completely closing the broker API.

Returns:

1 when Zorro is logged in, 0 otherwise.

Remarks:

Example:

 

See also:

Brokers, broker plugin

► latest version online LookBack, UnstablePeriod

LookBack

Number of bars that are executed before Zorro can begin to trade (default = 80). Required for most indicators or other functions that need a certain amount of previous price history for their calculations. If left at the default value, it is automatically adapted to the needed lookback period by subsequent indicators. Otherwise set it to a value that is sure to cover the longest period of all used indicators, assets, and time frames. Set it to 0 when the script needs no lookback period. The maximum lookback period for live trading is one year. Backtests can have any lookback period as long as it does not exceed the total number of bars. 

LookBackNeeded

Initially 0; automatically set by any indicator or function to the maximum lookback period needed so far (see example). If LookBack is at its default and only a single asset is used, LookBack is automatically set to LookBackNeeded. If LookBack is set to a value less than LookBackNeeded, an error message is printed and the script terminates if not restarted (see example). 

UnstablePeriod

Runs recursive TA-Lib indicators over the given period (default = 40) at any call. Background: Some indicators are influenced by an infinite number of past bars. They are recursive or cumulative - indicators "with memory". Examples are the EMA (Exponential Moving Average) and the ATR (Average True Range). They use their previous bar's value in their algorithm, which in turn use the value of their previous bars, and so forth. This way a given value will have influence on all the subsequent values. In contrast, a simple moving average (SMA) only reflects the average of its time period, without any influence from bars further in the past.
Because a price series is always finite and starts at a certain point, the effect of missing past bars is the more significant, the closer to the start a recursive indicator is calculated. Thus a trade strategy using the EMA or derived indicators (such as the MACD) could behave different dependent on its bar number. The UnstablePeriod value allows to strip off such initial unstable period and remove the influence of all bars prior to this period. This way indicators are guaranteed to behave the same way regardless of the amount of past data. For coming as close to the real cumulative formula as possible, strip off as much data as you can afford. Using 40 bars (default) is reasonable for most indicators.  

LookBackResolution

Candle resolution of the historical data that is loaded at trading start for filling the lookback period, in minutes. Also used for assetHistory. At 0 (default), the resolution is equivalent to BarPeriod. If BarOffset is active or .t1 data is used, the lookback resolution is accordingly increased. Set LookBackResolution to 1, 5, 15, 30, 60, 240, or 1440 for enforcing a higher or lower resolution of the lookback data. A higher lookback resolution can replicate the backtest better when tick functions are used, a lower resolution can help with long lookback periods when the broker history is limited.
 

TradesPerBar

Maximum number of trades and series per bar, or 0 (default) for automatically estimating the maxima. Determines the allowed total number of trades when multiplied with the assets and bars of the simulation. Opening more trades causes an Error 049 message, .more series cause Error 041 Set this to a higher value when the strategy needs to enter an unusual number of trades or create an unusual number of series, as for special systems such as grid trading, or for training an advise function. Set it to a low value, such as 1, for saving memory when the system does not trade more often than once per bar on average.

MinutesPerDay

Minimum daily trading time of the least traded asset in minutes (default = 360, i.e. 60 minutes * 6 hours). Internally used for determining the maximum lookback time. Set this to a lower value when Error 047 indicates that the lookback period gets not enough historical bars at trading start. This can be the case when an asset is only traded a few hours per day, or when you use a bar function that produces only a few bars per day.

Type:

int

Remarks:

Example:

// restart script when lookback period is exceeded
void run()
{
  LookBack = max(30,LookBackNeeded);
  ...
  SMA(seriesC(),50); // need more than 30 bars!
  if(LookBack < LookBackNeeded)
    return quit("+Restart with new LookBack");
  ...
}

See also:

Bars, BarPeriod, NumBars, TimeFrame, BarOffset, Trading Start, Date

 

► latest version online loop

loop(string Name1, string Name2, ... ) : string

loop(Assets) : string

Enumerate assets or algos for training their parameters or rules separately. The loop function gets a list of pointers, such as asset or algo name strings. In [Test] and [Trade] mode it returns the first pointer on the first call, the next pointer on the next call and so on. It returns 0 after the last pointer. In [Train] mode it returns the first pointer in the first component training cycle, the next pointer in the next cycle and so on. Component training cycles end after the last pointer.

of(string Name1, string Name2, ... ) : string

of(Assets) : string

Enumerate strings or pointers as above, but for general purposes and without the special behavior in [Train] mode.

Returns

Name1 on the first call or training cycle, Name2 on the next call or training cycle, and so on. The last call or cycle returns 0.

Parameters:

Name1, Name2 ...

Up to 40 pointers, all different, normally strings with an asset or algo name.

Assets

Predefined array with all asset names; used for enumerating all assets in the asset list.

 
The following variables are valid after a loop call (not used for of):

Loop1

Loop2

Current return value of the first and second loop, normally the current asset/algo name.

Itor1

Itor2

Current cycle number of the first and second loop, starting with 0.

NumLoops1

NumLoops2

Total number of cycles of the first and second loop.
 

Remarks:

Examples (see also Workshop 6):

// of() nesting
string Str1,Str2,Str3;
while(Str1 = of("AA","BB","CC"))
while(Str2 = of("XX","YY","ZZ"))
while(Str3 = of("11","22","33"))
  printf("\n %s %s %s",Str1,Str2,Str3);

// filling a string array
string Strings[5];
for(i=0; Strings[i]=of("A","B","C","D","E"); i++);
// portfolio strategy with 3 assets and 3 trade algos, 
// all components separately trained, using loop()
 
function tradeTrendLong()
{
  var MyParameter = optimize(...);
  ...
}
 
function tradeTrendShort()
{
  var MyParameter = optimize(...);
  ...
}

function tradeBollinger()
{
  var MyParameter = optimize(...);
  ...
}
 
function run()
{
  while(asset(loop("EUR/USD","USD/CHF","GBP/USD"))) // loop through 3 assets
  while(algo(loop("TRL","TRS","BOL"))) // and 3 different trade algorithms
  { 
    if(Algo == "TRL") tradeTrendLong();
    else if(Algo == "TRS") tradeTrendShort();
    else if(Algo == "BOL") tradeBollinger();   
  }
}
// portfolio strategy with 3 assets and 3 trade algos, 
// with common trained parameters, using for(used_assets)
 
var CommonParameter;

function tradeTrendLong()
{
  ...
}
 
function tradeTrendShort()
{
  ...
}

function tradeBollinger()
{
  ...
}
 
function run()
{
  asset("EUR/USD");
  asset("USD/CHF");
  asset("GBP/USD"); // select 3 assets
  
  CommonParameter = optimize(...);

  for(used_assets) // loop through the 3 assets
  {
    algo("TRL"); tradeTrendLong();
    algo("TRS"); tradeTrendShort()
    algo("BOL"); tradeBollinger()
  }
}
// portfolio strategy with 3 assets and 3 trade algos, 
// with a combination common and separately trained parameters, 
// using of()
 
var CommonParameter,MyParameter1,MyParameter2,MyParameter3;

function tradeTrendLong()
{
  // uses CommonParameter,MyParameter1;
  ...
}
 
function tradeTrendShort()
{
  // uses CommonParameter,MyParameter2;
  ...
}

function tradeBollinger()
{
  // uses CommonParameter,MyParameter3;
  ...
}
 
function run()
{
  ...
  asset("EUR/USD"); // select asset before optimize()

  CommonParameter = optimize(...);
  MyParameter1 = optimize(...);
  MyParameter2 = optimize(...);
  MyParameter3 = optimize(...);

  while(asset(of("EUR/USD","USD/CHF","GBP/USD"))) // loop through 3 assets
  while(algo(of("TRL","TRS","BOL"))) // and 3 different trade algorithms
  { 
    if(Algo == "TRL") tradeTrendLong();
    else if(Algo == "TRS") tradeTrendShort();
    else if(Algo == "BOL") tradeBollinger();   
  }
}

See also:

optimize, while, algo, asset, Assets

► latest version online

Margin, Risk, Lots, Capital

Trade parameters 3 - investment control

The trade size - the number of contracts or currency units purchased - can be determined in four different ways. For directly ordering a certain number of contracts, use Amount or Lots. For investing a certain sum of money, use Margin. For risking a certain sum of money by determining the worst case loss, use Risk.

Lots

Trade size given by an integer number of lots (default = 1; max = LotLimit). 1 lot is defined as the smallest possible order size of the selected asset. Thus, a lot can be a multiple or a fraction of a contract, determined by LotAmount. Since it's the minimum, the trade size can never be less than 1 lot (see remarks). In binary trading mode (BINARY flag) or if Margin is used for the trade size, Lots determines the minimum number of lots to be opened per trade (normally 1). If Lots is 0, no trades are opened.

Amount

Alternative fractional trade size in multiples or fractions of 100,000 units for currencies, and of 1 contract or 1 unit for anything else (default = 0 = trade size given by Lots). This number is independent of the broker's minimum order size and is similar to a 'MT4 lot' for currencies (see remarks). If a nonzero Amount amounts to less than one lot, 1 lot is opened.

Margin

Alternative trade size by invested margin, in units of the account currency (default = 0.00000001 for always opening at least 1 lot). With no leverage, Margin is simply the money invested to purchase the asset. On a leveraged account, Margin is a part of the trade size - for instance 1% at 1:100 leverage - deposited for opening a trade. As long as the trade is open, the margin amount is locked on the account and can not be used for further trades. If Margin is 0 or negative, no trades are opened. Otherwise the trade size is Margin divided by MarginCost.
  The Lots variable still determines the minimum number of lots to be opened per trade. If the trade size by Margin is smaller than Lots and the MARGINLIMIT flag is set, trades are skipped. If the ACCUMULATE flag is set, the size of skipped trades is accumulated until it reaches the Lots amount. Keep Margin at its default value for controlling the number of lots only with the Lots variable.

Risk

Alternative trade size given by the trade risk in units of the account currency (default = 0 = no risk limit). The risk is the theoretical maximum that a trade can lose; it is determined from trade size, stop loss distance, commission, and spread (formula: RiskPerLot = PIPCost /PIP * (Spread + abs(Stop-Price)) + CommissionPerLot; Margin = Risk/RiskPerLot * MarginCost). Since risk is undefined when a trade has no stop loss, the Risk parameter must always be given in combination with Stop. If the risk of a trade at the given Margin is higher than the Risk variable, the trade margin is accordingly reduced.
  When the RISKLIMIT flags is set, trades are skipped when even with a trade size of 1 lot the trade risk is still higher than twice the given Risk value. When Margin is not set, the trade size is only limited by Risk; this can lead to extreme trade sizes and subsequent margin calls when the Stop distance is tight. Due to minimum lot amounts and/or deviations of entry price and current price, the real trade risk displayed in the trade log can deviate from the set up Risk value, especially with tight Stop distances.  

Capital

Initial invested capital in units of the account currency (default = 0 = no initial capital). This has no direct effect on trading, but on calculating the strategy performance in the simulation. Set this to the initial capital when the strategy reinvests profits; Zorro then calculates CAGR instead of AR/ROI and determines performance parameters from the initial invested capital instead of the required capital. If the free capital (Capital minus loss minus total margin on leveraged accounts) becomes negative, Zorro will set the MARGINCALL flag and liquidate trades for reducing the margin. Make sure to set Capital well above the required capital on leveraged accounts, and to reinvest in a way that margin calls are avoided. Setting Capital to a negative amount assumes temporary remargining and disables trade liquidation.

ScholzBrake

Global loss limit in units of the account currency (default = 0 = no loss limit). This limit is required due to a special 2021 German tax law invented by finance minister Olaf Scholz. Taxes are now due for all winning trades, while for losing trades only EUR 10,000 per year can be deducted. Set ScholzBrake to the maximum allowed loss at session start and at the begin of any year. All further losses will then decrease it until it reaches zero. Trades are then suspended until ScholzBrake is set again. In [Trade] mode the ScholzBrake variable is decreased by all losses of Zorro instances that have set it and are trading on real accounts on the same PC. It is updated once per bar and at any trade. See also LossGlobal and Scholz tax.

Type:

var
 

MaxLong

MaxShort

Maximum number of open and pending long or short trades with the same asset and algo. If the limit amount is reached in [Test] or [Trade] mode, enter calls do not enter more trades, but they still close reverse positions and update the stop limits, profit targets, and life times of open trades to the values that the new trade would have. If set to a negative number, open trades are not updated. The limits are ignored in [Train] mode. 

Type:

int
 

Remarks:

Examples:

// set margin from slider
Margin = slider(1,500,0,2000,"Margin","Average margin in $");

// activate the Scholz Brake
if(is(INITRUN) || year(0) != year(1))
  ScholzBrake = 9900;

See also:

enterLong/Short, Stop, Spread, PIP, PIPCost, MarginCost, Win/LossPayout, TradeMode

 

► latest version online Markowitz Mean/Variance Optimization

Mean/Variance Optimization

markowitz (var* Covariances, var* Means, int N, var Cap) : var

markowitz (var* Covariances, var* Means, int N, var* Caps) : var

Performs a mean / variance optimization (MVO) using the algorithm from the 1959 publication Portfolio Selection: Efficient Diversification of Investments by Harry M. Markowitz; also referred to as modern portfolio theory (MPT). The fucntion calculates the optimal distribution of capital among a portfolio of assets or algorithms for achieving a given total return or variance. The algorithm starts with assigning all capital to the component with the highest return. It then adds or removes more components step by step, calculating the optimal return/variance combination on any step.
 
The efficient frontier is a line consisting of corner points in a return over variance plot. At any corner point the portfolio composition changes by adding or removing one or several components. Between the corner points the included components don’t change, only their capital allocation changes linearly. Connecting all corner points with lines establishes the efficient frontier with the maximum return for any given variance (see image).
 

Efficient Frontier
Efficient frontier of an ETF portfolio, x axis = variance, y axis = annual return in %

Parameters:

Covariances A var[N*N] array containing the covariance matrix of the component returns.
Means A var[N] array containing the mean of the component returns.
N Number of components, at least 3.
Cap Weight cap in 0..1 range; soft weight limit of a single asset at the minimum variance point, or 0 for no weight cap.
Caps A var[N] array containing individual weight limits in 0..1 range, or 0 for no weight cap.

Returns

Variance at the efficient frontier point with the best Sharpe Ratio (i.e. return divided by standard deviation), or 0 if the efficient frontier could not be calculated. The efficient frontier line segments are internally stored for subsequent markowitzReturn or markowitzVariance calls.

 

markowitzReturn (var* Weights, var Variance) : var

Calculates the return and the optimal capital allocation weights for a given variance at a previously calculated efficient frontier. The markowitz() function must be called before for calculating the frontier.

markowitzVariance (var* Weights, var Return) : var

Calculates the variance and the optimal capital allocation for a given return at a previously calculated efficient frontier. The markowitz() function must be called before for calculating the frontier.

Parameters:

Weights A var[N] array to be filled with the capital allocation weights of the N portfolio components. The sum of the weights is 1. Pass 0 when no weights are needed.
Variance The desired variance. When below the lowest variance, the return and weights at the left end of the efficient frontier are returned.
Return The desired return. When below the lowest return of the efficient frontier, the variance and weights at the left end of the efficient frontier are returned.

Returns

The optimal portfolio return at the given variance, or vice versa..

Modifies

Weights - set to the capital allocation weights.
 

Remarks:

Example:

#define N    10  // 10 assets
#define DAYS 252 // 1 year
 
vars Returns[N];
var  Means[N];
var  Covariances[N][N];
 
function run()
{
  BarPeriod = 1440;
  StartDate = 20150101;
  LookBack = DAYS;
  
  string Name;
  int n = 0;
  while(Name = loop(.../*list of some assets*/ ... )) 
  {
    asset(Name);    
    Returns[n] = series((price(0)-price(1))/price(1));
    Means[n] = SMA(Returns[n],DAYS);
    n++;
  }
  
  int i,j;
  if(!is(LOOKBACK))
  {
// generate covariance matrix
    for(i=0; i<N; i++)
      for(j=0; j<N; j++)
        Covariances[i][j] = Covariance(Returns[i],Returns[j],DAYS);

// calculate efficient frontier
    var OptimalV = markowitz(Covariances,Means,N);
    printf("\nBest daily return %.3f %% at variance %.4f",
      100*markowitzReturn(0,OptimalV),OptimalV);

// plot the frontier
    for(i=1; i<70; i++) {
      var VStep = i*OptimalV*0.03;
      var Return = markowitzReturn(0,VStep);
      plotBar("Frontier",i,VStep,Return,LINE|LBL2,BLACK);
    }
    plotGraph("Max Sharpe",1./0.03,markowitzReturn(0,OptimalV),SQUARE,RED);
    PlotScale = 6;
    PlotHeight1 = 300;    
    quit("");
  }
}

See also:

OptimalF, Covariance, distribute, knapsack

? latest version online Matrix functions

Vector and matrix functions

The following functions can be used for vector and 2-dimensional matrix algebra.

matrix(int rows, int cols): mat

Creates a vector or a matrix with the given number of rows and columns. The matrix is initialized to zeros. If cols is omitted, an identity matrix with 1s in the diagonal is created.

Parameters:

rows - number of rows of the matrix, or 1 for defining a row vector.
cols - number of columns, or 1 for defining a column vector. Omit this for a square identity matrix.

me(mat X, int row, int col): var

Macro for accessing an element of the matrix X at a given row and column, with no range check.

ve(mat V, int n): var

Macro for accessing the n-th element of the row or column vector V, with no range check.

Parameters:

X, V - matrix to be accessed.
row - row number of the element, starting with 0.
col - column number of the element, starting with 0.

matSet(mat X, mat A): mat

Copies matrix A to matrix X, and returns X. A and X must have the same number of rows and columns.

Parameters:

X - destination matrix or vector
A - source matrix or vector

matSet(mat X, int row, int col, mat A): mat

Copies matrix A to a sub-range of matrix X that starts at the given row and colunm, and returns X. A and X can have different numbers of rows and columns.

Parameters:

X - destination matrix
A - source matrix
row - row number of the block, starting with 0.
col - column number of the block.

matSet(mat X, var c): mat

Sets all elements of matrix X to the value c, and returns X.

Parameters:

X - destination matrix.
c - value to be set.

matTrans(mat X, mat A): mat

Copies the transpose of A to X, and returns X. The number of columns of X must be identical to the number of rows of A, and vice versa.

Parameters:

X - destination matrix.
A - source matrix.

matAdd(mat X, mat A, mat B): mat

Adds A and B, stores the result in X, and returns X. A, B, and X must have the same number of rows and columns.

Parameters:

X - destination matrix.
A,B - source matrices.

matSub(mat X, mat A, mat B): mat

Subtracts B from A, stores the result in X, and returns X. A, B, and X must have the same number of rows and columns.

Parameters:

X - destination matrix.
A,B - source matrices.

matMul(mat X, mat A, mat B): mat

Multiplies A with B, stores the result in X, and returns X. X and A must have the same number of rows, X and B must have the same number of columns, and the B number of rows must be identical to the A number of columns.

Parameters:

X - destination matrix.
A,B - source matrices.

matScale(mat X, var c): mat

Multiplies all elements of X with c, and returns X.

Parameters:

X - destination matrix.
c - multiplication factor.
 

matSaveCSV(mat X, string FileName)

Saves the matrix X to a CSV file for further evaluation with Excel or other programs.

Parameters:

X - source matrix.
FileName - CSV file name. 
 

Remarks:

Example:

void matPrintf(mat X)
{
  int i,j;
  printf("\n");
  for(i=0; i < X->rows; i++) {
    for(j=0; j < X->cols; j++)
      printf("%.0f ",me(X,i,j));
    printf("\n");
  }
}

void main()
{
  int i,j;
  mat A = matrix(3,4);
  for(i=0; i < A->rows; i++)
    for(j=0; j < A->cols; j++)
      me(A,i,j) = i+j;
  matPrintf(A);

  mat B = matTrans(matrix(A->cols,A->rows),A);
  matPrintf(B);

  mat C = matrix(B->rows,A->cols);
  matMul(C,B,A);
  matPrintf(C);
  
  mat D = matrix(8);
  matSet(D,4,4,C);
  matPrintf(D);
}

See also:

series

► latest version online memory

memory(int mode): size_t

Returns the current memory consumption of the script.

Parameters:

mode -
   0:
Current total memory allocation in bytes, including virtual memory and cache pages that have been written to.
   1: Sum of all memory allocations in bytes by functions, price data, and series during the script run.
   2: Current number of allocated memory areas by functions, price data, and series.
   4: Memory allocation in bytes per asset for historical price data.

Returns:

See mode.

Remarks:

Example:

function run()
{ 
  BarPeriod = 1;
  LookBack = 1;
  ...
  printf("\rMemory %i areas %i kb",memory(2),memory(0)/1024);
  plot("Memory",memory(0),NEW,RED);
}

See Also:

timer, malloc, zalloc

► latest version online

min, max

min(int x, int y): int

min(var x, var y): var

max(int x, int y): int

max(var x, var y): var

Returns the minimum or maximum of x and y.

Parameters:

x,y - any var or int.

Returns:

The minimum or maximum of x and y.

Remarks:

The returned variable type depends on the type of the arguments; for constants use a decimal point to mark them as var (f.i. 1 is int, but 1. is var).

Example:

var x = max(0.,y); 

See also:

abs, between, ifelse, MinVal, MaxVal

► latest version online

Mode

System Flags

Zorro's behavior can be set up through system flags - that are internal "switches" that can be either set (activated) or reset (deactivated). By default, they are normally off. Flags that affect the bar generation, such as TICKS, must be set in the initial run at start of the script before selecting any assets. The other flags can be set or reset at any point. Some flags are status flags and can be read only. Flags are set, reset, or read with the following functions:

set (int Flag1, int Flag2..., int Flag10)

Sets or resets the given system flags (up to 10). A flags is reset by adding |OFF to the flag name. For instance, set(PARAMETERS,TICKS|OFF); sets the PARAMETERS flag and resets the TICKS flag.

is (int Flag): int

Returns nonzero (or true) when the given system flag is set, otherwise zero or false

The main system flags:

SKIP1

SKIP2

SKIP3

Do not enter trades in the first, second, or third of every 3 weeks of the historical price data (the period can be set up with DataSkip). These flags can be used for out-of-sample testing by separating test data from training data while still covering the same time period. These flags must be set in the INITRUN.
 

PEEK

Allow peeking in the future through negative price function offsets; [Test] and [Train] mode only. Sometimes required for advise functions and price difference analysis.

NOLOCK

Don't synchronize the API and file accesses of multiple trading Zorro instances, to prevent that they interfere with each other. Set this flag for speeding up API access when the trading Zorros don't share the broker API, or when the broker API allows simultaneous sccesses from multiple sources (MT4/MT5 does not). This flag must be set in the INITRUN.

NOWATCH

Ignore all watch statements. Can be set for trading a script that is full of watches.

NOSHIFT

Do not shift dynamic series and do not change their first element on series() calls. Useful for shifting series only under special conditions, f.i. for emulating special bars or for not updating indicators outside market hours.

NOFACTORS

Don't calculate OptimalF factors in test and training. Useful when OptimalF calculation takes a long time due to a large number of trades. For training script-generated factors with a user-defined algorithm, set both FACTORS and NOFACTORS flags and set OptimalF, OptimalFLong, OptimalFShort to a script calculated value in the FACCYCLE training run (if(is(FACCYCLE)) ...). 

COMMONSTART

Delay the simulation until price history of all assets is available. Otherwise the simulation starts either at the StartDate or at the history begin of the first asset, whichever is later, and missing price history of other assets produce an Error 055 message. This flag must be set in the INITRUN.

OPENEND

Do not close the remaining open trades at the end of a simulation cycle; leave them open and ignore their results. This has two purposes. In [Test] mode it eliminates the effect of prematurely closed trades on the performance report. In [Train] mode it prevents that parameters or rules are affected by the results of those trades.

ALLCYCLES

Do not reset the statistics values inside the STATUS structs when a new sample cycle is started. The Long/Short statistics and the portfolio analysis are then the result of all sample cycles so far. Print any sample cycle to the log; otherwise only the last cycle is logged. This flag is always set in [Train] mode. This flag must be set in the INITRUN.
 

TICKS

Tick-precise intrabar simulation. This flag must be set in the INITRUN. If it is set, not only the Open, Close, High, and Low, but also the price curve inside any bar is evaluated for triggering entry, stop, trail, or takeprofit limits. TMFs are run on every tick in the simulation, instead of only once per bar. This flag gives a more accurate simulation result, but also requires more time for a simulation cycle, and allocates more memory.
  If this flag is not set, an intra-bar approximation is used for simulating entry and exit. In this approximation, a stop loss is always triggered earlier than a profit target, resulting in a slightly pessimistic simulation. Trades closed by TMFs are sold at the open price of the next bar. This less accurate simulation is sufficient in most cases, but TICKS should be used when trades enter and exit at the same bar, when stop loss, or takeprofit distances are small, or when trading is strongly affected by tick functions or TMFs.

LEAN

Use compressed historical data. marketVal and marketVol are not available, .t1 data is stored without bid prices, and the open/close price of a bar is approximated by the center point of its first and last tick (except for EOD historical data). This flag can reduce memory requirement when market volume and candles are not needed by the strategy, and the historical data has a much higher resolution than one bar period, f.i. M1 data with 1-hour bars. This flag reduces the memory requirement for backtests by 30%. It must be set in the INITRUN before calling asset().

LEANER

Use only the Close from historical price data; disregard Open, High, Low, and Mean. Indicators that require full candles, such as ATR or TypPrice, can not be used. This flag is useful for strategies with hundreds or thousands of stocks, and reduces in combination with the LEAN flag the memory requirement by 50%. It must be set in the INITRUN before calling asset().

RECALCULATE

Run a full LookBack period at the begin of every WFO cycle in [Test] mode. Discard the content of all series at lookback start, and recalculate them from the parameters and rules of the new cycle. Otherwise the series keep their values from the previous test cycle. RECALCULATE increases the test time, but simulates the start behavior of a new trading session a bit more realistically. This flag must be set in the INITRUN. See also DataHorizon.

PRELOAD

In [Trade] mode, use Zorro's historical price data for the LookBack period, rather than loading price data from the broker's price server or from external data sources. If historical data is not sufficient for filling the complete lookback period, the remaining data between the end of the history and the current time is still loaded from the broker or data source. This flag is useful for reducing the start time of a system, for overcoming history limitations of broker servers, or for very long lookback periods. Recent price history files from the current and the last year must be available in sufficient resolution and preferably from the same broker or data source. LookBackResolution can be used to enforce the same resolution for pre-loaded and live historical data. Use the Download script or the assetHistory function for getting the most recent data. This flag must be set in the INITRUN.
  In [Test] mode this flag suppresses the warning message when the lookback period is too long for a normal trading session. It also causes historical data to be reloaded again for any simulation cycle, which allows cycles with different bar periods or with shuffled or otherwise manipulated price data for generating reality check histograms.
 

PARAMETERS

[Train] mode: generate strategy parameters with optimize calls and store them in Data/*.par. This flag must be set before calling optimize. If this flag is not set, parameters are not generated in the training run, but still loaded from previously generated *.par files.
[Test] / [Trade] mode: load optimized parameters. If this flag is not set, the default parameters from the optimize calls are used.

FACTORS

[Train] mode: generate OptimalF capital allocation factors and store them in Data/*.fac. [Test] / [Trade] mode: load OptimalF factors for allocating capital. If this flag is not set, all OptimalF factors are 1.
OptimalF factors are normally generated from the whole backtest period. For generating them individually per WFO cycle, use the ALLCYCLES training mode.

RULES

[Train] mode: use the advise machine learning functions to generate trade rules or machine learning models, and store them in Data/*.c or Data/*.ml. This flag must be set before calling advise. Otherwise rules or models are not generated, but loaded from previously generated *.c or *.ml files.
[Test] / [Trade] mode: load trade rules or machine learnign models and use them in the advise functions. If this flag is not set, the advise functions always return the value 100.

VCPP

Use the Visual Studio C++ compiler, rather than the lite-C compiler, for compiling trade rules with the RULES flag. Default for the 64-bit version.
 

MARGINLIMIT

Don't enter a trade when even the minimum amount of 1 Lot exceeds twice the used Margin value, or when the trade margin plus the trade risk exceeds the account balance. Trades skipped due to too-high risk or too-low balance are indicated in the log. This flag has no effect in training mode or for phantom trades that are always executed regardless of the margin.

RISKLIMIT

Don't enter a trade when even with the minimum amount of 1 Lot, the trade risk is still higher than twice the than the allowed Risk. Also don't enter a new trade in [Trade] mode when the total risk of all open trades exceeds the available margin left in the account. Setting this flag can reduce profit, as trades with a high stop loss distance are often profitable trades. Trades skipped due to too-high risk or too-low account are indicated in the log. This flag has no effect in training mode or for phantom trades that are always executed regardless of the risk.

ACCUMULATE

Accumulate the Margin of trades skipped by MARGINLIMIT or RISKLIMIT, until the accumulated margin is high enough to overcome the limits. The trade is then executed and the accumulated margin is reset. This allows trading - although less frequently - with very small capital that would otherwise result in trade sizes of less than one lot. This flag has no effect in training mode.
 

BINARY

Simulate binary options for training and testing. This flag must be set in the INITRUN. In binary mode the trade profit is not proportional to the price difference between entry and exit, but determined by the WinPayout or LossPayout of the selected asset. The stake can be set with the Margin variable for the backtest; in live trading the stake is set with the Lots variable or with an order comment, dependent on the broker. Rollover and commission are ignored for binary trades. Spread is 0 by default in binary mode, but can be set to a nonzero value for simulating an additional disadvantage. The trade time can be set through ExitTime. Stop loss and profit targets are normally not set for binary trades, but can be used for betting on negative or positive excursions in special binary modes. Hedging should be enabled by Hedge = 2 for preventing the premature close of opposite trades.

NFA

Activate NFA Rule 2-43(b) compliant trading. This is required for most US based brokers, such as IB. Zorro handles NFA compliant trading in a transparent way, so a strategy normally needs not be modified for NFA compliance. When the NFA flag is set, the following NFA restrictions are observed and worked around:

The NFA flag must be set in the INITRUN. It is automatically set when the selected account has a nonzero NFA parameter in the account list. Do not set this flag for non-NFA compliant accounts. You can find out if your account is NFA compliant by manually opening a long and short position of the same asset in the broker platform. If two positions are then really open, your account is not NFA compliant and you must not set the NFA flag. If the short position cancels the long one, your account is NFA compliant and the NFA flag must be set. MT4/5 accounts are normally not NFA compliant even when the broker is located in the US; but they can be FIFO compliant and require exiting trades in the order of their entry.
 

EXE

Do not run the simulation, but compile the script to an executable in .x file format. Executable scripts start faster and can be used for distributing strategies without revealing the source code. If a .c and a .x script with the same name are found in the Strategy folder, the .c script has priority. Zorro S required. The EXE flag must be set in the INITRUN; it can also be set by command line or with the [Action] scrollbox.

STEPWISE

Do not run, but single step through the simulation in [Test] mode. A click on [Step] moves one bar forward. [Skip] moves to the next opening or closing a position. The current chart and trade status will be displayed on every step in a browser window. For details see debugging.

STRAIGHT

Don't correct or check the lookback period and switch the ta-lib indicators from time-descending series mode to time-ascending array mode. In this mode they accept any data array of sufficient length and can be used even when series are disabled.
 

LOGFILE

Generate and export logs and statistics files in the Log and Data folders, dependent on [Test], [Train], or [Trade] mode. This flag affects the backtest speed.It is always set in [Trade] mode. When LogNumber is set, the number is appended to the filenames, which allows different log files from the same script for comparing or appending. This flag must be set in the INITRUN.

EXPORTED

Export charts and training curves in the Log and Data folders, dependent on [Test], [Train], or [Trade] mode (see export). This flag must be set in the INITRUN.

BALANCE

Use balance rather than equity for the profit curve in the chart, for the monthly profit table in the performance report, and for the values in the exported profit/loss curves. This flag must be set in the INITRUN.

PIPRETURN

Use volume-independent returns in pip units for the profit curve in the chart, for the monthly profit table in the performance report, and for the values in the exported profit/loss curve. This flag must be set in the INITRUN.

MAECAPITAL

Do not calculate the required capital, annual return, and the 'underwater profile' on the chart from maximum drawdown, but from maximum adverse excursion (see performance report for the difference). This is for comparing with results of other platforms and normally produces slightly higher required capital. This flag must be set in the INITRUN.
 

TESTNOW

Run a test immediately after training, without clicking [Test]. Only when multiple cores are not used and when test and training use similar mode flags. If the simulation is repeated multiple times (NumTotalCycles), TESTNOW causes the price curve to be generated anew at the begin of every cycle, which is useful for testing different bar offsets or detrend modes. Since the test run uses the settings from the previous training, its result can differ from a normal test. This flag must be set before calling asset(). This flag must be set in the INITRUN.

PLOTNOW

Plot a chart immediately after testing, without clicking [Result]. Automatically set when the script plots a histogram with plotBar. This flag must be set in the INITRUN.

 

Remarks

Example:

function run()
{
  if(Train) set(SKIP3); // leave every 3rd week for the test
  if(Test) set(SKIP1,SKIP2,TICKS,FAST,LOGFILE); 
  ...
}

See also:

DataSkip, Status flags, setf, Zorro.ini

 

► latest version online modf

modf(var x, var* p): var

Splits x in a fractional and integer part, each of which has the same sign as x.

Parameters:

x - any number.
p - var pointer, or 0 if the integer part is not needed.

Returns:

Fractional part of x. Integer part is stored in *p.

Speed:

Fast

Example:

var y;
var x = modf(123.456,&y); // x = 0.456 and y = 123.0 

See also:

floor, ceil, fmod, %

► latest version online

Monte Carlo simulation

Monte Carlo Analysis

"Monte Carlo" is a common term for an analysis based on randomized data or random samples. Zorro can run a Monte Carlo analysis for its own strategies, as well as for trade lists that are generated by other software and stored in .csv files. The Monte Carlo method improves the performance analysis. It generates not a single sequence of trades and a single equity curve, but a distribution of many possible curves. Compared to standard simulation, the Monte Carlo method produces results that are more accurate and less subject to random fluctuations.

When using Monte Carlo analysis, the equity curve from the backtest is randomly resampled many times. This generates many different equity curves that each represent a different order of trades and different price movements inside each trade. The curves are analyzed, and their results are sorted according to their performance. This way a confidence level is assigned to every result. The confidence level determines how many of the curves had the same or better performance than the given result.

Without Monte Carlo analysis, the annual rate of return is calculated from the backtest equity curve. For example, it might be found that the annual return over the curve was 200%. With Monte Carlo analysis, hundreds or thousands of different equity curves are analyzed, and the annual return determined from them might be, for instance, 145% with 95% confidence. This means that of all the thousands of possible outcomes of the simulation, 95% had annual return rates better than or equal to 145%.

Monte Carlo analysis is particularly helpful in estimating the risk and capital requirement of a strategy. The maximum historical drawdown is normally used as a measure of risk, but this means we're basing our risk calculations on a historical price curve that won't repeat exactly. Even if the statistical distribution of trades is the same in the future, the sequence of those trades and their equity movements are largely a matter of chance. Calculating the drawdown based on one particular sequence is thus somewhat arbitrary: with a sequence of several losses in a row, you can get a very large drawdown. But the same trades arranged in a different order, such that the losses are evenly dispersed, might produce a negligible drawdown. This randomness in the result can be eliminated by Monte Carlo analysis that takes many different equity curves and many different trade sequences into account.

Example of the result distribution in the performance report:

Confidence level     AR   DDMax  Capital
 
 10%                236%  1440$  1390$
 20%                227%  1550$  1470$
 30%                218%  1680$  1570$
 40%                209%  1830$  1680$
 50%                202%  1940$  1760$
 60%                193%  2140$  1900$
 70%                186%  2340$  2040$
 80%                174%  2730$  2320$
 90%                165%  3080$  2580$
 95%                145%  4010$  3580$
100%                104%  5640$  4710$

The first column identifies the confidence level; in the next columns the annual return, the maximum drawdown, and the required capital at that level are listed. The most likely result is at 50% confidence level (here, 202% annual return). This means that half the simulations had this or a better result, and half a worse result. The higher the confidence level, the more pessimistic are the results. The result at 100% confidence level is the worst of all simulations; thus the probability to get this (or an even worse) result is 1/n, where n is the number of simulations. Note that the capital can be smaller than the maximum drawdown when the test period was longer than 3 years (capital and annual return are based on a 3-years normalized drawdown).

The following variables are used for Monte Carlo simulation:

MonteCarlo

Number of simulations for Monte Carlo analysis (default: 200). Every simulation generates a different equity curve. The more simulations, the more accurate is the result, but the more time is needed for the computation. If set to 0, no Monte Carlo simulation is performed.

Confidence

Confidence level for the performance analysis in percent (0..100); determines the calculation of the main performance parameters such as annual return, drawdown, required capital, etc. The higher the confidence level, the more pessimistic are the results. At 0 (default) the performance parameters are calculated from the real equity curve and not from the Monte Carlo simulation.

Remarks

Examples

function run()
{
  MonteCarlo = 1000; // 1000 simulations
  Confidence = 75; // 75% confidence level
  ...

See also:

Testing, Win/Loss, Performance

 

► latest version online year, month, week, day, dow, hour, minute

Time and Calendar Functions

year (int offset): int

Year number of the given bar, f.i. 2011.

month (int offset): int

Month number of the given bar, 1..12.

week (int offset): int

Week number of the given bar, 0..53, with Monday as first day of week.

day (int offset): int

Day of month of the given bar, 1..31.

dom (int offset): int

Number of days of the month of the given bar, 28..31.

tdm (int offset): int

Trading day of the month of the given bar, 1..23. Uses BarZone for determining Weekends and Holidays (see BarMode) when counting trading days.

tom (int offset): int

Total number of trading days of the month of the given bar, 20..23. (tom(offset)-tdm(offset)) gives the days until the last trading day of the month. 

dow (int offset): int

Day of week of the given bar, MONDAY (1), TUESDAY (2) ... SUNDAY (7).

ndow (int offset): int

Number of the current day of week in the current month; to be used f.i. for finding the first Monday (1), the third Friday (3), etc.

ldow (int zone,int offset): int

Local day of week of the given bar in the given time zone (see timeOffset below), considering daylight saving time (see dst below). 

hour (int offset): int

Closing hour of the given bar, 0..23, with 1 second precision. hour(0) gives the current hour and can be used to apply different trade tactics in the US, European, or Asia-Pacific session.

lhour (int zone, int offset): int

Closing local hour of the given bar in the given time zone (see timeOffset below), considering daylight saving time (see dst below). F.i. lhour(EST) returns the current hour at the New York Stock Exchange. 

minute (int offset): int

Closing minute of the given bar, 0..59, with 1 ms precision. minute(0) gives the current minute.

tod (int offset): int

Time of day of the given bar in the form hhmm, f.i. 2107 for 9:07 pm. A time in hhmm can be converted to minutes with int MinuteOfDay = ((tod(0)/100)*60 + (tod(0)%100);.

ltod (int zone, int offset): int

Local time of day of the given bar in the form hhmm.

tow (int offset): int

Time of week of the given bar in the form dhhmm. d is the weekday number starting with Monday(1), f.i. 52107 = Friday 9:07 pm. See also StartWeek, EndWeek.

ltow (int zone, int offset): int

Local time of week of the given bar in the form dhhmm.

second (): var

Closing second of the current bar. The decimals give the fraction of the second with 1 ms precision. The current second number of the day can be calculated with var SecondOfDay = modf(wdate(0),0)*24*60*60;.

second (var Time): var

Second of the given Time value in Windows DATE format. The decimals give the fraction of the second with 1 ms precision. The returned value can be different to the seconds printed by strdate, which are rounded to the next full second. 
 

minutesAgo (int bar): int

Total number of minutes between the end of the bar with the given offset and the end of the current bar.

minutesWithin (int bar): var

Total number of minutes from start to end of the bar with the given offset. Normally identical to BarPeriod, but can be different due to weekends, holidays, daylight saving time, gaps due to lack of price quotes, or when called from an intrabar function.
 

workday (int offset): bool

Returns true when the given date or the end of the given bar is not a holiday or a weekend determined by Holidays, StartWeek, and EndWeek.

market (int zone, int offset): bool

Returns true when the end of the given bar is within market hours given by StartMarket and EndMarket (see also suspended()).

at (int hhmm): bool

Returns true when the given time in hhmm format is at the end or inside the current bar in the BarZone.

dst (int zone, int offset): int

Returns 1 when daylight saving time is active at the end of the given bar, otherwise 0. A simplified daylight saving schedule is used, based on US east coast daylight saving periods for all time zones in America, Sydney daylight saving periods for Australian and Pacific time zones, and European daylight saving periods for all other time zones. UTC and JST time zones have no daylight saving time. For years between 2000 and 2024 the real historical time switching dates are used, for all other years estimated dates from an average of the historical dates.
 

Parameters:

bar Offset of the bar for which the date or time is returned; 0 for the current bar, 1 for the previous bar and so on. When multiple time frames are used, bar offsets are multiplied with TimeFrame.
offset Offset of the bar for which the date or time is returned; or NOW for the current PC time; or a day number in DATE format with no decimals.
zone UTC for UTC/GMT time, WET for London, CET for Frankfurt, EST for New York, CST for Chicago, JST for Tokyo, AEST for Sydney, or the zone offset in hours, from -12...+12 (with daylight saving) or from UTC-12..UTC+12 (without daylight saving). For the daylight saving period, US periods are used for time zones -4 or below, Australian periods are used for time zones 10, 11, 12, and European periods for all other time zones. Note that JST is defined as UTC+9 since Japan uses no daylight saving.

Usage:

hour(1) returns the hour of the previous bar.

 

wdate (int offset): var

Returns UTC date and time of the given bar offset in Windows DATE format. 

ldate (int zone, int offset): var

Returns date and time in the given time zone of the given bar offset in DATE format. 

wdateBar (int n): var

Returns UTC date and time of the bar with the given bar number (0...NumBars-1) in DATE format.

wdatef (string Format, string DateTime): var

Returns date and time in DATE format of the given DateTime string. Can be used to parse date/time fields in CSV files in various formats. Seconds are read with decimals.

strdate (string Format, int offset): string

strdate (string Format, int zone, int offset): string

strdate (string Format, var Date): string

Returns a temporary string in the given Format with the date and time of the given bar offset or DATE variable; can be used in printf statements. Predefined formats are YMD, HM, HMS, YMDHMS. If the Format string ends with "%S.", milliseconds are added; otherwise seconds are rounded to the next full second. Examples: strdate(YMD,0) returns a string with the date of the current bar, and strdate("%y-%m-%d %H:%M:%S.",NOW) returns the current date and time in millisecond precision. 

nthDay(int Year, int Month, int Dow, int N): var

Returns the Windows DATE format of the Nth given weekday of the given month. For instance, nthDay(2016,11,FRIDAY,3) returns the date of the third friday in November 2016. Source code in contract.c, which must be included for this function.

dmy (int YYYYMMDD): var

ymd (var Date): int

Convert a date in the YYYYMMDD format to the Windows DATE format and vice versa. For instance, ymd(wdate(NOW) - 10) returns the date from 10 days ago in the YYYYMMDD format.

utm (var Date): int

mtu (int UnixTime): var

Convert a date in Windows DATE format to Unix time in seconds since 1-1-1970, resp. from Unix time to DATE. Unix time in milliseconds can be converted to seconds by dividing by 1000.

Parameters:

Format

Format string, see format codes. Some often used format strings are predefined in variables.h: YMD = "%Y%m%d";   HMS = "%H:%M:%S";    YMDHMS = "%y%m%d %H:%M:%S".

offset Bar offset, 0 for the current bar, 1 for the previous bar and so on, or NOW for the current time (see remarks).
YYYYMMDD Date as integer, f.i. 20180403 for April 3, 2018.
Date Date/time in Windows DATE format, as returned from wdate(). Days are represented by whole number increments starting on Saturday, 30 December 1899, midnight UTC. The time of the day is represented in the fractional part of the number. This format allows easy and precise date comparison down to a microsecond (= 0.000001/(24*60*60)).
DateTime Date/time string in various formats, see format codes.

Usage:

printf("\nDate: %s",strdate(YMD,0)); prints the current bar date in YYYYMMDD format. wdatef("%d.%m.%y","15.9.16") returns the DATE value of September 15, 2016.

 

timeOffset (int zone, int days, int hour, int minute): int

Returns the offset (in bars) of the last bar closing at the given time, or a negative offset for a bar in the future.

Parameters:

zone UTC for UTC time, ET for New York, WET for London, CET for Frankfurt, AEST for Sydney, JST for Tokyo, or a number giving the zone offset in hours to UTC. Daylight saving time is considered in non-UTC zones from 2000 to 2024.
days Number of days in the past, or 0 for the day of the current bar, or a negative number for days in the future.
hour Hour of the day, 0..23. If above 23, days is adjusted accordingly.
minute Minute of the hour, 0..59. If above 59, hour is adjusted accordingly.

Usage:

timeOffset(ET,1,16,0) returns the bar offset of yesterday's NYSE closing time. timeOffset(ET,0,9,30) returns 0 when the NYSE is just opening. if(0 == timeOffset(0,0,TIME/100,TIME%100)) is fulfilled when the current time is at the HHMM value given by TIME

 

Remarks:

Examples:

// buy at UTC midday on the last day of the month, 
// sell 2 trading days later
if(NumOpenLong == 0) { // nothing yet bought this month
  if((tom(0) == tdm(0)) && hour() >= 12)
    enterLong();
} else if(tdm(0) >= 2)
    exitLong();

// print New York local time to the log
printf(strdate("# NY: %H:%M",EST,0)); // close all position at 16:15 local time if(at(1615)) { printf("\nClosing all positions"); exitLong("**"); exitShort("**"); } // convert a time stamp from EST to UTC TimeStamp -= (EST+dst(EST,TimeStamp))/24.;

See also:

bar, dayHigh, timer, BarZone, BarMode, format codes

► latest version online mouse

mouse(int* x, int* y, HWND hWnd): int

Returns the mouse button state and mouse coordinates, relative to a window or to the screen.

Parameters:

x, y - pointers to int variables to be filled with the mouse coordinates.
hWnd - window handle as returned by the window function, or 0 for returning the mouse position in screen coordinates.

Returns:

0 for no mouse button pressed down, 1 for left, 2 for right, 4 for middle button.

Remarks:

Example:

void main()
{
  while(wait(50)) {
    int x,y;
    HWND h = window("Script Editor");
    int button = mouse(&x,&y,h);
    printf("\r%04d %04d %d",x,y,button);
  }
}

See also:

window, keys

► latest version online center, compress, scale, normalize, zscore

Normalization functions

The following functions can be used for normalizing and compressing time series or indicator values to a range that's independent of the asset and time frame. They suppress long-term trend and emphasize short cycles in various ways. Normalization is often required for machine learning algorithms.

center (var Value, int TimePeriod): var

Centers Value by subtracting its median over the TimePeriod. Using the median instead of the mean reduces the effect of outliers.

compress (var Value, int TimePeriod): var

Compresses Value to the -100...+100 range. For this, Value is divided by its interquartile range - the difference of the 75th and 25th percentile - taken over TimePeriod, and then compressed by a cdf function. Works best when Value is an oscillator that crosses the zero line. Formula: 200 * cdf(0.25*Value/(P75-P25)) - 100.

scale (var Value, int TimePeriod): var

Centers and compresses Value to the -100...+100 scale. The deviation of Value from its median is divided by its interquartile range and then compressed by a cdf function. Formula: 200 * cdf(0.5*(Value-Median)/(P75-P25)) - 100.

normalize (var Value, int TimePeriod): var

Normalizes Value to the -100...+100 range by subtracting its minimum and dividing by its range over TimePeriod. Formula: 200 * (Value-Min)/(Max-Min) - 100 . For a 0..100 oscillator, multiply the returned value with 0.5 and add 50. 

zscore (var Value, int TimePeriod): var

Calculates the Z-score of the Value. The Z-score is the deviation from the mean over the TimePeriod, divided by the standard deviation. Formula: (Value-Mean)/StdDev.

 

Normalization results

Parameters:

Value - Variable, expression, or indicator to be normalized.
TimePeriod - Normalization period.

Returns:

Normalized Value.

Remarks:

Example:

function run()
{
  set(PLOTNOW);
  PlotWidth = 600;
  PlotHeight1 = PlotHeight2;
  PlotBars = 400;
  LookBack = 200;
  var ATR100 = ATR(100);
  plot("ATR 100",ATR100,NEW,RED);
  plot("center",center(ATR100,100),NEW,RED);
  plot("compress",compress(ATR100-0.003,100),NEW,RED);
  plot("scale",scale(ATR100,100),NEW,RED);
  plot("normalize",normalize(ATR100,100),NEW,RED);
  plot("zscore",zscore(ATR100,100),NEW,RED);
}

See also:

AGC, FisherN, advise, cdf

 

► latest version online NumAssetsListed

NumAssetsListed

Number of assets in the selected asset list.

NumAssetsUsed

Number of assets selected by asset calls.

Type:

int, read/only

Example:

for(i=0; i<NumAssetsListed; i++) {
  string AssetName = Assets[i];
  ...
}

See also:

asset, asset list

 

► latest version online Bar, NumBars

NumBars

Total number of bars in the simulation, determined from Lookback, StartDate and EndDate, MaxBars, and from the available data in the price history files. Is 0 in the initial run before the first asset call.

NumTicks

Total number of ticks processed in the simulation, determined from the price history resolution. Is 0 in the initial run before the first asset call.

Bar

Current bar number including the lookback period and preceding history, usually from 0 to NumBars-1. The number is not necessarily counted up straight since bars can be skipped (see remarks).

StartBar

Number of the first bar after the lookback period. (Bar-StartBar) is the current duration of the test or trade period in bars. (StartBar-LookBack) is the number of the first bar after the INITRUN.

EndBar

Number of the last bar.

AssetFirstBar

Number of the first valid bar of the selected asset. Can be different for asset whose histories begin later than the initial bar. 

Day

Current trading day number of the simulation, starting with the end of the lookback period. Periods with no bars, such as weekends and holidays, are skipped.

Type:

int, read/only

Remarks:

Example:

printf("\nBar %i of %i",Bar,NumBars);

See also:

Bars, BarPeriod, BarOffset, LookBack, StartDate, run

 

► latest version online NumCores

NumCores

Determines the number of logical CPU cores for WFO training (Zorro S required). Either a positive value for the number of cores to use, or a negative value for the number of cores not to use, f.i. -1 for using all available cores but one. Default = 0 for no multi-core training. The WFO cycles are evenly distributed among parallel Zorro processes assigned to different CPU cores. The more cores, the faster is the training run; f.i. assigning 5 cores of a 6-core CPU will reduce the training time by 80%.

Core

The current core number in a WFO multicore training run, or the current process number, or 0 when no multicore process is running (read/only).

Type:

int

Remarks:

Example:

NumCores = -2; // use all logical cores but two

See also:

NumWFOCycles, Training, Processes

 

► latest version online NumSampleCycles, SampleOffset, SampleCycle

Oversampling

Backtests become more accurate when more data is available and more trades can be opened. The problem: price data is normally in short supply. Oversampling the bars is a method to run multiple test cycles on slightly different price curves that are derived from the original curve and contain the same inefficiencies. This produces more trades, generates more realistic backtests, and allows to evaluate the effect of price curve randomness on the system performance.

Oversampling can be applied to training or backtest, either per-bar or per-cycle. Per-bar oversampling requires price data with higher resolution than the bar period, f.i. M1 data with 1-hour bars. Oversampling shifts the bar begin by a fraction of the bar period on any cycle. This results in different bars and - dependent on the strategy - more or less different trades with the same price curve.

For per-cycle oversampling, a time offset is added to the start of any cycle. It is normally used to detect start date/time dependences of the trading system in backtests.

A description of per-bar oversampling with an example can be found on http://www.financial-hacker.com/better-tests-with-oversampling. The following variables activate and control oversampling:

NumSampleCycles

Number of oversampling cycles (default = 0 = no oversampling). When set to a number n > 1, the simulation is repeated n times. For per-bar oversampling, the bars are resampled in any cycle with different BarOffset values. This generates a slightly different price curve for every cycle, while maintaining the trend, spectrum, and most other characteristics of the curve. For per-cycle oversampling, the SampleOffset is added to the start time of any test, training, or WFO run. The performance result is calculated from the average of all cycles. This way more data for test and training is generated and a more accurate result can be achieved.

SampleOffset

Time offset in bars for per-cycle oversampling. The begin of a test or training cycle is automatically shifted by that time at any cycle. If at 0 (default), per-bar oversampling is used instead.

SampleCycle

The number of the current cycle from 1 to NumSampleCycles. Automatically set at the begin of any cycle.

Type:

int

Remarks:

Examples:

function run() {
  NumSampleCycles = 4; // 4 cycles per-bar oversampling
  ...

function run() {
  NumSampleCycles = 3; // 3 cycles per-cycle oversampling
  SampleOffset = 10;   // with 10 bars start difference 
  ...

See also:

bar, BarOffset, NumWFOCycles, NumOptCycles, NumTotalCycles, ALLCYCLES

 

► latest version online NumTotalCycles, TotalCycle

Multiple Test Cycles

NumTotalCycles

Repeat the complete simulation in training or test mode in as many cycles as given by this variable (default = 0 = no repetitions). This is normally used for doing reality checks, plotting histograms, running special Montecarlo simulations or evaluating result statistics dependent on changed parameters or randomized price curves.

TotalCycle

The number of the cycle from 1 to NumTotalCycles. Read/only, automatically set by NumTotalCycles.

LogNumber

Appends the given number to the log, trade list, performance report, and chart image files generated by the simulation. By setting LogNumber = TotalCycle, different log files, performance reports, and chart images (when PL_FILE is set) can be generated for any cycle. At 0 (default), no logs, reports, or images are produced when NumTotalCycles is set. Set this variable right at the begin of run or main, before calling any function that might write something into the log. 

Type:

int

Remarks:

Example (see also Detrend.c, MRC.c):

function run()
{
  LogNumber = TotalCycle;
  BarPeriod = 1440;
  StartDate = 2015;
  NumYears = 1;
  LookBack = 0;
 
// run this simulation 1000 times  
  NumTotalCycles = 1000;
 
// some random trading strategy
  if(random() > 0)
    enterLong();
  else 
    enterShort();
 
// plot the result of every run in a bar graph  
  if(is(EXITRUN))
    plotHistogram("Profits",Balance/PIPCost,250,1,RED);   
}

See also:

NumSampleCycles, NumWFOCycles, Log ► latest version online NumWFOCycles, WFOCycle, SelectWFO

Walk Forward Optimization

Walk Forward Optimization (WFO in short) is a variant of cross-validation for time series and was first published by Robert Pardo as a backtest method for algorithmic trading strategies. It trains and tests the strategy in several cycles using a data frame that "walks" over the simulation period. This allows an out-of-sample backtest that still uses most of the historical data. WFO is not restricted to backtests, but can be continued In live trading and regularly adapt the strategy parameters to the current market situation. In this way, a WFO trained strategy is essentially a 'parameter-less' system.

For greatly reducing the backtest time with WFO, Zorro separates test and training and stores all trained parameters, trading rules, or machine learning models separately for any WFO cycle. This way a backtest needs not perform all optimizations again. It automatically switches between sets of parameters or rules. To activate WFO in a strategy script, only the following variable needs to be set:

NumWFOCycles

Number of cycles in a Walk Forward Optimization / Analysis (default = 0 = no Walk Forward Optimization). If NumWFOCycles is set to a positive number at or above 2, rolling walk forward optimization is enabled with the given number of cycles; if it is set to a negative number at or below -2, anchored walk forward optimization is enabled. In Walk Forward Optimization, a data frame consisting of a training and test period is shifted over the simulation period in the given number of cycles (see image for ).

WFOCycle
Simulation period
1
LookBack
Training
Test1
2
LookBack
Training
Test2
3
LookBack
Training
Test3
4
LookBack
Training
Test4
5
 
LookBack
Training
OOS Test
LookBack
Test5
WFO Test
Look
Back
Test1
Test2
Test3
Test4
Rolling Walk Forward Optimization (NumWFOCycles = 5)
 
WFOCycle
Simulation period
1
LookBack
Training
Test1
2
LookBack
Training
Test2
3
LookBack
Training
Test3
4
LookBack
Training
Test4
5
LookBack
Training
WFO Test
Look
Back
Test1
Test2
Test3
Test4
Anchored Walk Forward Optimization (NumWFOCycles = -5)

Strategy parameters and trade rules are generated separately for every cycle in [Train] mode, and are separately loaded for every test segment in [Test] mode. This way, the strategy test is guaranteed to be out of sample - it uses only parameters and rules that were generated in the preceding training cycle from data that does not occur in the test. This simulates the behavior of real trading where parameters and rules are generated from past price data. A Walk Forward Test gives the best prediction possible of the strategy performance over time.

WFOPeriod

Alternative to NumWFOCycles; number of bars of one WFO cycle (training + test), f.i. 5*24 for a one-week WFO cycle with 1-hour bars. For getting a fixed test period of N bars, set WFOPeriod to N*100/(100-DataSplit). Since the number of WFO cycles now depends on the number of bars in the history, asset must be called before. The WFO period is automatically adjusted so that the simulation period covers an integer number of WFO cycles. 

SelectWFO

Set this to a certain WFO cycle number for selecting only this cycle in a training or test; otherwise the process runs over all cycles. Setting it to -1 selects the last cycle that's normally used for live trading.

Type:

int

WFOCycle

The number of the current WFO cycle, from 1 to NumWFOCycles. Automatically set during WFO test and training.

WFOBar

The bar number inside the current WFO cycle, starting with 0 at the begin of the cycle. Can be used to determine when the cycle starts: if(WFOBar == 0). Automatically set during a WFO test.

Type:

int, read/only

Remarks:

Examples:

// anchored WFO, 10 cycles 
function run()
{
  NumWFOCycles = -10;
  DataSplit = 80; // 20% test cycle
  DataSlope = 2;  // more weight to more recent data
  set(PARAMETERS,TESTNOW); // run a test immediately after WFO
  ...
}

// set WFO period and DataSplit from a fixed test and training cycle
function setWFO1(int TestDays,int TrainDays)
{
  WFOPeriod = (TestDays+TrainDays) * 1440/BarPeriod;
  DataSplit = 100*TrainDays/(TestDays+TrainDays);
}

// set NumWFOCycles from a fixed test cycle in days
// (DataSplit and LookBack should be set before)
function setWFO2(int TestDays)
{
asset(Asset); // requires NumBars, so asset must be set
int TestBars = TestDays * 1440/BarPeriod;
var Split = ifelse(DataSplit > 0,DataSplit/100.,0.85); int TrainBars = TestBars * Split/(1.-Split);
int TotalBars = NumBars-LookBack;
NumWFOCycles = (TotalBars-TrainBars)/TestBars + 1;
} // set a fixed date for the WFO test start // modifies StartDate, needs EndDate and TestDate in YYYYMMDD function setWFO3(int TestDate) { var Split = ifelse(DataSplit > 0,DataSplit/100.,0.85); var TrainDays; if(WFOPeriod > 0) TrainDays = Split*WFOPeriod*BarPeriod/1440; else if(NumWFOCycles > 1) TrainDays = Split/(1.-Split)*(dmy(EndDate)-dmy(TestDate))/(NumWFOCycles-1); else return; StartDate = ymd(dmy(TestDate)-TrainDays); }

See also:

Training, Tutorial, DataSplit, DataHorizon, NumSampleCycles, NumTotalCycles, NumOptCycles

 

► latest version online optimize, objective

User-supplied optimization targets and algorithms

This page is about user-defined methods and objectives for backtest-based parameter optimization. For other sorts of optimization, for instance training neural nets or user-specific models, use adviseLong(NEURAL, ...) with a user-supplied neural() function.

objective(): var

A user-supplied objective function can be used for calculating individual optimization targets or for analyzing the preceding training run. It is called at the end of every optimization step and supposed to return a performance metric based on the statistics or trade results of that step. If required, it can also be used to store results in a file or dataset for further evaluation, such as plotting a 3D parameter chart. A default objective function is located in the default.c file. It uses the Pessimistic Return Ratio (PRR) for the optimization target, requires at least 5 trades, and treats the highest win and worst loss as outliers:

// optimizing objective based on PRR
var objective()
{
  if(NumWinTotal < 2 || NumWinTotal+NumLossTotal < 5) 
    return 0.; // needs at least 5 trades
  var wFac = 1./sqrt(1.+NumWinTotal); 
  var lFac = 1./sqrt(1.+NumLossTotal);
  var Win = WinTotal, Loss = LossTotal;
// remove possible outlier wins/losses
  if(NumLossTotal > 1) {
    Loss -= (NumLossTotal-1)*LossMaxTotal/NumLossTotal;
    Win -= (NumWinTotal-1)*WinMaxTotal/NumWinTotal;
  }
// return PRR
  return (1.-wFac)/(1.+lFac)*(1.+Win)/(1.+Loss);
}

For some strategies, for instance binary options, the above PRR is not the best optimization target. For alternative targets, use an individual objective function in your script. It will override the default objective. Examples:

// alternative objective function based on Calmar ratio
var objective()
{
  return(WinTotal-LossTotal)/max(1,DrawDownMax);
}

// alternative objective function based on Sharpe ratio
var objective()
{
  if(!NumWinTotal && !NumLossTotal) return 0.;
  return ReturnMean/ReturnStdDev;
}

// alternative objective function for binary options
var objective()
{
  return ((var)(NumWinLong+NumWinShort))/max(1,NumLossLong+NumLossShort);
}

parameters(): int

Alternatively to Zorro's own Ascent, Genetic, or Brute Force algorithms, parameters can be optimized with an external optimization algorithm in an external DLL, R package, or Python library, or with a script-based algorithm  (Zorro S 2.32 or above). For this a parameters function must be supplied and the ASCENT, BRUTE, GENETIC flags must be off. The parameters function is automatically called after the initial run (INITRUN) of any optimization step. Its purpose is setting up the Value elements of the Parameters list for the subsequent training. The Values can be either directly calculated or retrieved with a function from an external library. The parameters function is supposed to return 0 when the last step is reached and the optimization is finished after that step.

Below an example of a parameters function for a script-based brute force optimization. The condition StepCycle == 1 intializes the parameters, afterwards they are counted up until all have reached their maximum.

int parameters()
{
// set up parameters for brute force optimization
  int i;
  for(i=0; i<NumParameters; i++) {
    if(StepCycle == 1 || Parameters[i].Value >= Parameters[i].Max)
      Parameters[i].Value = Parameters[i].Min;
    else { // count up
      Parameters[i].Value += Parameters[i].Step;
      break;
    } 
  }
// last step reached?
  for(i=0; i<NumParameters; i++)
    if(Parameters[i].Value < Parameters[i].Max)
      return 1;  // not yet
  return 0; // yes, optimization complete
}

The objective function now got the job not only to return the performance metric, but also to store the best result in the BestResult variable and its parameter values in the Best elements of the Parameters list. Alternatively, the best parameter values can be retrieved from an external library in the objective call after the last optimization step.

var objective()
{
// calculate performance metric from profit factor
  var Result = ifelse(LossTotal > 0,WinTotal/LossTotal,10);
// store best result and best parameters
  int i;
  if(BestResult < Result) {
    BestResult = Result;
    for(i=0; i<NumParameters; i++)
      Parameters[i].Best = Parameters[i].Value;
  }
  return Result;
}
 

Remarks:

Example: Using an external optimization library

// assumed library functions:
// set_experiment(Parameters) // initialize the module with a list of parameters
// get_suggested(Parameters) // return suggested parameter assignment
// set_observation(Result) // deliver the result from the suggested parameters
// get_optimized(Parameters) // return the best parameter set

int MaxSteps = 1000; // optimization budget

int parameters()
{
// initialize librariy and send parameter boundaries
  if(StepCycle == 1)
    set_experiment(Parameters);
// get parameter assignment for the current step
  get_suggested(Parameters); 
// optimization complete?
  if(StepCycle == MaxSteps) 
    return 0;	// yes
  else return 1;
}

var objective()
{
// calculate performance metric from profit factor
  var Result = ifelse(LossTotal > 0,WinTotal/LossTotal,10);
  BestResult = max(Result,BestResult);
// send result to library
  set_observation(Result);
// after the last step, get best parameter set 
  if(StepCycle == MaxSteps) 
    get_optimized(Parameters);  
  return Result;
}

See also:

Training, TrainMode, optimize, NumParameters, advise

► latest version online TrainMode

Training variables

TrainMode

Training method; affects the way how parameters, factors, rules, or machine learning models are generated in the training process. The following flags can be set:

Flags:

ASCENT Ascent parameter optimization and parameter chart export. Evaluates the effect of any parameter on the strategy separately. Starts with the first parameter and applies the results of already optimized parameters and the defaults of the rest. Seeks for 'plateaus' in the parameter space, while ignoring single peaks. This is normally the best algorithm for a robust strategy, except in special cases with highly irregular parameter spaces or with interdependent parameters.
BRUTE Brute force parameter optimization and parameter spreadheet export (Zorro S required). Evaluates all parameter combinations, exports them to a *par.csv  file in the Log folder, and selects the most profitable combination that is not a single peak. Can take a long time when many parameters are optimized or when parameter ranges have many steps. Useful when parameters affect each other in complex ways, or when it is necessary to evaluate results from any parameter combination. Brute force optimization tends to overfit the strategy, so out-of-sample testing or walk-forward optimization is mandatory.
GENETIC Genetic parameter optimization (Zorro S required). A population of parameter combinations is evolved toward the best solution in an iterative process. In each iteration, the best combinations are stochastically selected, and their parameters are then pair-wise recombined and randomly mutated to form a new generation. This algorithm is useful when a large number of parameters - 5 or more per component - must be optimized, or when parameters affect each other in complex ways. It will likely overfit the strategy, so out-of-sample or walk-forward testing is mandatory.
TRADES Use trade sizes by Lots, Margin, etc. Large trades get then more weight in the optimization process. Set this flag in special cases when the trade volume matters, f.i. for optimizing the money management or for portfolio systems that calculate their capital distribution by script. Otherwise trade sizes are ignored in the training process for giving all trades equal weights.
PHANTOM Exclude phantom trades. Otherwise phantom trades are treated as normal trades in the training process.
PEAK Optimize toward the highest single peak in the parameter space, rather than toward hills or plateaus. This can generate unstable strategies and is for special purposes only. For instance when optimizing not a parameter range, but a set of different algorithms or different assets.
ALLCYCLES Generate individual OptimalF factor files for all WFO cycles, instead of a single file for the whole simulation. This produces low-quality factors due to less trades, but prevents backtest bias.

NumParameters

Number of parameters to optimize for the current asset/algo component that was selected in a loop (int, read/only, valid after the first INITRUN).

ParCycle

Current parameter or current generation, runs from 1 to NumParameters in Ascent mode, or from 1 to Generations in Genetic mode (int, read/only, valid after the first INITRUN).

StepCycle

Number of the optimize step, starting with 1 (int, read/only, valid after the first INITRUN). The number of step cycles depends on the number of steps in a parameter range and of the population in a genetic optimization. Counts up after any step until the required number is reached or StepNext is set to 0. 

StepNext

Set this to 0 for early aborting the optimization (int).

NumTrainCycles

Number of training cycles (int, default = 1) for special purposes, such as training a combination of interdependent rules and parameters in a given order (see Training). In any cycle, set either RULES, or PARAMETERS, or both, dependent on training method. Not to be confused with WFO cycles or Generations.

TrainCycle

The number of the current training cycle from 1 to NumTrainCycles, or 0 in [Test] or [Trade] mode (int, read/only). The training mode of the current cycle can be determined with the PARCYCLE, RULCYCLE, FACCYLE flags.

LogTrainRun

Set this to a identifier number for logging a particular training run. The identifier is a 5-digit number in the format WFSPO, where W = WFO cycle, F = first loop iteration, S = second loop iteration, P = parameter number, and O = optimize step. At 11111 the very first training run is logged. 

Population

Maximum population size for the genetic algorithm (int, default = 50). Any parameter combination is an individual of the population. The population size reduces automatically when the algorithm converges and only the fittest individuals and the mutants remain.

Generations

Maximum number of generations for the genetic algorithm (int, default = 50). Evolution terminates when this number is reached or when the overall fitness does not increase for 10 generations.

MutationRate

Average number of mutants in any generation, in percent (int, default = 5%). More mutants can find more and better parameter combinations, but let the algorithm converge slower.

CrossoverRate

Average number of parameter recombinations in any generation, in percent (int, default = 80%). 

BestResult

Highest objective return value so far (var, starting with 0).

Parameters

Pointer to a list of PARAMETER structs for the current asset/algo component. The Min, Max, and Step elements are set up in the list after the first INITRUN in [Train] mode. The PARAMETER struct is defined in trading.h.

Remarks:

Example:

setf(TrainMode,TRADES+PEAK);

See also:

Training, optimize, advise, OptimalF, objective, setf, resf
► latest version online OptimalF

Money Management and Capital Assignment Factors

For re-investing profits or assigning capital to portfolio components, Zorro can determine the optimal capital assignment factors - named OptimalF - for components in a portfolio strategy. For this, it evaluates the component's trade history for calculating the investment amount that generates the maximum end profit while avoiding a negative balance. For instance, if OptimalF is 0.05, then the suggested invested margin is 5% of the available capital. The margin can be smaller - for reasons mentioned in workshop 6, you should use the square root rule for reinvesting - but it must not be higher. The OptimalF algorithm was developed by Ralph Vince and described in several publications (see books).

OptimalF factors can also be used for allocating the capital to the components of a multi-asset system under certain conditions. The total allocated margin must be at any time small compared to the total invested capital. This is normally the case for trading systems with independent assets on leveraged accounts, where the capital is mostly needed for buffering drawdowns. The suggested margin of a trade is then the available capital multiplied with OptimalF. There are several methods to determine the available capital of a portfolio component; a method on the conservative side is dividing the initial total capital by the square root of components and multiplying it with the square root of gains (see example). Systems that don't fulfil the requirements, for instance portfolio rebalancing systems that are always in the market with 100% capital, normally use other algorithms such as mean_variance or momentum-based weights for distributing the capital.

For generating OptimalF factors, set the FACTORS flag. The factors are then calculated in a special test run at the end of the [Train] process, and stored in a file Data\*.fac. It's a simple text file that looks like this:

AUD/USD:ES          .036  1.14   45/87     0.1
AUD/USD:ES:L        .036  1.14   45/87     0.1 
AUD/USD:ES:S        .000  ----    0/0      0.0  
EUR/USD:VO          .027  2.20   24/23     3.3  
EUR/USD:VO:L        .027  1.58   12/11     0.9 
EUR/USD:VO:S        .032  2.90   12/12     2.5 
NAS100:ES           .114  1.42   63/90     4.6 
NAS100:ES:L         .101  1.39   33/44     2.1  
NAS100:ES:S         .128  1.46   30/46     2.5  
USD/CAD:BB          .030  1.41   19/25     1.3 
USD/CAD:BB:L        .030  1.41   19/25     1.3  
USD/CAD:BB:S        .000  ----    0/0      0.0  
USD/CAD:HU          .012  1.74   48/36     3.3  
USD/CAD:HU:L        .066  1.42   24/20     0.2 
USD/CAD:HU:S        .012  1.79   24/16     3.1  
USD/CHF:CT          .104  1.60   16/17     0.6  
USD/CHF:CT:L        .104  1.60   16/17     0.6  
USD/CHF:CT:S        .000  ----    0/0      0.0  
USD/CHF:CY          .025  1.10   21/24     0.1  
USD/CHF:CY:L        .025  1.10   21/24     0.1  
USD/CHF:CY:S        .000  ----    0/0      0.0 
USD/CHF:HP          .025  1.45   31/48     3.2 
USD/CHF:HP:L        .000  ----    0/0      0.0 
USD/CHF:HP:S        .025  1.45   31/48     3.2  
USD/CHF:VO          .011  3.93   17/8      7.6 
USD/CHF:VO:L        .011  3.93   17/8      7.6 
USD/CHF:VO:S        .000  ----    0/0      0.0 

The first column identifies the component; it consists of the asset name and the algorithm identifier. "S" or "L" are separate statistics for short or long trades. The second column contains the OptimalF factors for that component. The higher the factor, the more capital should be invested in the strategy component. Since calculated separately for any component, the factors do not sum up to 1. A 0 indicates that this component should not be traded. The further columns contain the profit factor, the number of winning and losing trades, and the weight of the component.

As the factors are stored in a simple text file, they can be edited anytime with a text editor, even while live trading. Zorro detects if factors have been changed, and automatically reloads them. If the factors are evaluated in the strategy, as in some of the Z strategies, a component can be excluded from further trading by setting its factor to zero, or by placing a minus sign in front of it for making it negative.

Variables

When OptimalF factors have been generated, they can be accessed or modified with the following variables:

OptimalF

OptimalFLong

OptimalFShort

OptimalF factors, combined for long/short and separately for long and short trades of the current strategy component that is selected with the asset and algo functions. For long-only systems, only OptimalFLong is relevant. The margin to be invested per trade can be calculated by multiplying the investment amount with OptimalF. If a component was unprofitable in the training run, its OptimalF factor is zero.

OptimalFRatio

Set this in [Train] mode to generate OptimalF factors with the given ratio of the highest to the lowest factor (default = 0 = no ratio). For instance, at OptimalFRatio = 3 large factors are reduced and small factors are increased so that the highest OptimalF factor is 3 times the lowest factor. The average of all factors remains unchanged. Useful for preventing large component margin differences when using OptimalF factors in portfolio systems.

Remarks

Examples of different investment methods (see also workshop 6):

// multi-asset system: reinvest the square root of profits separately per component and long / short trades
var AvailableCapital = Capital/sqrt(Number_Of_Components);
Margin = ifelse(For_A_Long_Trade,OptimalFLong,OptimalFShort)*AvailableCapital*sqrt(1+(WinLong-LossLong)/AvailableCapital);
 
// single-asset system: reinvest the square root of your total profits
Margin = OptimalF * Capital * sqrt(1 + (WinTotal-LossTotal)/Capital);

See also:

Margin, Win/Loss, Markowitz, Performance, TrainMode, Tutorial

 

► latest version online optimize

optimize (var default, var min, var max, var step, var bias) : var

Optimizes a trading strategy parameter for the current asset and algo combination. In [Test] and [Trade] mode the function returns the previously optimized parameter value. In [Train] mode the function performs an optimization process to find the most robust parameter value between min and max

Several optimization methods (Ascent, Genetic, Brute Force, Extern) are available through TrainMode. The optimization target can be individually defined with a user-supplied objective function. The optimization process consists of many cycles. For every cycle the optimize function generates a different value between min and max and tests its effect on the strategy. At the end of the optimization, a parameter chart is plotted that reveals the effect of a certain parameter on the strategy (see below). The best or most robust parameter values are then selected and stored in a .par file.

The chart above is a result of a parameter optimization with the Ascent algorithm. The parameter is a stop distance. It varies from 2 to 10 in 10% steps. At any step the number of winning trades (black bars), losing trades (grey) and performance (red) of the parameter are displayed. We can see that high stop distances increase the number of winning trades, but have only a small effect on the overall performance. The Ascent optimizer would here likely select 8 as the most robust parameter value, rather than the single peak at 4.29. This behavior can be changed with TrainMode.
  

The contour chart above is generated by optimizing two parameters with the Brute Force algorithm. The first parameter, a time period, runs from 20 to 60 in steps of 5; the second, a signal threshold, from 0.6 to 1.4. Green areas indicate high performance, red areas low performance of the parameter combination. The optimizer would here likely select the 35 / 0.8 parameter combination. If more than two parameters are optimized, the first two are used for the contour chart.

The size of parameter or contour charts can be set up with PlotHeight2. By using multiple optimize calls in the code, any number of parameters can be optimized (up to 16 per asset/algo component). The more parameters, the more trades are needed for a meaningful optimization, and the longer will it take. As a rule of thumb, have at least 40 trades per parameter and WFO cycle, and do not optimize more than 3 or 4 parameters for a trade entry signal and 2 or 3 for exit. Too many optimized parameters generate an overfitted strategy and reduce the out-of-sample performance. Dependent on the method, the best value of a parameter can affect the optimization of the next parameters. If the script contains loop calls, a separate parameter set for the asset/algo combination in each loop is optimized.

Training also produces a log with the results of any training step. Here's an example log of the first cycle of an Ascent parameter walk-forward optimization:

Walk[1] Bar 400..23372

Parameter 1 step 1: 0.10 => 0.96 1729/1933
Parameter 1 step 2: 0.20 => 0.91 1290/1540
Parameter 1 step 3: 0.30 => 0.87 990/1184
Parameter 1 step 4: 0.40 => 0.83 840/1034
Parameter 1 step 5: 0.50 => 1.06 747/759
Parameter 1 step 6: 0.60 => 1.01 669/715
Parameter 1 step 7: 0.70 => 0.87 604/712
Parameter 1 step 8: 0.80 => 0.86 490/616
Selected p1[5] = 0.504 => 0.96

Parameter 2 step 1: 1.00 => 1.06 471/1035
Parameter 2 step 2: 1.50 => 1.08 553/953
Parameter 2 step 3: 2.00 => 1.08 599/907
Parameter 2 step 4: 2.50 => 1.07 646/860
Parameter 2 step 5: 3.00 => 1.06 676/830
Parameter 2 step 6: 3.50 => 1.07 725/781
Selected p2[3] = 2.000 => 1.07

Parameter 3 step 1: 1.00 => 0.97 883/623
Parameter 3 step 2: 1.50 => 0.99 806/700
Parameter 3 step 3: 2.00 => 1.02 755/751
Parameter 3 step 4: 2.50 => 1.06 711/795
Parameter 3 step 5: 3.00 => 1.04 657/849
Parameter 3 step 6: 3.50 => 1.06 622/884
Selected p3[5] = 3.01 => 1.05

EUR/USD: 0.504 2.000 3.01=> 1.112

Parameters stored in OptTest_1.par
The log displays the first walk-forward cycle (Walk[1]) with 3 parameters. The numbers after '=>' are the objective return value and the numbers of won and lost trades. The selected parameter value and its objective are interpolated between steps. The second-last line, beginning with "EUR/USD:", is the content added to the parameter file.   

Parameters:

default Default value of the parameter, returned when the PARAMETERS flag is not set in [Test] or [Trade] mode. This should be the estimated best value of the parameter, normally in the middle of its range. Must be >= 0.
min, max Parameter range, minimum and maximum values (>= 0). For optimizing negative parameter ranges, subtract an offset or add a minus sign to the optimize call. If both min and max are 0, the default value is stored in the parameter file.
step Optional step width. If at 0 or omitted, in Ascent optimization 10% is added to the parameter for every step; in Brute Force or Genetic optimization 1/10 of the min..max range is added. A positive step value is added at every step.A negative step value is interpreted as a percentage to be added, for instance -20 adds 20% per step (Ascent optimization only). Using a percentage gives a higher step resolution to smaller values at the begin of the range. For best results, select the step value so that every parameter has about 10..15 optimization steps. The recommended minimum number of steps is 5, the maximum number of steps (MAX_STEPS) can be found in trading.h.
bias Optional preference of low or high parameter values (Ascent optimization only). When 0 or omitted, select the optimal parameter. When > 0, prefer higher parameter values even when their rank is lower by the given bias in percent. When < 0, prefer lower parmeter values even when their rank is lower by abs(bias) in percent. Preferring values from the low or high range can make sense f.i. for setting a Stop as tight as possible even when its value is not optimal.

Returns

Current, default, or optimized parameter value, depending on training or test/trade mode.
   

Remarks:

Examples:

// trend trading with Moving Averages and optimized parameters
function run()
{
  set(PARAMETERS);
// for optimizing time periods, set the LookBack variable to the
// maximum possible value (here, TimeCycle 100 * TimeFactor 5)
  LookBack = 100*5;
var TimeCycle = optimize(30,10,100,5); var TimeFactor = optimize(3,1,5);
// allow 3% tolerance for preferring low stop distances Stop = ATR(10) * optimize(3,1,10,0.5,-3); vars Price = series(price(0)); vars MA1 = series(SMA(Price,TimeCycle)); vars MA2 = series(SMA(Price,TimeCycle*TimeFactor)); plot("MA1",*MA1,0,BLUE); plot("MA2",*MA2,0,BLUE); if(crossOver(MA1,MA2)) enterLong(); else if(crossUnder(MA1,MA2)) enterShort(); }

See also:

Training, TrainMode, loop, NumParameters, NumTrainCycles, Workshop 5, Statistics

► latest version online order

order (int type): int

User-supplied function that is called by the trade engine every time when Zorro enters or exits a trade. Can be used to access external broker APIs, control external trade platforms by sending key strokes or mouse clicks, or just pop up a message for manually entering or exiting a trade.

Parameters:

type - 1 for entering, 2 for exiting a trade.

Returns:

0 when the trade could not be opened or closed, 1 otherwise.

Remarks:

Example:

int order(int type)
{
  string bs = "Buy";
  if(type == 2) bs = "Sell";
  string ls = "Long";
  if(TradeIsShort) ls = "Short";
  play("alert.wav");
  return msg("%s %s!",bs,ls);
}

See also:

Brokers, enterShort/Long, exitShort/Long, msg, login, API, broker plugin, user supplied functions

► latest version online Order Book Analysis Functions

Market Depth / Order Flow Analysis

The order book is a list of currently open buy or sell orders, sorted by their quoted price and volume. Every broker and exchange maintains the order book. It can often be read by API functions and evaluated for getting an overview of the current market situation, as well as of the expected price trend when buy and sell orders are heavily imbalanced.

The following functions can be used for retrieving order book data - also referred to as "market depth", "DOM", or "level 2" data - from the broker or from historical .t2 data files. The data can be directly used for analyzing the order flow from the distribution of quotes and their volumes in the order book (Zorro S required).


BTC/USD June 2018, cumulative order volume distribution

The above histogram is generated from a order flow distribution analysis. The height of a bar is the cumulative volume of a price in the order book, blue for bid and red for ask prices. The volume is expressed as a 0..1 percentage of the total ask or bid volume, whichever is higher. You can see that the situation in the diagram is dominated by offers (100% vs. 65%), which might indicate a further falling bitcoin price.
 

orderUpdate (string Name, int Handle): int

Retreives the current order book, and puts the list of open quotes in a dataset with the given Handle for further evaluation. In [Test] or [Train] mode the quotes are read from a historical data file Name.t2 or Name_YYYY.t2 that is supposed to contain historical order book data. In [Trade] mode the order book is directly downloaded from the broker API with the GET_BOOK command. Every T2 record of the resulting dataset contains 3 fields: the timestamp, the quoted price (negative for bid), and the quoted volume. The T2 struct is defined in trading.h. The function sets the OrderRow variable to the row of the first quote in the dataset, and returns the number of quotes open at the current time.
 

orderCVD (T2* Quotes, int N, var Distance): int

Generates a cumulative volume distribution of the price/volume pairs from the given Quotes list of T2 records. The list is normally generated by a previous orderUpdate call. The Quotes pointer can be obtained from a dataset with dataStr(Handle,OrderRow,0). Distance is the maximum deviation of the highest ask or lowest bid from the center price; quotes further away are not considered. The function returns the number of quotes within Distance.

cpd(var Price): var

Returns the cumulative relative volume of the given Price, in percent (0..100). 100 is equivalent to the total ask or bid volume, whichever is higher. Negative prices return the bid volume, positive prices the ask volume.

Parameters:

Name Name of the order book file, without the trailing ".t2" and without a year number, or 0 for using the current asset name.
Handle Handle of the dataset to receive the order book data.
Quotes List of quotes in T2 format.
N Number of quotes.
Distance Maximum order book range.
Price Quote price.
   

Remarks:

Example:

// plot an order flow profile
void main() 
{
  StartDate = 20180110;
  LookBack = 0;
  BarPeriod = 10;
  PlotScale = 10;
  set(PLOTNOW);

  assetList("AssetsCrypto");
  asset("BTC/USD");

// load today's order book
  int N = orderUpdate("BTCUSD",1);
  T2* Quotes = dataStr(1,OrderRow,0);
  printf("\nOrderbook: %i quotes",N);

  var Distance = 0.05*price(); // +/- 5%
  int N2 = orderCVD(Quotes,N,Distance);
  printf(", %i in range",N2);
  Distance *= 1.5;
  var Price = priceClose() - Distance;
  int i;
  for(i=0; i<100; i++) {
    Price += Distance/50;
    plotBar("Ask",i,Price,cpd(Price),BARS|LBL2,RED);
  }
  Price = priceClose() - Distance;
  for(i=0; i<100; i++) {
    Price += Distance/50;
    plotBar("Bid",i,Price,cpd(-Price),BARS|LBL2,BLUE);
  }
}

See also:

contractCPD, cdf

 

► latest version online Outlier and Split detection

Outliers, stock splits, price anomalies

PriceJump

An unusual price deviation in historical or live price data was detected. Automatically set by calling a price or contractUpdate function; sensitivity can be set with the Outlier variable. PriceJump can be evaluated in scripts for handling outliers and stock splits or merges. Afterwards reset it to 0 for 'arming' it for the next price deviation.

0 - No price anomaly since the last reset.
1 - Outlier detected, see below.
2..10 - Stock split by the given factor detected, f.i. 4 for a 1:4 stock split.
-2..-10 - Stock merge by the given factor detected, f.i. -4 for a 4:1 stock merge. 

Type:

int
 

Outlier

Sensitivity factor of the outlier detection for historical and for live prices (default = 1.1+2*sqrt(BarPeriod/1440)). All outliers above the given factor or below its reciprocal are suppressed with a 0.0001 EMA factor. For instance, Outlier = 1.15; suppresses all outliers that deviate from the last price by more than 15% in any direction. PriceJump is set to 1 when an outlier was detected. Negative ask-bid spreads are also suppressed. Set Outlier to 0 for not detecting splits, outliers, and negative spreads; set it to between 1 and 2 for detecting them; set it to above 2 for high volatility assets and for treating splits as outliers. 

Type:

var
 

NumOutliers

Number of outliers and price jumps detected so far in historical or live data (Zorro 2.42 or above). Can be reset to 0 for starting a new count.

Type:

int
 

Remarks:

Example:

function run()
{
  ...
  Outlier = 1.05;  // detect and suppress price deviations by more than 5%
  PriceJump = 0;   // in case it was set before
  price();
  if(PriceJump >= 2) { // stock split detected 
    printf("\nStock split %i:1 - manually close all open contracts!",PriceJump);
    for(open_trades) cancelTrade(ThisTrade); // clear the open trade list
    PriceJump = 0;
    setf(TradeMode,PHANTOM); // suspend further trading
  }
  ...
}

See also:

tick, TickTime, price

 

► latest version online Control panel

User interface panels

For setting up strategy parameters, triggering actions, or displaying something, the following elements are available:

The control panel can be created by script or with Excel™. The panel is a rectangular grid of multi-purpose cells, similar to a spreadsheet. Every cell can display text, numbers, or a color, and can serve as a numeric or text input field or as a push, toggle, radio, or spin button. With this concept, even complex control functions for commercial strategies can be realized in a simple way. Such a control panel does not look particularly pretty, but it's functional:

Panel

A panel can have a virtually unlimited number of rows and columns. If there are more than fit on the screen, the panel can be scrolled with scrollbars on the right and bottom side. Header rows or columns can be defined that stay fixed when the rest of the panel scrolls. This way, individual asset parameters can be set up or displayed for multi-asset strategies. An example of a scrolling panel with thousands of rows can be found in the History.c script.

The following functions can be used for defining and handling a control panel or for populating the [Action] scrollbox:

panel (int rows, int columns, int color, int size)

Opens an empty control panel with the given number of rows (at least 2) and columns (at least 1), grid color, and horizontal cell size in pixels. If a control panel was already open, it is replaced by the new one. The panel is opened next to the Zorro window, and can be freely moved by dragging it with the mouse. If the number of rows or columns exceed the screen size, scrollbars are added to the panel. A negative size limits the panel height to the height of the Zorro panel. panel(0,0,0) removes the panel.

panel (string filename, int color, int size)

As above, but loads a control panel from a .csv spreadsheet file. Every cell of the panel is set up from the corresponding spreadsheet cell. The cells in the file must be separated with  ;  or  ,  delimiters. Every row must end with a newline character. The following control characters in the cell content have a special meaning:  !...  - cell is a button;  #...  - cell can be edited;  @  - merge the cell with the next not-empty cell to the left. The default colors are grey for text cells, red for numbers, white for editable cells, and blue for buttons; they can be changed with the ColorPanel variables.
  CSV delimiters and control characters must otherwise not appear in cell content. For still displaying them, the cell content can be set with panelSet() after loading the panel. Examples of spreadsheet-generated panels can be found in the Strategy folder.

panelSet (int row, int col, string text, int color, int style, int type)

Set properties or update content of an individual cell.
 
row - row number starting with 0, or -1 for the [Result] button, or -2 for the [Action] scrollbox.
col - column number starting with 0, or line number in the [Action] scrollbox. 
text - content to appear in the cell, button, or scrollbox line, or 0 for not changing the content. Numbers can be converted to text with strf.
color - background color of the cell, or 0 for not changing the color.
style - 1 normal text, 2 highlighted text, 3 greyed-out text, +4 right-aligned, +8 left-aligned, +12 centered, +16 bold, or 0 for not changing the style.
type - 1 normal cell, 2 editable cell, 4 button, 0 for not changing the type. If row is -2 and type is nonzero, the text is selected in the [Action] scrollbox.

The default colors for generating the panel from a spreadsheet are stored in ColorPanel[n], where n = 0 for text background, 1 for number background, 2 for an editable field, 3 for a button, 4 for highlighted text, 5 for greyed out text.

panelGet (int row, int col): string

Returns a temporary string with the current content of the cell. Numbers can be converted from the returned string with sscanf, atof, or atoi.

panelMerge(int Row, int Col, int NumRows, int NumCols)

Merges the given area of the panel to a single cell, with the content of the first cell. Row / Col is the upper left corner and NumRows / NumCols the size.

panelFix(int NumRows, int NumCols)

Gives the number of header rows and columns that stay fixed on a scrolling panel. Header cells cannot be merged.

panelSave (string Filename)

Saves the panel content in .csv format to the given FileName, or to "Data\*.pan" when the name is empty or zero.

panelLoad (string Filename)

Loads the saved panel content in .csv format from the given FileName, or from "Data\*.pan" when the name is empty or zero. Only editable or button cells are affected.
 

click(int Row, int Col)

User-supplied function that is triggered by the following GUI actions:
- mouse click on a button cell,
- entering a number or editing a cell,
- clicking the [Result] button (Row = -1),
- selecting a previously set action from the [Action] scrollbox (Row = -2).
- selecting an asset with the [Asset] scrollbox (Row = -3; asset name in AssetBox).
Dependent on the desired behavior of the button, the function can then change the cell color or content through panelSet, and/or save the panel content with panelSave().

Remarks:

Examples: (see also Download.c, History.c, PanelTest.c)

// click function example
function click(int row,int col)
{
  if(!is(RUNNING)) return; // prevent clicks after the end of the script
  if(row < 0) return; // prevent clicks by result button or scroll box
  sound("click.wav");
string Text = panelGet(row,col);
if(Text == "Buy") // toggle button between "Buy" and "Sell"
panelSet(row,col,"Sell",0,0,0);
else if(Text == "Sell")
panelSet(row,col,"Buy",0,0,0);
else if(Text == "Close All") // push button to close all trades call(1,closeAllTrades,0,0); else if(Text == "Enter Trade") // push button to enter position call(1,enterMyTrade,0,0); else if(Text == "Cancel") // back to last saved state panelLoad("Data\\mypanel.csv"); panelSave("Data\\mypanel.csv"); }
// Entering a code number
int Code = 0;

void click(int row,int col)
{
  sound("click.wav");
  Code = atoi(panelGet(0,0));
}

void enterNumber()
{
  Code = 0;
  panel(1,4,GREY,30);
  print(TO_PANEL,"Enter your code number");
  panelSet(0,0,"",0,1,2);
  panelMerge(0,0,1,3);
  panelSet(0,3,"Ok",ColorPanel[3],1,4);
  while(!Code) wait(0);
  panel(0,0,0);
  printf("\nCode: %i",Code);
}
// Start a program via Action scrollbox
void click(int row,int col)
{
  if(row == -2 && col == 0) // first action line
    exec("Editor","Strategy\\Z.ini",0);
}

function run()
{
  panelSet(-2,0,"Edit Z9.ini",0,0,0);
  ...
}

See also:

sliders, wait, user supplied functions, ColorPanel

 

► latest version online peak, valley

peak(vars Data) : bool

valley(vars Data) : bool

Determines if the Data series had a maximum (peak) or minimum (valley) at the previous bar.

peak(vars Data, int TimePeriod) : int

valley(vars Data, int TimePeriod) : int

Returns the bar offset of the last maximum (peak) or minimum (valley) of the Data series, or 0 when the series had no peak or valley within the TimePeriod.

Parameters:

Data Time series
TimePeriod Number of series elements to test

Returns:

true or bar offset at the peak or valley, false or 0 if no peak and valley was found.

Modifies

rMomentum - Data movement in percent per time frame; indicates the 'sharpness' of the peak or valley.

Algorithm:

bool peak(var* Data) { 
  rMomentum = min(Data[1]-Data[2],Data[1]-Data[0])/Data[0]*100.;
  return (Data[2] <= Data[1]) && (Data[1] > Data[0]); 
}
bool valley(var* Data) {
  rMomentum = min(Data[2]-Data[1],Data[0]-Data[1])/a[0]*100.;
  return (Data[2] >= Data[1]) && (Data[1] < Data[0]); 
}  

Remarks:

Example:

function run()
{
  vars Trends = series(LowPass(series(price()),1000),3);
  if(valley(Trends))
    enterLong();
  if(peak(Trends))
    enterShort();
}

See also:

crossOver, crossUnder, rising, falling, peakF, valleyF, predict

► latest version online

Performance Report

Performance Report

Zorro can analyze the performance of its own strategy scripts, and also strategy results from other platforms that are imported from .csv trade lists. A click on [Result] after a test run produces a chart, a log, a trade list in CSV format, and a performance report with portfolio analysis. Chart images and reports are stored in the Log folder and can be displayed with Zorro's image viewer or text editor. Exported charts are .png image files, generated reports are simple .txt files. This way they can be easily posted to websites, imported in documents, exported to spreadsheets, or further evaluated with the Performance script. Additionally, datasets from charts can also be exported and further evaluated with third party software, f.i. an R data analysis package.

During a session, the performance report is available through the report function. In [Trade] mode the chart and performance report is part of the .htm status page. It can be displayed in a web browser and is updated every minute. It also contains a list of all open and pending trades, as well as any specific information that is printed by the script.

This is an example of a performance report:

Walk-Forward Test Z4 portfolio
 
Simulated account   AssetsFix.csv (NFA)
Bar Period          4 hours (avg 266 min)

Simulation period   06.06.2007-02.12.2017 (9483 bars)
Test period         10.06.2010-02.12.2017 (6259 bars)
WFO test cycles     11 x 569 bars (137 days)
WFO training cycles 12 x 3224 bars (111 weeks)
Lookback period     700 bars (169 days)
Monte Carlo cycles  200
Fill mode           Realistic (slippage 5.0 sec)
Avg bar             5.1 ticks  470.5 pips range
Spread              0.7 pips (roll -0.70/-0.31)
Lot size            1.0
Capital invested    5000$
 
Gross win/loss      90452$-73672$, +20252p, lr 16325$
Virtual win/loss    90024$-74112$
Average profit      5309$/year, 459$/month, 27$/day
Max drawdown        -2025$ 12% (MAE -2287$ 14%)
Total down time     78% (TAE 93%)
Max down time       119 days from May 2011
Max open margin     656$
Max open risk       1863$
Trade volume        $10164943 (2610626$/year)
Transaction costs   -637$ spr, -365$ slp, -906$ rol, -1102$ com
Capital required    $2681
 
Number of trades    1370 (351/year)
Percent winning     44%
Max win/loss        810$ / -407$
Avg trade profit    12$ 10.4p (+99.0p / -56.5p)
Avg trade slippage  -0.27$ 0.2p (+1.1p / -1.4p)
Avg trade bars      16 (+23 / -10)
Max trade bars      141 (34 days)
Time in market      355%
Max open trades     14
Max loss streak     17 (uncorrelated 13)
 
Annual return       161%
Profit factor       1.38 (PRR 1.28)
Reward/Risk ratio   10.5
Sharpe ratio        1.94 (Sortino 1.98)
Kelly criterion     0.63
Ulcer index         12%
Scholz tax          14026 EUR
Cycle performance   1.39 1.40 1.31 1.33 1.37 1.38

Year Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Total%
2010              -2  20 -16  20 -13  22  10  -8   8   +38
2011  -1  15  15  -9  26  -1  -1  13   6  28   1   1  +135
2012   2  -9  21   1   3  -2  -0   3   1   0   7  13   +51
2013   6  13  -7  -4   9   6  -8  -4   0   1  -2  -8    -3
2014   6   3   0  -0   4  -2   3  -6   8  12  -9   5   +23
2015   5  -7   8  13  -1   0   0   1   3  -1  -5 -12   +17
2016  19   1   6   9   6 -10  10   3   1  -1   6  -2   +30
2017  -1  -7   6  -5   0  -0  -1   2   1  -5  -1   7    -6
 
Confidence level     AR   DDMax  Capital
 10%                236%  1440$  1390$
 20%                227%  1550$  1470$
 30%                218%  1680$  1570$
 40%                209%  1830$  1680$
 50%                202%  1940$  1760$
 60%                193%  2140$  1900$
 70%                186%  2340$  2040$
 80%                174%  2730$  2320$
 90%                166%  3080$  2580$
 95%                146%  4010$  3580$
100%                104%  5640$  4710$
 
Portfolio analysis  OptF  ProF  Win/Loss  Wgt%  Cycles
 
AUD/USD avg         .014  1.36  281/475   11.4  XXXXXXXXXX/
EUR/USD avg         .006  1.41   91/115    6.2  XXXXXX\XXXX
GER30 avg           .030  1.33   34/45     2.5  X/X/\.\XXXX
SPX500 avg          .077  1.75   96/162   16.4  XXXXXXXXXXX
USD/JPY avg         .006  1.69  130/175    8.7  XXXXXXXXXX/
XAG/USD avg         .008  1.35  113/152    5.3  \\XXXXXXXX/

BB avg              .010  1.29   82/111    2.6  X/\XXX/\XXX
CT avg              .019  1.38  106/126    7.6  XXXX\XXXXXX
CY avg              .017  1.83   60/66     5.6  /XXX\/XXX/X
ES avg              .025  1.34  163/275    7.4  XXXXX/XXXXX
HP avg              .017  1.34  332/440   19.2  XXXXXXXXXXX
LP avg              .009  1.54  106/219   11.3  \XXXXXXXXXX
VO avg              .014  1.71  217/288   24.2  /XXXXXXXXXX

AUD/USD:ES          .036  1.14   45/87     0.1  \\/\\//\\//
AUD/USD:ES:L        .036  1.14   45/87     0.1  \\/\\//\\//
AUD/USD:ES:S        .000  ----    0/0      0.0  ...........
AUD/USD:HP          .024  1.18   76/102    2.2  X/\XX\/XX\/
AUD/USD:HP:L        .024  1.13   47/68     1.2  //./\\///\/
AUD/USD:HP:S        .043  1.35   29/34     1.0  \/\\/./\\\/
AUD/USD:LP          .029  1.66   75/149    7.5  \/X///X\/X/
AUD/USD:LP:L        .029  1.80   42/71     6.8  \//////\/\/
AUD/USD:LP:S        .058  1.23   33/78     0.6  \/\///\\///
EUR/USD:CT          .009  1.11   22/33     0.5  X/\X\X\X/XX
EUR/USD:CT:L        .027  1.22   10/19     0.7  \/\/\\\\.//
EUR/USD:CT:S        .000  0.91   12/14    -0.2  /.\\\/\//\\
EUR/USD:HP          .036  1.32   45/59     2.4  \///\/\\\/\
EUR/USD:HP:L        .036  1.32   45/59     2.4  \///\/\\\/\
EUR/USD:HP:S        .000  ----    0/0      0.0  ...........
EUR/USD:VO          .027  2.20   24/23     3.3  .X.//.\X/XX
EUR/USD:VO:L        .027  1.58   12/11     0.9  ././...//\\
EUR/USD:VO:S        .032  2.90   12/12     2.5  .\../.\\.//
GER30:BB            .038  1.03    2/4      0.0  /......\\/\
GER30:BB:L          .038  1.03    2/4      0.0  /......\\/\
GER30:BB:S          .000  ----    0/0      0.0  ...........
GER30:CT            .256  1.92    3/2      0.6  \/./...\/..
GER30:CT:L          .256  1.92    3/2      0.6  \/./...\/..
GER30:CT:S          .000  ----    0/0      0.0  ...........
GER30:ES            .267  1.82    2/3      0.2  ..\/\../..\
GER30:ES:L          .000  ----    0/0      0.0  ...........
GER30:ES:S          .267  1.82    2/3      0.2  ..\/\../..\
GER30:VO            .051  1.31   27/36     1.7  ////\.\\/\/
GER30:VO:L          .051  1.31   27/36     1.7  ////\.\\/\/
GER30:VO:S          .000  ----    0/0      0.0  ...........
SPX500:ES           .110  1.44   17/21     1.3  //\/\///\\\
SPX500:ES:L         .110  1.44   17/21     1.3  //\/\///\\\
SPX500:ES:S         .000  ----    0/0      0.0  ...........
SPX500:LP           .006  1.04   17/55     0.2  \\/\\\\\//\
SPX500:LP:L         .006  1.04   17/55     0.2  \\/\\\\\//\
SPX500:LP:S         .000  ----    0/0      0.0  ...........
USD/JPY:BB          .057  1.51   24/36     0.6  \/\\/\/.\./
USD/JPY:BB:L        .000  ----    0/0      0.0  ...........
USD/JPY:BB:S        .057  1.51   24/36     0.6  \/\\/\/.\./
USD/JPY:CT          .016  1.46   16/19     2.0  \./..\/.\//
USD/JPY:CT:L        .016  1.46   16/19     2.0  \./..\/.\//
USD/JPY:CT:S        .000  ----    0/0      0.0  ...........
USD/JPY:HP          .024  2.24   34/29     6.4  .XX/\/\/.\/
USD/JPY:HP:L        .024  1.22    9/13     0.3  .\//\/\..\/
USD/JPY:HP:S        .028  2.65   25/16     6.0  ./\/.../.\/
XAG/USD:CT          .038  1.87   13/13     0.6  ....\///\\/
XAG/USD:CT:L        .000  ----    0/0      0.0  ...........
XAG/USD:CT:S        .038  1.87   13/13     0.6  ....\///\\/
XAG/USD:HP          .018  1.43   44/46     1.1  \\/X/\//X//
XAG/USD:HP:L        .105  4.16    6/3      0.3  .\./....\/.
XAG/USD:HP:S        .014  1.33   38/43     0.8  \\/\/\/////
XAG/USD:VO          .011  1.39   40/62     3.0  .\/\//\\\//
XAG/USD:VO:L        .000  ----    0/0      0.0  ...........
XAG/USD:VO:S        .011  1.39   40/62     3.0  .\/\//\\\//

The following table shows the meaning of the values. Most of the calculated values are only valid for a profitable report (Gross Win > Gross Loss); they are meaningless when the profit is negative. Indicated values with a "$" suffix are in units of the account currency (not necessarily US-$), indicated values with a "p" or "pips" suffix are in pips.

 
Bar period Bar period in seconds, minutes, or hours, and average bar duration in minutes. The average bar duration is the date difference of the last and first bar, divided by number of bars. It is used in subsequent figures for converting bar numbers to time periods - hours, days, or weeks - and might not reflect the real period. Variations in bar duration are caused by weekends, holidays, skipped bars, special bars, or gaps in the historical data.
Simulation period Time of the WFO run (usually 4..5 years) and the number of bars, without the preceding lookback period.
Test period Time and bar number of the test; simulation period without training and lookback.
WFO test cycles Number and length of the WFO test cycles in bars, hours, trading days, or calendar weeks. This is also the recommended period for re-training a WFO system. The given time periods are based on the average bar duration displayed above, and a 5-day week.
WFO train cycles Number and length of the WFO training cycles.
Lookback period Amount of data to be collected before test or training begins.
Monte Carlo cycles /
Confidence
Number of Monte Carlo simulation cycles, and selected confidence level (if any) for the following performance figures.
Fill mode Simulated fill mode (Naive or Realistic) and Slippage.
Avg bar Average number of price ticks and average high-low difference per bar in the simulation; for single asset strategies.
Spread Spread in pips, and long/short rollover fee in account currency units; for single asset strategies.
Commission Roundturn commission in account currency units; for single asset strategies.
Lot size Lot size in contracts or underlying units; for single asset strategies.
   
Gross win / loss Sums of all wins and all losses in account currency units, the overall volume-neutral result in pips, and the result by linear regression of the equity curve (ReturnLR). A negative pip result and a positive gross win - or vice versa - is possible due to different assets pip costs and different trade volumes.
Virtual win / loss Sums of all wins and all losses of phantom trades in currency units, for virtual hedging. Normally worse than the gross win / loss due to higher transaction costs.
Average profit Annual, monthly, and daily profit (workdays only); simply the difference between start and end balance divided by the number of periods.
Max drawdown / MAE Maximum drawdown during the test period, in account currency units and in percent from the gross profit. The drawdown is the difference between a balance peak and the lowest subsequent equity valley (balance = account value, equity = balance plus value of the all open trades). MAE (maximum adverse excursion) is the difference between an equity peak and the lowest subsequent equity valley. Drawdown is dangerous, MAE is not (see remarks below).
Total down time / TAE Percentage of time when the current equity is below a preceding balance peak. TAE (time in adverse excursion) is the time when trades are open and the current equity is below a preceding equity peak. Strategies often have up to 90% down time, but can be still profitable.
Max down time Longest drawdown duration, i.e. maximum time from a balance peak to the lowest subsequent equity valley. The BALANCE flag can be used for generating a balance chart and identifying the balance peaks in the profit curve.
Max open margin Maximum total margin allocated during the backtest period.
Max open risk Maximum loss when all open trades hit their initial Stop at the worst possible moment. Dependent on the stop distances and the likeliness of such an event, the Max open risk can far exceed the Capital required. This parameter has no meaning for virtual hedging strategies or for strategies that use no stop loss for some trades.
Trade volume Total and annualized value of all assets bought and sold, in units of the account currency (see remarks).
Transaction costs Total costs of spread (Spr), slippage (Slp), swap/rollover/margin interest (Rol) and commission (Com) for all trades. Slippage and rollover can increase the profit in some cases; costs are then positive. In test mode the simulated costs are displayed, in trade mode the real costs when available through the broker API. Otherwise the cost are estimated and can be inaccurate. Slippage cost in trade mode is calculated from the difference of price at order time and fill price.
Capital required Required initial capital for trading the strategy; equivalent to the maximum margin on non-leveraged accounts, and the sum of maximum margin and normalized maximum drawdown or MAE on leveraged accounts. This amount would be required when the strategy is entered at the worst possible moment of the simulation, for instance directly at a balance peak preceding a drawdown. For strategies that reinvest profits (Capital variable), the displayed value is multiplied with the ratio of initial and final balance, and thus can be much smaller than the maximum drawdown or open margin.
   
Number of trades Number of trades in the backtest period. Only real trades are counted, not phantom or pending trades.
Percent winning Percentage of winning trades.
Max win / loss Maximum win and loss of all trades.
Avg trade profit Average return of a trade in account currency units and in volume-neutral pips; separately listed for winning (+) and losing (-) trades. Robust strategies should return a multiple of the spread. Avoid systems that generate either many trades with small average returns, or few trades with very large average returns.
Avg trade slippage Average slippage cost of a trade in account currency units and in pips; separately listed for positive (+) and negative (-) slippage. In test mode it's the simulated slippage, in trade mode the real slippage.
Avg trade bars Average number of bars of a trade; separately for winning (+) and losing (-) trades.
Max trade bars Maximum time a trade was open.
Time in market Total time of all trades compared to the backtest time. This can be more than 100% when several trades are open at the same time. The smaller the time in market, the less exposed is the capital to the market risk.
Max open trades Maximum number of simultaneously open trades.
Max loss streak /
uncorrelated
Maximum number of consecutive losses during the test, and the theoretical number under the assumption of uncorrelated returns, i.e. equally distributed wins and losses. If the real number is noticeably higher, wins and losses tend to cluster with this strategy, and an equity curve trading algorithm could improve the performance.
   
Annual return (AR) Annualized profit divided by the required initial capital; main performance parameter for systems that don't reinvest profits. Equivalent to the Calmar Ratio. Depends on maximum drawdown and can thus be subject to random fluctuations (see remarks).
Return on margin
(ROI)
Annualized profit divided by maximum required margin. For accounts with no leverage, where the investment is mainly used for margin, not for buffering drawdowns,
Annual growth rate
(CAGR)
Compound annual growth rate of the investment; the nth root of the total equity growth, where n is the number of years in the test period. Displayed for strategies that have the Capital variable set and are assumed to reinvest profits. The annual return on capital (ROI) is also displayed.
Profit factor / PRR Gross win divided by gross loss. The pessimistic return ratio (PRR) is the profit factor multiplied by (1-1/sqrt(W))/(1+1/sqrt(L)); it gives a worse result when the number of trades is low.
Reward/Risk
ratio
End profit divided by maximum drawdown.
Sharpe / Sortino
ratio
The Sharpe ratio is the annualized ratio of mean and standard deviation of the bar returns when the system is in the market; the Sortino ratio is the annualized ratio of mean and semi-deviation. Bar returns are bar profits divided by invested or required capital. Bars with no open trades do not contribute. The Sharpe ratio is a popular performance gauge (see remarks) and should be > 1 for good strategies.
Kelly criterion Ratio of mean and variance of the bar returns when the system is in the market; bars with no trades do not contribute. The kelly criterion is the optimal investment factor for a single-asset, single-algo, single-trade per bar strategy to maximize the profit.
R2 coefficient Coefficient of determination; the similarity of the equity curve with a straight line ending up at the same profit. The closer R2 is to 1, the steadier are the profits and the better they will be possibly reproduced in real trading (see remarks).
Ulcer index Mean drawdown percentage; a measure of length and depth of drawdowns (see remarks). The higher the ulcer index, the stronger your stomach must be for trading the script. The ulcer index should be < 10% for preventing ulcer.
Scholz tax 26.375% of the gross win, minus EUR 10,000 for any trading year. Only displayed when tax is due and ScholzBrake is not set.
Cycle performance Separate profit factors of the oversampling cycles. High profit differences between cycles are a sign of an unstable strategy.
   
Year Annual and monthly returns, either in percent of the Capital required for non-reinvesting strategies, or as percent change from the preceding month or year for reinvesting strategies with nonzero Capital variable.
   
Monte Carlo
analysis
Performance analysis (see Monte Carlo Method; non-reinvesting strategies only) by evaluating many possible equity curves with different distributions of trades and returns. A strong serial correlation of trade returns can cause Monte Carlo results higher than the result from the real equity curve.
Confidence level Confidence level of the following performance parameters. F.i. at 95% confidence level, 95% of all simulations generated the same or better results, and 5% generated worse results.
AR Annual return at the given confidence level.
DDMax Maximum drawdown (not normalized) at the given confidence level.
Capital Capital requirement at the given confidence level.
   
Portfolio
analysis
Performance analysis per asset, per algorithm, and per component. Only components with trades are listed. The figures are taken from the last oversampling cycle; when the ALLCYCLES flag is set, they are taken from all cycles.
OptF OptimalF factors for portfolio components (see Money Management; non-reinvesting strategies only). When 0, the component was unprofitable in the test. Calculating OptimalF factors can take a long time when many trades were opened. When the NOFACTORS flag is set or Capital is nonzero with a reinvesting strategy, no factors are calculated.
ProF Profit factor (gross win divided by gross loss, including phantom trades). A '++++' in the column indicates that there were only winners, '----' indicates that there were no winners.
Win / Loss Number of winning and losing trades, including phantom trades.
Wgt% Weight of the component in percent; component profit divided by total profit. Indicates the contribution of the component to the whole strategy. The weight can be negative, f.i. with a losing component and a positive overall result.
Result Current profit or loss of the component in live trading.
Cycles Profit separated by WFO cycles. '/' is a winning cycle, '\' a losing cycle, 'X' is a cycle with both winning and losing components, and '.' is a cycle without trades.

Additionally, the performance can be evaluated by user criteria or stored for further evaluation with the user-supplied evaluate function.

Remarks

Keep in mind that that all those performance figures are derived from historical data (even when it's out-of-sample data). The future is unknown, so there is no guarantee to achieve the same performance in live trading. Many figures - f.i. Sharpe ratio, Monte Carlo analysis, drawdown extrapolation, R2 coefficient - are based on mathematical models that assume a Gaussian distribution of returns. However there is no guarantee that real returns always follow a Gaussian distribution. For those reasons, don't interpret too much into performance figures. Even a system with excellent theoretical performance can cause real loss of money.

* See Malik Magdon-Ismail / Amir Atiya, "Maximum Drawdown", 2004.

See also:

Testing, Chart, evaluate, report, Monte Carlo Simulation

► latest version online

PIP, PIPcost, Leverage

Asset parameters 2

PIP

Size of 1 price point (pip) of the current asset in units of the counter currency, f.i. 0.0001 for EUR/USD. Can be used to set a price in pips, f.i. Stop = 10*PIP. For converting a price difference to pips, divide it by PIP.

PIPCost

Value of 1 pip profit or loss per lot in units of the account currency; determined by the lot size (LotAmount) and the exchange rate of account currency and counter currency. This value should normally remain constant during the simulation for not adding artifacts to the strategy performance figures; but if desired for special purposes, it can be calculated by script to fluctuate with the exchange rate (see example below). When the asset price rises or falls by x, the equivalent profit or loss in account currency is x * Lots * PIPCost/PIP. The counter currency exchange rate - its value in account curreny units - is PIPCost / (PIP * LotAmount).

MarginCost

Required margin for puchasing and holding 1 lot of the current asset in units of the account currency.Either directly taken from the asset list, or calculated from the leverage by MarginCost = Asset price * PIPCost / PIP / Leverage. The number of lots at a given margin is Margin / MarginCost. For options and futures, MarginCost is calculated per underlying unit. The variable is updated in real time when the asset list contains a negatve MarginCost parameter that represents Leverage. For special margin requirements, f.i. for covered options or option combos, set this variable in the script to the maximum of initial, maintenance, and end-of-day margin per underlying unit before entering the trade.

Leverage

Asset value divided by margin cost. Determines the account's buying power, i.e. the asset amount that can be purchased with account currency. MarginCost and Leverage can be converted into each other:  Leverage = Asset price * LotAmount * CCValue / MarginCost or Leverage = Asset price * PIPCost / PIP / MarginCost, where CCValue is the value of the counter currency in account currency units. For brokers that support the SET_LEVERAGE command, Leverage and MarginCost must be set on the fly before entering a trade.

LotAmount

The number of contracts per Lot with the current asset. Determines the minimum order size and depends on the lot size of the account. For currencies, the lot size of a micro lot account is normally 1000 contracts; of a mini lot account 10000 contracts; and of a a standard lot account 100000 contracts. Some brokers offer also lot sizes that are a fraction of a contract, f.i. 0.1 contracts per lot for CFDs or diginal coins. Some brokers allow arbitrary lot amounts when they support the SET_AMOUNT command.

LotLimit

Maximum number of Lots with the current asset (default: 1000000000/LotAmount). Can be set to a smaller number for safety reasons. If the number of lots exceeds this limit, for instance due to a script bug, the order is not executed and an error message is issued.

InitialPrice

The initial asset price taken from the asset list. Can be set to the first historical price and used to adapt parameters (such as MarginCost) to asset price changes in the backtest (f.i. MarginCost = InitialMarginCost * priceClose() / InitialPrice). 

AskPrice

The last ask price of the current asset in live trading mode or by calling priceQuote. The last bid price is AskPrice - Spread.

Type:

var, read/only if not mentioned otherwise (edit the Asset List for permanently changing asset parameters).

 

AssetBar

Bar number of the last received price quote of the current asset, from 1 (first bar) up to Bar (current bar). Can be used to determine if there was a quote in the current bar.

Type:

int, read/only
 

Remarks:

Examples:

// set stop / profit target at a fixed pip distance
Stop = 10*PIP;
TakeProfit = 40*PIP;
// let profits fluctuate with the account currency exchange rate
function convertProfits(string Rate)
{
char OldAsset[15]; strcpy(OldAsset,Asset); // store original asset
if(!asset(Rate)) return;
var Price = price();
asset(OldAsset);
if(Price > 0.)
PIPCost = PIP*LotAmount/Price;
} // call this function from run(), f.i. convertProfits("EUR/USD"); // when account currency = EUR and counter currency = USD.

See also:

Stop, Spread, Lots, price, asset, asset parameters

 

► latest version online plot, plotBar, plotGraph, ...

Plot functions

Zorro can generate charts, histograms, or scatter plots, and export the data in CSV or image format for further evaluation. The profile library contains often-used functions for diagrams. The following plot commands are available:

plot(string Name, var Value, int Type, int Color)

plot(string Name, vars Data, int Type, int Color)

Plots a variable, or the first element of the Data series, on the current bar position on the chart. The value can be plotted as a dot, line, bar, or band, dependent on Type. The plot is linked to the current asset. 

plotBar(string Name, int Pos, var Label, var Value, int Type, int Color)

Adds a value to a given x axis position in a histogram. A histogram consists of vertical bars that display the sum, average, standard deviation, maximum, or minimum of the values added to any x position. It can be exported to a CSV file for further evaluation.

plotHistogram(string Name, var Value, var Step, var Weight, int Color): int

Variant of plotBar for plotting a histogram bar. The Value range is dividied in equidistant Steps. Bars belonging to the same Step are accumulated by adding their Weights. Returns the x axis position of the bar.

plotGraph(string Name, var X, var Y, int Type, int Color)

Plots a dot, symbol, line, spline, or polygon at a given X,Y position with the given Color either to the current chart, to the current histogram created by plotBar, or to a scatter plot when Type was combined with GRAPH. Call this function to mark certain events in the price chart.

plotText (string Name, var X, var Y, string Text, int Style, int Color)

Displays the Text with the given Color at a given X,Y relative or absolute position (dependent on Style, see below)  on the current chart, scatter plot, or histogram. Text on relative positions scrolls with the other chart elements and is only visible when the chart is above a zoom factor determined by PlotText.

plotData (string Name): DATA*

Returns a pointer to the DATA struct with the data stored by a chart plot or histogram with the given Name. The DATA struct is defined in include\trading.h. DATA->Data[DATA->start] is the first valid data point, and DATA->Data[DATA->end] is the last. The data points are in chronological order. DATA->Data[n] is the data point at bar number n, and its timestamp is therefore wdateBar(n). The function can be used for retrieving the plot curve, for analyzing it, for modifying data before plotting, or for exporting it to other charting tools.

plotChart (string FileName)

Generates a chart or histogram from the previous plot calls, and displays it with the chart viewer or stores it in a .png file with the given FileName when PL_FILE is set. Exports it to a CSV file when PL_EXPORT is set. Deletes all plot elements so that new plots can be built afterwards. Use this function to update charts or histograms in real time (see PayOff.c). If this function is not used, the chart is generated at the end of a backtest or when clicking [Result].
 


Chart with candles and indicators, created with plot commands


Autocorrelation histogram created with plotBar commands


MAE scatter plot, created with plotGraph commands.

 

Parameters:

Name The name for the chart legend; a string constant with up to 15 characters. Different curves - including the upper and lower curve of a band - must have different names. If the name begins with '#', it does not appear in the chart legend.
Pos The bar number in a histogram in the -4000..4000 range. Determines the bar's position on the horizontal axis. All plotBar commands with the same bar number affect the height of the bar in various ways, dependent on the Type parameter.
X,Y In a histogram, X is the bar number in -4000..4000 range, and Y is the position on the vertical axis.
In a scatter plot, X and Y are horizontal and vertical coordinates in arbitrary units.
In a price chart, X is the bar offset (0 = current bar), and Y is the position on the left or right vertical axis.
Label A number printed on the horizontal axis at the given Pos position, or NIL for no label. For better readability, label numbers should be in the ±0.1 .. ±1000 range and should be not too tight together.
Value The data value to be plotted or added to the bar. Use 1 in combination with SUM for counting events. For plotting an int, cast it to var.
Text Short text to be plotted on the chart; up to 15 characters. For longer texts put several plotText calls together.
Data The data series whose most recent value is plotted.
Type

The type of the data curve or element in the chart or histogram. All elements with the same Name must be of same Type. Use 0 for a simple line on the chart, or a combination of:

BARS
- plot a bar or vertical mark. For plot and plotBar.
DOT - plot a colored dot. For plot, plotBar, and plotGraph.
BAND1 - for plot, plot the upper line of a band, use the color for the upper and lower lines. Main chart only.
BAND2 - for plot, plot the lower line of a band, use the color for filling the band. Main chart only.
LINE - for plot, use a thick line. Otherwise it's a thin line.
AVG - for plot, use the average of all sample cycles.

MAIN - for plot, plot this and all following curves in the main price chart.
NEW - for plot, plot this and all following curves in a new extra chart (see remark below about plot order).
AXIS2 - use a second y axis on the right side of the chart.
LOG - use a logarithmic y scale.

AVG - for plotBar, plot the average of all bar values at the current x position.
DEV
- for plotBar, plot the standard deviation of all values at the current x position.
SUM - for plotBar, plot the sum of all values at the current x position.
MINV - for plotBar, plot the minimum of all values at the current x position.
MAXV - for plotBar, plot the maximum of all values at the current x position.
NRM - for plotBar, normalize the values of all bars to 1.

LINE - for plotGraph, draw a straight line from the last position to the current position.
SPLINE - for plotGraph, draw a curved line from the last position to the current position.
END - for plotGraph, end point of a line or polygon started with LINE.
GRAPH - for plotGraph, plot the element to a scatter plot. 
DEL - for plotGraph, delete the previous plot and start over.
DOT - draw a colored dot. The size can be determined with PlotScale.
SQUARE - draw a colored square.
TRIANGLE - draw a upwards pointing colored triangle.
TRIANGLE2,3,4 - draw a colored triangle pointing to the left, right, or down.
CROSS - draw a colored cross.
CROSS2 - draw a colored diagonal cross.

Style Text style; all texts with the same Name must have the same Style. Text centered = 0 (default), aligned to Bottom Left = 1, Bottom Center = 2, Bottom Right = 3, Left = 4, Right = 6, Top Left = 7, Top Center = 8, Top Right = 9. Add 32 for text at absolute X,Y pixel position (0,0 = upper left corner of the chart). Add 64 for text on a white background. Add 128 for large bold text.
Color Color and transparency of the data curve, bar, symbol, or text, in the format as described under Colors.. Elements on a histogram or scatter plot can have different colors; on a chart all elements with the same Name must have the same Color. If at 0, nothing is plotted, but the data is stored for later analyzing it by plotData. Use the color function for generating color ranges, and the plot2 function for plotting curves in two colors.

Remarks:

Examples (see also profile.c, indicators.c):

// Compare price distribution (red) with random distribution (blue)
function run()
{
  vars Price = series(price(0));
  int num = NumRiseFall(Price,20);
  plotBar("Price",2*num,num,1,SUM|BARS,RED);	

  vars Random = series(0);
  Random[0] = Random[1] + random();
  num = NumRiseFall(Random,20);
  plotBar("Random",2*num+1,0,1,SUM|BARS,BLUE);	
}
// plotGraph test
function run()
{
  set(PLOTNOW);
// plot green dots above the price at every 20th bar
  if(Bar%20 == 0)
plot("Dotted",1.01*price(),DOT,GREEN);
if(Bar == 500) { // plot a single blue rectangle in the price chart plotGraph("Rectangle",0,0.99*price(),LINE,BLUE); // start point plotGraph("Rectangle",-500,0.99*price(),LINE,BLUE); // 1st corner plotGraph("Rectangle",-500,1.01*price(),LINE,BLUE); // 2nd corner plotGraph("Rectangle",0,1.01*price(),LINE,BLUE); // 3rd corner plotGraph("Rectangle",0,0.99*price(),LINE|END,BLUE); // 4th corner and end point // plot a single red dot plotGraph("Single Dot",-250,price(),DOT,RED); } }
// plot tiny triangles above / below price for buy and sell signals
PlotScale = 8; // size of triangle
if(SignalBuy) plot("Buy",0.9*priceLow(),MAIN|TRIANGLE,GREEN);
if(SignalSell) plot("Sell",1.1*priceHigh(),MAIN|TRIANGLE4,RED);
// plot a chart title
if(Init) plotText("Title",60,25,"My Chart",32+64+128+7,BLACK);
Examples of plotting symbols can be found in the Predict script and in the profile library.

See also:

printf, PlotMode, PlotScale, plotProfile, dataChart, Colors, color

► latest version online PlotBars, PlotScale

Chart variables

PlotBars

Maximum number of bars to plot in the chart, or 0 for plotting all bars (default). A positive number plots the bars from the start, a negative number from the end. Enter a small number for increasing the scale of the chart and zoom in to the start or to the end.

PlotStart

Start bar number of the chart. Can be used to start the chart at a certain bar, f.i. 10 bars before the opening a trade (for this set PlotStart = Bar-10; in the trade trigger code). If at 0 (default), the chart starts 10 bars before the end of the LookBack period. 

PlotDate

Alternative start of the chart, as a date in the yyyymmdd format.

PlotScale

Candle and symbol width in pixels (default 5). Use a negative number for displaying hi-low bars instead of candlesticks. The candle type is automatically switched between black/white candles, hi/low bars, and plain lines dependent on this variable and the chart size.

PlotText

Zoom threshold for visibility of text labels on the chart, in percent of a candle width (default = 1 = text visible on candles > 0.01 pixel). 

PlotLabels

Distance in bars between x-axis labels in histograms (default = 0 = label at any bar). Use this for better readability of labels in tight histograms.

PlotBorder

The size of the border around the chart where the axis labels are displayed, in pixels (default: 50).

PlotWidth

Maximum chart image width in pixels (default 2000), or 0 for no chart. The candle width is accordingly reduced when the chart exceeds PlotWidth. Note that too-large chart images can't be displayed with most image viewers. The initial chart width in the interactive chart viewer is 600 pixels plus borders.

PlotHeight1

Height of the main chart with the price bars in chart images, in pixels (default 480). 

PlotHeight2

Height of additional charts (plot with type=NEW), in pixels (default 160). Also used for the parameter or contour charts in training, and for determining the proportions of additional charts in relation to the main chart in the interactive chart viewer.

PlotPeriod

Period in minutes for updating the chart on the trade status page (default: 60 = 1 hour).

Range:

0..999999

Type:

int

Remarks:

See plot. For removing chart elements such as price candles or equity curves, set their Color to 0.

Example:

function run()
{
  PlotBars = 2000;
  ...
}

See also:

plot, PlotMode, profile, Color, StartDate

 

► latest version online PlotMode

PlotMode

Determines what's plotted at the end of a test or when clicking [Result]. Use setf and resf for setting and resetting the flags.

Flags:

PL_LONG  - begin the chart already with the LookBack period. Otherwise the chart begins a few bars before the test or trade period.
PL_ALL  - plot all asset specific curves, regardless of the selected asset (except for price curve and trades).
PL_ALLTRADES - plot all trades, regardless of the selected asset.
PL_FINE - plot equity and price curves in higher resolution.
PL_DIFF - plot the equity/balance difference to the initial value, rather than the total value. For better visibility of small equity changes when Capital is invested.
PL_FILE - export the chart to a .png image, rather than opening the chart viewer. The file name is composed from the script name, an optional asset name, and the LogNumber.
PL_TITLE - print the chart title in the upper right area of the chart.
PL_BENCHMARK - plot the equity as a line, instead of a bar graph, for comparing equity curves with benchmarks.
PL_DARK - use a dark theme with a black background for the chart.

Type:

int

Example:

function run()
{
  ...
  setf(PlotMode,PL_ALL|PL_FINE);
  ...
}

See also:

plot, PlotScale, Colors, setf, resf, mode

 

► latest version online polyfit, polynom

polyfit (var* Coeff, var* Data, int Length, int Order, var Weight) : var

Polynomial regression. Generates a polynomial of the form y = anxn + ... + a1x + a0  that is the best fit to a section of a price series or any other data series. This polynomial can be used for extrapolating the Data series into the future, and thus predicting future prices.

Parameters:

Coeff A var[8] array for storing the calculated polynomial coefficients an, or 0 for storing the coefficients internally.
Data Data series to be approximated by the polynomial.
Length Number of elements in the Data series to be approximated by the polynomial.
Order Order of the polynomial (1..7). Use 1 for linear regression, 2 for parabolic (quadratic) regression, and higher numbers for nth-order regression.
Weight Ratio of the weight of the last data value to the weight of the first value, for "fading-memory" polynomials. Use 1 for equal weights.

Returns

Correlation coefficient, normally in the 0..1 range. Gives the similarity of the price curve and the polynomial.

Modifies

Coeff - set to the coefficients of the polynomial, in the order of their index, starting with Coeff[0]. The remaining coefficients are set to 0.

 

polynom (var* Coeff, int Offs) : var

Returns the value of the polynomial with the given coefficients at a given offset.

Parameters:

Coeff A var[8] array that contains the polynomial coefficients an, or 0 for using the last coefficients generated by polyfit.
Offs The offset into the polyfit Data series of the returned polynomial value. Use negative offsets for extending the series into the future.

Returns

Value of the polynomial at the given bar offset.
 

Remarks:

Examples:

// least square moving average indicator
var LSMA(vars Data,int Period,int Offset)
{
  polyfit(0,Data,Period,1,0);
  return polynom(0,Offset);
}

// quadratic least square moving average indicator
var QLSMA(vars Data,int Period,int Offset)
{
  polyfit(0,Data,Period,2,0);
  return polynom(0,Offset);
}

// predict price change by parabolic regression
function run()
{
vars Diff = series(price(0)-price(1)); var Correlation = polyfit(0,Diff,15,2,1);
// sum up the differences for predicting the price change over the next 3 bars
var Change3 = polynom(0,-1)+polynom(0,-2)+polynom(0,-3);

plot("Prediction",price(0)+Change3,MAIN,BLUE);
plot("Correlation",Correlation,NEW,GREEN);
}

See also:

frechet, advise, predict

► latest version online predict

predict (int Type, vars Data, int TimePeriod, var Threshold): int

Predicts an event, such as a crossover of two curves or a peak of a curve, several bars before it happens. A polynomial regression function is used for the prediction. This function can be used to generate early trade signals.

Most trade systems analyze the recent price curve with functions or indicators that introduce more or less lag. This means that trade signals are always late, which reduces the profit of a system. One way to minimize lag is using low-lag functions (for instance, higher-order lowpass filters instead of moving averages). Another way is predicting the signals before they actually occur. This is the purpose of the predict function that works by extrapolating signal curves into the future.

Parameters:

Type Event to be predicted:
CROSSOVER - crossing of Data over the zero line
PEAK - Data peak
VALLEY - Data valley
+PARABOLIC - use parabolic instead of linear regression.
Data Data series to be predicted, with a minimum length of TimePeriod.
TimePeriod Number of Data points used for the prediction. Do not use more date than a typical 'swing' of the curve.
Threshold Prediction threshold, or 0 for no threshold.

Returns

Bar offset (negative) of the predicted event, in the -TimePeriod..0 range. 0 predicts that the event will happen in the next bar. -TimePeriod is returned when no event is predicted.

Modifies

rMomentum - Predicted Data movement per bar at the time of the event.

Remarks:

Examples:

function run()
{
  vars Prices = series(price());
  var LP50 = LowPass(Prices,50);
  var LP150 = LowPass(Prices,150);
  
  var CO = predict(CROSSOVER,series(LP50-LP150),10,0.5*PIP); // predict crossover
  var CU = predict(CROSSOVER,series(LP150-LP50),10,0.5*PIP); // predict crossunder
    
  plot("LP50",LP50,0,RED);
  plot("LP150",LP150,0,BLUE);
  plot("CrossOver",CO,NEW,BLUE);
  plot("CrossUnder",CU,0,RED);
}
// Trading with crossover vs. trading with prediction
#define USE_PREDICT
function run() 
{
  BarPeriod = 1440;
  asset("SPX500");
  vars Osc = series(StochEhlers(series(price()),10,20,10));
#ifndef USE_PREDICT  // use normal crossover
  if(crossOver(Osc,0.8)) 
    enterShort();
  if(crossUnder(Osc,0.2))
    enterLong();
#else                // use predicted crossover
  if(predict(CROSSOVER,series(Osc[0]-0.8),10,0.01) > -5) 
    enterShort();
  if(predict(CROSSOVER,series(0.2-Osc[0]),10,0.01) > -5) 
    enterLong();
#endif
}
Examples of signal prediction can also be found in the Predict and Ehlers scripts.

See also:

frechet, advise, polyfit, crossOver, peak/valley, predictMove, predictSeason 

► latest version online price

Price and Volume functions

price(int offset) : var

Returns the TWAP (time-weighted average price) of the selected asset. TWAP is the mean of all price ticks of the bar or time frame with the given offset. For indicators the mean price is normally preferable to the close price, since it is less susceptible to random fluctuations and makes systems more robust and less dependent on the data feed.

priceO(int offset) : var

priceC(int offset) : var

priceH(int offset) : var

priceL(int offset) : var

Returns the open, close, maximum and minimum price of the current asset and the bar or time frame with the given offset. priceC(0) returns the current price.

priceReal() : var

Returns the current real price when OHLC prices have been artificially calculated by special bars. The TICKS flag must be set.  

marketVal(int offset) : var

marketVol(int offset) : var

Returns additional market data, such as spread, quote size, trade volume, accumulated volume, tick rate, or quote frequency - see remarks and examples below. The type of data returned depends in live trading on the Broker API, and in backtesting on the fVal and fVol fields in .t6 historical data, or on the ask-bid difference and volume field in .t1 or .t2 data. Normally marketVal returns the average historical spread of the last bar period, and marketVol returns the average historical trade volume of the last bar period. For the total historical volume of a bar, multiply marketVol() with BarPeriod divided by the historical data resolution in minutes. The market functions require Zorro S and no LEAN flag.

Parameters:

offset Bar or time frame number for which the prices are returned, relative to the current bar or time frame. 0 for the the current bar or time frame. Negative offsets return prices of future bars or time frames when the PEEK flag is set; otherwise they give an error message.

Returns:

Price or raw market data.
 

seriesO(): vars

seriesH(): vars

seriesL(): vars

seriesC(): vars

Returns pointers to a temporary open, high, low, and close price series of the current asset. Can be used as a price series parameter to any function or indicator, or for referencing a price N bars ago (example: var PreviousClose = (seriesC())[1]; - mind the parentheses). Unlike real series, they don't consume memory and can be conditionally called. Temporary series do not support TimeFrame and special bars, and keep their content only until the next indicator call.
  

priceSet (int offset, var Open, var High, var Low, var Close)

Modifies the open, high, low, close, and mean price of the current asset at the given bar, for dummy assets, artificial price curves, displaying special candles, or using a different price source. Use offset = -1 for modifying the prices of the next bar when trades should be entered at the modified prices. Does not affect intrabar prices in TICKS mode.

priceQuote (var Timestamp, var Quote) : int

Enters a new price quote of the current asset in the system; especially for HFT simulation or when prices are not available from the broker connection. Filters outliers and updates the current best ask (AskPrice) and best bid (AskPrice - Spread). Increases Bar after every BarPeriod when no run function is used. Price quotes are also printed to the log when Verbose is at 3 or above, so be careful with Verbose for preventing awfully large log files.

Parameters:

offset Optional bar number for which the prices are returned, in time frames before the current time frame. If 0, the price of the current bar is returned. Negative offsets return future prices when the PEEK flag is set; otherwise they give an error message.
Timestamp The exchange timestamp of the quote in DATE format. Quotes older than the previous quote are ignored.
Quote The price. Quote > 0 indicates an ask price, Quote < 0 a bid price.

Returns:

1 if the price quote was accepted, 0 if an Outlier was detected or the timestamp was outdated.
  

priceRecord ()

Appends the current OHLC candle and the current spread and volume of the current asset to the begin of the price history in .t1 or .t6 format; only in [Trade] mode and after the INITRUN. This allows recording live prices for re-training or other purposes.
 

Remarks:

Examples:

BarPeriod = 60; // set up 1 hour bars (60 minutes)

TimeFrame = 4;
asset("EUR/USD");
vars EUR4Hs = series(price(0));	// create a series of 4 hour EUR mean prices
...
TimeFrame = frameSync(24);
asset("SPX500");
vars SPXDays = series(priceC(0));	// create a series of daily S&P 500 close prices
TimeFrame = 1;
...
// plot a daily spread histogram
void run()
{
  BarPeriod = 10;
  PlotLabels = 4;
  plotBar("Spread",
    (60*hour(0)+minute(0))/BarPeriod,tod(0),marketVal(0),AVG,RED);
}
// convert accumulated daily trade volume to per-bar volume
var volumeDiff(var* Buffer,var Volume)
{
// Buffer[1] = previous accumulated volume
  if(Buffer[1] > Volume) // day change? Reset volume
    Buffer[1] = 0;
  Buffer[0] = Volume-Buffer[1]; // per-bar volume difference
  Buffer[1] = Volume;
  return Buffer[0]; 
}

void run()
{
  BarPeriod = 1;
  LookBack = 0;
  
  assetList("AssetsIB");
  brokerCommand(SET_PRICETYPE,2); // trade prices
  brokerCommand(SET_VOLTYPE,4); // trade volume
  
  vars VBuffer = series(0,-2);
  var Volume = marketVol(0);
  if(Live) // if marketVol is daily accumulated volume
    Volume = volumeDiff(VBuffer,Volume);
  printf("# Vol %.0f",Volume);
}	

See also:

enterLong/Short, series, asset, Spread, PIP, Detrend, dayHigh/Low, History

► latest version online printf, print, msg

printf (string format, ...)

Prints text and variables in a log file and the message window in [Test] and [Trade] modes. This is a standard C function. It allows to display messages and variables.

print (int to, string format, ...)

Similar to printf, but prints text and variables to a target channel given by the to parameter, such as the log file, a .csv spreadsheet file, the HTML status page, or the performance report.

msg (string format, ...): int

Displays text and variables in a modal or nonmodal message box with [Yes] and [No] buttons, and waits for the user to click one of the buttons. Format strings beginning with '#' open a nonmodal box.

Returns

0 when [No] was clicked, 1 when [Yes] was clicked.

Parameters:

to

TO_WINDOW - print to the message window and the log file (default for printf).
TO_LOG - print in [Test] mode only to the log file, in [Trade] and in STEPWISE mode also to the message window.
TO_FILE - print in [Test] and [Trade] mode to the log file.
TO_ANY - print in all modes to the message window and log file (= TO_WINDOW+TRAINMODE).
TO_DIAG - print to the diag.txt file (see Verbose). Note that log files and diag.txt file are not yet open in the INITRUN.
TO_ALERT - print to an alert box and stop black box recording when the ALERT flag is set for Verbose.
TO_OUT - print to a file with the script name and extension ".out" in the Log folder.
TO_CSV - print to a file with the script name and extension ".csv" in the Data folder, for exporting data.
TO_CSVHDR - print to the first line of the CSV file; for writing a header before writing the data..
TO_REPORT - print in the EXITRUN to the performance report, for displaying additional performance parameters.
TO_HTML - print in [Trade] or in STEPWISE mode to the HTML file that displays the live trading status. HMTL format codes can be included in the text.
TO_TITLE - print to the title bar.
TO_INFO - print to the info field. print(TO_INFO, 0) displays the current account or simulation state in the info field.
TO_PANEL - print to the caption bar of the current control panel.
TO_CHART - print to the caption bar and - if PL_TITLE is set - to the title of the current chart.
+TRAINMODE - print also in [Train] mode.

format

C-style format string, containing arbitrary text with placeholders for subsequent variables (see format codes). The placeholders begin with a percent sign '%' and define the format of the displayed number. Examples: "$%.2f" prints a var with a dollar symbol and 2 decimals; "%i" prints an int; "%+4.1f" prints a var with +/- sign, 4 digits, and one decimal. For printing float variables, use the %f placeholder and typecast them to (var).

... Expressions or variables, one for each placeholder in the format text.

Remarks:

Examples:

// printf example
var vPrice = 123.456;
float fDiscount = 1.5;
int nNumber = 77;
...
printf("\nPrice %.3f for %d items, total %.3f, discount %.1f%%",
  vPrice, nNumber, nNumber*vPrice, (var)fDiscount);
// HTML status message example
if(ATR(30) < Threshold)
  print(TO_HTML,"<br>Low volatility - ATR30 = %.3f",ATR(30));
// nonmodal box example
function run()
{
  if(is(INITRUN))
    msg("#Nonmodal message box.\nClick [Ok] to exit script!");
  if(is(AFFIRMED))
    quit("Ok!");
  Sleep(10);
}
// CSV file output example
function run()
{
  LookBack = 100;
  asset(Asset);
  print(TO_CSVHDR,"Date,Price,SMA,EMA\n");
  print(TO_CSV,"%s,%.4f,%.4f,%.4f\n",
    strdate(YMDHMS,0),
    priceC(),
    SMA(seriesC(),50),
    EMA(seriesC(),50));
}

See also:

sound, string, Editor, keys, sprintf, strf, progress, watch, format codes

► latest version online

plotPriceProfile, plotWeek, plotMAEGraph

profile.c - price, trade, correlation, and seasonal analysis

The profile.c library contains functions for generating price or trade profiles, MAE charts, or seasonal analysis charts in [Test] mode. The functions are normally used during strategy development for analyzing trade profits or for finding seasonal effects. They can also be used as templates for building user-specific profile charts.

plotPriceProfile (int bars, int type)

Generates a price difference profile starting at the current bar. This function is normally called by a trade signal for checking its predictive power. It produces a histogram of the mean price movement following the signal. The red bars in the histogram are the averaged mean price difference to the entry point in pips, the blue/grey bars are their standard deviations, divided by 4 for better scaling.


Price difference profile at entering a trade with the workshop 4 system. Note the small adverse excursion after 24 hours.

Parameters:

bars Number of bars to plot in the price profile..
type

Type of the plot.
0 = plot price difference in pips to the first bar.
1 = plot price change in pips from the previous bar.
2 = plot negative price difference to the first bar (for short trades).
3 = plot negative price change from the previous bar.


 

plotTradeProfile (int bars)

Generates a profit distribution chart after the test run. The distribution shows the frequency of trades dependent on their profit or loss. The blue bars in the chart are the number of trades (right y axis), the red bars are the sum of their returns in pips (left y axis). The x axis is the trade profit or loss in pips. The example chart below shows a system with about 100 trades that hit their profit target at 100 pips, and about 30 trades that hit their stop at 100 pips loss. Inbetween there's a distibution of trades with mostly small losses. Note that the shown profit/loss is not exactly 100 pips due to spread and slippage.


Profit distribution of a system with Stop and TakeProfit distances both at 100 pips.

Parameters:

bars > 0 Number of bars to plot in the profit distribution. The more bars, the finer the resolution.
bars < 0 Step width of the profit distribution in pips per bar. The smaller the step width, the finer the resolution.

 

plotMAEGraph (int Type)

plotMAEPercentGraph (int Type)

plotMFEGraph (int Type)

plotMFEPercentGraph (int Type)

Generates a Maximum Adverse Excursion / Maximum Favorable Excursion graph after the test run. The Maximum Adverse Excursion (MAE) is the worst drawdown of an individual trade, the difference of its highest interim profit to its worst interim loss. The Maximum Favorable Excursion (MFE) is the opposite. The graph displays any trade as a dot in a chart showing its MAE or MFE on the x axis and its profit on the y axis in pip units or in percent of the price. Winning trades are green, losing trades are red. This graph can be used for analyzing the exit behavior of the trades and finding the optimal stop or take profit distances.


MAE graph of the Workshop 5 system.

Parameters:

Type Not used yet - always 0.

 

plotDay (var value, int type)

plotWeek (var value, int type)

plotMonth (var value, int type)

plotYear (var value, int type)

Seasonal analysis; generates a day, week, month or year distribution of a given value in a histogram (see also seasonal strength). This way seasonal effects of a trading system, a variable, or a price curve can be found. The red bars in the chart are the average value at a certain hour or day, the blue bars are their standard deviations divided by 4 for better scaling.


Week statistics with hourly returns of the workshop 4 EUR/USD system. Note the seasonal oscillation of the standard deviation - a price curve effect known as heteroskedasticity.

 

plotWFOCycle (var value, int type)

Analyzes the development of the value over a WFO cycle (x axis = trading days since cycle start). Can be used to determine if the profit deteriorates towards the end of the cycle, thus suggesting shorter cycles.

Parameters:

value Value to be evaluated, for instance price(0), Spread, Equity, etc..
type

0 = evaluate value difference to the season begin.
1
= evaluate value difference to the previous bar.
4 = evaluate absolute value.


 

plotDayProfit ()

plotWeekProfit ()

plotMonthProfit ()

plotQuarterProfit ()

plotWFOProfit ()

Plots bars equivalent to the gain or loss of the previous day, week, month, quarter, or WFO cycle in a new window in the price chart (thus, clicking [Result] is required). The blue bars are the profit in units of the account currency, the red bars are the loss. Since the bars represent the previous period, they are shifted by one period.


Quarterly profit/loss of the Workshop 4 EUR/USD system.
 

 

plotCorrelogram(var Value, int Lag)

plotCorrelogram(var Value1, var Value2, int Lag)

Generates in the EXITRUN a histogram of the correlation of a variable with itself, or with another variable, for sequential values of lag. On random data, such autocorrelations should be near zero for all time lags. But if the data is non-random, one or more of the autocorrelations will be significantly non-zero.

The above correlogram displays a significant correlation (0.14) of the variable with its value from the previous bar. The correllations with earlier bars (2...49) are not as significant.

Parameters:

Value1 First variable to be evaluated, for instance price(0)-price(1).
Value2 Second variable to be evaluated.
Lag

Maximum autocorrelation lag.

 

plotHeatmap(string name, var* Data, int Rows, int Cols)

plotHeatmap(string name, mat Matrix)

Plots a heatmap in the EXITRUN from the given Matrix or the Data array of size Rows*Cols, usually a correlation matrix. The matrix elements must be in the 0..1 range, and are displayed as squares with a color range from blue (0) to red (1). Since the heatmap is simulated with colored dots, you might need to scale the chart before exporting so that it looks nice. Examples of heatmap usage can be found on Financial Hacker. A different type of heatmap - a contour chart - can be plotted with the dataChart function.

Parameters:

Data Pointer of a var array with Rows * Cols elements.
Rows, Cols Array size.
 

plotBuyHold(string Benchmark, int Color)

Compares the equity curve with a benchmark. Call this function at any run for plotting the buy-and-hold profit curve of the given Benchmark asset - for instance, "SPY" - in the given Color. The position size is detemined by Leverage and Capital. If Capital is zero, the position size is given by Lots. The equity curve is displayed with a blue line for better comparison.
 

plot2(string Name,var Value,int Type,int Select,int Color1,int Color2)

Like plot, but plots a two-color curve. Select can be a boolean expression; at true (nonzero) the curve is plotted in Color1, otherwise in Color2.
 

Remarks:

Examples:

#include <profile.c>
// Price difference profile
function run()
{
  vars Trend = series(LowPass(series(price()),500));
  if(valley(Trend)) {
    plotPriceProfile(40,0);
    enterLong();
  } else if(peak(Trend)) {
    plotPriceProfile(40,2);
    enterShort();
  }
}
#include <profile.c> 
// Trade distribution or quarterly profit/loss
function run()
{
  vars Trend = series(LowPass(series(price()),500));
  Stop = 100*PIP;
  TakeProfit = 100*PIP;
  if(valley(Trend))
    enterLong();
  else if(peak(Trend))
    enterShort();

  PlotHeight1 = 320;
  PlotScale = 4;
  plotTradeProfile(50);
}
#include <profile.c>
// Weekly return analysis or quarterly profit/loss
function run()
{
  vars Trend = series(LowPass(series(price()),500));

  if(valley(Trend))
    enterLong();
  else if(peak(Trend))
    enterShort();

  PlotHeight1 = 320;
  PlotScale = 3;
  set(PLOTNOW);
  plotWeek(Equity,1);
//plotQuarterProfit(); }
#include <profile.c>
// Correlogram of High-to-High differences of selected asset
function run()
{
  PlotHeight1 = 320;
  PlotScale = 10;
  plotCorrelogram(priceHigh(0)-priceHigh(1),50);
}
#define DAYS  252 // 1 year
#define NN    30  // max number of assets
#include <profile.c>
// plot a heatmap of asset correlations
function run()
{
  BarPeriod = 1440;
  StartDate = NOW;
  LookBack = DAYS;

  vars Returns[NN];
  var Correlations[NN][NN]; // NN*NN matrix
  int N=0;
  while(asset(loop("USD/CAD","EUR/USD","GBP/USD","USD/JPY"))) 
    Returns[N++] = series(RET(1));

  int i,j;
  if(is(EXITRUN)) {
    for(i=0; i<N; i++)
      for(j=0; j<N; j++)
        Correlations[N*i+j] = Correlation(Returns[i],Returns[j],DAYS);
    plotHeatmap("Correlation",Correlations,N,N);
  }
}
// Plot a 2-color RSI
var RSI2(vars Data,int TimePeriod)
{
  var RSIVal = RSI(Data,RSIPeriod);
  plot2("RSI2",RSIVal,BARS,RSIVal > 50.,GREEN,RED);
  return RSIVal;
}

See also:

plot, plot parameters, colors, season

► latest version online progress

progress (int n1, int n2): int

Displays a red/green progress bar during a [Test] run. Prevents unresponsiveness during long computations or external function calls.

Parameters:

n1

Length of the first part of the progress bar in percent. Green when > 0, red when < 0.

n2

Length of the second part of the progress bar in percent; green when > 0, red when < 0, 0 for a neutral bar color.

Returns:

0 when the [Stop] key was hit, nonzero otherwise.

Remarks:

Example:

if(!progress(0,0)) return;

See also:

printf, wait

► latest version online

putvar, getvar

putvar (string FileName, string VarName, var Value)

getvar (string FileName, string VarName): var

Writes, updates, or reads a variable to or from a file or the Windows registry. Can be used for storing data globally or exchanging data between different scripts or Zorro instances.

Parameters:

FileName Name of the file. If it does not exist, it is created. If 0, the variable is stored in the Windows registry.
VarName Name of the variable.
Value Value to be written or updated.

Returns:

Current variable value (getvar). If the variable does not yet exist, 0 is returned.

Remarks:

Example:

function increase_global_counter(string Name)
{
  string FileName = "\\Log\\Counters.csv";
  lock();
  int Counter = getvar(FileName,Name);
  putvar(FileName,Name,Counter+1);
  unlock();
}

See also:

file, strvar, lock

► latest version online Python bridge | Zorro Project

Python Bridge

Python is the most popular interactive script language, created by Guido van Rossum and first released in 1991. Spaces, tabs and line feeds matter in Python code, and enforce a clean code style with correct indentations. Python is interpreted and thus slow compared to compiled languages like C. So it is not suited for high-speed strategies, but its strengths are a simple and modern syntax, and access to all sorts of libraries for machine learning and data analysis. Many tasks can be solved in the same way in lite-C as well as in Python, but Python often has already a dedicated command implemented or library available for that.

There are two methods to run Python code from Zorro. The Python bridge allows directly calling Python functions from a Zorro script and using their results for trade signals. A script can start a Python session, send price data or indicator values to it, and use Python machine learning packages for training and prediction. Alternatively, Zorro can directly call the Python interpreter pythonw.exe and exchange data via files. This method is slower, but works even when the used Python library does not support embedded Python. Examples for both methods are can be found below.

Installation and test

Here's the procedure (Zorro 2.5 or above)

Make sure that any needed Python module is installed to your Python folder (don't use the pip --user option). Afterwards the module can be mounted with the import statement. Please read the remarks about issues with particular modules.

The following functions are available via Python bridge for sending data to Python, performing computations, and receiving data back:

pyStart(string Filename, int Mode): int

Start a new Python session, and optionally load and run Python code from the file Filename. This function must be called before any Python computations can be done. It returns 0 when the Python session could not be started, otherwise nonzero. The Python session will stay alive until the end of the Zorro session.  

Parameters:

Filename

Name of a .py file in the Strategy folder containing all needed Python functions and variables (f.i. "MySource.py"), or 0 for not loading a file.

Mode

A combination of the following flags, otherwise 0.
1 - log Python output and errors to the files PyOut.txt and PyError.txt in the Log folder
2 - don't release Python variables and modules at session end. Required for some modules (see remarks).


pyX(string Code): int

Execute Python Code. It can contain an expression, like "a = b + c", a function call, a function definition, an import, or any other valid Python statement. If the code cannot be excuted, 0 is returned, otherwise nonzero.

Parameters:

Code Python code to be executed. Separate lines with \n and mind whitespace indentations.

pySet(string Name, int n)

pySet(string Name, var d)

pySet(string Name, string s)

pySet(string Name, var *v, int elements)

Store an int, var, string, series or double array in the Python variable Name. Since the target type depends on the variable type, make sure to use the correct type. When storing constants, typecast them with (int) or (var) if in doubt. 

Parameters:

Name Name of the Python variable to be set (see remarks).
i int value to be assigned to a Python integer variable.
d var value to be assigned to a Python floating point variable.
s char* string to be assigned to a Python Unicode string variable.
v Pointer of the var array or series to be assigned to a Python list.
elements Length of the vector or series. Make sure to give the exact number of elements.
 

pyInt(string Name): int

pyVar(string Name): var

pyStr(string Name): string

pyVec(string Name, var *v, int elements)

Return the Python variable with the given Name as an int, double, string, or array. Errors, such as wrong variable names or types, return -1.

Parameters:

Name Name of a Python variable, previously defined by pyX or pySet (see remarks).
v Pointer of the var array to be filled with the Python list.
elements Number of elements of the vector; must be identical in the Python code and in the lite-C script.

Remarks:

Examples:

// run some Python code
function main()
{
  if(!pyStart(0,1)) {
    printf("Error - Python won't start!");
    return;
  }

  var Vec[5] = { 0,1,2,3,4 };
  pySet("PyVec",Vec,5);
  pyX("for i in range(5): PyVec[i] *= 10\n");
  pyVec("PyVec",Vec,5);

  int i;
  printf("\nReturned: ");
  for(i=0; i<5; i++) printf("%.0f ",Vec[i]);

// test a function
  pyX("def PySum(V):\n Sum = 0.0\n for X in V:\n Sum += X\n return Sum\n\n");
  pyX("Result = PySum(PyVec)");
  printf("\nSum: %.0f",pyVar("Result"));
}
// run a Python function
function main()
{
  if(!pyStart("MyPythonScript.py",1)) {
    printf("Error - Python script won't start!");
    return;
  }
  pyX("Result = MyPythonFunction()");
  var Result = pyVar("Result");
  printf("\nResult: %.2f",Result);
}
// trade by a Python signal
function run()
{
  if(is(INITRUN))
    if(!pyStart("MyPythonScript.py",1))
      return quit("Error - Python script won't start!");

  asset(Asset);
  pySet("Price",priceC()); // send current price to Python
  pyX("Signal = PythonTradeAlgorithm(Price)");
  int Signal = pyInt("Signal"); // received trade signal
  if(Signal > 0)
    enterLong();
  else if(Signal < 0)
    enterShort(); 
}
// start Python interpreter and process a data array

// Python side: --------------------------------
#read signals from file
Signals = []
with open("Data/In.txt","r") as File:
  for Line in File:
    Signals.append(float(Line))
# process signals and write result back to file
Processed = processMyData(Signals)
with open("Data/Out.txt","w") as File:
  for Line in Processed:
    File.write(str(Line))
    File.write('\n')


// Zorro side: --------------------------------
/ send signals to file 
string Content = zalloc(NumSignals*32);
for(i=0; i<NumSignals; i++)
  strcat(Content,strf("%.4f\n",Signals[i]));
file_write("Data\\In.txt",Content,0);
// call the Python interpreter 
string PythonPath = "MyPythonPath\\Pythonw";
exec(PythonPath,"Strategy/MyPython.py",1);
// get returned signals
file_read("Data\\Out.txt",Content,NumSignals*32);
Signals[0] = atof(strtok(Content,"\n"));
for(i=1; i<NumSignals; i++)
  Signals[i] = atof(strtok(0,"\n"));
...

See also:

Code conversion, R Bridge, Python cheat sheet

► latest version online quit

quit (string Text): int

Terminates or aborts the session or simulation cycle, and prints the text to the message window.

Returns

1 if Text begins with '!' or '#', otherwise 0.

Remarks:

Example:

if(Equity < 100)
  return quit("!Out of money!");

See also:

printf, run, wait, version, ExitCode

► latest version online

random

random() : var

Returns a random number uniformly distributed in the range from -1.0 to +1.0 (borders not included). Uses the Lehmer algorithm with a period of 2,147,483,646.

random(var Max) : var

Returns a random number uniformly distributed from 0 up to, but not including Max.

seed(int s)

Initiates a fixed random number sequence, dependent on the "seed" s. Affects random, randomize, and Detrend.

Example:

if(is(INITRUN)) 
  seed(365);
...
if(random() > 0) ... // true in 50% of all cases

See also:

abs, sign, randomize, Detrend

► latest version online

randomize | Zorro Project

randomize(int Method, var *Out, var *In, int Length): var

Randomizes a data array by different methods.

Returns:

Last value of the resulting data array; usually the accumulated return.

Parameters:

Method BOOTSTRAP Randomize the data differences by bootstrap with replacement.
  SHUFFLE Randomize the data differences by montecarlo permutation.
  DETREND Detrend the data before randomizing, by subtracting the mean data difference.
Out Array to be filled with the randomized data, or In for modifying the original array.
In Array containing the original data.
Length Number of elements of the In and Out arrays.

Remarks:

Example:

var OriginalReturn = EquityCurve[Length-1];
var RandomizedReturn = randomize(BOOTSTRAP,0,EquityCurve,Length);

See also:

Detrend, random ► latest version online R bridge for algo trading | Zorro project

R Bridge

The R bridge connects Zorro scripts and algorithmic trading systems to the R environment. It allows R computations and access to R packages in your C or C++ based script.

R is an interactive script language for data analysis and charting, developed in 1996 by Ross Ihaka and Robert Gentleman of Oakland University as successor to the S language. The latter was developed by John M. Chambers and his colleagues in the Bell Labs company in 1976. Today, R is still being improved by the R Development Core Team including John Chambers. Although R is an interpreted language and thus slow compared to 'real' programming languages such as C, it has many advantages:

For any imaginable algorithm there's normally already one or several R packages available. This makes R an excellent tool for financial research or analyzing financial time series, using the newest published methods. However, R is not a 'clean' programming language such as C - it is full of traps to the beginner, so using it effectively needs some experience. It's also not suited for trading (although some packages provide rudimentary backtest, optimization, and even broker connection functions!). The solution to this is the R bridge that allows including R computations in a Zorro strategy and using their results for trade signals. The lite-C script can start R sessions, send price data or indicator values to it, and call training and predicting functions from R machine learning packages. The bridge uses the R.dll by Bernd Kreuss. Its original distribution with source code is contained in the Zorro\Source folder. The code that accesses the DLL is located in the r.h header file.

Many problems can be solved in lite-C as well as in R, but R often has already a dedicated command implemented for that. For instance, downloading and converting historical price data from an online source requires about 10 code lines in lite-C (here), but often only a single line in R (read.csv("http://ichart.finance.yahoo.com/table.csv?...")).

Installation and test

A thorough intro to using R with Zorro can be found on RobotWealth.

R bridge functions

The r.h header defines several functions to directly assign and read data types without the need for manually formatting and executing snippets of R code for these purposes. Not all possible data types are directly supported, the main emphasis is on vectors and matrices containing floating point values. Once you have transferred the bulk of your data as vectors or matrices into the R session, you can execute snippets of R code to combine them or convert them into something more complex if needed.

Normally you want to get huge amounts of numeric data into the R session quickly, but not the other way. The assumption is that you want to feed it with price data on order to crunch numbers, and only need to get back a single value or a vector as a result.

All vector functions have a parameter for the number of elements. Be careful that the array you supply has the correct size, or it will crash the R console. Expressions or code snippets have a maximum size of 1000 characters per command; larger pieces of code must be sent with several commands or - much better - be called as a function in the R environment. See RTest.c for example usage.

Rstart(string Filename, int debuglevel): int

Start a new R session, and load R functions and variables from the file Filename in the Strategy folder. This must be called in the INITRUN before any R computations can be done. Returns 0 when the R session could not be started, otherwise nonzero. The R session will stay alive until the end of the simulation or Zorro session. 

Parameters:

Filename

Name of a .r file in the Strategy folder containing all needed R functions and variables (f.i. "MySource.r"), or "" (default) for not sourcing a file.

debuglevel

0 - only output fatal errors (default)
1 - output warnings and notes
2 - output every R message
The R output goes to the system debug monitor. Use the free DebugView tool from Microsoft, or call Rx(..., 3) to view R output).

Rrun(): int

Return 1 if the R session is ready for input, 2 if the session is busy with a computation, and 0 if the session is terminated. R will terminate on any fatal error in the code. You should check this regularly. The last command prior to the termination will be found in the debug output. If R is not running anymore, the R bridge won't emit any more messages and will silently ignore all commands.

Rx(string code, int mode): int

Execute a line of R code. Similar to the Ri / Rd / Rv functions below, as evaluating an expression is also just executing code; the difference is that Rx() can send commands asynchronously, allows R and Zorro to do computations in parallel, and prints R output to the Zorro window.

Parameters:

code R code line to be executed (1000 characters max).
mode

0 - synchronous. Wait until the computation was finished (default).
1 - asynchronous. Return immediately. Rrun() can be used to check when the next R command can be sent.
2 - synchronous, using a wait loop to react on events during computation. Return 1 when finished, or quit with returning 0 when the session was aborted due hitting [Stop] or due to a fatal R error.
3 - synchronous using with a wait loop as above, but direct R print output to the Zorro window. The debuglevel must be set accordingly.

Rset(string name, int n)

Rset(string name, var d)

Rset(string name, var *v, int elements)

Rset(string name, var *m, int rows, int cols)

Rset(string name, string s)

Store an int, var, series, matrix, or string in the R variable name. Since the target type depends on whether the variable is int, var, or a pointer, make sure to use the correct type especially when storing constants, and typecast it with (int) or (var) if in doubt. 

Parameters:

name Name of the R variable.
i int value to be assigned to a R integer variable.
d var value to be assigned to a R floating point variable.
v Pointer of the var array or series to be assigned to a R vector.
m Pointer of the var array to be assigned to a R matrix, by row order.
elements Length of the vector or series. Make sure to give the exact number of elements.
rows, cols Number of rows and columns of the matrix.
 

Ri(string expression): int

Rd(string expression): var

Rv(string expression, var *v, int elements)

Evaluate the given R expression, and return the result as an int, double, or vector. The expression can be a single variable, a R function, or any R code that will evaluate to the required variable type.

Parameters:

expression R expression to be evaluated (1000 characters max).
v Pointer of the var array to be filled with the R vector.
elements Number of elements of the vector; must be identical in the R code and in the lite-C script.

Remarks:

Examples (see also advise):

// do some R calculations
#include <default.c>
#include <r.h> function main() { Rstart("",2); // enable output var vecIn[5],vecOut[5]; int i; for(i=0; i<5; i++) vecIn[i] = i; Rset("rin",vecIn,5); // set up a vector Rx("rout <- rin * 10"); // perform some arithmetics Rx("print(rout)",3); // print rout to the Zorro window Rv("rout",vecOut,5); // read it back if(!Rrun()) printf("Error - R session aborted!"); else for(i=0; i<5; i++) printf("%.0f ",vecOut[i]); }
// Run a statistics test (ADF)
function run()
{
  set(PLOTNOW);
  BarPeriod = 1440;
  LookBack = 100;
  if(Init) {
    if(!Rstart())
      return quit("Error - R won't start!");
    Rx("rm(list = ls());"); // clear the workspace
    Rx("library(tseries)"); // load time series library
  }
  if(is(LOOKBACK)) return;
	
  Rset("Data",rev(seriesC()),LookBack); // send Close series to R
  Rx("ADF = adf.test(Data)"); // Augmented Dickey-Fuller test
  plot("ADF p-value",Rd("ADF$p.value"),NEW,RED); //display p-value
}
// template for computing trade signals in R
#include <r.h>

int Size = 200; // number of candles needed by the R algorithm  

bool Rcheck()
{
   if(!Rrun())
     return quit("R session aborted!");
   return true;
}

function run()
{
  BarPeriod = 60;
  LookBack = Size;
  asset("EUR/USD");  
  
  if(Init)) {
    Rstart("MySignals.r",1);
// load all required R objects from a file in the Zorro Data folder 
    Rx(strf("load('%sData/MyObjects.bin')",slash(ZorroFolder)));
// make sure everything loaded ok
    if(!Rcheck()) return;
  }

// generate reverse price series (latest price comes last)
  vars O = rev(seriesO())),
    H = rev(seriesH())),
    L = rev(seriesL())),
    C = rev(seriesC()));
    
  if(!is(LOOKBACK)) {
// send the last 200 candles to R
    Rset("Open",O,Size);
    Rset("High",H,Size);
    Rset("Low",L,Size);
    Rset("Close",C,Size);

// let R compute the signal
    var Signal = Rd("Compute(Open,High,Low,Close)");
    if(!Rcheck()) return; // Compute() error
    
    if(Signal > 0 && !NumOpenLong)
      enterLong();
    if(Signal < 0 && !NumOpenShort)
      enterShort();
  }
}
// export historical price data to R
string name = "Data\\Export.csv";

function run()
{
  BarPeriod = 60;
  StartDate = 20140101;

  if(Init) // write the header
    file_write(name,"Date, Open, High, Low, Close",0);
  else
    file_append(name,strf(
      "\n%04i-%02i-%02i %02i:%02i, %.5f, %.5f, %.5f, %.5f",
      year(),month(),day(),hour(),minute(),
      priceOpen(),priceHigh(),priceLow(),priceClose()));
}

/* in R:
> Data <- read.csv('C:/Projects/Zorro/Data/export.csv') # mind the forward slashes
> plot(Data$Close)
> ...
*/

See also:

Code conversion, DLL interface, advise, R course

► latest version online distribute, assign, knapsack

Portfolio distribution functions

distribute (var* Weights, var* Values, int N, int M, var* Caps) : var

distribute (var* Weights, var* Values, int N, int M, var Cap) : var

Calculate portfolio weights for a subset of M assets out of a N-asset portfolio. The M largest positive elements of the Values array are taken; the other elements are ignored. The resulting weights are stored in the Weights array and normalized so that they sum up to 1. A weight limit can be applied either individually (Caps array) per element, or globally (Cap) for all elements. Weights are clipped at the limit, and the remainders are distributed among the other positive weights. The total weight sum can end up less than 1 if the weight limit restriction were otherwise violated.This function is normally used to distribute asset weights in a portfolio dependent on individual parameters such as momentum.

Parameters:

Weights Output array of size N to receive M normalized weights. The other elements are 0.
Values Input array of size N, for instance projected profits, momentums, or Sharpe ratios of portfolio components.
N Number of elements in the Weights, Values, and Caps arrays.
M Max number of resulting nonzero weights, or 0 for distributing all N weights.
Caps Array of weight limits in the 0..1 range, or 0 for no weight limit, or -1 for ignoring the component.
Cap Global limit in the 0..1 range to be applied to all weights, or 0 for no weight limits.

Returns

Sum of weights after applying the limits.
 

assign (int* Items, var* Costs, var* Weights, int N, var Budget) : var

Given current Costs and portfolio Weights of N assets, distribute the given Budget according to asset weights. The Weights array can be generated with the distribute or markowitz functions. Due to the integer amounts, normally not the whole budget can be assigned. The function assigns in several steps as much of the budget as possible, and returns the rest.

Parameters:

Items Output array of size N to receive the weight-equivalent amounts for all assets.
Costs Input array of size N containing the current asset cost, such as price or margin cost.
Weights Input array of size N containing the normalized portfolio weights.The sum must be 1.
N Number of elements of the arrays.
Budget Available capital to distribute, for instance the current account equity.

Returns

Budget remainder that could not be assigned.
 
 

knapsack (int* Items, var* Costs, var* Values, int N, var Budget, var Cap) : var

Given current Costs and expected Values of N assets, calculate the optimal asset amounts that maximize the total value, while keeping the total cost below the given Budget. The function performs an unbounded knapsack optimization. It can be used to optimally distribute small capital among a portfolio of stocks or ETFs. 

Parameters:

Items Output array of size N to receive the optimal amounts for all assets.
Costs Input array of size N containing the current asset cost, such as price or margin cost.
Values Input array of size N containing projected prices or expected profits.
N Number of elements of the arrays.
Budget Available capital to distribute, for instance the current account equity.
Cap Maximum Budget weight per asset in the 0..1 range (f.i. 0.5 = max 50% of Budget), or 0 for no asset limits.

Returns

Total expected value of the portfolio.
 

Remarks

Examples (see also the Alice6 system from the Black Book):

// knapsack test
void main() 
{
  var wt[5] = {30,40,70,80,90};
  var val[5] = {40,50,100,110,130};
  int Items[5];
  var Total = knapsack(Items,wt,val,5,500,0); 
  printf("\nItems: %i %i %i %i %i => %.2f",
    Items[0],Items[1],Items[2],Items[3],Items[4],Total);
}
// portfolio rebalancing
void run() 
{
  ...
  assetList("MyPortfolio"); // max 100 assets
  static var Weights[100],Strengths[100];
  if(month(0) != month(1)) // rebalance any month
  {
// calculate asset strengths
    while(asset(loop(Assets)))
      Strengths[Itor1] = RET(30*PERIOD);
// enter positions of the the 4 strongest assets
    distribute(Weights,Strengths,NumAssetsListed,4,0);
    int i;
    for(i=0; i<NumAssetsListed; i++) {
      asset(Assets[i]);
      int NewShares = Balance * Weights[i]/priceClose(0) - LotsPool;
      if(NewShares > 0)
        enterLong(NewShares);
      else if(NewShares < 0)
        exitLong("",0,-NewShares);
    }
  } 
}

See also:

renorm, OptimalF, markowitz

► latest version online report

report (int Number) : string

Returns a string with content dependent on the given Number

Number:

1 Temporary string containing the current performance report.
2 Content of the Zorro.ini file. Use strvar or strtext for parsing.
3 Temporary string containing the command line.
4 Content of the .par parameters file, if any. Empty in the initial run.
5 Content of the .c rules file, if any. Empty in the initial run.
6 Content of the .fac factors file, if any. Empty in the initial run.
10 Temporary list of open trades similar to the status page. Empty when no trade was opened.
12 User name from the [Login] field.
20 Broker name selected with the scrollbox, f.i "MT4" or "FXCM".
21 Account name from the Name field in the account list.
23 Account identifier from the Account field in the account list.
24 Account currency from the CCY field in the account list.
25 The Script name.
26 Current script folder.
32 QuandlKey from zorro.ini.
33 AVApiKey from zorro.ini.
34 IEXKey from zorro.ini.
35 EODKey from zorro.ini.

Remarks

Example:

// send a performance report every day by email
if(Live && dow(0) != dow(1)))
  email(TO,FROM,"Zorro Report",report(1),SERVER,USER,PASSWORD);

See also:

printf, system strings, performance report

► latest version online

results

results (int Mode, int Num): var

Sums up the results of the most recent trades by different criteria. 

Parameters:

Mode

Type of the result sum, a combination of:
0
     - sum up profits
+1   - return number of trades
+2
   - sum up entry/exit price differences
+3   - return average entry price
+8   - consider only open trades
+16 - consider only closed trades
+32 - consider only trades of current asset / algo combination
+64 - consider only winning trades
+128 - consider only losing trades
+256 - consider only long trades
+512 - consider only short trades

Num Number of trades to consider. Enter a large number for all trades.

Remarks:

Examples:

var NumWins = results(1+64,30);	// Number of winning and won trades
var NumLosses = results(1+128,30);	// Number of losing and lost trades
var Wins = results(2+64,30)/PIP;	// Total win in pips
var Losses = results(2+128,30)/PIP;	// Total loss in pips
var AvgProfit = ifelse(NumWins+NumLosses > 0,
  (Wins-Losses)/(NumWins+NumLosses),0.); // Average profit per trade
var WinRate = ifelse(NumWins+NumLosses > 0,
  NumWins/(NumWins+NumLosses);	// Win rate of last 30 trades
 

See also:

Trade statistics, for(trades), strategy statistics

► latest version online Retraining, retesting

Retraining and Retesting

A live trading strategy should be retrained in regular intervals for adapting its parameters to the current market situation. It should also be retested after a certain trading time for verifying that the script behaves identically in live trading and in the backtest. Retraining and retesting are Zorro S features and are executed by a separate process, this way preventing any interruption of the live trading process. The newly trained parameters, rules, or machine learning models are then automatically loaded by the trading system at the begin of the next bar.

Retraining

The retraining process (Zorro S required) is triggered either automatically by setting the ReTrainDays variable, or manually by clicking the [Train] button while trading. A second Zorro instance will pop up and start a training run, while the first Zorro continues trading. In this training run the ReTrain boolean variable is nonzero. The script can then download new price data from the server, and generate a new parameter and AI rule set (see also workshop 5). After finishing the training run, the second Zorro closes. The new parameters, rules and factors are then automatically loaded by the trading instance. Example of a retraining statement in the script:

if(ReTrain) { 
  EndDate = 0;     // set the end date to the current date
  UpdateDays = -1; // reload new price data from the server
  SelectWFO = -1;  // select the last cycle for re-optimization
  reset(FACTORS);  // don't re-train factors
}

If the trading system is connected to a broker that does not provide enough price history (such as MT4), the recent price history can be downloaded from a different source with a specific history symbol or by another Zorro instance with the Download script.

A trading system can be retrained even without a ReTrain statement in the script. In that case just download price history and train the system with another Zorro instance. Training produces new .c, .par, .fac, or .ml files in the Data folder. You can train on a different PC and then copy the files into the Data folder of the trading system. Zorro will detect that the files were updated, and load them automatically at the end of the current bar.

Re-training a WFO system is required in intervals corresponding to the length of a WFO test cycle, which is displayed in the performance report. The system normally needs not to be retrained more often, with one exception. If live trading results of a certain component or asset are exceptionally bad, the market could have been changed - for instance it could have got a different dominant cycle. In that case an extraordinary training run can be useful for getting the strategy back on track. On the other hand, re-training continues open trades and running functions with different parameters, which can cause a slight reduction in profit. So don't re-train too often.

Retesting

The retesting process (Zorro S required) is triggered by clicking the [Test] button while trading. Its purpose is verifying whether the script behaves in the simulation exactly as in live trading. For this, Zorro downloads historical data and then performs a backtest over the period since the begin of trading. The log, profits, and trades from the backtest can then be compared to the live trading session.

For retesting the trading session, a second Zorro instance will perform the actual test while the first Zorro continues trading. The start date of the trading session and the bar offset are transferred to the second instance via command line. In the test run the ReTest boolean variable is nonzero and can be used to set up test parameters in the script. After finishing the test, the second Zorro closes. The trade list *_trd.csv can then be compared to trades.csv. Example of a retesting statement in the script:

if(ReTest) { 
  StartDate = Command[0]; // get start date and bar offset from the trading Zorro via command line
  BarOffset = Command[1];
  EndDate = 0;     // set the end date to the current date
  UpdateDays = -1; // reload new price data from the server
  NumSampleCycles = 0; // no sample cycles
  SelectWFO = -1;  // use parameters from the last WFO cycle
}

Retest a system when at least about 50 trades have been entered; retests with few trades make not much sense. Make sure that the test runs with exactly the same parameters - bar offset, trade volume, etc. - as live trading. For retesting accurately, the price data must come from the same source as in live trading, must have T1 resolution, and the asset list must reflect the actual trading account parameters. The following effects cause to differences between test and trading results:

Remarks

See also:

bar, mode, Testing, Training, ReTrainDays, command line

 

► latest version online return

return

return expression

Terminates the function. In the second case the given expression, variable, or constant is returned as result to the calling function, where it can be evaluated.

Parameters:

expression - value to be returned, like a variable, expression or constant.

Example:

var compute_days(var age, var days)
{
  return age * days;
}

See also:

function, expression

► latest version online

rev, conv

rev(vars Data, int Length): vars

Generates a reverse copy of the Data series which starts with the oldest elements. Useful for sending data series to other software that needs the series in old-to-new order.

conv(float* fData, int Length): vars

Converts a float array to a temporary var series, for passing it to functions as a series parameter.

Parameters:

Data Series or array.
length The number of elements; LookBack if 0.

Returns

Generated series.

Remarks

Example:

// generate reverse price series (latest prices last)
vars O = rev(seriesO(),0),
  H = rev(seriesH(),0),
  L = rev(seriesL(),0),
  C = rev(seriesC(),0));

See also:

series, diff, shift

► latest version online rising, falling

rising (vars Data) : bool

falling (vars Data) : bool

Determines if a series rises or falls, i.e. if Data[0] is above or below Data[1].

Parameters:

Data Data series.

Returns:

true if the series rises or falls, false otherwise.

Modifies

rMomentum - Data movement in percent per time frame; indicates the 'speed' of the rising or falling.

Remarks:

Example:

function run()
{
  ...
  vars Price = series(price());
  if(rising(Price)) 
    enterLong();
  else
    exitLong();
}

See also:

crossOver/Under, peak/valley, risingF/fallingF, slope series

► latest version online round, roundto

roundto(var x, var step): var

Rounds a variable to the closest multiple of a given step width. F.i. roundto(Price,PIP) rounds Price to 1 pip, and roundto(x,1) rounds x to the nearest integer.

round(var x): var

Rounds a variable to the nearest integer.

Parameters:

x - var to be rounded.
step - rounding accuracy.

Returns:

Rounded value of x.

Remarks:

For rounding x up to the next higher step, use roundto(x+0.5*step, step). For rounding x down to the next lower step, use round(x-0.5*step, step).

Example:

Price = roundto(Price,PIP); // round price to the next full pip

See also:

floor, ceil

► latest version online

run

run()

Main strategy function, written by the user. It is automatically started after clicking on the [Train], [Test], and [Trade] buttons. After the first run, it runs again at the end of every bar. This is continued until the end of the simulation or until [Stop] is hit. The run function initializes sliders and sets up all needed indicators.

Remarks:

Example:

function run()
{
  vars SmoothPrice = series(LowPass(series(price()),100));

  if(valley(SmoothPrice)) enterLong();
  if(peak(SmoothPrice)) exitLong();
}

See also:

Workshop 4, Bars and Candles, Bar, BarPeriod, tick, tock, user supplied functions

► latest version online Predefined strings: Script, Algo, Asset, History...

Predefined and user-supplied strings

Script

Name of the script without the ".c" extension. Can be changed for generating or loading parameters, rules, and factors from files with a different name, this way sharing training data with other scripts. Script names must not contain spaces or special characters.

Algo

The current algorithm identifier (read/only), set up by the algo function or a TMF. Algo identifiers should be short and must not contain spaces or special characters.

Asset

The current asset name (read/only), set up initially by the [Asset] scrollbox. Is automatically changed at runtime with the asset function, a tick function, a TMF or a trade loop

AssetPrev

The name of the previously selected asset; in a trade loop the name of the asset that was selected before the loop.

Assets

A NULL-terminated string array containing the names of all available assets in the asset list (read/only). Can be used either as a loop parameter, or for enumerating assets, f.i. for(N = 0; Assets[N]; N++). The asset names are valid after the first asset or assetList call. The number of assets in the list is returned by assetList.

AssetBox

The name of the currently visible asset in the [Asset] scrollbox (read/only).

Account

The name selected with the [Account] scrollbox, f.i "Demo" or "FXCM".

Factors

Name suffix and extension of the file containing the OptimalF factors; can be set for selecting between different sets of factors. If not set up otherwise, the file begins with the script name and ends with ".fac", f.i. "Z12.fac". Set Factors f.i. to "oos.fac" for reading the factors from the file "Z12oos.fac".

History

User-supplied string with path, name suffix, and extension of the historical data files, for selecting between different sets and types (.t1, .t2, .t6, .t8) of price histories. If not set, .t6 and the HistoryFolder path is assumed. A '*' character at the begin of the name represents the asset name and an optional year number, '?' represents the asset name with no year number. History must be set before the first asset() or assetHistory() call. Examples (for AAPL 2015):
History = "*a.t6";
reads price history from AAPL_2015a.t6; if not found, uses AAPLa.t6.
History = "?.t6"; reads price history from AAPL.t6.
History = "History\\Temp\\*.t1"; reads tick data history from History\Temp\AAPL_2015.t1.
History = "D:\\Data\\Options\\*.t8"; reads price history from option chains in D:\Data\Options\AAPL.t8.

Curves

User-supplied string with path, name, and extension of the file containing the exported equity or balance curves of all optimize parameter variants in [Train] mode (see export). Also used for storing the daily equity curve in [Test] mode. The LOGFILE flag must be set for exporting curves. If the file already exists, the curves are appended to the end. If Curves is not set, no curves are exported in [Train] mode, and in [Test] mode the equity/balance curve is exported to a *.dbl file in the Log folder.

SymbolTrade

The trading symbol of the current asset as set up in the asset list, or the plain asset name when the asset has no assigned trading symbol. Does not contain the optional broker or price source name. Can be modified with strcpy.

SourceTrade

The trading account name assigned to the current asset as set up in its symbol, or an empty strig for the currently selected account. This account is used for sending orders and broker commands. Can be modified with strcpy.

SymbolLive

SourceLive

SymbolHist

SourceHist

The symbols and account names assigned to the current asset for live and historical prices. Can be modified with strcpy.

ZorroFolder

The folder in which Zorro was installed, with trailing backslash, f.i. "C:\Program Files\Zorro\" (read/only). This is not necessarily the root folder of the data files - see the remarks about UAC.

WebFolder

The folder for the HTML page that displays the trade status. If not set up otherwise, the HTML documents are generated in the Log folder. Can be set up in the Zorro.ini file.

Type:

string
 

Remarks:

Examples:

Script = "MyScriptV2"; // store and load parameters under "MyScriptV2.par"
History = "*s1.t6";    // read historical data from f.i. "EURUSD_2013s1.t6"
History = "?.t6";    // enforce historical data with no year number, f.i. "EURUSD.t6"
WebFolder = "C:\\inetpub\\vhosts\\httpdocs\\trading";  // VPS web folder
assetAdd(Asset,strf("%s:%s",NewSource,SymbolLive)); // change source of current asset

See also:

algo, asset, report, LogNumber, included scripts

 

Scripts

Script repository

The following scripts are included as examples and utilites and can be started from the [Script] scrollbox. Please note that not all scripts will start right away. Some are for Zorro S, some need training first, some need more historical data, and some need Python, R, or modules like DeepNet or RQuantlib. Please check out the description in the source code.

Analysis

Calculates mean, standard deviation, skew, and kurtosis of the price curve of the selected asset.

Benchmark

Simple RSI strategy with 10-year backtest for speed tests and comparisons.

Binary

Example strategy for binary options with 5-minute bets.

BrokerArb

Broker arbitrage example. Exploits forex price differences between two brokers. Requires Zorro S and an account list with entries for the two brokers. 

BuyHold

SPY buy-and-hold strategy as a benchmark for comparison.

CalculatePi

Example script that calculates the first 1000 digits of Pi

Chart

Opens a t1, t6, or t8 historical data file in the History folder and displays its content in a chart.

Control

Example script for the ZorroControl integration toolkit (Zorro S required). 

CSVfromHistory

Small script for converting price history from Zorro's .t6 format to CSV.

CSVtoHistory

Small example script for converting price history in some .csv formats to Zorro's .t6 format, optionally split into separate year files. The input/output file names and the format definition can be edited in the script.

CSVtoOptions

Small example script for converting options EOD history in .csv format to Zorro's .t8 format. The symbol and CSV format definition can be edited in the script.

CSVtoTicks

Small example script for converting price history in .csv format to Zorro's .t1 format. The input/output file names and the format definition can be edited in the script.

CurrencyStrength

Strategy that exploits the relative strength of currencies and invests in the strongest forex pair. Requires forex history.

DateCalculator

Displays the Windows date and the Unix date for a given calendar date that can be set with the sliders. Click [Stop] to terminate.

Deeplearn

Short-term machine learning strategy from the article on Financial Hacker. Requires R with Caret and a deep learning package. Can switch between libraries Deepnet, H2O, MxNet, and Keras/Tensorflow.

Deeplearn+

64-bit version of the DeepLearn script, for Python and PyTorch.

Detrend

Displays the effect of detrending or randomizing price curves.

Distribution

Price distribution chart, comparing the distributions of two assets.

Download

Script for adding new assets or downloading the price history of single assets or asset lists; details under History.

DownloadEOD

Script for accessing a website, and downloading, converting, and storing price data in Zorro .t6 format, as an example for using HTTP and dataset functions.

DTree

Multi-asset decision tree example. Demonstrates why out-of-sample tests for machine learning are mandatory. Requires forex history.

Ehlers

The system from the paper "Predictive Indicators for Effective Trading Strategies" by John Ehlers, converted to lite-C by DdlV. With and without crossover prediction.

Filter

Test Zorro's spectral filter and cycle detection functions with a sine chirp.

Gap

A simple gap trading system based on a book by David Bean as an example of time dependent trading (not really profitable).

GapFinder

Finds gaps in historical data and displays them with a red line.

Grid

For your grid trading experiments. Better do not trade this system with real money!

History

Displays the content of .t8, .t6, and .t1 historical data files in a spreadsheet. Uses the panel function and thus requires Zorro S; a history viewer for the free Zorro version is available on the download page.

Impulse

Test the impulse response of moving averages, smoothing filters, and lowpass filters.

InvestCalculator

Calculates the investment from initial capital and accumulated profit with the square root rule. The exponent can be set with the slider from 1.000 (linear investment) up to 2.000 (square root investment). Click [Stop] to terminate.

Keystrokes

Demonstrates controlling other applications by sending key strokes or mouse clicks to them. Opens Windows Notepad, writes something in it, saves the text, and closes Notepad.

Indicatortest

Displays the curves of popular indicators.

Luxor

System from a trading book by Jaeckle and Tomasini. Textbook example of an overfitted strategy: better don't trade it outside the backtest period used in the book. GBP/USD history required.

Mandelbrot

Example script that uses the Windows API for producing Mandelbrot fractals.

Martingale

For your martingale experiments. Better do not trade this one with real money!

MinWinRate

Scalping debunker. Plots a histogram of the required win rates for a given asset and trading cost dependent on trade duration in minutes. 

MRC

Framework for a Montecarlo Reality Check. Enter the script name to be tested, and run it in [Train] mode. 

MultiTrain

Example of training both rules and parameters.

_New

Strategy template generated by clicking on [New Script]. Rename it afterwards. Don't use scripts names that are very long or have blanks or other non-letter characters. Source in Source\Template.c

OpenGL

Example script that demonstrates using external libraries, in this case the OpenGL 3D graphics library.

OptimizeTest

Walk-forward optimization of a 12 parameters portfolio strategy with the ascent, brute force, or genetic optimizer. Useful for comparing optimization speed and methods. Forex history required.

OptionsCalculator

Calculates the value and delta of call and put options with the Black-Scholes formula. Underlying price, strike distance to the price, and expiration can be set with the sliders, other parameters can be set up in the script. Click [Stop] to terminate.

PanelTest

Example script that demonstrates a complex spreadsheet-defined strategy control panel (Zorro S required).

Payoff

Interactive script that displays payoff diagrams of arbitrary option combinations. The strike distance can be set with a slider. R and the RQuantLib package must be installed.

Perceptron

Simple machine learning system with walk forward analysis.

Performance

Script with a function for evaluating arbitrary values from the performance report, and an example that stores the Montecarlo parameters in a .csv file and prints the 50% level.

Predict

Predicts peaks, valleys, and crossovers in a price curve several bars in advance.

PriceDist

Compares the price ranges and price distributions of two assets (EUR/CHF and EUR/USD) for finding grid-trading-worth inefficiencies. EUR/CHF history required.

Process

Example of two interacting Zorro processes. Move the "Other" slider and see the effect on the other Zorro (Zorro S required). 

PythonTest

Short script for testing the correct installation of Python.

RandomPrice

Plots a price curve together with a random curve. Let fellow traders guess which one is which.

RandomWalk

Displays a histogram of short-term price movements by random and by real prices.

RecordT1

Records live prices of the current asset to a .t1 file in [Trade] mode. A click on [Save] stores the file. Zorro S required.

Regime

Script for testing the response of some indicators to a market regime change from cycles to trending.

Rtest

Short script for testing the correct installation of R.

Sentiment

Connects to the Interactive Brokers TWS, downloads the SPY options chain, and plots today's market sentiment histogram. Zorro S required.

Simulate

Script for generating a chart and performance report from a trade list in .csv format. Demonstrates how to import data from a spreadsheet to a dataset. The script can be modified for importing other trade lists in different csv formats.

SpecialBars

Script illustrating the use of Range, Renko, Haiken Ashi, or Point-and-Figure bars for single-asset and multiple-asset strategies.

Spectrum

Spectral analysis chart example. SPX500 history required.

TradeCosts

Lists the ratio of spread to daily volatility of the main assets. Find out which assests are least expensive to trade. Forex and CFD history required.

TradeTest

A script that opens a panel with buttons for manually opening and closing positions in different modes. Useful for testing broker/account features, f.i. if it's a NFA account, if it is FIFO compliant or if partial closing is supported. Description under Broker Plugin.

TradeOptions

A script that opens a panel with buttons for manually opening and closing options with IB. Set strike and life time, select a combo, then click [Buy] or [Sell]. 

Turk2

A simple market sentiment system as described in the "Mechanical Turk" article. Not very profitable, but demonstrates the principle. SPY options history required.

WFOProfile

Framework for a walk-forward optimization profile. Enter the script name to be tested, and run it in [Train] mode. 

Workshop1 .. Workshop8

Scripts containing the code examples from the workshops. Forex and SPY history required for workshops 6 and 8.

Z1 ... Z13

Zorro's included trade systems. Executable systems only; no source code. Forex and CFD history required. Zorro S required for some of the sytems.

ZStatus

Small script for displaying the live balances and profits of all your accounts on a single panel (details here; Zorro S required).

See also:

Workshops

► latest version online

season

Seasonal and directional strength

predictMove (vars Data, int Length, int Horizon, var Percentile): var

Predicts the magnitude of a price movement, based on statistical analysis of price changes in the Data series. Returns the given Percentile of Data changes within the given time Horizon. Example: if the function returns 10 at Horizon = 20 and Percentile = 95, then in 95% of all cases the Data value moved by 10 or less in any direction within 20 bars (and therefore moved by more than 10 only in 5% of all cases). For statistical significance Length should be large compared to Horizon. The minimum length of the Data series is Length+Horizon.

predictSeason (vars Data, int Length, int Horizon, int Season): var

Predicts the expected price movement within a given time Horizon, based on the current time and seasonal movement in the Data series. Example: if the function returns 1.5 at Horizon = 20 and Season = 4, then the Data series is expected to rise by 1.5 within 20 bars from now, based on the annual movement at the same date in previous years. If Horizon == 0, the function returns no price move, but the average seasonal Data value at the current date. For statistical significance the Data series should cover at least 3 or 4 seasons. 

Returns:

Predicted data change / average seasonal data.

Parameters:

Data A data series, usually from price functions price(), priceClose()
Length The length of the data series, normally the LookBack period.
Horizon Price movement duration in bars.
Percentile Price movement percentile to return, f.i. 5 for the lowest 5% or 95 for the highest 5%.
Season 1 for daily, 2 for weekly, 3 for monthly, or 4 for annual seasons.

Remarks:

Example:

function run()
{
  StartDate = 2010;
  BarPeriod = 1440;
  LookBack = 300;	
 
  vars Prices = series(price());
  if(!is(LOOKBACK)) {
    LifeTime = 1;
    var WeeklyMove = predictSeason(Prices,LookBack,LifeTime,2);
    if(WeeklyMove > 0)
      enterLong();
    else if(WeeklyMove < 0)
      enterShort();
  }
}

See also:

asset, ROC, predict

► latest version online exitLong, exitShort

exitLong(string Filter, var Price, int Lots)

exitShort(string Filter, var Price, int Lots)

Exits all long or all short trades that match the given filter condition (see below), at market or at a given price limit, until the given number of lots is closed.

exitTrade(TRADE*, var Price, int Lots): int

Exits a particular trade completely or partially at market or at a given price limit. Returns 0 when the order failed, otherwise nonzero.

cancelTrade(int Id)

cancelTrade(TRADE*)

Cancels an open trade with a particular identifier or TRADE* pointer without sending a close order to the broker and without modifying the statistics or the balance. If the trade was still open on the broker side, it will become orphaned and is not anymore under Zorro control. Use this function for removing an externally closed position that was not transmitted to Zorro, for instance on NFA compliant accounts.

Parameters:

Filter 0 or "" for closing all trades with the current algo/asset component (default).
An algo name for closing all trades with the given algo and the current asset.
An asset name for closing all trades with the given asset and the current algo.
"c" for closing all call trades with the current component.
"p" for closing all put trades with the current component.
"cp" for closing all contract trades with the current component.
"u" for closing all underlying trades with the current component.
"*" for closing all trades with the current asset.
"**" for closing all trades.
TRADE* A pointer to the trade to be closed, or ThisTrade for closing the current trade in a trade enumeration loop.
Price Optional price or distance to current price for selling the position, or 0 (default) for selling at market. A positive price or price distance constitutes an exit stop, a negative price is an exit limit (similar to Entry). An exit stop closes the position when the asset price is at or worse than the given price, like a stop loss; an exit limit closes the position when the asset price is at or better than the given price, like a profit target.
Lots Optional number of lots to be closed, or 0 (default) for closing all open lots. Partially closing trades is not available with some brokers.
Id Identifier of the trade to be cancelled. Either the full number as assigned by the broker, or the last 4 digits as displayed in the Zorro window.

Remarks:

Example:

exitShort(0,1.234); // exits all short trades with the current Algo/Asset as soon as the price is at or above 1.234 (exit stop).

See also:

enterLong/Short, Entry, Hedge, Fill

► latest version online

series

series(var Value, int Length): vars

Creates a time series of the given Length with the given Value, and returns the pointer to the series. A series is a var array that contains the history of the variable. It is normally used by indicators in algorithmic trading. Series can be static or dynamic. Dynamic series are automatically shifted at every time frame, so that every element corresponds to a certain bar or time frame; the [0] element to the value at the current bar or time frame, the [1] element to the value from one bar or time frame ago, and so on. If a series is static by giving a negative Length, or if the NOSHIFT flag is set, the series is not shifted.

Parameters:

Value Optional data value of the series. The series is initially filled with this value, otherwise with 0.
Length Optional number of elements of the series; must not change once set. When omitted or 0, the series gets the length of LookBack. A negative number allocates a static series that can be shifted by script with the shift function.

Returns:

Pointer to the var array (the vars type is just a var* pointer).

Usage:

vars Prices = series(price()); defines a series with the length of the LookBack period that contains the mean prices of the current asset
 

ref(var Value, int Index): var

Convenience macro that generates a series and returns the Value from Index time frames ago. All macros are defined in variables.h.
 

SeriesLength

int, r/o, containing the number of elements of the date series returned by the last series() call.

SeriesBuffer

var* pointer that can be set to a memory area, and will then be returned by the next series call (lite-C only). After a series call it is set to the returned pointer. This allows to use static or pre-filled buffers for a series. For an example, see Financial Hacker: Truncated Indicators.
  

Remarks:

Examples:

// create a series with the high-low price differences
vars PriceRanges = series(priceH()-priceL());
 
// compare the current range with the range from 3 bars ago
if(PriceRanges[0] > PriceRanges[3])
  ...
 
// calculate a 20-bar Simple Moving Average containing the price differences from 5 bars ago
var Average5 = SMA(PriceRange+5,20);


// wrong use of conditional series
if(priceClose() > Threshold) {
  vars X = series(priceClose()); // error message!
  vars Y = series(SMA(X,100)); // error message!
  ...
}

// correct use of conditional series
vars X = series(), Y = series();
if(priceClose() > Threshold) {
  X[0] = priceClose(); // ok!
  Y[0] = SMA(X,100);
  ...
}

// exclude out-of-market bars from a series
if(!market(EST,0)) set(NOSHIFT);
vars InMarketPrices = series(priceC());
set(NOSHIFT|OFF);

// use arrays of series
vars MySeriesArray[3]; // declare an array of series 
...
for(i=0; i<3; i++) 
  MySeriesArray[i] = series(); // fill the array with series 
...
(MySeriesArray[0])[0] = 123; // access the first element of the first series. Mind the parentheses!

See also:

price, sort, rev, diff, shift, sync ► latest version online Mode

Helper macros

setf (int Mode, int Flag)

Macro that sets a Flag or flag combination in the variable Mode. For instance, setf(TradeMode,TR_PHANTOM) sets the TR_PHANTOM flag for the TradeMode variable.

resf (int Mode, int Flag)

Macro that resets a Flag or flag combination in the variable Mode.

isf (int Mode, int Flag): int

Macro that evaluates to nonzero when the a Flag in the Mode variable is set.  For instance, if(isf(TrainMode,PEAK)) ... checks if the PEAK flag is set in the TrainMode variable.

swap (var a, var b)

Macro that sets a to b and b to a.

Remarks

Example:

function run()
{
  setf(TrainMode,PEAK);
  setf(Detrend,SHUFFLE); 
  ...
}

See also:

set, PlotMode, TrainMode, TradeMode ► latest version online shift

shift (vars Data, var Value, int Length)

Shift the first Length elements of Data by one position, and fill Data[0] with Value

Parameters:

Data Series or array to be shifted.
Value New value for the first element.
Length Number of elements to be shifted. If negative, the array is shifted backwards.

Remarks

Example:

function run()
{
  ...
  vars Data10 = series(0,-10); // create a static series
  if(SomeCondition) shift(Data10,NewValue,10);
  ...

See also:

series, diff, rev, tick
 ► latest version online sign

sign(int x): int

sign(var x): var

The sign of x: -1, 0, or 1.

Parameters:

x - any var or int.

Returns:

x > 0 1
x == 0 0
x < 0 -1

Example:

x = sign(2); // x is now 1
x = sign(-599); // x is now -1 
x = sign(0); // x is now 0 

See also:

sqrt, abs, cdf

► latest version online

wait

wait (int n): int

Suspends execution for the time given by n in milliseconds. The user interface remains responsive during the wait time. The function returns 0 when the [Stop] button was hit or quit was called, otherwise nonzero. If n is negative, the function checks and updates the user interface only every nth time, for not delaying fast loops. Use wait in a loop for avoiding unresponsiveness during long computations or external function calls. For displaying progress during very long processes, use progress instead.

Parameters:

n - wait time in milliseconds when positive, user interface update frequency when negative, 0 for immediate user interface update.

Returns

0 when the script is to be terminated, 1 otherwise.

Remarks:

Examples:

Rx("TrainNeuralNet()",1); // start a long computation
while(Rrun() == 2)
  if(!wait(100)) return;  // wait until computation is finished or [Stop] was hit
while(some_long_loop) {
  ...
  if(!wait(-1000)) break; // prevent unresponsiveness in long loops
}

See also:

timer, run, R bridge, lock, BUSY, quit, version, progress, suspended, call

► latest version online

slider

slider(int number, int pos, int min, int max, string name, string tooltip): var

Sets up an initial position, range, name, and tooltip for one of the three customizable sliders. Returns the current position of the slider. Depending on the selected asset or other conditions, different slider positions can be preset at the beginning.

slider(int number): var

Returns the position of the slider with the given number. The slider must have been set up before.

slider(int number, int pos)

Sets the slider with the given number to a new position. This way the script can display numerical values to the user while running. The slider must have been set up before.

Parameters:

number Slider number: 0 for the Period slider, 1..3 for the 3 user-specific sliders.
pos Slider position. The initial position is set when the script is run the first time.
min, max Minimum (left) and maximum (right) position of the slider, positive integers, min < max.
name Text displayed next to the slider (like "Margin"). If the name begins with a dot (like ".Panic"), a decimal point is displayed in front of the last 3 digits in the edit window.
tooltip Tool tip text to describe the slider function, or 0 for no tool tip.

Returns

Current value of the slider.
 

Remarks:

Example:

// Investment calculator
void main()
{
  slider(1,10000,1,20000,"Capital","Initial capital");
  slider(2,10000,0,20000,"Profit","Profit so far");
  slider(3,2000,1000,2000,".Root","Root"); // 1.000 = linear, 2.000 = square root
  while(wait(100))
    print(TO_INFO,"\rInvestment: $%.0f",
      slider(1)*pow(1+slider(2)/slider(1),1./(slider(3)/1000.)));
}

See also:

optimize, panel

► latest version online slope, line

slope (int Type, vars Data, int TimePeriod) : var

Connects the two highest maxima or lowest minima of the Data series within the given TimePeriod, and returns the slope of the connecting line. This function can be used to calculate traditional divergence or convergence (triangle) patterns.

Parameters:

Type PEAK for a line through the maxima, VALLEY for a line through the minima.
Data Time series.
TimePeriod Period to be examined.

Returns:

Slope (movement per bar, positive for a rising, negative for a falling line), or 0 if the series is flat or no minima/maxima are found.

Modifies

rMin, rMinIdx - value and bar offset of the lower connecting point.
rMax, rMaxIdx - value and bar offset of the higher connecting point.
 

line (int Offset) : var

Returns the value of a straight line at the bar with the given Offset (0 = current bar). The line is defined as connecting the rMin, rMinIdx, rMax, rMaxIdx coordinates which can be either set directly, or generated with slope or with Min/Max/MinIndex/MaxIndex

Parameters:

Offset Bar offset

Returns:

Value of the line at the given Offset.

Remarks:

Example:

var HighsDn[10] = { 1,1,1,2,1,1,1,3,1,1 }; // mind the reverse order
var LowsUp[10] = { 1,1,1,0.7,1,1,1,0.5,1,1 };

function main()
{
  printf("\nSlope dn %.3f",slope(PEAK,HighsDn,10));
  printf("\nSlope up %.3f",slope(VALLEY,LowsUp,10));
}

See also:

crossOver, crossUnder, rising, falling, peak, valley, peakF, valleyF, predict

► latest version online

sortData, sortIdx

sortData (var* Data, int Length)

Sorts the Data array in ascending (Length > 0) or descending (Length < 0) order.

sortIdx (var* Data, int Length): int*

Does not affect the Data array, but returns a sorted int array of the indices from the smallest to the greatest Data value.

sortRank (var* Ranks, var* Data, int Length): int*

Stores the rank of any Data element in 0..1 range in the Ranks array, from 0 for the smallest to 1 for the greatest element. If the elements are OHLC prices, candle patterns can be encoded this way for machine learning algorithms.

findIdx (var* Data, int Length, var Value): int

Returns the index of the highest Data element below or equal to Value, or -1 when no such element was found. 

Parameters:

Data var / double array or series to be sorted
Rank var / double array to receive the Data ranks.
Length Length of the Data array; negative for sorting in descending order.
Value Value to be found in the Data array.,

Returns

sortIdx, sortRank: Pointer to a temporary int array of size Length containing the numbers of the Data values after sorting. The array will be overwritten by the next sortIdx or sortRank call. Returns 0 when a parameter is invalid.

Remarks

Example:

// Spearman trend indicator
var Spearman(var* Data,int Period)
{
Period = clamp(Period,2,g->nLookBack);
int* Idx = sortIdx(Data,Period);
var sum = 0; int i,n;
for(i=0,n=Period-1; i<Period; i++,n--)
sum += (n-Idx[i])*(n-Idx[i]);
return 1. - 6.*sum/(Period*(Period*Period-1.));
}

See also:

randomize, frechet, series, dataSort

► latest version online sound

sound(string name)

Plays a .wav sound file.

Parameters:

name

Name of the sound file, f.i. "test.wav". If the file is not found in the Zorro folder, a standard chime is played.

Remarks:

Example:

sound("win.wav");

See also:

printf, progress

► latest version online

Spread, Slippage, Commision, Rollover

Asset parameters 1 - profits and costs

Spread

Difference between ask and bid price of the current asset; by default taken from the asset list when offline, or from the current ask and bid price when connected to a broker. The trade profit is reduced by the spread. For using historical spreads in the backtest, set Spread = marketVal(); before entering or exiting a trade. For a backtest with no transaction costs, set Spread = 0. Spread is ignored for Options and futures that use the ask-bid spread of the selected contract. Spread is also ignored in binary trading mode (BINARY flag). 

Slippage

Simulated slippage in seconds (default = 5), effective in [Test] mode for entering and exiting trades and for entry and exit limits. Has no effect on contracts, in [Train] mode, in 'naive' Fill mode, or when Penalty is nonzero. Slippage is simulated by filling orders not at the current price, but at the most likely price after the given number of seconds. The price is estimated by extrapolation of the next candle. Different bar periods and bar offsets therefore produce different slippage, but of similar magnitude. The larger the Slippage variable, the larger is the price range and thus the fill price deviation from the current price.
   Slippage has normally a negative effect on trend trading systems, but can also be in favor of the trader, especially with counter-trend or mean-reversion systems. It is recommended to test systems with and without slippage for determining its effect on the result. Slippage at 0 disables extra slippage, but entry, stop, or takeprofit fill prices can still deviate from the given limit (see Fill algorithm). Setting Slippage to a negative amount simulates asymmetric slippage that is always in adverse direction. 

Penalty

Alternative fixed slippage in counter currency units, for special purposes (default = 0). Puts a 'penalty' on open and close prices of subsequent trades. Effective in [Test] and [Train] modes for entering and exiting trades and for entry and exit limits. Overrides the slippage simulation and the fill algorithm when nonzero. Positve penalty reduces the trade profit, negative penalty increases it.

Commission

Broker's fee for opening and closing a trade, taken from the asset list, in account currency units per 10000 contracts for currencies, per contract for all other assets, per underlying unit for options, or as percent of the trade volume when negative. The trade profit is reduced by this amount when the position is closed. Commission is automatically reduced by 50% for expired options (since fees are usually not charged for expiration), and is initialized to zero in binary trading mode (since binary brokers get their commission via payout, see below). When set in the script, it must be set individually for every traded asset. Commission is equivalent to an additional spread with a size in pips of Commission*LotAmount/10000/PIPCost for currencies, and Commission*LotAmount/PIPCost for all other assets. If a negative percent value is given (f.i. -0.25 for 0.25%), the commission of a trade is -Percent/100 * PIPCost/PIP * Price * Lots.

RollShort

RollLong

Daily rollover interest (also called 'swap') per 10000 contracts for currencies, or per contract for all other assets. Taken from the assets list when offline, otherwise the broker's current rollover value is used when provided by the API. The rollover is daily interest paid to or received from the broker for borrowing or lending margin. For instance, when you hold a EUR/USD long position, you receive interest from the broker for lending the EUR and pay interest for borrowing the USD - the difference is the rollover. Negative rollover contributes to the losses, positive rollover to the profits. As to be expected, negative rollover values are more frequent and greater than positive rollover values. For CFDs, rollover is usually trend compensating - for instance, upwards trending index CFDs get a negative RollLong for eliminating long-term trend profit. Rollover can affect the performance of long-term strategies and cause an asymmetry between long and short trades when positions are hold for several weeks.

Interest

Annual interest on margin loan, in percent; reduces the account balance in the simulation. Set this in the script when interest is not calculated per swap/rollover, but per total maintenance margin. Usual values (2019) are 1.5% for EUR accounts and 3% for USD accounts. The interest calculation assumes no rollover and equal leverage of all traded assets.

PriceOffset

Amount added to historical or live prices of the current asset, for special purposes such as shifting negative prices to the positive scale. Set it after selecting the asset, but before loading history for affecting historical prices. Set Detrend = NOPRICE for preventing that negative prices are treated as erroneous.

WinPayout

LossPayout

Payout in percent of the invested Margin for binary trading (set(BINARY)). Winning trades win the invested amount plus the WinPayout, losing trades lose the invested amount minus the LossPayout. The payout variables must be set individually for every traded asset. Spread and Commission should be set to 0 for normal binary trades.

Type:

var

Remarks:

Examples:

// ignore broker spread, set constant spread for performance calculation 
Spread = 3*PIP; 

// allow negative prices by shifting them to the positive range
assetAdd("Neg"); // select asset with negative prices
Detrend = NOPRICE; // prevent treating them as erroneous
PriceOffset = 1000; //  shift them
asset("Neg"); // load the shifted prices

See also:

enterLong/Short, price, Stop, Lots, Margin, PIP, asset lists

 

► latest version online Algo Trading in C/C++ | Getting started with Zorro

Welcome to Algorithmic Trading in C/C++! Getting Started

Zorro is an ecosystem for financial research and algorithmic trading. It is not a 'robot' and not a trading platform, but has some elements of both. While trading platforms are normally specialized on manual trading, Zorro is mainly for algorithmic trading in automated or semi-automated mode. Technically it's a front end to an on-the-fly C and C++ compiler with a set of modules for time series analysis, machine learning, optimization, and online API connection. It allows with a few button clicks to edit algo trading strategies, train machine learning algorithms, optimize and backtest systems with several methods, and deploy them for live trading. This workflow is reflected in the six buttons of Zorro's control panel.

Everything you're doing with Zorro is based on a script. There are many included scripts for all possible tasks, usually with a set of parameters that can be edited for adapting them to particular needs. In order to use Zorro effectively, you need to know how to edit and write scripts in the lite-C or C++ language. There are books, online courses, and also a short tutorial in this manual.

Control panel

We admit: the user interface is not particularly pretty. But you can add your own user interface for your algo trading tools and scripts, with as many colorful buttons as you want. The default one is designed for minimum space (~ 300x600 pixels), so several Zorro instances can run on a small notebook screen in the background. There is no menu. Entry fields and scroll boxes allow setting up the broker connection and selecting the script and assets to trade.

Broker

Account
Primary account (Demo, Real, etc) and broker connection (FXCM, IB, MT4, etc) for live trading.
Login
User ID, account number, password, API token, or other credentials for the selected account.
Status
Server time and market state in live trading mode. Green when connected, red when offline.

Strategy

Script
Select a script from the Strategy folder. See scripts for included scripts and the tutorial for writing your own scripts.
Asset

Select the primary asset. You can add any asset - crypto, stocks, futures, options - to this scrollbox through asset lists

Action

Run predefined Zorro tasks, programs, or script functions with a mouse click.

Period

Bar period in minutes; normally set up by script. 

Slider1..3
User configurable, to modify script parameters. 

Buttons

Help
Open the help manual and script reference.
Edit
Open the selected script in the editor.
Test
Test the script with historical price data in single-step, tick-based, or walk-forward mode.
Train Train the script by optimizing parameters, generating trade rules, or training neural networks.
Trade

Run the script live with broker connection. Trade assets and start earning (or losing) money.

Result
Open the chart viewer and the editor with the log and performance analysis.

Output

Progress
Trading or test status. Profits green, losses red.
Info Numerical status display. 
Messages What Zorro has to tell. Double click to copy.

What you need

Some traders own a desk full of PCs and monitors with an impressive show of indicators and price curves. Good for boasting on trader forums, but useless for serious algo trading. You only need two machines:

Getting started

See also

Zorro Home, Content, What's New, Algo Trading Principles, Tutorial, Z Strategies

► latest version online

Strategy statistics

Strategy performance statistics

The following system variables can be evaluated in the objective or evaluate function. Some become available only at the end of a simulation or a live trading session, others are updated at any bar during the test or session. There is also a set of trade statistics that can be evaluated during the backtest. The statistics variables can be used for calculating individual performance metrics and printing them to the performance report.
 

DrawDownMax

Maximum drawdown, the largest difference between a balance peak and the lowest subsequent equity valley during the simulation period, in account currency units. Updated at any bar.

MAE

Maximum adverse excursion, the largest difference between an equity peak and the lowest subsequent equity valley during the simulation period, in account currency units. Updated at any bar.

AEDepth

Current adverse excursion, the difference between the last equity peak and the current equity value, in account currency units. Updated at any bar.

MarginMax

Maximum margin sum of open trades during the simulation period, in account currency units. Updated at any trade.

RiskMax

Maximum possible loss sum of all open trades during the simulation period, in account currency units. The possible loss is calculated by the stop loss distance of a trade, or estimated at 10% of the price if no stop loss was used. Updated at any trade.

SpreadCost

Total loss by bid-ask spreads during the simulation period, in account currency units. Updated at any trade.

SlippageCost

Total win or loss by simulated slippage during the simulation period, in account currency units. Updated at any trade.

RollCost

Total win or loss by rollover and margin interest during the simulation period, in account currency units. Updated at any trade.

CommissionCost

Total loss by commissions during the simulation period, in account currency units. Updated at any trade.

ReturnMean

Mean of all bar returns on investment. The bar return on investment is the equity difference to the previous bar, divided by Capital. If Capital is not set, the sum of normalized maximum drawdown and maximum margin is used. Only available at the end.

ReturnStdDev

Standard deviation of all bar returns on investment. Can be used together with ReturnMean for calculating the annualized Sharpe Ratio and other metrics.Only available at the end.

ReturnUlcer

Ulcer Index in percent; average drawdown divided by peak equity.Only available at the end.

ReturnR2

R2 coefficient; the similarity of the equity curve with a straight line ending up at the same profit.Only available at the end.

ReturnLR

Gross profit by linear regression of the equity curve. Only available at the end.

ReturnCBI

The last CBI value based on previously stored backtest data.Only available at the end.
   

DrawDownBars

Total number of bars spent in drawdown, updated at any bar. For the percentage spent in drawdown, divide by the duration of the test, (Bar-StartBar)

DrawDownBarsMax

Maximum length of a drawdown in bars. Only available at the end.

LossStreakMax

Maximum number of losses in a row. Only available at the end.

NumOpenMax

Maximum number of simultaneously open trades. Updated at any bar.

InMarketBars

Total number of bars with open trades. Updated at any bar.

InMarketSum

Sum of the durations of all trades, in bars. Updated at any trade. Can be bigger than the duration of the test when several positions are simultaneously open.

Type:

int for numbers that count something, otherwise var. Read/only.
 

ResultsAtBar

Array in chronological order containing the sums of wins and losses of all open and closed trades at every bar, in account currency units. (var)ResultsAtBar[Bar] is the result at end of the simulation.

Type:

float* pointer, or 0 when no results were calculated.

ProfileAtBar

Array in chronological order containing the blue equity, balance, or pip return profile at every bar. 

DrawDownAtBar

Array in chronological order containing the red underwater profile at every bar.

Type:

var* pointer, or 0 when no profile was calculated. 

ResultsDaily

Array in chronological order containing the balance or equity (dependent on the BALANCE flag) at the end of every day, in account currency units. ResultsDaily[Day] is the end result. This array is at the end of the simulation automatically saved to a .dbl file for the CBI calculation, and to a .csv file for further evaluation.

Type:

var* pointer, or 0 when no daily results were calculated.

 

Remarks:

Example:

// print the annualized Sharpe Ratio to the performance report 
function evaluate()
{ 
  if(NumWinTotal == 0) return;
  var MySharpe = ReturnMean/ReturnStdDev;
// roughly adjust to annual in-market bars
  MySharpe *= sqrt(InMarketBars/NumYears);
  printf(TO_REPORT,"\nMy Sharpe: %.2f",MySharpe);
}

See also:

Trade statistics, Cold Blood Index, for(trades), NumBars, performance report ► latest version online Stop, Profit, Trail, Entry

Trade parameters 1 - entry and exit limits

Entry

Entry limit, entry stop: Enter the subsequent trade when the price reaches the given limit or moves by the given distance (default = 0 = enter immediately at market). The value can be positive or negative, and can be either directly a ask price level, or a distance to the current close price. A positive price level or positive distance constitutes an entry stop, a negative price level or negative distance constitutes an entry limit.
  An entry limit buys when the price moves against the trade direction and touches or crosses the limit. It increases the profit because it buys at a price that went in opposite direction to the trade. An entry stop buys when the price moves in trade direction and reaches or crosses the limit; it reduces the profit, but enters only when the price is moving in favorable direction, and thus acts as an additional trade filter. For a long trade, an entry limit should be below and an entry stop should be above the current price. If the entry price is not reached within the allowed time period (set through EntryTime), the trade is cancelled and a "Missed Entry" message is printed to the log file.

OrderLimit

Use a limit order at the given limit price, instead of a market order, for subsequent trades in live trading. Has only an effect if supported by the broker API; has no effect in training or backtests. OrderLimit is different to an Entry limit because it is handled on the broker's side (see remarks) and is - if supported by the API - also valid for exits. If not sent in GTC mode, the order is automatically cancelled if not filled within the wait time set for the broker API. 
  The price limit is directly used for the order and thus must be an ask price for enterLong and a bid price (ask minus Spread) for enterShort. For a combo order it must be the sum of the ask and bid limits of all legs. The script must round the limit to an integer number of pips if required by the broker API. Unlike normal orders, not filling a limit order does not produce a "Can't open trade" error message in the log. For avoiding that it affects subsequent entries and exits, set OrderLimit back to 0 after entering. A tick or tmf function can be used to resend orders with different OrderLimit values for catching the best price within a certain time period.

Stop

Stop loss: Close the subsequent trade.when the price moved in adverse direction to the given limit or by the given difference to the fill price (default = 0 = no stop loss). When a new trade cannot be entered due to the MaxLong/MaxShort limit, open trades of the same component are automatically updated to the stop that the new trade would have. A good value for Stop is derived from the ATR, f.i. 3*ATR(20). Setting a stop loss is recommended for risk control.

StopFactor

Stop loss distance sent to the broker API to act as a 'safety net' in units of the real stop loss distance (default = 0 = don't send stop). At the value 1.5 the stop sent to the broker is 50% more distant than the stop on the Zorro side. This allows to stop out the trade even when the broker connection breaks down, but hides the real stop value from the broker and thus prevents 'stop hunting' or similar practices. At 0 the stop is only controlled on the Zorro side, at 1 it is only controlled by the broker. If StopFactor is set to a negative value or if a BrokerStop function is not available in the broker plugin, the broker stop is only placed at opening the trade, but not updated afterwards. StopFactor has no effect on pool trades in Virtual Hedging Mode. Broker stops on NFA compliant accounts require separate orders and must be explicitely enabled with the SET_ORDERTYPE command.

StopPool

Broker stop loss distance for pool trades in Virtual Hedging Mode (non-NFA mode only; default 0 = no stop loss), in price units. For instance, at 0.25 any pool trade sends a stop to the broker at 25% distance from the initial price.

TakeProfit

Profit target: Close the subsequent trade.when the price moved in favorable direction to the given limit or by the given difference to the fill price (default = 0 = no profit target). When a new trade cannot be entered due to the MaxLong/MaxShort limit, open trades of the same component are automatically updated to the profit target that the new trade would have.A profit target takes profits early, which increases the number of winning trades, but normally reduces the overall profit of a strategy. It is preferable to use TrailLock instead of setting a profit target.

Trail

Trailing:  Raise the stop loss value as soon as the price reaches the given limit, or goes in favorable direction by the given distance in price units (default = 0 = no trailing). Has only an effect when a Stop is set. The stop loss is increased in a long position, and decreased in a short position so that it normally follows the price at the distance given by the sum of the Stop and Trail distance. For instance, Stop = 10*PIP and Trail = 5*PIP lets the stop follow the price at 15 pips distance. A slower or faster 'movement speed' of the stop loss can be set through TrailSlope.

TrailSlope

Trailing 'speed' in percent of the asset price change (default = 100%); has only an effect when Stop and Trail are set and the profit is above the trail distance. Example: The asset price of a long position was above the Trail limit and goes up by more 10 pips. TrailSlope = 50 would then raise the stop loss by 5 pips. TrailSlope = 200 would raise the stop loss by 20 pips. 

TrailLock

'Locks' a percentage of the profit (default = 0 = no profit locking); has only an effect when Stop and Trail are set and the price has exceeded the trail distance. A stop loss is then automatically placed at the given percentage of the current price excursion. Example: A long position is currently in profit by 20 pips above the entry price. TrailLock = 80 would then place the stop loss at 16 pips above entry, thus locking 80% of the profit (without trading costs). TrailLock = 1 (or any small number) would set the stop loss at the entry price when the current price reaches the Trail value. Using TrailLock is in most cases preferable to setting a profit target.

TrailStep

Automatically raise the stop loss every bar by a percentage of the difference between current asset price and current stop loss (default = 0 = no automatic trailing); has only an effect when Stop and Trail are set and the profit is above the trail distance. Example: A long position has a stop at USD 0.98 and the price is currently at USD 1.01. TrailStep = 10 will increase the stop loss by 0.003 (30 pips) at the next bar. TrailStep reduces the trade time dependent on the profit situation, and is often preferable to a fixed exit time with ExitTime. Stopping and restarting a strategy counts as a step and will raise the stop loss as if a bar had passed.

TrailSpeed

Speed factor for faster raising the stop loss before break even, in percent (default = 100%). Has only an effect when Stop and Trail are set and the profit is above the trail distance. Example: TrailSpeed = 300 will trail the stop loss with triple speed until the entry price plus spread is reached, then continue trailing with normal speed given by TrailSlope and TrailStep. Has no effect on TrailLock. This parameter can prevent that a winning trade with a slow rising stop turns into a loser.

Type:

var

Remarks:

Examples:

// set stop at 1% distance 
Stop = 0.01*priceClose(); 
enterLong();

// place limit order at ask/bid center
OrderLimit = priceC() - 0.5*Spread;
enterLong(); 

See also:

bar, enterLong/Short, exitLong/Short, EntryTime, ExitTime, Lots, TMF

 

► latest version online Algorithmic Trading and Market Inefficiencies

How - and why - does algorithmic trading work?

Model-based algorithmic trading strategies exploit market inefficiencies for predicting price trends of financial assets. Any market inefficiency establishes a price curve anomaly - a deviation of the price curve from pure randomness. Obviously, trading strategies can only work when those inefficiencies really exist. In a perfect, efficient market, prices would only be affected by real events, such as the invention of a new technology or the publication of company results. All traders are 'informed', decide rationally and act immediately. The price curves in such a hypothetical market would be pure random-walk curves that contain no information for predicting future prices.

Fortunately for algo trading strategies, real markets are far from this theoretical ideal. Market inefficiencies are everywhere. They are caused by slow reaction on news, incomplete information, rumors, patterns of repetitive behavior, and irrational trading (such as following advices by 'analysts'). Suche effects can be described by market models. They allow a strategy to predict a small part of the price curve with some degree of accuracy. The problem: those effects are small in relation to the ubiquitous 'noise', i.e. randomness in price curves. They are not visible to the human eye, nor can they be reliably detected by classical "technical indicators". Serious statistical methods or machine learning algorithms are normally required for exploiting market inefficiencies.

Example of a potential market inefficiency made visible by spectral analysis:

The histogram displays the amplitudes of regular cycles found in the S&P 500 price curve over a week. The x axis is the cycle duration in hours. As you can see, the highest peak is a cycle of about 24 hours, some minor peaks are at 36, 54, and 64 hours, and a broad peak at 120 hours, equivalent to a weekly cycle (a week is 5 days x 24 hours, as weekends are skipped in price curves). Cycles arise from synchronized trading behavior. Some of them remain stable for several days or weeks. They can be exploited for generating profit in automated strategies. You can find an example for cycle based trading in the tutorial. A similar trade method is used in Zorro's Z2 strategy.

Often, temporary price patterns establish and can be used by intelligent algorithms for predicting short-term trends. The following curve is produced by a 'deep learning' neural net structure:

The net was fed with EUR/USD price changes of the last 60-minutes bars, and predicted the price direction of the next hour. The blue curve shows the accumulated correct predictions minus the wrong predictions from a walk-forward analysis. The price direction was predicted with 57% accuracy. This is close to the maximum that can be achieved with machine learning; tales of 90% or 100% prediction accuracy belong to the fairy world (or rather, to backtest bias). The neural net was re-trained every 4 weeks, as the patterns began to lose their predictive power after that time. We can see that some short-term predictive patterns pop up almost all the time in the market. Such patterns are not found in random data.

Another interesting inefficiency can be seen in the following price distribution:

In the chart, the height of a bar is equivalent to how often a certain price appears in the price curve. The red bars are based on the EUR price in Swiss Francs (EUR/CHF) from October 2011 up to January 2015; the green bars are the EUR price in US Dollars (EUR/USD) in the same time period. You can generate such a chart with the Distribution script. We can see that the red EUR/CHF price distribution is quite narrow and ends abruptly on the left, as if cut with a knife. In contrast, the green EUR/USD price distribution is much wider with a broad peak in the middle. It resembles a bell curve (more or less), which indicates a random price distribution. The EUR/CHF distribution however is no bell curve. It reveals a market inefficiency that can generate profit in automated trading. In this case, the inefficiency is the result of the CHF price ceiling that was established by the Swiss National Bank from September 2011 until January 2015. It prevented the EUR from dropping below 1.20 CHF. This kind of market inefficiencies can be very successfully exploited by grid trading strategies.

Developing algo trading strategies

There are plenty possibilities for algo trading strategies that really work. What you need to know:

What never works is stacking indicators until the backtest turns positive. Indicators are possibly the result, but never the begin of strategy development. The general process is described here: Build Better Strategies. For practical development, read the Black Book and /or take the Algo Bootcamp (see links). When you need help with strategy coding, contact our development service.

When you develop an algorithmic trading strategy, you use historical price data for testing and optimizing. When you trade it, prices are live. There is no guarantee that the past is repeating. The market and its inefficiencies undergo changes of any kind all the time. Therefore, even when you're using proven systems such as the Z strategies, you always deal with an element of uncertainty. The future is unknown. For not relying only on your luck, learn as much as you can, and develop and deploy as many different strategies as possible.

See also:

Zorro Platform, Strategy Workshops, Bars & Candles, Tips&Tricks, Links

 

Structs

Structs

A struct is an assembled object that contains variables, pointers, or further structs. Members of a struct are individually accessed using the struct name, followed by a '.' and the member name. Example:

typedef struct { 
  int x; 
  int y; 
  string name;
} SPOT;  // defines a struct type named "SPOT"
...
SPOT myspot; // creates an uninitalized SPOT struct named "myspot"
SPOT* pspot; // creates an uninitalized pointer to a SPOT struct ... myspot.x = 10; myspot.name = "test!";

A struct can contain pointers to previously defined structs, and even pointers on itself, but no pointers to later defined structs:

typedef struct SPOT { 
  int x; 
  int y; 
  string name;
  struct SPOT* next; // pointer to a SPOT struct
} SPOT;  // defines a struct type named "SPOT"

In lite-C, struct pointers can be initialized to a static struct. Example:

SPOT* myspot = { x = 1; y = 2; name = "my struct"; }  
// creates a new SPOT struct with initial values that is then referenced through the myspot pointer

Working with structs is explained in any C/C++ book, so we won't cover it here in detail. For creating structs or arrays of structs at run time, the standard C library functions malloc and free can be used. For initializing or copying structs, use the C library functions memset() and memcpy():

function foo()
{
  SPOT myspot; 
  memset(myspot,0,sizeof(myspot)); // set the struct content to zero (it's undefined after malloc)
  myspot.x = 1;  
  SPOT* spot_array = malloc(100*sizeof(myspot)); // creates an array of 100 SPOT structs
  memcpy(&spot_array[0],myspot,sizeof(myspot));  // copy the myspot struct to the first member of the array
  ...
  free(myspot_array); // removes the created structs
}

 !!  There is plenty information about C, C++, or Windows library functions on the Internet or in online C courses. It is highly recommended for advanced lite-C programming to work through such a course or book and learn about malloc, memcpy and all the other library functions that you can use.

sizeof(struct)

The sizeof() macro that is used in the example above gives the size of a variable or a struct instance in bytes. This can be used to initialize structs:
#define zero(struct) memset((void*)&struct,0,sizeof(struct))
...
SPOT speed;
zero(speed);	// initializes the SPOT struct "speed" to zero

 !!  Unlike C/C++, sizeof(some struct) requires that at least one instance of the struct exists. Otherwise sizeof will return 0.

 !!  Arrays are internally treated as a pointer to a memory area. So, sizeof(some array) and sizeof(some pointer) always evaluates to 4 because that is the size of a pointer.

See also:

Variables, pointers, functions

► latest version online string functions

String functions

The following functions - mostly from the standard C library - can be used to manipulate strings:

strcpy (string dest, string src): string

Fills the char array dest with the content of string src. The buffer size of dest must not be exceeded.

Parameters:

dest - destination string, either a char array of sufficient length or a temporary string as returned from strmid, strf, or strx.
src - source string
 

strcat (string dest, string src): string

Appends a copy of src to the end of the string dest. The buffer size of dest must not be exceeded.

strcatf (string dest, string src): string

Like strcat, but appends to the front of dest. Can be used to insert a string into another string. This is not standard C string function.

Parameters:

dest - destination string, a char array of sufficient length or a temporary string as returned from strmid, strf, or strx.
src - source string
 

strlen (string str): int

Returns the number of characters in the given string.

Parameters:

str - string
 

strcount (string str, char c): int

Returns the number of occurrences of the character c in the given string. This is not a standard C string function.

Parameters:

str - string
c - character to be counted, f.i. 0x0a for a line feed.
 

strcmp (string str1, string str2): int

strncmp (string str1, string str2, int count): int

Compares all or the first count characters of the strings (case sensitive) and returns 0 if they have the same content. For comparing strings with text constants and case insensivity, the == and != operators can alternatively be used.

Parameters:

str1 - first string to compare
str2 - second string to compare
count - number of characters to compare
 

strstr (string str1, string str2): string

Returns a substring beginning with the first occurrence (case sensitive) of str2 within str1, or NULL when str2 is not contained in str1. This function is often used to check if a string has the same content or is contained in another string, or to parse string content by keywords. 

Parameters:

str1 - string to search within
str2 - substring to be found
 

strchr (string str, int c): string

strrchr (string str, int c): string

Returns a substring beginning with the first (strchr) or last (strrchr) occurrence of the character c in the string str, or NULL when c is not contained in str. This function can be used f.i. for getting the extension (search for '.') or the file name (search for '\\') from a path name. Another use is checking if a certain character is contained in a set of characters.

Parameters:

str - string to search within
c - character to be found
 

strtok (string str, string delimiter): string

Returns a substring from the string str, or 0 when no more substring is found. The set of characters in the delimiter string specifies the characters that separate the substrings.

Parameters:

str - string to separate the first substring from, or 0 for continuing the search with the next substring of the last str string. The string is modified by replacing the delimiters with 0 bytes.
delimiter - character or set of characters that separate the substrings, f.i. "," for the comma that separates fields in a CSV file, or "\n" for a line feed.
 

strvar (string str, string name, var default): var

Parses a number from the string str that is preceded by the identifier name. If no identifier name is found in the string str, the function returns the default value. Can be used to parse .ini or .json files. This is not a standard C string function.

Parameters:

str - string containing the number.
name - substring, variable identifier, or 0 to read the first number out of the string.
val - returned when no identifier name or no number was found in the string.
 

strtext (string str, string name, string default): string (temporary)

Parses text from the string str and returns a temporary string containing the text preceded by the identifier name. If the text contains spaces, it should be in double quotes inside str. If no identifier name is found in the string str, the function returns default. Can be used to parse .ini files or JSON strings. This is not a standard C string function.

Parameters:

str - string containing the text to be extracted.
name - substring, text identifier. For a JSON token, put it in double quotes followd by a colon (f.i. "\"datetime\":").
default - returned when no identifier name was found in the string.
 

strtr (TRADE*): string

Returns a temporary string (see remarks) with a trade identifier, as in the log messages. This is not a standard C string function.

Parameters:

TRADE pointer.

strcon (CONTRACT*): string

Returns a temporary string (see remarks) with a contract symbol. This is not a standard C string function.

Parameters:

CONTRACT pointer.
  

strmid (string str, int first, int count): string

Returns a temporary string (see remarks) that contains a substring of str, with length count and starting at position first. This is not a standard C string function.

Parameters:

str - string containing the source text.
first - position of the first character of the copy; first = 0 copies the string from the begin.
count - number of characters in the copy; 1000 characters maximum. If count exceeds the length of str, it is copied until the end.
 

strxc (string str, char orig, char repl): string (temporary)

strx (string str, string orig, string repl): string (temporary)

strx (string out, int size, string str, string orig, string repl)

Returns a temporary string (see remarks) or fills an existing string with a copy of str where all ocurrences of orig are exchanged with repl. This is not a standard C string function. For cutting off a string at a certain character, replace it with 0 (f.i. strxc(Name, '.', 0) cuts off the extension from a file name). 

Parameters:

out - target string to be filled. Must be allocated before and have sufficient size.
size - size of the target string, or 0 for the same size as the source string.
str
- string containing the source text, 1000 characters maximum.
orig - original text or char to be exchanged, or 0 for inserting repl at the begin.
repl - replacement text or char.
 

strw (string str): short* (temporary)

Converts an 8-bit char string to a temporary 16-bit wide string, i.e. an array of shorts. Useful for passing small strings, such as file names, to DLLs that require 16-bit characters. This is not a standard C string function.

Parameters:

str - string containing the text to be converted, 1000 characters max.
 

stridx (string Str, int Index): int

Assigns the given Str string to the given Index number, a unique integer in the 1..1000 range. Useful for indexing arrays or datasets with strings, such as asset or algo names. If Index is 0, the function generates and return an index This is not a standard C string function.

Parameters:

str - string to be converted to an index, 15 characters max.

strxid (int Index): string

Returns the string associated to the given Index. Example: int i = stridx("EUR/USD",0), j = stridx("USD/JPY",0); printf("\n%i %i %s %s",i,j,strxid(i),strxid(j));. This is not a standard C string function.

 

strf (string format, ...): string (temporary)

Returns a temporary string (see remarks) with data and variables formatted by the format string (see format codes). This is not a standard C string function.

Parameters:

format - format string, limited to a single line (no "\n" characters) and to maximum 1000 characters of the returned string.
 

sprintf (string dest, string format, ...): int

Like strf, but fills a given char array with formatted data, and returns the number of characters written. The dest char array must have sufficient size.

Parameters:

dest - destination string, a char array of sufficient length.
format - format string (see format codes).
 

sscanf (string str, string format, ...): int

Reads formatted data from a string into variable pointers or substrings according to the format string (see example). Returns the number of variables read from the string. The execution time of this function depends on the string length, so don't use it for very long strings such as the content of a whole file. Special format codes are supported, f.i. "%[^\n]" reads a substring until the end of the line, or "%[^,]" parses a line into comma-separated strings. Details can be found in any C/C++ manual or online documentation.  !!  Unlike sprintf and printf, for reading floating point numbers with %f the target variable must be float; var or double require %lf.

Parameters:

str - string to parse.
format - format string similar to format codes, but with additional fields.
 

atof (string str): var

atoi (string str): int

Converts a number from a string to a var or int variable. If the string does not begin with a valid number, 0 is returned.

Parameters:

str - string containing a number.
 

sftoa (var Value, int Digits): string

Returns Value converted to a temporary string with the given number of significant Digits. For instance sftoa(0.00123456, 4) will return "0.001235", and sftoa(12.3456, 4) will return "12.35". A negative Digits number enforces the number of decimals. This is not a standard C string function.

Parameters:

Value - value to convert.
Digits - number of nonzero digits when positive, decimals when negative.
 

Remarks:

Examples (see also file functions and Simulate.c):

// read a variable out of a string
// with a format like "name = 123.456"
var readVar(string str,string name)
{
  float f = 0; // sscanf needs float
  string s = strstr(str,name);
  if(s) s = strstr(s,"=");
  if(s) sscanf(s+1,"%f",&f);
  return f;
}

function main()
{
  var Test1 = 0,Test2 = 0;
  string s = file_content("Strategy\\vars.ini");
  if(s) {
    Test1 = readVar(s,"Test1");
    Test2 = readVar(s,"Test2");
  }	
  printf("\nTest1 = %f, Test2 = %f",Test1,Test2);
}
// make a temporary string permanent
...
static char MyPermanentString[100];
strcpy(MyPermanentString,strx("I like Ninja!","Ninja","Zorro"));
...

See also:

printf, string, strdate, file_functions, putvar, format codes

► latest version online suspended

suspended(): int

Returns 0 when trading is allowed, otherwise the current status of suspended trading. 

Returns:

0 Trades can be entered.
1 Before the first valid bar (plus lookback period) of the current asset, or after the last valid bar
2 Before the WFO data horizon or inside a SKIP period.
4 Inside a time frame.
8 Outside market hours.
16 [Stop] button was clicked.

The numbers can be combined, f.i. 12 = 4+8 = inside a time frame and outside market hours.

Remarks:

Example:

// don't shift series outside market hours
if(suspended()&8) set(NOSHIFT); else set(NOSHIFT|OFF);

See also:

Bar, frame, wait, BUSY, market, TimeFrame, BarZone, BarMode

► latest version online switch..case

switch (expression) { case value: instructions...  default: instructions... }

The switch statement allows for branching on multiple values of an int variable or expression. The expression is evaluated and compared with the case values. If it matches any of the case values, the instructions following the colon are executed. The execution continues until either the closing bracket or a break statement is encountered. If the expression does not match any of the case statements, and if there is a default statement, the instructions following default: are executed, otherwise the switch statement ends.

Example:

int choice;
...
switch(choice)
{
case 0:
printf("Zero! ");
break;
case 1:
printf("One! ");
break;
case 2:
printf("Two! "); break;
default:
printf("None of them! ");
}

See also:

if, while, break, continue, comparisons ► latest version online Symbols

Asset Symbols for the Zorro platform

Asset symbols are used for identifying a particular asset for online sources or broker APIs. They are given in the Symbol column of the asset list, but can also be set directly in the algo trading script. In the simplest case the symbol is identical to the asset name (f.i. "EUR/USD"). Different brokers use often different symbols for the same asset; for instance the DAX CFD has the symbol "GER30" on FXCM, "DE30/EUR" on Oanda, and "DEU.IDX/EUR" on Dukascopy. Zorro accepts a simple symbol, an extended symbol, or a combination of symbols. Extended symbols are needed when the asset is traded on several exchanges or requires additional information, such as expiration date or strike price. An asset can have a combination of up to three different symbols: the first one for trading, the second for retrieving live prices, and the third for retrieving historical prices.

An extended symbol is a text string in the format Source:Root-Type-Exchange-Currency ("IB:AAPL-STK-SMART-USD").
Assets with an expiration date, such as futures, have the format Root-Type-Expiry-Class-Exchange-Currency ("SPY-FUT-20231218-SPY1C-GLOBEX-USD").
Stock or index options have the format Root-Type-Expiry-Strike-P/C-Exchange-Currency ("AAPL-OPT-20191218-1350.0-C-GLOBEX-USD").
Future options have the format Root-Type-Expiry-Strike-P/C-Class-Exchange.("ZS-FOP-20191218-900.0-C-OSD-ECBOT").
Forex symbols that need a particular exchange have the format Currency/Countercurrency-Exchange ("NOK/USD-IDEALPRO").
Up to 3 symbols can be combined: SymbolTrade!SymbolLive!SymbolHist ("IB:*-STK-SMART-USD!STOOQ:*.US!STOOQ:*.US").

The symbols for futures and option contracts are often not entered in the asset list, but internally generated frrom the contract chain; the asset list then contains only the symbol of the underlying. The contract symbols can be retrieved with the strcon function. Parsing the symbol string is the job of the broker plugin. Parts of symbols can be omitted, for instance "SPY-FUT---GLOBEX-USD" determines a contract chain of SPY futures with all trading classes and all expiration dates.

An asset can have multiple symbols that are separated by exclamation marks '!'. They are accessible in the script under the names SymbolTrade, SymbolLive, SymbolHist. If the Symbol field is empty or contains '*', the asset name is used for the symbol. If it contains 2 symbols, the first is used for trading and the second for live and historical price data from the broker or online source. Of 3 symbols, the first is used for trading, the second for live prices, the third for downloading historical prices. An empy symbol of a multiple symbol combination disables its function - for instance, "AAPL!!" allows trading, but does not retrieve live or historical prices from the broker. This can be used in special cases when no market data was subscribed or prices are retrieved by other means, like with the assetHistory or priceQuote functions.

Symbol examples:

* Symbol identical to the asset name.
*-STK-SMART-USD General US stock or ETF, smart routing.
STOOQ:*.US General US stock or ETF, no trading, live and historical prices from STOOQ.
TNOW-STK-SMART-USD!TNOW-STK-SMART-USD!YAHOO:TNOW.L TNOW ETF, trading and pricing with current broker, historical prices from Yahoo.
OandaReal:EUR/USD!FXCMDemo:EUR/USD EUR/USD traded with OandaReal using prices from FXCMDemo.
AAPL-OPT-20190918-1350.0-C-GLOBEX AAPL Call option expiring Sep 18, 2019, at $1350 strike.
DAX-FUT-20201218-FDAX-DTB-EUR DAX future expiring Dec 18, 2020, trading class FDAX.

See also:

Broker, asset, asset list, broker arbitrage

► latest version online

malloc

C Memory Functions

malloc(long size): void*

Allocates a contiguous memory area with the given size in bytes. This can be used to create arrays of dynamic size. This function is slow, so it is recommended to use it for allocating memory areas in the initial run.  If there is not enough memory, 0 is returned.

realloc(void* ptr, long size): void*

Reallocates the memory area given by ptr. It must have been previously allocated by malloc. Returns a pointer to the new memory area. The old memory area is freed, the old content is copied over to the new area. This function is slow. If there is not enough memory, the old memory area is not freed and 0 is returned.

zalloc(long size): void* (temporary)

Like malloc, but returns a pointer to a temporary memory area that is initialized to zero and preserved until the next zalloc call. This function is fast, and the memory area needs not be freed. Use this for allocating a single temporary array inside functions.

Parameters:

size - Size of the allocated memory area, in bytes.

Returns:

void* pointer to the allocated memory area, or 0 when the allocation failed.
 

free(void* ptr)

Releases a contiguous memory area that was allocated with malloc.

Parameter:

ptr - void* pointer to the allocated memory area.
 

memcpy(void* dest, void* src, long size)

Copies size bytes from src to dest. Can be used to copy a whole struct or array with a single function.

Parameters:

dest - Target address.
src - Source address.
size - Number of bytes to copy.
 

memset(void* dest, int c, long size)

Sets the first size characters of dest to the character c. Can be used to initialize a whole struct or array to zero.

Parameters:

dest - Target address.
c - value to copy, 0..255.
size - Number of bytes to copy.
 

memcmp(void* buffer1, void* buffer2, long size)

Compares buffer1 with buffer2. Returns 0 when both are identical, otherwise nonzero.

Parameters:

buffer1 - First buffer.
buffer2 - Second buffer.
size - Number of bytes to compare.
 

Remarks:

Example (see also http):

var calculate_something()
{
  var *Buffer = zalloc(100*sizeof(var));
  int i;
  for(i=1; i<100; i++)
    buffer[i] = buffer[i-1]+1;
  return Buffer[99]; // last element
}

See also:

sizeof, memory

► latest version online

Technical Indicators

Technical Indicators and Technical Analysis

Technical Analysis is based on the belief that 'technical indicators' give insight into the current and future market situation. A technical indicator is a function that retrieves information from a time series of prices or trade volumes, and converts them to an output that can be used as a trade signal for buying or selling at the right moment. For instance, the Simple Moving Average (SMA) indicator with a Time Period 100 adds the last 100 prices and divides the sum by 100, this way getting the average price of the last 100 bars. If the current price rises above the average, some traders believe that prices will further rise, and take this as a buy signal. It the price falls below the average, they believe that prices will further fall and they must sell.

Price 1




 Indicator
 Function








 Buy/Sell
Signal
Price 2
Price 3
      ...
Price 100

Indicators usually generate buy or sell signals when they reach a threshold, cross each other, or cross the price curve. About 1000 different indicators are published in books and trading magazines, from the very simple to the very bizarre. The most often used are available in Zorro's indicators and time series library. The library contains moving averages, oscillators, bands, momentum, strength indices, divergence, support and resistance, linear and polynomial regression, signal processing, and machine learning. It is divided in several sections:

Aside from the indicators and time series functions, price curves and portfolio performance can also be analyzed with other methods:

Most indicators use the TA-Lib indicator library by Mario Fortier (www.ta-lib.org) and Zorros indicators.c library. Both are open source and included in the Zorro\Source folder. The most usual indicator categories are marked:

 Mkt  = Market State, f.i. for detecting trend or cycle regime,
 Avg  = Averaging or lowpass filter for curve smoothing,
 Rng  = Indicator based on price ratios or differences,
 Osc  = Oscillator, a measure usually in 0..100 range.

Indicators that do not fall under any above category are not marked.

If you need an indicator that is is missing, simply add it. Most technical indicators are primitive and can be implemented in a few minutes with a few lines of code. How to implement an indicator is described in Workshop4a and in the Petra on Programming series on Financial Hacker. The file indicators.c contains the source codes of many nonstandard indicators, so you can use it as learning material for adding more complex indicators.

AC(vars Data): var

Osc Accelerator Oscillator by Bill Williams; the difference of the AO indicator (see below) and its 5-bar simple moving average (SMA). Believed to indicate acceleration and deceleration of a 'market driving force' (whatever that means). For Data normally a MedPrice or price series is used. Source code in indicators.c.

ADO(): var

Osc Accumulation/Distribution Oscillator: ((Close-Low)-(High-Close))/(High-Low). Ranges from -1 when the close is the low of the bar, to +1 when it's the high. Supposed to gauge supply and demand by determining whether traders are generally "accumulating" (buying) or "distributing" (selling). This indicator was published in many individual variants to the formula, but none of them seems any better than the other. Uses the current asset price series. Source code in indicators.c.

ADX(int TimePeriod): var

ADX(vars Open, vars High, vars Low, vars Close, int TimePeriod): var

Osc Average Directional Movement Index. Moving average of the DX indicator (see below). The first version uses the current asset price series and does not support TimeFrame. The returned values range from 0 to 100.

ADXR(int TimePeriod): var

Osc Average Directional Movement Index Rating. The average of the current ADX and the ADX from TimePeriod bars ago. Uses the current asset price series and does not support TimeFrame.

Alligator(vars Data): var

Avg Alligator Indicator by Bill Willams. Consist of three lines: blue = SMA(13) delayed by 5 bars; red: SMA(8) delayed by 2 bars; green: SMA(5). Indicates a down trend with lines in the order blue-red-green (top to bottom), and an uptrend with green-red-blue. The closer the Alligator’s lines move, the weaker the trend gets and vice versa. Does not contain the additional 3 bars lag of the original Alligator algorithm (use Data+3 for that). For Data normally the high/low average (MedPrice series) is used. Result in rRed, rGreen, rBlue. Source code in indicators.c.

ALMA(vars Data, int TimePeriod, int Sigma, var Offset): var

ALMA(vars Data, int TimePeriod): var

Arnaud Legoux Moving Average. Based on a Gaussian distribution with a bias towards the begin of the Data series (i.e. more recent prices). Parameters: Sigma (distribution width, default 6); Offset (bias factor, default 0.85). Source code in indicators.c.

AO(vars Data): var

Avg Awesome Oscillator by Bill Williams; simply the difference of a 5-bar and a 34-bar SMA. For Data normally a MedPrice or price series is used. Source code in indicators.c.

APO(vars Data, int FastPeriod, int SlowPeriod, int MAType): var

Osc Absolute Price Oscillator; a more general version of the AO. Returns the difference between two moving averages. Parameters: FastPeriod (Number of period for the fast MA), SlowPeriod (Number of period for the slow MA), MAType (Type of Moving Average).

Aroon(int TimePeriod): var

Osc Aroon indicator. Consists of two lines (Up and Down) that measure how long it has been since the highest high/lowest low has occurred within the time period. Uses the current asset price series. Does not support TimeFrame. Result in rAroonDown, rAroonUp.

AroonOsc(int TimePeriod): var

Osc Aroon Oscillator. Calculated by subtracting the Aroon Down from the Aroon Up. The return value will oscillate between +100 and -100. Uses the current asset price series. Does not support TimeFrame.

ATR(int TimePeriod): var

Rng Average True Range. A measure of price volatility; useful for calculating stop loss or profit target distances. Formula: ATR = (ATR1 * (TimePeriod-1) + max(High,Close)-min(Low,Close)) / TimePeriod, where ATR1 is the ATR from the last bar. Uses the current asset prices. TimeFrame must be zero or positive. The function internally creates series when TimeFrame is > 1, (see remarks). See also: Volatility, CVolatilty, TrueRange, ATRS.

ATR(vars Opens, vars Highs, vars Lows, vars Closes, int TimePeriod): var

Rng Average True Range from arbitrary price series, with arbitrary offset and time frame. Use this function when TimeFrame is not constant, but changes from bar to bar, f.i. when skipping bars outside market hours or aligning to the end of day.

ATRS(int TimePeriod): var

Rng Simple Average True Range. SMA of the TrueRange over the TimePeriod, using the current asset price series. A measure of price volatility, simpler to calculate than the ATR, but adapting slow to volatility changes and thus less suited for stop loss / profit targets. Used by the MTR4 platform instead of the real ATR. Does not support TimeFrame. Source code in indicators.c.

AvgPrice(): var

Average Price. Simply (Open+High+Low+Close)/4 with the current asset price series.

BBands(vars Data, int TimePeriod, var NbDevUp, var NbDevDn, int MAType)

Bollinger Bands. Consist of three lines; the middle band is a moving average (usually a 20 periods SMA) of the Data series. The upper and lower bands are NbDevUp standard deviations above and NbDevDn standard deviations below the middle band. The bands widen and narrow when the volatility of the Data series is higher or lower, respectively. They collapse to a line when the standard deviation is less than 0.0001. Result in rRealUpperBand, rRealMiddleBand, rRealLowerBand. Parameters: NbDevUp (Deviation multiplier for upper band, usually 2), NbDevDn (Deviation multiplier for lower band, usually 2), MAType (Type of Moving Average, usually MAType_SMA). Example in Indicatortest.c.

BBOsc(vars Data, int TimePeriod, var NbDev, int MAType): var

Osc Bollinger Bands Oscillator; the percentage of the current value of the series within the Bollinger Bands.

Beta(vars Data, vars Data2, int TimePeriod): var

Beta value. A measure of a single asset's prices versus the overall market index. The asset price is given in Data and the market index price (f.i. S&P 500, DAX, etc) is given in Data2. The algorithm calculates the change between prices in both series and then stores these changes as 2-dimensional points. The x value of any point is the Data2 (market) change and the y value is the Data (asset) change. The beta value is the slope of a linear regression line through these points. A beta of 1 is simple the line y=x, so the asset varies percisely with the market. A beta of less than one means the asset varies less than the market and a beta of more than one means the asset varies more than the market.

BOP(): var

Balance Of Power; simply (Close - Open)/(High - Low). Uses the current asset price series.

CCI(int TimePeriod): var

Osc Commodity Channel Index. Variation of the price from its statistical mean, typically oscillates between +/-100. Formula: CCI = (TypPrice-SMA(TypPrice)) / (.015 x SMA(Numerator)). Uses the current asset price series. Does not support TimeFrame.

CCYI(vars Data, int TimePeriod): var

Ehlers' Correlation Cycle Indicator. Measures the Spearman correlation of the Data series with a sine wave. Source code in indicators.c. Details on Financial Hacker

CCYIR(vars Data, int TimePeriod): var

CCYI rate of change; the first derivative of the CCYI. Source code in indicators.c.

CCYIState(vars Data, int TimePeriod, var Threshold): var

Mkt Market state based on the CCYI. Measures the phase angle difference between the Data series and a correlated sine wave, and returns 1 for rising trend regime, -1 for falling trend regime, and 0 for cycle regime. The critical angle difference in degrees is set with the Threshold parameter (0..45). Source code in indicators.c. Details on Financial Hacker

CI(int TimePeriod): var

Osc Choppiness Index; measures single bar price volatility in relation to the volatility of the past TimePeriod in a 1..100 range. Uses the current asset price series. Does not support TimeFrame.

ChandelierLong(int TimePeriod, var Multiplier): var

ChandelierShort(int TimePeriod, var Multiplier): var

Chandelier exit; the highest price of TimePeriod minus the ATR multiplied with Multiplier. Normally used as a trailing Stop Loss, for keeping trades in a trend and preventing an early exit as long as the trend continues. Source code in indicators.c. Does not support TimeFrame. Example in the TMF chapter.

CGOsc(vars Data, int TimePeriod): var

Osc Center of Gravity oscillator, by John Ehlers; computes the deviation of prices from their center within the TimePeriod. Can be used to identify price turning points with almost zero lag. Source code in indicators.c.

Chikou(int Shift): var

Chikou line belonging to the Ichimoku indicator; simply a delayed Close moved forward by Shift. Uses the current asset price series. Source code in indicators.c. The traditional Ichimoku requires a future peeking Chikou line (Shift = -26); this can be achieved by setting Shift = 0 and moving the rest of the Ichimoku forward by Offset = 26.

CMO(vars Data, int TimePeriod): var

Osc Chande Momentum Oscillator. Similar to the RSI, but divides the total data movement by the net movement ((up - down) / (up + down)).

Coral(vars Data): var

Avg Coral Indicator, simply a T3 with TimePeriod = 60 and VolumeFactor = 0.4. Source code in indicators.c.

ConnorsRSI(vars Data, int RSIPeriod, int StreakPeriod, int RankPeriod): var

Osc Indicator by Larry Connors, mean of a 3 period RSI on Data, a 2 period RSI of the recent up/down streak, and the percent rank of the recent change. Fluctuates between 0 and 100 and believed to indicate the overbought (high values) or oversold (low values) level. The function internally creates series (see remarks). Source code in indicators.c.

CTI(vars Data, int TimePeriod): var

Ehlers' Correlation Trend Indicator. Measures the Spearman correlation of the Data series with a rising trend. Source code in indicators.c.

DChannel(int TimePeriod)

Donchian Channel; the minimum and maximum value of the priceHigh and priceLow functions over the time period. Basis of the famous Turtle Trading System. Uses the current asset price series. Does not support TimeFrame. Result in rRealUpperBand, rRealLowerBand.

DCOsc(vars Data, int TimePeriod): var

Osc Donchian Channel Oscillator; the percentage of the current Data value within the Donchian Channel. Uses the current asset and current TimeFrame.

Decycle(vars Data, int CutOffPeriod): var

Ehlers' Decycler, a low-lag trend indicator; simply Data - HighPass2(Data,CutOffPeriod). Removes all cycles below CutOffPeriod from the Data series and keeps the trend. The function internally creates series (see remarks). Source code in indicators.c.

DEMA(vars Data, int TimePeriod): var

Avg Double Exponential Moving Average.

Divergence(vars Highs, vars Lows, vars Data, int TimePeriod): int

Mkt Regular or hidden divergence. Draws lines through the two most prominent peaks and valleys of the Highs and Lows series, and compares with lines through the two most prominent valleys and peaks of the Data series within the TimePeriod. Highs and Lows are usually from a price curve, while Data is from an oscillating indicator, such as MACD, CCI, RSI, Stoch, etc. Some traders believe that a divergence between the lines predicts a trend change. Bullish regular divergence means that the price makes lower lows, while the oscillator does not. Bearish regular divergence means that the price makes higher highs, while the oscillator does not. Bullish hidden divergence means that the oscillator makes higher highs, while the price does not. Bearish hidden divergence means that the oscillator makes lower lows, while the price does not. The function returns:
0 - No divergence
1 - Bullish regular divergence
2 - Bearish regular divergence
4 - Bullish hidden divergence
8 - Bearish hidden divergence.
The returned value can be a combination, f.i. 5 = 1+4 = bullish regular + bullish hidden divergence. All combinations are possible. 
The slope of the determining line (movement per bar) is stored in rSlope. Source code in indicators.c.

DPO(vars Data, int TimePeriod): var

Osc Detrended Price Oscillator; believed to detect early changes in price direction. DPO = Data[0] - SMA(Data[n/2+1],n), where n is the TimePeriod. Source code in indicators.c.

DX(int TimePeriod): var

DX(vars Open, vars High, vars Low, vars Close, int TimePeriod): var

Osc Directional Movement Index by Welles Wilder, who discovered that "the interaction of sun, moon, and earth is the basis of all market movement". In case that celestial bodies refrain from moving the market, he also invented the DX, which he believed to indicate trend strength. The values range from 0 to 100, but rarely get above 60. The first DX version uses the current asset price series and does not support TimeFrame. Formula: DX = 100 * abs(PlusDI-MinusDI) / (PlusDI+MinusDI). For PlusDI and MinusDI see the description below.

EMA(vars Data, int TimePeriod): var

Avg Exponential Moving Average. Emphasizes more recent data values. It uses the formula EMA = alpha * data + (1-alpha) * EMA1, where alpha is 2.0/(TimePeriod+1), and EMA1 is the previous EMA value. The higher the time period, the smaller is alpha and the higher is the smoothing effect of the EMA formula. The function requires a Data length of TimePeriod+UnstablePeriod+1.

EMA(var Input, int TimePeriod): var

EMA(var Input, var alpha): var

Avg Exponential Moving Average as above, alternative version that uses a single variable as input and also accepts the alpha parameter (see above). Does not use UnstablePeriod, but creates an internal series.

ER(vars Data, int TimePeriod): var

Osc Kaufman's Efficiency Ratio, the amount of price changes in the given interval in relation to the absolute price change. Source code in indicators.c.

FractalHigh(vars Data, int TimePeriod): var

Fractal High, an indicator by Bill Williams, believed to signal when the market reverses (has nothing to do with fractals). Returns the highest Data value when it is in the center of the TimePeriod, otherwise 0.

FractalLow(vars Data, int TimePeriod): var

Fractal Low. Returns the lowest Data value when it is in the center of the TimePeriod, otherwise 0.

HAOpen(): var

HAClose(): var

HAHigh(): var

HALow(): var

Haiken Ashi prices of the current bar, based on the current asset prices. Except for HAClose, the functions internally create series (see remarks). Source code in indicators.c.

HH(int TimePeriod, int Offset): var

Highest value of the priceHigh function over the TimePeriod ending with Offset (default 0). F.i. HH(3,1) returns the highest price of the 3 bars before the current bar. Uses the current asset price series. Sets rMaxIdx to the bar offset with the highest price. TimeFrame > 1 is supported. Source code in indicators.c. See also dayHigh.

HMA(vars Data, int TimePeriod): var

Avg Hull Moving Average by Alan Hull; attempts to address lag as well as to smooth out some choppiness. Formula:HMA(n) = WMA(2*WMA(n/2) – WMA(n)),sqrt(n)). The function internally creates a series (see remarks). Source code in indicators.c.

HTDcPeriod(vars Data): var

Osc Hilbert Transform - Dominant Cycle Period, developed by John Ehlers. Hilbert transform algorithms are explained in Ehler's book "Rocket Science for Traders" (see book list). This function is equivalent, but less accurate than the DominantPeriod function.

HTDcPhase(vars Data): var

Osc Hilbert Transform - Dominant Cycle Phase.

HTPhasor(vars Data): var

Hilbert Transform - Phasor Components. Result in rInPhase, rQuadrature.

HTSine(vars Data): var

Osc Hilbert Transform - SineWave. Result in rSine, rLeadSine.

HTTrendline(vars Data): var

Avg Hilbert Transform - Instantaneous Trendline.

HTTrendMode(vars Data): int

Mkt Hilbert Transform trend indicator - returns 1 for Trend Mode, 0 for Cycle Mode.

IBS(): var

Osc Internal Bar Strength; relative position in percent of the close price with respect to the low to high range for the same period. Source code in indicators.c.

Ichimoku()

Ichimoku(int PeriodTenkan, int PeriodKijun, int PeriodSenkou, int Offset)

Avg Ichimoku Kinko Hyo indicator. Invented by the journalist Goichi Hosoda in the 1930s, and today again in fashion due to its enormous number of colorful lines. The Ichimoku is a mix of the medium prices of 3 time periods. Offset (default 0) determines the bar for calculating the indicator, and can be used to shift all lines forward. The function returns 4 variables:

rTenkan = (HH+LL)/2 with PeriodTenkan (default 9)
rKijun = (HH+LL)/2 with PeriodKijun (default 26)
rSenkouA = (rTenkan+rKijun)/2, shifted forward by PeriodKijun. Forms a "cloud band" with rSenkouB.
rSenkouB = (HH+LL)/2 with PeriodSenkou (default 52), shifted forward by PeriodKijun

Another line belonging to the Ichimoku, the Chikou line, is future peeking and calculated separately. The function uses the current asset price series. It internally creates series when TimeFrame is > 1 (see remarks). Source code in indicators.c.

KAMA(vars Data, int TimePeriod): var

Avg Kaufman Adaptive Moving Average. An exponential moving average adjusted by price volatility, so its time period becomes shorter when volatility is high.

KAMA2(vars Data, int ERPeriod, int FastPeriod, int SlowPeriod): var

Avg Kaufman Adaptive Moving Average as above, but the volatilty detection period as well as the short and long time period can be set separately. Internally creates series (see remarks). Source code in indicators.c.

Keltner(vars Data, int TimePeriod, var Factor): var

Avg Keltner Channel, by Charles Keltner. A Simple Moving Average - SMA(Data,TimePeriod) - with side bands in the distance Factor * ATRS(TimePeriod). Results in rRealUpperBand, rRealMiddleBand, rRealLowerBand. Source code in indicators.c.

LL(int TimePeriod, int Offset): var

Lowest value of the priceLow function over the TimePeriod ending with Offset (default 0). F.i. LL(3,10) returns the lowest price between the last 10 and the last 13 bars. Uses the current asset price series. Sets rMinIdx to the bar offset with the lowest price. TimeFrame > 1 is supported. Source code in indicators.c. See also dayLow

LSMA(vars Data, int TimePeriod, int Offset): var

Avg Least Squares Moving Average. Calculates a linear regression line over the recent TimePeriod, and returns the value of that line at the given bar Offset (0 for the current bar). Negative offsets return future values of the regression line and can be used for forecasting. See also QLSMA, polyfit, LinearReg, predict. Source code in indicators.c.

MACD(vars Data, int FastPeriod, int SlowPeriod, int SignalPeriod)

Rng Moving Average Convergence/Divergence. The MACD is an intermediate-term trend indicator, created by subtracting a 26-period Exponential Moving Average (EMA, see above) from a 12-period EMA. A nine-period EMA is then applied to the MACD result to create a 'signal line'. A MACD Histogram line is finally created from the difference of the MACD to its signal line. It is believed that the zero crossing of the histogram from below is a buy signal, zero crossing from above a sell signal. The formula is:
 
rMACD = EMA(Data,FastPeriod)-EMA(Data,SlowPeriod);
rMACDSignal = EMA(rMACD,SignalPeriod);
rMACDHist = rMACD - rMACDSignal;

 
Results in rMACD, rMACDSignal, rMACDHist. Returns: rMACD. Parameters: FastPeriod (time period for the fast MA), SlowPeriod (time period for the slow MA), SignalPeriod (time period for smoothing the signal line).

MACDExt(vars Data, int FastPeriod, int FastMAType, int SlowPeriod, int SlowMAType, int SignalPeriod, int SignalMAType)

Rng MACD with controllable MA type. Result in rMACD, rMACDSignal, rMACDHist. Parameters: FastPeriod (time period for the fast MA), FastMAType (Type of Moving Average for fast MA), SlowPeriod (time period for the slow MA), SlowMAType (Type of Moving Average for slow MA), SignalPeriod (time period for smoothing the signal line), SignalMAType (Type of Moving Average for signal line).

MACDFix(vars Data, int SignalPeriod)

Rng Moving Average Convergence/Divergence Fix 12/26. Result in rMACD, rMACDSignal, rMACDHist. Parameters: SignalPeriod (time period for smoothing the signal line).

MAMA(vars Data, var FastLimit, var SlowLimit)

Avg MESA Adaptive Moving Average, developed by John Ehlers (see links). Result in rMAMA, rFAMA. Parameters: FastLimit (Upper limit use in the adaptive algorithm), SlowLimit (Lower limit use in the adaptive algorithm).

MedPrice(): var

Center price; simply the center point (High+Low)/2 of the current candle. For the mean price - the average of all price ticks of the candle - use price(). For the middle price between Ask and Bid, take the ask price and add Spread/2.

MidPoint(vars Data, int TimePeriod): var

MidPoint over period. Simply (highest value + lowest value)/2.

MidPrice(int TimePeriod): var

Midpoint price over period. Simply (highest high + lowest low)/2 of the current asset price series. Does not support TimeFrame.

MinusDI(int TimePeriod): var

MinusDI(vars Open, vars High, vars Low, vars Close, int TimePeriod): var

Osc Minus Directional Indicator, part of the DX indicator, ratio of a smoothed MinusDM to ATR in percent. If the function is not called with different price series, the current asset price series is used.

MinusDM(int TimePeriod): var

MinusDM(vars Open, vars High, vars Low, vars Close, int TimePeriod): var

Rng Minus Directional Movement, the difference of current low and previous low. In the first version the current asset price series is used.

MMI(vars Data, int TimePeriod): var

Mkt Market Meanness Index by Financial Hacker. Measures the mean reversal tendency of the market in a 0..100% range. Random numbers have a MMI of 75%. Real prices are more or less autocorrelated, so the probability of a real price series to revert to the mean is less than 75%, but normally more than 50%. The higher it is, the 'meaner' the market behaves towards trend following systems. The Market Meanness Index can determine when trend following systems will become more profitable (MMI is falling) or less profitable (MMI is rising), and thus prevent losses in unprofitable periods. Source code in indicators.c.

Mom(vars Data, int TimePeriod): var

Rng Momentum. Simply Data[0] - Data[TimePeriod]. See also RET and  diff.

MovingAverage(vars Data, int TimePeriod, int MAType): var

Avg Moving average. Parameters: MAType (Type of Moving Average, see remarks).

MovingAverageVariablePeriod(vars Data, vars Periods, int MinPeriod, int MaxPeriod, int MAType): var

Avg Moving average with variable period given by the Periods series. Parameters: MinPeriod (Value less than minimum will be changed to Minimum period), MaxPeriod (Value higher than maximum will be changed to Maximum period), MAType (Type of Moving Average, see remarks).

NATR (int TimePeriod): var

Osc Normalized Average True Range, by John Forman. Similar to the ATR, except it is being normalized as follows: NATR = 100 * ATR(TimePeriod) / Close. Uses the current asset price series. Does not support TimeFrame.

NumWhiteBlack (var Body, int Offset, int TimePeriod): var

Number of white minus number of black candles in the given TimePeriod. Offset is the distance to the current bar (0 = current bar), Body is the minimum length of a candle to be counted. Source code in indicators.c

OBV (vars Data, var Volume): var

On Balance Volume, by Joseph Granville. The cumulative difference of the up and down volume. Data is normally a Close series, Volume can be taken from marketVol. Because it is cumulative, its absolute value depends on the start date and previous history. Source code in indicators.c.

Pivot (int TimePeriod, int Offset)

The 'pivot point' (High+Low+Close)/3 of the given TimePeriod at the given bar Offset. Uses the current asset time series, but TimeFrame is supported. See also dayPivot. Source code in indicators.c.

PlusDI (int TimePeriod): var

PlusDI (vars Open, vars High, vars Low, vars Close, int TimePeriod): var

Osc Plus Directional Indicator, a part of the DX indicator, ratio of a smoothed PlusDM to ATR in percent. In the first version the current asset price series is used.

PlusDM (int TimePeriod): var

PlusDM (vars Open, vars High, vars Low, vars Close, int TimePeriod): var

Rng Plus Directional Movement, the difference of current high and previous high. In the first version the current asset price series is used.

PPO(vars Data, int FastPeriod, int SlowPeriod, int MAType): var

Osc Percentage Price Oscillator, the normalized difference of two averages. Parameters: FastPeriod (Number of period for the fast MA), SlowPeriod (Number of period for the slow MA), MAType (Type of Moving Average).

ProfitFactor(vars Data, int Length): var

Returns the profit factor of the Data series. The profit factor is the ratio of the sum of positive returns (i.e. Data[i-1] > Data[i]) to the sum of negative returns (Data[i-1] < Data[i]). The returned value is clipped to the 0.1...10 range. Use its reciprocal when the Data array is in not in series order, but in ascending order, as wins and losses are then swapped. Source available in indicators.c

QLSMA(vars Data, int TimePeriod, int Offset): var

Avg Quadratic Least Squares Moving Average. Calculates a parabolic regression curve over the recent TimePeriod, and returns the value of that curve at the given bar Offset (0 for the current bar). Negative offsets return future values of the regression curve and can be used for forecasting. See also LSMA, polyfit, LinearReg, predict. Source code in indicators.c.

Resistance(vars Data, int TimePeriod): var

Resistance line; returns the average of the two highest Data peaks in the TimePeriod, or 0 when no peaks are found. The slope (Data difference per bar) of the line through the two peaks is stored in rSlope and can be used to determine if the resistance line is flat enough. See also Support, Divergence, pivot levels. Source code in indicators.c.

RET(int TimePeriod): var

Rng Return of the current asset: (Close(0)-Close(TimePeriod))/Close(TimePeriod). Source code in indicators.c

ROC(vars Data, int TimePeriod): var

Rng Rate of change, 100 scale: ((price-prevPrice)/prevPrice)*100.

ROCP(vars Data, int TimePeriod): var

Rng Rate of change: (price-prevPrice)/prevPrice. See also diff.

ROCR(vars Data, int TimePeriod): var

Rng Rate of change ratio: (price/prevPrice).

ROCR100(vars Data, int TimePeriod): var

Rng Rate of change ratio, 100 scale: (price/prevPrice)*100.

ROCL(vars Data, int TimePeriod): var

Rng Logarithmic return: log(price/prevPrice).

Roof(vars Data, int CutoffLow, int CutoffHigh): var

Avg Ehlers' roofing filter, prepares the Data series for further computation by removing trend and noise. Applies a 2-pole highpass filter followed by the Smooth filter. Recommended values for the low and high cutoff periods are 10 and 50. The minimum length of the Data series is 2. The function internally creates series (see remarks). Source available in indicators.c.

RSI(vars Data, int TimePeriod): var

Osc Relative Strength Index, by Welles Wilder. Ratio of the recent upwards data movement to the total data movement; range 0..100. The RSI is believed to indicate overbought/oversold conditions when the value is over 70/below 30. It is an accumulative indicator. Formula: RSI = 100 * Up/(Up+Dn), where Up = EMA(max(0,Data[0]-Data[1]),TimePeriod) and Dn = EMA(max(0,Data[1]-Data[0]),TimePeriod)

RSIS(vars Data, int TimePeriod): var

Osc Relative Strength Index variant, using the simple average of the Up/Dn sums instead of an EMA. This RSI variant is used by several trading platforms instead of the original version.

RVI(int TimePeriod): var

Osc Relative Vigor Index, by John Ehlers. Ratio of price change to the total price range: (C-O)/(H-L), averaged over the time period and smoothed with a FIR filter. Oscillates between -1 and 1. The function internally creates a series (see remarks). Source code in indicators.c.

SAR(var Step, var Min, var Max): var

Parabolic SAR by Welles Wilder. The SAR runs above or below the price curve, depending on the current trend; each price curve crossing is believed to indicate a trend change. Parameters: Step (acceleration factor increment, normally 0.02), Min (acceleration factor minimum value, normally 0.02), Max (acceleration factor maximum value, normally 0.2). SAR is a recursive function that depends on the direction of the initial price candle; for consistent values the LookBack period should be long enough to contain at least one price curve crossing. Uses the current asset prices. The function internally creates a series (see remarks). Source code in indicators.c, example in Indicatortest.c

SentimentLW(int TimePeriod): var

Osc Market Sentiment Index by Larry Williams, based on the averaged differences of day closes to day highs/lows. Returns a 0..100 percentage. Williams believed that a high index indicates a bullish market saturation and predicts sells, while a low index indicates no bullishness and predicts buys. Uses the current asset prices. The function internally creates a series (see remarks). Source code in indicators.c.

SentimentG(int TimePeriod): var

Osc Genesis Sentiment Index, the current price in relation to the given TimePeriod, usually 6 months. Returns a 0..100 percentage; believed to indicate a bullish market with a high index and bearishness with a low index. Uses the current asset prices. The function internally creates a series (see remarks). Source code in indicators.c.

Sharpe(vars Data, int TimePeriod): var

Sharpe ratio; the mean of the Data series divided by its standard deviation. Source code in indicators.c.

SIROC(vars Data, int TimePeriod, int EMAPeriod): var

Rng Smoothed Rate of Change (S-RoC) by Fred G Schutzman. Differs from the ROC (see above) in that it is based on the exponential moving average (EMA) of the Data series. Believed to indicate the strength of a trend by determining if the trend is accelerating or decelerating. Formula: (Current EMA - Previous EMA)/(Previous EMA) x 100. Source code in indicators.c.

SMA(vars Data, int TimePeriod): var

Avg Simple Moving Average; the mean of the Data series, i.e. the sum of all elementes divided by the time period. Use Moment when long time periods are required.

SMAP(vars Data, int TimePeriod): var

Avg Mean of Data as above, but counting only nonzero positive values. Can be used to skip inactive periods f.i. for calculating the average volume. Source code in indicators.c.

Smooth(vars Data, int CutoffPeriod): var

Avg Ehlers' 'Super Smoother' filter, a 2-pole Butterworth filter combined with a SMA that suppresses the Nyquist frequency. Can be used as a low-lag universal filter for removing noise from price data. The minimum length of the Data series is 2. The function internally creates series (see remarks). Source available in indicators.c.

SMom(vars Data, int TimePeriod, int CutoffPeriod): var

Rng Smoothed Momentum by John Ehlers; indicates the long term trend direction. TimePeriod is the momentum period, CutoffPeriod is a Butterworth filter constant for lowpass filtering the momentum. Source code in indicators.c.

Sortino(vars Data, int TimePeriod): var

Sortino ratio; the mean of the Data series divided by its semi-deviation, i.e. by the standard deviation of Data values below the mean. Source code in indicators.c.

Stoch(int FastK_Period, int SlowK_Period, int SlowK_MAType, int SlowD_Period, int SlowD_MAType)

Stoch(vars Open, vars High, vars Low, vars Close, int FastK_Period, int SlowK_Period, int SlowK_MAType, int SlowD_Period, int SlowD_MAType)

Osc Stochastic Oscillator (not related to stochastic, but its inventor, George Lane, looked for an impressive name). Measures where the Close price is in relation to the recent trading range. Formula: FastK = 100 * (Close-LL)/(HH-LL); SlowK = MA(FastK); SlowD = MA(SlowK). Some traders believe that the SlowK crossing above SlowD is a buy signal; others believe they should buy when SlowD is below 20 and sell when it is above 80. Two versions; the first version uses the current asset price series and does not support TimeFrame. Result in rSlowK, rSlowD. Returns SlowD. Parameters:
FastK_Period
- Time period for the HH and LL to generate the FastK value, usually 14.
SlowK_Period - Time period for smoothing FastK to generate rSlowK; usually 3.
SlowK_MAType - Type of Moving Average for Slow-K, usually MAType_SMA.
SlowD_Period - Time period for smoothing rSlowK to generate rSlowD, usually 3.
SlowD_MAType - Type of Moving Average for Slow-D, usually MAType_SMA.

StochEhlers(vars Data, int TimePeriod, int CutOffLow, int CutOffHigh): var

 Osc Predictive stochastic oscillator by John Ehlers. Measures where the Data value is in relation to its range within TimePeriod. The data runs through a 2-pole highpass filter with period CutOffHigh and through a Butterworth lowpass filter with period CutOffLow. Indicator algorithm explained in Ehler's "Predictive Indicators" paper; usage example in the Ehlers script. Source code in indicators.c. The function internally creates series (see remarks).

StochF(int FastK_Period, int FastD_Period, int FastD_MAType): var

Osc Stochastic Fast; Stoch without the SlowK part. Measures where the Close price is in relation to the recent trading range; Formula: Fast-K = 100 * (Close-LL)/(HH-LL); Fast-D = MA(Fast-K). Uses the current asset price series, and does not support TimeFrame. Results in rFastK, rFastD. Returns FastD. Parameters:
FastK_Period
(Time period for the HH and LL of Fast-K, usually 14),
FastD_Period
(Moving Average Period for Fast-D; usually 3),
FastD_MAType
(Type of Moving Average for Fast-D, usually MAType_SMA).

StochRSI(vars Data, int TimePeriod, int FastK_Period, int FastD_Period, int FastD_MAType): var

Osc Stochastic Relative Strength Index. The fast Stoch applied to the RSI in hope to get an even better result. Results in rFastK, rFastD. Returns FastD. Parameters:
TimePeriod
- for the RSI, usually 14,
FastK_Period
- Time period for building the Fast-K line, usually 14,
FastD_Period
- Smoothing for making the Fast-D line, usually 3,
FastD_MAType
- Type of Moving Average for Fast-D,usually MAType_SMA).

Support(vars Data, int TimePeriod): var

Support line; returns the average of the two lowest Data valleys in the TimePeriod, or 0 when no valleys are found. The slope (Data change per bar) of the line through the two valleys is stored in rSlope and can be used to determine if the support line is flat enough. See also Resistance, Divergence, pivot levels. Source code in indicators.c.

T3(vars Data, int TimePeriod, var VFactor): var

Avg An extremely smoothed Moving Average by Tim Tillson. Uses a weighted sum of multiple EMAs. Parameters: VFactor (Volume Factor, normally 0.7).

TEMA(vars Data, int TimePeriod): var

Avg Triple Exponential Moving Average by Patrick Mulloy, calculated from (3xEMA)-(3xEMA of EMA)+(EMA of EMA of EMA).

Trima(vars Data, int TimePeriod): var

Avg Triangular Moving Average (also known under the name TMA); a form of Weighted Moving Average where the weights are assigned in a triangular pattern. F.i. the weights for a 7 period Triangular Moving Average would be 1, 2, 3, 4, 3, 2, 1. This gives more weight to the middle of the time series. It causes better smoothing, but greater lag.

Trix(vars Data, int TimePeriod): var

1-day Rate-Of-Change (see ROC) of a Triple EMA (see TEMA).

TrueRange(): var

Rng True Range (TR); max(High[0],Close[1])-min(Low[0],Close[1]) of the current asset price series. See also ATR, ATRS.

TSI(vars Data, int TimePeriod): var

Mkt Trend Strength Index, an indicator by Frank Hassler who believed that it identifies trend strength. A high TSI value (above ~ 1.65) indicates that short-term trend continuation is more likely than short-term trend reversal. The function internally creates series (see remarks).

TypPrice(): var

Typical Price. Simply (High + Low + Close)/3. Uses the current asset price series.

UltOsc(int TimePeriod1, int TimePeriod2, int TimePeriod3): var

Osc Ultimate Oscillator. Parameters: TimePeriod1 (Number of bars for 1st period.), TimePeriod2 (Number of bars for 2nd period), TimePeriod3 (Number of bars for 3rd period). Uses the current asset price series. Does not support TimeFrame.

UO(vars Data, int CutOff): var

Osc Universal oscillator by John Ehlers, from S&C Magazine 1/2015. Removes white noise from the data, smoothes it and runs it through the AGC filter. Detects trend reversals very early. Output in the -1..+1 range. Source code in indicators.c. The function internally creates series (see remarks).

Volatility(vars Data, int TimePeriod): var

Annualized volatility of the Data series; standard deviation of the logarithmic returns, multiplied with the square root of time frames in a year. Data must be a series of positive values, normally a price series. This is the standard measure of volatility used in financial models, such as the Black-Scholes model.
  For irregular Data series, corrections can be required to annualize the volatilty for all time frames. If Data contains flat out-of-market periods, multiply the result with the square root of trading bars per day divided by bar periods per day (example: with 60 minute bars and 8 trading bars per day, multiply with sqrt(8./24) = 0.58). If Data has a variable number of bars per element by time frame synchronizing (frameSync), divide the result by the square root of the average bars per time frame.
  The function internally creates series (see remarks). Source code in indicators.c. A practical alternative for options calculations is VolatilityOV.

VolatilityC(int TimePeriod, int EMAPeriod): var

Rng Chaikin Volatility indicator by Marc Chaikin; measures volatility in percent as momentum of the smoothed difference between High and Low. An increase in the Chaikin Volatility indicates that a bottom is approaching, a decrease indicates that a top is approaching. TimePeriod is the period of the momentum (normally 10), EMAPeriod determines the smoothing (also, normally 10). Uses the current asset price series. The function internally creates series (see remarks). Source code in indicators.c.

VolatilityMM(vars Data, int TimePeriod, int EMAPeriod): var

Rng Min/Max volatility of the Data series; the difference of MaxVal and MinVal in the time period, smoothed by an EMA (set EMAPeriod = 0 for no smoothing). The function internally creates a series when EMAPeriod > 0 (see remarks). Source available in indicators.c. For the volatility of price candles, use ATR or ATRS.

VolatilityOV(int Days): var

Annualized volatility of the current asset, calculated over the given number of Days (usually 20) regardless of the bar period. Empirical formula used by options software packages (OptionVue™) for estimating the values of options, alternatively to Volatility(). Source code in contract.c, which must be included for using this indicator.

VolatilityP(int Days): var

Parkinson volatility of the current asset, calculated over the given number of Days (usually 10) regardless of the bar period. The Parkinson Volatility is the normalized square root of the natural logarithm of the ratio of the daily high to low. Source code in indicators.c.

VWAV(vars Data, vars Weights, int TimePeriod): var

Avg Volume Weighted Average Value, the scalar product of Data and 1-normalized Weights with length TimePeriod. Can be used to calculate a volume averaged price (VWAP) by setting Data to a price series and Weights to a corresponding volume series, f.i. vars Prices = series(priceClose()); vars Volumes = series(marketVol()); var VWAP = VWAV(Prices,Volumes,Period); (Zorro S required). Source code in indicators.c

WCLPrice(): var

Weighted Close Price, the average of High, Low, and twice the Close. Uses the current asset price series.

WillR(int TimePeriod): var

Rng Williams' Percent Range. Formula: -100* (HH-Close)/(HH-LL). Uses the current asset price series. Does not support TimeFrame.

WMA(vars Data, int TimePeriod): var

Avg Linear Weighted Moving Average; the weight of every bar decreases linearly with its age.

ZigZag(vars Data, var Depth, int Length, int Color): var

ZigZag indicator; converts the Data series into alternating straight 'legs' with at least the given Depth (in Data units) and Length (in bar units). For not missing legs, the Depth threshold should be sufficient low. ZigZag is non-predictive and can only identify trends in hindsight. Returned: rSlope (the slope of the last identified leg; upwards legs have a positive slope, downwards legs a negative slope); rPeak (the bar offset of the last identified peak); rLength (the number of bars of the last zig or zag that ends with rPeak). If a nonzero Color is given, the zigzag lines are plotted in the chart. Source code in indicators.c, example in Indicatortest.c. The function internally creates series (see remarks).

ZMA(vars Data, int TimePeriod): var

Avg Zero-lag Moving Average by John Ehlers; smoothes the Data series with an Exponential Moving Average (EMA) and applies an error correction term for compensating the lag. The function internally creates a series (see remarks). Source in indicators.c.
 

Standard parameters:

TimePeriod The number of bars for the time period of the function, if any; or 0 for using a default period.
MAType The type of the moving average used by the function: MAType_SMA (default), MAType_EMA, MAType_WMA, MAType_DEMA, MAType_TEMA, MAType_TRIMA, MAType_KAMA, MAType_MAMA, MAType_T3.
Data A data series in descending order (newest data first), normally derived from the price functions price(), priceC() etc. Alternatively a user created series or any other double float array with the given minimum length. If not mentioned otherwise, the minimum length of the Data series is normally TimePeriod (see remarks). Some functions require a second data series Data2.
Open, High,
Low, Close
Price data series can be explicitly given for some indicators, for using price series generated from a different asset or with a different TimeFrame. Otherwise the prices of the current asset with a time frame equivalent to the bar period are used.

Returns:

Price variation or percentage, dependent on the function, for the current bar.

Usage example:

MACD(Price,12,26,9) calculates the standard MACD for the given Price series. The results are stored in the global variables rMACD, rMACDSignal, and rMACDHistory.

Remarks:

Examples:

// plot some indicators
function run()
{
  set(PLOTNOW);
  var* Price = series(price());

// plot Bollinger bands BBands(Price,30,2,2,MAType_SMA);
plot("Bollinger1",rRealUpperBand,BAND1,0x00CC00);
plot("Bollinger2",rRealLowerBand,BAND2,0xCC00FF00);
plot("SAR",SAR(0.02,0.02,0.2),DOT,RED);
ZigZag(Price,1*PIP,5,BLUE);

// plot some other indicators
plot("ATR (PIP)",ATR(20)/PIP,NEW,RED);
plot("Doji",CDLDoji(),NEW+BARS,BLUE);
plot("FractalDim",FractalDimension(Price,30),NEW,RED);
plot("ShannonGain",ShannonGain(Price,40),NEW,RED);
}
// compare the impulse responses of some low-lag MAs
function run()
{
  set(PLOTNOW);
  BarPeriod = 60;
  MaxBars = 500;
  LookBack = 150;
  asset(""); // dummy asset
  ColorUp = ColorDn = 0; // don't plot a price curve
PlotWidth = 800; PlotHeight1 = 400; vars Impulse = series(1-genSquare(400,400)); int Period = 50; plot("Impulse",Impulse[0],0,GREY); plot("SMA",SMA(Impulse,Period),0,BLACK); plot("EMA",EMA(Impulse,Period),0,0x800080); plot("ALMA",ALMA(Impulse,Period),0,0x008000); // best representation of the impulse plot("Hull MA",HMA(Impulse,Period),0,0x00FF00); plot("Zero-Lag MA",ZMA(Impulse,Period),0,0x00FFFF); // fastest MA with no overshoot plot("Decycle",Decycle(Impulse,Period),0,0xFF00FF); // fastest MA with some overshoot plot("LowPass",LowPass(Impulse,Period),0,0xFF0000); plot("Smooth",Smooth(Impulse,Period),0,0x0000FF); }

See also:

Spectral filters, Time series, Normalization, Candle patterns, Machine learning

► latest version online Test mode

Testing a Strategy. Reality Check

Backtesting has the purpose to determine an algo trading strategy's profit expectancy in live trading. For this, the strategy is usually simulated with a couple years of historical price data. However, a simple backtest will often not produce a result that is representative for live trading. Aside from overfitting and other sorts of bias, backtest results can differ from live trading in various ways that should be taken into account. Before going live, the final backtest should be verifies with a Reality Check (see below). Testing a strategy is not a trivial task.

Testing a script with Zorro

For quickly testing a strategy, select the script and click [Test]. Depending on the strategy, a test can be finished in less than a second, or it can run for several minutes on complex portfolio strategies. If the test requires strategy parameters, capital allocation factors, or machine-learned trade rules, they must be generated in a [Train] run before. Otherwise Zorro will complain with an error message that the parameters or factors file or the rules code was not found.

If the script needs to be recompiled, or if it contains any global or static variables that must be reset, click [Edit] before [Test]. This also resets the sliders to their default values. Otherwise the static and global variables and the sliders keep their settings from the previous test run.

If the test needs historical price data that is not found, you'll get an error message. Price data can be downloaded from your broker, from Internet price providers, or from the Zorro download page. It is stored in the History folder in the way described under Price History. If there is no price data for all years in the test period, you can substitute the missing years manually with files of length 0 to prevent the error message. No trades take then place during those years.

All test results - performance report, log file, trade list, chart - are stored in the Log folder. If the folder is cluttered with too many old log files and chart images, every 60 days a dialog will pop up and propose to automatically delete files that are older than 30 days. The minimum number of days can be set up in the Zorro.ini configuration file.

The test simulates a real broker account with a given leverage, spread, rollover, commission, and other asset parameters. By default, a microlot account with 100:1 leverage is simulated. If your account is very different, get your actual account data from the broker API as described under Download, or enter it manually. Spread and rollover costs can have a large influence on backtest results. Since spread changes from minute to minute and rollover from day to day, make sure that they are up to date before backtesting. Look up the current values from the broker and enter them in the asset list. Slippage, spread, rollover, commission, or pip cost can alternatively be set up or calculated in the script. If the NFA flag is set, either directly in the script or through the NFA parameter of the selected account, the simulation runs in NFA mode.

The test is not a real historical simulation. It rather simulates trades as if they were entered today, but with a historical price curve. For a real historical simulation, the spread, pip costs, rollovers and other asset and account parameters had to be changed during the simulation according to their historical values. This can be done by script when the data is available, but it is normally not recommended for strategy testing because it would add artifacts to the results.

The test runs through one or many sample cycles, either with the full historical price data set, or with out-of-sample subsets. It generates a number of equity curves that are then used for a Monte Carlo Analysis of the strategy. The optional Walk Forward Analysis applies different parameter sets for staying always out-of-sample. Several switches (see Mode) affect test and data selection. During the test, the progress bar indicates the current position within the test period. The lengths of its green and red area display the current gross win and gross loss of the strategy. The result window below shows some information about the current profit situation, in the following form:

 3:   +8192 +256  214/299  

3: Current oversampling cycle (if any).
+8192 Current balance.
+256 Current value of open trades.
214 Number of winning trades so far.
/299 Number of losing trades so far.

After the test, a performance report and - if the LOGFILE flag was set - a log file is generated in the Log folder. If [Result] is clicked or the PLOTNOW flag was set, the trades and equity chart is displayed in the chart viewer. are displayed. The message window displays the annual return (if any) in percent of the required capital, and the average profit/loss per trade in pips. Due to different pip costs of the assets, a portfolio strategy can have a negative average pip return even when the annual return is positive, or vice versa.

The content of the message window can be copied to the clipboard by double clicking on it. The following performance figures are displayed (for details see performance report):

Median AR Annual return by Monte Carlo analysis at 50% confidence level.
Profit Total profit of the system in units of the account currency.
MI Average monthly income of the system in units of the account currency.
DD Maximum balance-equity drawdown.
Capital Required initial capital.
Trades Number of trades in the backtest period.
Win Percentage of winning trades.
Avg Average profit/loss of a trade in pips.
Bars Average number of bars of a trade. Fewer bars mean less exposure to risk.
PF Profit factor, gross win divided by gross loss.
SR Sharpe ratio. Should be > 1 for good strategies.
UI Ulcer index, the average drawdown percentage. Should be < 10% for ulcer prevention.
R2 Determination coefficient, the equity curve linearity. Should be ideally close to 1.
AR Annual return per margin and drawdown, for non-reinvesting systems with leverage.
ROI Annual return on investment, for non-reinvesting systems without leverage.
CAGR Compound annual growth rate, for reinvesting systems.

For replaying or visually debugging a script bar by bar, click [Replay] or [Step] on the chart window. 

Backtest result files

When the LOGFILE flag is set, a backtest generates the following files for examining the trade behavior or for further evaluation:

The messages in the log file are described under Log. The other file formats are described under Export. The optional asset name is appended when the script does not call asset, but uses the asset selected with the scrollbox. When LogNumber is set, the number is appended to the script name, which allows comparing logs from the current and previous backtests. When [Result] is clicked, the chart image and the plot.csv are generated again for the asset selected from the scrollbox. The chart is opened in the chart viewer and the log and performance report are opened in the editor.

Backtest resolution - TICKS, M1, T1, HFT..

The required price history resolution, and whether using TICKS mode or not, depends on the strategy to be tested. As a rule of thumb, the duration of the shortest trade should cover at least 20 ticks in the historical data. For long-term systems, such as options trading or portfolio rebalancing, daily data as provided from STOOQ™ or Quandl™ is normally sufficient. Day trading strategies need historical data with 1-minute resolution (M1 data, .t6 files). If entry or exit limits are used and the trades have only a duration of a few bars, TICKS mode is mandatory. Scalping strategies that open or close trades in minutes require high resolution price history - normally T1 data in .t1 or .t2 files. For backtesting high frequency trading systems that must react in microseconds, you'll need order book or BBO (Best Bid and Offer) price quotes with exchange time stamps, available from data vendors in CSV or NxCore tape format. An example of how to test a HFT system can be found on Financial Hacker. For testing with T1, BBO, or NxCore data, Zorro S is required.

The file formats are described in the Price History chapter. In T1 data, any tick represents a price quote. In M1 data, a tick represents one minute. Since the price tick resolution of .t1 and .t6 files is not fixed, .t1 files can theoretically contain M1 data and .t6 files can contain T1 data - but normally it's the other way around. Since many price quotes can arrive in a single second, T1 data usually contains a lot more price ticks than M1 data. Using T1 data can have a strong effect on the backtest realism, especially when the strategy uses short time frames or small price differences. Trade management functions (TMFs) are called more often, the tick function is executed more often, and trade entry and exit conditions are simulated with higher precision.

For using T1 historical price data, set the History string in the script to the ending of the T1 files (for instance History = "*.t1"). Zorro will then load its price history from .t1 files. Make sure that you have downloaded the required files before, either with the Download script, or from the Zorro download page. Special streaming data formats such as NxCore can be directly read by script with the priceQuote function. An example can be found under Fill mode.

Naturally, backtests take longer and require more memory with higher data resolution and longer test periods. Memory limits can restrict the number of assets and backtest years especially with .t1 or .t2 backtests in TICKS mode. For reducing the resolution of T1 data and thus the memory requirement and backtest time, increase TickTime. Differences of T1 backtests and M1 backtests can also arise from the different composition of M1 ticks on the broker's price server, causing different trade entry and exit prices. For instance, a trade at 15:00:01 would be entered at the first price quote after 15:00:01 with T1 data, but at the close of the 15:00:00 candle with M1 data. If this difference is relevant for your strategy, test it with T1 data. The TickFix variable can shift historical ticks forward or backward in time and test their effect on the result.

Backtest results vs live results

Zorro backtests are as close to real trading as possible, especially when the TICKS flag is set, which causes trade functions and stop, profit, or entry limits to be evaluated at every price tick in the historical data. Theoretically a backtest generates the same trades and thus returns the same profit or loss as live trading during the same time period. In practice there can and often will be deviations, some intentional and desired, some unwanted. Differences of backtest and live trading are caused by the following effects:

Backtest bias and reality checks

The likeliness that the strategy exploits real inefficiencies depends on in which way it was developed and optimized. There are many factors that can cause bias in the test result. Curve fitting bias affects all strategies that use the same price data set for test and training. It generates largely too optimistic results and the illusion that a strategy is profitable when it isn't. Peeking bias is caused by letting knowledge of the future affect the trade algorithm. An example is calculating trade volume with capital allocation factors (OptimalF) that are generated from the whole test data set (always test at first with fixed lot sizes!).

Data mining bias (or selection bias) is caused not only by data mining, but already by the mere act of developing a strategy with historical price data, since you will selecting the most profitable algorithm or asset dependent on test results. Trend bias affects all 'asymmetric' strategies that use different algorithms, parameters, or capital allocation factors for long and short trades. For preventing this, detrend the trade signals or the trade results. Granularity bias is a consequence of different price data resolution in test and in real trading. For reducing it, use the TICKS flag, especially when a trade management function is used. Sample size bias is the effect of the test period length on the results. Values derived from maxima and minima - such as drawdown - are usually proportional to the square root of the number of trades. This generates more pessimistic results on large test periods.

Before going live, always verify backtest results with a Reality Check. It can detect many source of bias. For details please read Why 90% of Backtests Fail. Zorro comes with scripts for two tests:

A WFO profile determines the dependence of the strategy on the WFO cycles number or length. It should be used for any walk-forward optimized trading system. For this, open the WFOProfile.c script, and enter the name of your strategy script in the marked line. In your script, uncomment the NumWFOCycles or WFOPeriod setting and - it it's a portfolio - disable all components except the component you want to test, and disable reinvesting. Then start WFOProfile.c in [Train] mode. If the resulting profile shows large random result differences as in the image below, something is likely wrong with that trading system.


Suspicious WFO profile. Don't trade this live.

The Montecarlo Reality Check determines whether a positive backtest result was caused by a real edge of the trading system, or just by over-training or randomness. Open the MRC.c script and enter the name of your strategy script in the marked line. In your script - it it's a portfolio - disable all components except the component you want to test, and disable reinvesting. Then start MRC.c in [Train] mode for trained strategies, or in [Test] mode when the strategy has no trained models, rules, or parameters. At the end you'll get histogram and a p-value. It gives the probability that the backtest result is untrustworthy. The p-value should be well below 5%-10% for a valid backtest. 


Lucky backtest result (black line). Don't trade this live, either.

 

See also:

training, trading, mode, performance, debugging, troubleshooting

 

► latest version online tick, tock

tick()

User-supplied function that is called on arrival of a new price quote of any asset used in the strategy. This function can be used when an immediate reaction on every new price quote is required.

tock()

User-supplied function that is called once per minute (adjustable via TockTime; unaffected by DayOffset), regardless of price quotes. This function can be used for auxiliary purposes, such as periodic input/output tasks when live trading.

callback(void* Address)

User-supplied function that is called when either the BrokerProgress function is triggered with a pointer as argument, or when Zorro receives a WM_APP+2 window message. This function is not in sync with other functions or I/O operations, so use the call scheduler when modifying global variables or accessing non-reentrant parts of the broker API.  

Remarks:

Examples (see also HWnd):

// print every price quote in a file
function tick()
{
file_append("Log\\ticks.txt",
strf("\n%i.%i %2i:%2i:%2.3f => %s %4.5f",
day(),month(),hour(),minute(),second(),Asset,priceClose()));
}
// shift a static series in a tick function
vars MySeries; // use a global series variable

function tick()
{ ... shift(MySeries,priceClose(),100); // shift the series ...
} function run() { ... MySeries = series(0,-100); // generate a static series ...

See also:

Bars and Candles, Bar, BarPeriod, TickTime, manage, run, call, user supplied functions

► latest version online TickTime, TickSmooth

Time and latency

TickTime

Minimum time in ms per asset between processing price ticks, in live trading and in backtests with tick-based (.t1 or .t2) historical data. Default = 500*sqrt(BarPeriod), i.e. 500 ms for a strategy with 1-minute bars. When multiple price quotes arrive during that time or are read from historical data, intrabar functions (TMF or tick) are excuted only with the most recent price quote. Set this to a higher value for faster backtests and for saving memory. Set it to a smaller value for reducing latency and for backtesting with higher time resolution. By setting TickTime to a negative value, TMF and tick functions run at the given time period in live trading even when no new price quote is arrived. For affecting tick-based backtests, set this variable before loading the price history with asset().

TockTime

Time in ms between subsequent tock calls in trade mode (default = 60000 ms).

TickFix

Time delay to be added to or subtracted from ticks in historical data, in ms (default = 0). Can be used for compensating inaccuracies in timestamps that lead to backtest differences between T1 and M1 ticks, or for determining the effect of small tick time differences on the result of the strategy. Set this variable before loading the price history by calling asset().

MaxRequests

Maximum number of requests per second to the broker API (0.1 .. 1000; default = 0 = no limit). Price and account requests, orders and status requests, and brokerCommand calls are limited to this rate. If it is exceeded, further requests are automatically delayed. Automatically set up at login by broker plugins that support the GET_MAXREQUESTS command. If the broker has a request limit and several Zorros are trading simultaneously on the same connection, reduce the request rate accordingly.

MaxTicks

Maximum number of ticks per request when downloading historical data. Automatically set up at login by broker plugins that support the GET_MAXTICKS command. If the broker has a download limit and several Zorros are downloading data simultaneously on the same connection, reduce the number of ticks accordingly.

UpdateTime

Time in ms for retrieving price updates for all used assets in live trading mode; determines the maximum possible speed of high-frequency strategies. The more assets are used and the slower the broker API, the broker server, or the Internet connection, the higher is the time needed for updating all assets.

Type:

int
 

Remarks:

Example:

function run()
{
  TockTime = 1000; // run the tock function any second
  ...
}

See also:

Mode, run, tick, tock, Outlier

 

► latest version online timer

timer(): var

High precision timer function. Can be used to measures the time between calls for testing function execution speed and optimizing the script for faster training.

Returns:

Time elapsed since Zorro start in milliseconds (1/1000 sec).

Remarks:

Example:

...
var Time = timer();
myfunction();
printf("\nmyfunction call time = %.3f ms",timer()-Time); // execution time in microsecond accuracy for the function call
...

See Also:

time functions, wait

► latest version online

EntryTime, LifeTime, EntryDelay

Trade parameters 2 - time limits

LifeTime

Trade time limit in bars. After the given number of bars (default: 0 for no time limit), the trade closes at the open of the next bar. Trades are only closed when the market is open. If the entry happens intrabar due to an Entry or OrderDelay setting, LifeTime = 1 causes a trade duration of 1 bar plus the remainder of the opening bar. The life time of open trades is extended by enter commands that do not open a trade due to MaxLong/MaxShort or due to a margin or risk limit. Open trades of the same component get then automatically the life time that the new trade would have. 

EntryTime

Pending order lifetime in bars. When an enter command is given, wait the given number of bars (default: 1) until the entry limit is met. If that does not happen during that time, the trade is removed and a "Missed Entry" message is printed to the log file.

Range:

Number of bars.

Type:

int
 

OrderDelay

Order entry delay in seconds. Open the trade either after the given delay time (default: 0 - no delay), or when the entry limit is met, whatever happens first. The delay can be given in high resolution (0.000001 = 1 microsecond). Useful for splitting a large order into smaller portions ("iceberg trades"), for an adaptive entry/exit algorithm or for simulating latency in HFT fill mode.

OrderDuration

GTC order duration in seconds. The order is automatically cancelled after the given time even when the trade is not completely filled. The broker API must support the SET_ORDERTYPE and DO_CANCEL commands. This variable affects all open GTC trades.

Range:

Second units, f.i. 1.23 == 1230 ms.

Type:

var

Remarks:

Examples (see also Grid.c and Hacks&Tricks):

// Use an entry limit and an entry delay for not entering trades at the same time
// Call this function before entering a trade
void setDelay(var Seconds)
{
	static int PrevBar = 0;
	static var Delay = 0;
	if(Bar != PrevBar) { // reset delay at any new bar
		Delay = 0;
		PrevBar = Bar;
	}
	Delay += Seconds; // increase delay within the bar
	OrderDelay = Delay;
	Entry = -0.2*PIP * sqrt(Delay); // entry limit for additional profit
}

See also:

bar, enterLong/Short, Stop, Entry, TickTime

 

► latest version online Tips & Tricks

Tips & Tricks

In the following you'll find short code snippets for common tasks.

I. Trade Management

Change stops and profit targets of all open long trades with the current algo and asset

exitLong(0,NewStop);
exitLong(0,-NewTakeProfit);

If a trade is opened, close all other pending trades

if(NumOpenTotal > 0) 
  for(open_trades)
    if(TradeIsPending)
      exitTrade(ThisTrade);

Lock 80% profit of all winning trades

for(open_trades)
  if(TradeIsOpen && !TradeIsPool && TradeProfit > 0) {
    TradeTrailLock = 0.80; // 80% profit (minus trade costs)
    if(TradeIsShort)
      TradeTrailLimit = max(TradeTrailLimit,TradePriceClose);
    else
      TradeTrailLimit = min(TradeTrailLimit,TradePriceClose);
  }

Iceberg trade: split a large trade into 10 small trades, one every 30 seconds

for(OrderDelay = 0; OrderDelay < 10*30; OrderDelay += 30)
  enterLong(Lots/10);

Calculate the value of all open trades with the current asset

var valOpen()
{ var val = 0;
for(open_trades)
if(strstr(Asset,PrevAsset) && TradeIsOpen)
val += TradeProfit;
return val;
}

Monitoring and modifying a certain trade

...
TRADE* MyTrade = enterlong();
...
ThisTrade = MyTrade; // connect trade variables to MyTrade var MyResult = TradeProfit; // evaluate trade variables ... exitTrade(MyTrade,0,TradeLots/2); // exit half the trade

Basket trading (creating an artificial asset from a 'basket' of real assets)

// generate a snythetic asset "USD" combined from the USD value of EUR, GBP, and AUD
var priceUSD()
{
  var p = 0;
  asset("GBP/USD"); p += price();
  asset("AUD/USD"); p += price();
  asset("EUR/USD"); p += price();
  return p;
}

// basket trade function with stop limit
int tradeUSD(var StopUSD)
{
  if((TradeIsLong && priceUSD() <= StopUSD) 
    or (TradeIsShort && priceUSD() >= StopUSD)) 
      return 1;   // exit the trade
  else return 0;  // continue the trade
}

// open a trade with the synthetic asset and a stop loss  
void enterLongUSD(var StopDistUSD)
{
  var StopUSD = priceUSD()-StopDistUSD;
  asset("GBP/USD"); enterLong(tradeUSD,StopUSD);
  asset("AUD/USD"); enterLong(tradeUSD,StopUSD);
  asset("EUR/USD"); enterLong(tradeUSD,StopUSD);
}

void enterShortUSD(var StopDistUSD)
{
  var StopUSD = priceUSD()+StopDistUSD;
  asset("GBP/USD"); enterShort(tradeUSD,StopUSD);
  asset("AUD/USD"); enterShort(tradeUSD,StopUSD);
  asset("EUR/USD"); enterShort(tradeUSD,StopUSD);
}
 
// plot a price curve of the synthetic asset
// (the plot command is linked to the last used asset -
// so "EUR/USD" must be selected in the scrollbox)
function run() 
{
  set(PLOTNOW);
  plot("USD",priceUSD(),0,RED);
}

II. Indicators & Signals

Generate an indicator with a different asset, time frame, and shift

//extended ATR function with individual asset and timeframe (in minutes)
var extATR(string symbol,int period,int length,int shift)
{ ASSET* previous = g->asset; // store previous asset if(symbol) asset(symbol); // set new asset if(period) TimeFrame = period/BarPeriod; // create price series with the new asset / timeframe
vars H = series(priceHigh()),
L = series(priceLow()),
O = series(priceOpen()), C = series(priceClose()); TimeFrame = 1; // set timeframe back g->asset = previous; // set asset back
return ATR(O+shift,H+shift,L+shift,C+shift,length); }

Calculate the weekend price change for gap trading

// use 1-hour bars, wait until Sunday Sunday 5pm ET, 
// then get the price change from Friday 5pm ET
if(dow() == SUNDAY && lhour(ET) == 5) {
int FridayBar = timeOffset(ET,SUNDAY-FRIDAY,5,0);
var PriceChange = priceClose(0) - priceClose(FridayBar); ...
}

Use a series to check if something happened within the last n bars

// buy if Signal1 crossed over Signal2 within the last 7 bars
...
vars crosses = series(0); // generate a series and set it to 0
if(crossOver(Signal1,Signal2)
  crosses[0] = 1; // store the crossover in the series
if(Sum(crosses,7) > 0) // any crossover within last 7 bars?
  enterLong();
...

Use a loop to check if something happened within the last n bars

// buy if Signal1 crossed over Signal2 within the last 7 bars
...
int i;
for(i=0; i<7; i++)
  if(crossOver(Signal1+i,Signal2+i)) { // crossover, i bars ago?
    enterLong();
    break; // abort the loop
  }
...

Align a time frame to a certain event

// Return a timeframe aligned to Event == true
// f.i. TimeFrame = frameAlign(hour() == 0); aligns to midnight
int frameAlign(BOOL Event)
{
  vars Nums = series(0,-1); // 1-element static series for storing the bar counter
  if(!Event) {
    Nums[0]++;        // count skipped bars
    return 0;         // continue the frame
  } else {
    int Skipped = -Nums[0]; // start a new time frame
    Nums[0] = 0;      // reset the counter
    return Skipped;
  }
}

Shift a series into the future

// the future is unknown, therefore fill 
// all unknown elements with the current value
vars seriesShift(vars Data,int shift)
{
  if(shift >= 0) // shift series into the past
    return Data+shift;
  else { // shift series into the future
    int i;
    for(i = 1; i <= shift; i++)
      Data[i] = Data[0];
    return Data;
  }
}

Use a function or indicator from an external DLL

// Use the function "foo" from the DLL "bar.dll"
// Copy bar.dll into the Zorro folder
int foo(double v1,double v2); // foo prototype
API(foo,bar) // use foo from bar.dll
 
function run()
{
  ...
  int result = foo(1,2);
  ...
}

III. Auxiliary

Find out if you have a standard, mini, or micro lot account

// Click [Trade] with the script below
function run()
{ asset("EUR/USD");
if(Bar > 0) {
if(LotAmount > 99999) printf("\nI have a standard lot account!");
else if(LotAmount > 9999) printf("\nI have a mini lot account!");
else printf("\nI have a micro lot account!");
quit();
}
}

Download historic price data

// Click [Trade] for downloading/updating the latest "NZD/USD" price data
function main()
{
  NumYears = 6;     // download up to 6 years data 
  assetHistory("NZD/USD",1); // update the price history
}

Export historical price data to a .csv file

// Click [Test] for exporting price data to a .csv file in the Data folder
// The records are stored in the format: time, open, high, low, close // f.i. "31/12/12 00:00, 1.32205, 1.32341, 1.32157, 1.32278" // Dependent on the locale, date and numbers might require a different format
function run() { BarPeriod = 1440; StartDate = 2010; EndDate = 2020; LookBack = 0; string Row = strf( "%02i/%02i/%02i %02i:%02i,%.5f,%.5f,%.5f,%.5f\n", day(),month(),year()%100,hour(),minute(), priceO(),priceH(),priceL(),priceC()); if(is(INITRUN)) file_delete("Data\\export.csv"); else file_append("Data\\export.csv",Row); }

Export historical price data from MT4 to a .csv file

// MQL4 EA script. Run it in the Tester.
// The CSV file is stored in Terminals\Commom\Files.
int Handle;
int CurrentTime;

// detect new bar for emulating run()
int isNewBar()
{
  if(CurrentTime != Time[0]){
    CurrentTime= Time[0];
    return(1);
  }
  return(0);
}

void OnInit()
{
  string Name = StringConcatenate("MT4_",Symbol(),".csv");
  Handle = FileOpen( Name,FILE_CSV|FILE_WRITE|FILE_COMMON, ',');
  if(Handle == INVALID_HANDLE)
    Print("Can't open ",Name,"!"); 
  CurrentTime = Time[0];
}

void OnTick() 
{
  if(isNewBar() && Handle != INVALID_HANDLE) {
    FileWrite(Handle,
      TimeToStr(Time[1],TIME_DATE),
      iOpen(Symbol(),Period(),1),
      iHigh(Symbol(),Period(),1),
      iLow(Symbol(),Period(),1),
      iClose(Symbol(),Period(),1));
  }
}

void OnDeinit()
{
  FileClose(Handle);
}

Print the description of a trade (like "[AUD/USD:CY:S1234]") in the log

void printTradeID()
{
string ls = "L", bo = "[", bc = "]";
if(TradeIsShort) ls = "S";
if(TradeIsPhantom) { bo = "{"; bc = "}"; }
printf("#\n%s%s:%s:%s%04i%s ",
bo,TradeAsset,TradeAlgo,ls,TradeID%10000,bc);
}

Plot equity curves of single assets in a multi-asset strategy

char name[40]; // string of maximal 39 characters
strcpy(name,Asset);
strcat(name,":");
strcat(name,Algo);
var equity = EquityShort+EquityLong;
if(equity != 0) plot(name,equity,NEW|AVG,BLUE);

Set up strategy parameters from a .ini file at the start

function run()
{
  static var Parameter1 = 0, Parameter2 = 0;
  if(is(INITRUN)) { // read the parameters only in the first run
    string setup = file_content("Strategy\\mysetup.ini");
    Parameter1 = strvar(setup,"Parameter1");
    Parameter2 = strvar(setup,"Parameter2");
  }
}
 
// mysetup.ini is a plain text file that contains
// the parameter values in a format like this:
Parameter1 = 123
Parameter2 = 456

Check every minute in [Trade] mode if an .ini file was modified

var Parameter1 = 0, Parameter2 = 0;
 
function tock() // run once per minute
{
  static int LastDate = 0;
  if(LastDate && !Trade) return; // already updated
  int NewDate = file_date("Strategy\\mysetup.ini");
  if(LastDate < NewDate) {
    LastDate = NewDate; // file was modified: update new parameters
    string setup = file_content("Strategy\\mysetup.ini");
    Parameter1 = strvar(setup,"Parameter1");
    Parameter2 = strvar(setup,"Parameter2");
  }
}

IV. Get Rich Quick

90% win rate

function run()
{
  Stop = 10*ATR(100);
  TakeProfit = Stop/10;
// open new trade at random after last trade hit its target
  if(NumOpenTotal == 0) { 
    if(random() < 0)
      enterShort();
    else 
      enterLong();
  }
}

Martingale

function run()
{
  BarPeriod = 1440;
// set up equal stop and profit limits
  Stop = TakeProfit = ATR(100);
// double the stake after every loss
  Lots = pow(2,LossStreakTotal); // winning guaranteed...
// open new trade at random after last trade hit its target
  if(NumOpenTotal == 0) { 
    if(random() < 0)
      enterShort();
    else 
      enterLong();
  }
}

Grid Trading

// helper function for finding trades at a grid line
bool findTrade(var Price,var Grid,bool IsShort) 
{
  for(open_trades)
    if((TradeIsShort == IsShort)
      and between(TradeEntryLimit,Price-Grid/2,Price+Grid/2))
        return true;
  return false;
}
  
function run() 
{
  BarPeriod = 1440;
  Hedge = 2; 
  EntryTime = ExitTime = 500;  
  var Price;
  var Grid = 100*PIP; // grid spacing
  var Current = priceClose();
// place pending trades at 5 grid lines  
// above and below the current price
  for(Price = 0; Price < Current+5*Grid; Price += Grid) {
    if(Price < Current-5*Grid)
      continue;
    if(Price < Current and !findTrade(Price,Grid,true))
      enterShort(0,Price,0,Grid);      
    else if(Price > Current and !findTrade(Price,Grid,false))
      enterLong(0,Price,0,Grid);
  }
}

See also:

Strategy, Indicators

► latest version online TMF & trade variables

Trade management functions (TMF)

Trade enter commands can receive the name of a trade management function (TMF) as the first argument: enterLong(MyTMF, ...);. Alternatively, a user function named manage can be defined as a global TMF for all entered trades:

int manage()

A TMF is a function for micro managing the trade. It is triggered for any open or pending trade at any price tick or price change of the traded asset. In most cases it's used for modifying entry, stop, or profit limits in a special way, thus overriding the standard trailing methods. Since a TMF runs at every tick, it has access to the most recent price quote. When the market is closed and no price ticks are received, TMFs are not executed.

TMFs are also executed at special events in the lifetime of a trade: Right before entering or exiting due to an Entry, Stop, or TakeProfit limit, and right after being closed. Which event it is can be checked in the TMF with the boolean expressions TradeIsEntry, TradeIsStop, TradeIsProfit, TradeIsClosed (described below).

If the TMF is passed to an enter command, it can receive up to 8 additional var parameters following the function name: enterLong(MyTMF, parameter1, parameter2...). They must also appear in the function's parameter list and keep their values during the lifetime of the trade. The alternative global manage function has no parameters.

A TMF has the return type int and should normally return 0; other return values have a special meaning:

TMF return values

0 Check the trade's Entry, Stop, TakeProfit, and Trail parameters, and exit or enter accordingly.
1 If the trade is open or pending, exit or cancel it now. If the exit order was unfilled due to an OrderLimit, repeat the order after the given OrderDelay.
2 If the trade is pending, enter it now. If the entry order was unfilled due to an OrderLimit, repeat the order after the given OrderDelay.
4 Don't use Entry, Stop, or TakeProfit for automatically entering or exiting. Exit or enter only when the TMF returns 1 or 2.
8 Execute the TMF at the next bar right before the run call, instead of at the next incoming tick.
16 Execute the TMF only at the following events: right before entering due to Entry, right before exiting due to Stop or TakeProfit, and right after the trade was closed.

The return values can be combined by addition. For instance, return 28  (28 = 4+8+16) executes the TMF only at the next bar or when Entry, Stop, or TakeProfit was hit, and does not automatically enter or exit in that case.   !!  When using a TMF, make sure the function has a return statement with the correct value!
 

Trade variables

In a selected trade, the variables listed below available for evaluation and partially for modification. Trades are automatically selected in a TMF or in a trade loop. They can also be selected by setting the ThisTrade pointer, f.i. with ThisTrade = enterLong();. In this case check if ThisTrade is nonzero, since accessing a variable of a nonexisting trade will cause Error 111. Without a selected trade, i.e. outside a TMF or trade enumeration loop and with no set ThisTrade pointer, trade variables have no meaning.

TradePriceOpen

The average ask price at the time when the trade or contract was filled; or the current ask price if the position was not yet filled.

TradeFill

The average fill price, equivalent to TradePriceOpen-ifelse(TradeIsShort,TradeSpread,0). In the backtest long positions are filled at the ask price, short positions are filled at the bid price.

TradePriceClose

The average ask price at the time when the position was closed or the contract was sold or covered. If the position is still open, it's the current best ask price of the asset or contract. If an option was exercised or expired, this variable contains the underlying ask price at expiration. In the backtest, short positions exit at the ask price, long positions exit at the bid price, i.e. ask minus spread.

TradeValue

The average current value of the trade, equivalent to TradePriceClose-ifelse(TradeIsLong,TradeSpread,0).

TradeSpread

The ask-bid spread at the trade opening time for short trades, or at the current and trade closing time for long trades.

TradeStrike

The strike price of the traded option (if any).

TradeUnderlying

The underlying price of the traded option (if any). 

TradeRoll

The current accumulated rollover/swap of the trade, negative or positive. Calculated by multiplying the trade duration with the trade volume and the RollLong/RollShort value. Trades that last shorter than 12 hours get no rollover. If RollLong and RollShort are both at 0, TradeRoll can be modified by the TMF for using a broker-specific swap calculation. If the broker API provides current rollover/swap values for open trades, TradeRoll is read from the broker API.

TradeMarginCost

The margin cost of the trade per underlying unit, set up at entry from MarginCost. Can be modified by the TMF for more complex margin calculations. The current allocated margin amount of the trade is TradeMarginCost * TradeLots.

TradeCommission

The commission cost of the trade per underlying unit, set up at entry from Commission. Can be modified by the TMF for using different commissions on entry and exit, or for more complex commission calculations.

TradeProfit

The current profit or loss of the trade in units of the account currency, including costs such as spread, rollover, slippage, and commission. With no trade costs and no spread, the current profit of a trade is (TradePriceClose-TradePriceOpen)*TradeUnits. The volume-neutral profit of the trade in pips is TradeProfit/TradeUnits/PIP. On NFA compliant accounts the profit is estimated, as there is no profit assigned to a single trade. For options, TradeProfit is determined by the difference of premium and current contract price. If the option is expired or was exercised, TradeProfit is determined by the extrinsic value, i.e. the difference of strike and underlying price minus the premium.

TradeDir

1 for a long trade and -1 for a short trade.

TradeUnits

Conversion factor from price change to win/loss in account currency units; normally TradeLots*PIPCost/PIP for assets, or TradeLots*Multiplier for options or futures. Examples see below. The price move equivalent to a certain profit (not considering spread and commission) is Profit/TradeUnits

TradeDate

The time in Windows Date format when a pending trade with OrderDelay will be opened. For open trades, the time at which it was opened.

TradeExitDate

The time in Windows Date format when the trade was closed (for closed trades) or will expire (for open or pending trades). The expiry date of options or futures is ymd(TradeExitDate).

TradeMFE

Maximum favorable excursion, the maximum price movement in favorable direction of the trade. Only valid after the trade was opened. TradeMFE*TradeUnits is the highest profit of the trade in account currency units while it was open (without trading costs).

TradeMAE

Maximum adverse excursion, the maximum price movement in adverse direction of the trade. Only valid after the trade was opened. TradeMAE*TradeUnits is the highest loss of the trade in account currency units while it was open (without trading costs).

TradeEntryLimit

Entry limit; initially calculated from Entry. The trade will be opened when the price reaches this value. Can be modified by the TMF by setting it to the desired price (not to a distance!).

TradeStopLimit

Current stop level, initially determined from Stop, and modified by trailing. Only valid when the trade is open. The trade will be closed when the price reaches this value. Can be modified by the TMF by setting it to the desired price (not to a distance!).

TradeStopDiff

Difference of the initial price to the initial stop limit; negative for long trades and positive for short trades. Initially determined from Stop and only valid when the trade is open. When TradeStopLimit was moved by trailing, the original stop position can be retrieved through TradePriceOpen+TradeStopDiff.

TradeProfitLimit

Profit limit, initially calculated from TakeProfit; only valid when the trade is open. The trade will be closed when the price reaches this value. Can be modified by the TMF by setting it to the desired price (not to a distance!). 

TradeTrailLimit

Trail limit, initially calculated from Trail; only valid when the trade is open and a stop limit was set. The stop limit will be moved when the price reaches this value. Can be modified by the TMF by setting it to the desired price (not to a distance!).

TradeTrailSlope

Trail slope factor in the range 0..1; only valid when the price is over the Trail limit, and a Stop limit was set. Can be modified by the trade function for changing the trail slope f.i. after breakeven.

TradeTrailStep

Trail step factor in the range 0..1; only valid when the price is over the Trail limit, and a Stop limit was set. Can be modified by the trade function for changing the trail step f.i. after breakeven.

TradeTrailLock

Trail lock factor in the range 0..1; only valid when the price is over the Trail limit, and a Stop limit was set. Can be modified by the trade function for changing the trail lock f.i. after breakeven.

Type:

float, read/only if not mentioned otherwise. Convert them to var when using them in print/printf statements!
 

TradeVar[0] .. TradeVar[7]

TradeStr[0] .. TradeStr[7]

TradeInt[0] .. TradeInt[15]

A 64-byte area in the TRADE struct for storing up to 8 trade-specific variables or strings, or up to 16 integers per trade. They can be accessed and modified by a TMF or a trade enumeration loop. The locations are shared, i.e. either TradeVar[N] or TradeStr[N] can be used, but not both with the same index N. Two subsequent TradeInt are shared with a TradeVar at half the index, f.i. TradeInt[10] and TradeInt[11] are shared with TradeVar[5]. TradeStr can only be modified with strcpy and has a maximum length of 7 characters unless extended over adjacent TradeStr variables. It is recommended to define meaningful names for the variables, like #define MyLimit TradeVar[0].

Type:

var, string, int
 

TradeLots

Number of open Lots. If the trade was partially closed or partially filled, the number of still open or filled lots. Automatically updated when the broker APIs supports individual trades.

TradeLotsTarget

Number of lots to be opened. If the trade was only partially filled, TradeLots is smaller than TradeLotsTarget. To treat an unfilled or partially filled trade as if it were completely filled, set TradeLots = TradeLotsTarget.

TradeLife

Trade life time in bars plus 1, or 0 for no time limit.

TradeBars

The number of bars since the trade was entered (for pending trades) or opened (for open trades). Not valid for resumed trades since they have no opening bar.

TradeBarOpen

Number of the opening bar of the trade. For pending trades, the number of the bar at which the trade was entered. Can be set to the current bar number (Bar) for resetting the wait time of pending trades. After the trade is opened, this number must not be changed anymore. It is not valid for resumed trades since they have no opening bar.

TradeBarClose

Number of the closing bar of the trade, or 0 if the trade is still open.

TradeContract

The contract type for options and futures, a combination of PUT, CALL, EUROPEAN, BINARY, or FUTURE.

TradeID

Trade identifier number, normally identical to the order ticket number in the broker platform. 0 when the trade was not yet opened, -1 when the trade is identified not with a number, but with a TradeUUID string.

Type:

int
 

TradeUUID

The trade identifier string when TradeID == -1.

TradeAlgo

The algorithm identifier of the trade. Also set to Algo during a TMF.

TradeAsset

The asset name of the trade. Also set to Asset during a TMF or a trade loop.

Type:

string, read/only
 

TradeIsShort

Boolean expression. Is true when the trade was entered with enterShort.

TradeIsLong

Is true when the trade was entered with enterLong.

TradeIsContract

Is true when the trade is an option or future contract.

TradeIsCall

Is true when the trade is a call option.

TradeIsPut

Is true when the trade is a put option.

TradeIsAsset

Is true when the trade used the same asset that was currently selected.

TradeIsPhantom

Is true when the trade was entered in phantom mode for virtual hedging or for equity curve trading.

TradeIsPool

Is true for a pool trade for virtual hedging.

TradeIsVirtual

Is true for a phantom trade for virtual hedging.

TradeIsPending

Is true when the trade was not yet opened, f.i. because it was just entered or its Entry limit was not yet met.

TradeIsMissed

Is true when the enter or exit order was unsuccessful due to an OrderLimit.

TradeIsOpen

Is true when the trade was opened and is not yet closed.

TradeIsClosed

Is true when the trade was completely closed. This is the last TMF execution of the trade. The TradeProfit variable contains the final result.

TradeIsExpired

Is true when the contract of the trade was expired.

TradeIsNewBar

Is true in a TMF at the first tick of a new bar.

TradeIsEntry

Is true in a TMF when the Entry limit was hit. Unless the TMF returns 4, the trade will now be opened.

TradeIsStop

Is true in a TMF when the Stop limit was hit. Unless the TMF returns 4, the trade will now be closed and the TMF will then be called a final time with TradeIsClosed.

TradeIsProfit

Is true in a TMF when the TakeProfit limit was hit. Unless the TMF returns 4, the trade will now be closed and the TMF will then be called a final time with TradeIsClosed.

Type:

bool, read/only
 

ThisTrade

The TRADE* pointer in a trade loop or TMF. All trade variables are accessed through this pointer. The TRADE struct is defined in include\trading.h. Its members are the above trade variables, redefined to easier-to-memorize names in include\variables.h. ThisTrade can be set by script (f.i. ThisTrade = enterLong();) for accessing variables of a just opened trade, but will not keep its value between run or tick functions.
 

Remarks:

Examples (see also trade loops):

// TMF for adaptive entry / exit by moving OrderLimit every 30 seconds 
int adaptLimit()
{
  if(TradeIsMissed) {
    var Step = max(0.2*Spread,PIP/2);
    OrderDelay = 30;   // try again in 30 seconds
    if(!TradeIsOpen) { // entry limit
      if(TradeIsLong) {
        if(OrderLimit > TradePriceOpen) 
          return 1;   // cancel trade
        OrderLimit += Step;  // adapt limit
      } else { // short
        if(OrderLimit < TradePriceOpen-Spread) 
          return 1;   
        OrderLimit -= Step;
      }
      OrderLimit = roundto(OrderLimit,PIP/2);
      return 2+16; // trigger tmf at next event
    } else { // exit limit
      if(TradeIsLong)
        OrderLimit -= Step;
      else
        OrderLimit += Step;
      OrderLimit = roundto(OrderLimit,PIP/2);
      return 1+16; // trigger tmf at next event
    }
  }
  return 16; // trigger tmf at next event
}
// TMF for calculating the fill amount of a GTC trade from the broker position.
// No other trade with the same asset must be open.
int adaptFill()
{
  if(Live && TradeLots < TradeLotsTarget) {
    TradeLots = (int)brokerCommand(GET_POSITION,SymbolTrade);
    return 0;
  } else
    return 16;
}
// TMF that moves the stop level in a special way 
int trailStopLong()
{
// adjust the stop only when the trade is in profit.
if(TradeIsOpen and TradeProfit > 0)
// place the stop at the lowest bottom of the last 3 candles TradeStopLimit = max(TradeStopLimit,LL(3));
// plot a line to make the stop visible in the chart
plot("Stop",TradeStopLimit,MINV,BLACK); // return 0 to let Zorro check the stop/profit limits
return 0;
} function run() { set(TICKS); // normally needed for TMF ... enterLong(trailStopLong); }
// print profit of every trade
...
for(open_trades)
  printf("\n%s profit %.2f",Asset,(var)TradeProfit);
...
// TMF that opens an opposite trade when stopped out,
// and opens a new trade when profit target is reached (Zorro 1.22 and above)
int reverseAtStop()
{
  if(TradeIsStop) { // stop loss hit?
    if(TradeIsShort)
      enterLong(reverseAtStop); // enter opposite trade
    else 
      enterShort(reverseAtStop);
  }
  if(TradeIsProfit) { // profit target hit?
    if(TradeIsShort)
      enterShort(reverseAtStop); // enter same trade again
    else 
      enterLong(reverseAtStop);
  }
// call the TMF at stop loss / profit target only  
  return 16;
}

function run()
{
  set(TICKS);  // normally needed for TMF
  Weekend = 3; // don't run TMF at weekend

  Stop = 100*PIP;
  TakeProfit = 100*PIP;
  if(Bar == LookBack) // enter the first trade directly at the first bar
    enterLong(reverseAtStop);
}
// TMF with parameters, for a Chandelier Stop
int Chandelier(var TimePeriod,var Factor)
{
  if(TradeIsLong)
TradeStopLimit = max(TradeStopLimit,ChandelierLong(TimePeriod,Factor));
else
TradeStopLimit = min(TradeStopLimit,ChandelierShort(TimePeriod,Factor));
return 8; // only update once per bar
} function run() { ... if(LongSignal) { Stop = ChandelierLong(22,3); enterLong(Chandelier,22,3); } ... }

See also:

trade statistics, enterLong/Short, Stop, LifeTime, AlgoVar, tick(), for(trades), user supplied functions

 

► latest version online TradeMode

Trade Mode, Phantom Trades, Equity Curve Trading

Phantom trades are "what-if" trades that are not sent to the broker. Instead the win or loss of a phantom trade is calculated from the price curve and the current slippage and trading costs. It is recorded in the Short/Long statistics, but not in the Total statistics. This way the performances of phantom and real trades can be evaluated separately. Phantom trades are used for equity curve trading or for Virtual Hedging.

Equity curve trading is a method of limiting losses by skipping unfavorable market periods. The strategy monitors the equity curves of any component of a portfolio strategy. When unusual losses are recognized, real trading with that component is suspended. For detecting whether it's worth to resume trading, the subsequent trades are then entered in phantom mode until their returns indicate that the market is getting profitable again. In this way the strategy switches in and out of phantom mode dependent on the market situation.

Several methods can be used for determining if the market it profitable or not. In the example below, the equity curve is permanently compared with its own long-term average by lowpass filtering. It the equity is below average and still falling, trading switches to phantom mode; it goes back to normal as soon as the equity curve is rising again. Other methods may be based on the balance instead of the equity curve, or on the ratio of winning to losing trades.

Equity curve trading does not improve the performance when there is no clear distinction between profitable and unprofitable market periods or when the strategy filters unprofitable periods anyway. But it can often improve the perception of a live trading system, and can prevent losses by expiration of market inefficiencies. An example of a system that suddenly became unprofitable is the 'Luxor' strategy in the script examples; here equity curve trading would have saved the day by stopping trading in December 2011.

Phantom trades and the overall trade behavior can be controlled with the following variable:

TradeMode

Determines with the flags listed below how trades are placed. Use setf and resf for setting and resetting flags.

Range:

TR_PHANTOM Enter trades in phantom mode. The trades are simulated and not sent to the broker API.
TR_GTC Enter trades in GTC (good-till-cancelled) mode, if supported by the broker API. Keeps the order active until filled or cancelled by the broker.
TR_AON Enter trades in AON (all-or-nothing) mode, if supported by the broker API.
TR_POS Check the position before exiting trades, and cancel the trade when the position was zero. GET_POSITION must be supported.
TR_FILLED Treat the trade as completely filled when trade status and filled orders are unavailable by the API.
TR_FRC Do not round stops and limits to the PIP value; for broker APIs that support any limit value.

Type:

int

Remarks:

Example (equity curve trading):

// don't trade when the equity curve goes down
// and is below its own lowpass filtered value
function checkEquity()
{
// generate equity curve including phantom trades
  vars EquityCurve = series(EquityLong+EquityShort);
  vars EquityLP = series(LowPass(EquityCurve,10));
  if(EquityLP[0] < LowPass(EquityLP,100) && falling(EquityLP))
    setf(TradeMode,TR_PHANTOM); // drawdown -> phantom trades
  else
    resf(TradeMode,TR_PHANTOM); // profitable -> normal trades
}

See also:

NFA, Hedge, Fill, broker arbitrage, setf, resf
► latest version online tradeUpdate

tradeUpdate(TRADE*, int NewLots, var NewStop, var NewTP, int NewLifeTime)

Resizes or updates parameters of a particular open or pending trade.

tradeUpdatePool()

Updates pool trades to the current open virtual trades in virtual hedging mode. Automatically performed after any trade with Hedge = 4, or once per bar with Hedge = 5. Must be called by script with Hedge = 6.

Parameters:

TRADE* Pointer to the trade to be updated, or 0 to update pool trades to the current open virtual trades.
NewLots New number of lots, or 0 for not resizing. For reducing the trade size, partial closing must be supported by the broker; for increasing the trade size, virtual hedging must be enabled.
NewStop New stop loss limit, or 0 for no change.
NewTP New profit target, or 0 for no change.
NewLifeTime New life time in number of bars, or 0 for no change.

Returns:

0 when the operation failed, otherwise nonzero.

Remarks:

Example:

function double_all_trades()
{
  if(Hedge < 4) return;
  for(open_trades) 
    if(TradeIsVirtual))
      tradeUpdate(ThisTrade,2*TradeLots,0,0,0);
  tradeUpdatePool(); // adapt pool trades
}

See also:

exitTrade, enterTrade, Lots, Stop, LifeTime, TMF, Fill, Hedge

► latest version online

Trade mode

Trading a strategy

Click [Trade]. Zorro will connect to the broker and start spending your money. This happens in several steps:

While trading, Zorro will detect when the .par, .c, .fac or .ml files that contain the parameters, rules, capital allocation factors, or machine learning models are updated by a re-training process. In that case they are automatically reloaded at the begin of the next bar period. It is recommended to retrain strategy parameters and rules regularly (see WFO) for taking care of market changes. This is especially required for parameter-free strategies that have been walk-forward optimized.

The Status Window displays the broker server date and time shifted to UTC, and the current price of the asset selected with the [Asset] scrollbox if that asset is also used in the strategy (otherwise its price is not available). The tiny square at the right is green when connected to the server, and red when the connection was lost or no data was received within 100*TickTime. The update interval of the server window depends on TickTime and the server response time. Arriving price quotes are indicated with a blinking asterisk. If the price is unavailable - for instance because the selected Asset was not used in the strategy -  "XXXXX" is displayed instead. "Closed" indicates that trading is currently suspended due to the BarMode and market time settings of your script and asset list, or because the broker server is currently in market close state. "Out of sync" indicates a discrepancy of server time and PC time. Check if the displayed time is the correct UTC time. The broker server and your PC need not be in the same time zone, but their clocks need to be correctly set up to their local time.

The left segment of the Progress Bar in live trading indicates the closed profit of the strategy since starting it, the right segment indicates the total profit or loss of all open trades (including phantom trades). When the right segment is green, your trades are in the profit zone; when the left segment is green, your account is in the profit zone. Otherwise the segment is red.

The Result Window below displays information about the account and profit situation, in the following form:

 4567  +1412  +50  !/\\' 

4567 Current equity, the sum of balance and value of all open trades of the account.
+1412 Money earned (or lost) by the strategy since the start, in account currency units.
+50 Current open trade value of the strategy, in account currency units.
!/\\' Current trades. ! is a trade with locked profit, / a winning, \ a losing, and ' a pending trade.

The Trade Status Page (see below) gives the most detailed view into the current account and trade situation. Note that Zorro displays prices as ask prices and profits/losses including commission, rollover, and spread, so the live values on the Zorro panel might look slightly different than the values in your trading platform for the same trades.

When something happens during a trading sessing, like the start of a new bar or opening a trade, Zorro prints a message in the message window and records it in the .log file that can be opened with any text editor. Details about Zorro's log files can be found in the Log chapter. 

Some trading restrictions apply to the free Zorro version. If you see in the Result Window that your equity has grown above the allowed limit, withdraw the excess profits from your account.

Trade status, chart and trade log

While trading, a HTML page named after the script and asset (f.i. MySystem_EURUSD.htm) in the Log folder displays a list with the live status of all open trades, a chart with the equity curve, and the current performance statistics. This document is generated when prices are available. Prices and trades automatically update once per minute, the chart once per hour. It can be displayed with any web browser. The HTML style can be changed by editing Source\status.htm and Source\zorro.css. When trading on a VPS, the HTML document can be stored in the server's public web folder (see WebFolder) for observing the current trade status via Internet on your PC or mobile phone. An instruction for setting up a VPS to display your trade status on the Internet can be found under Brokers.

The top of the trade status page contains information about the session start, the last update, the last drawdown depth and CBI, and the last warning message that required a follow-up, such as suspicious asset parameters (054) or potentially orphaned trades (070).

In the list, phantom trades in {..} winged brackets and pending trades in (..) parentheses. ID is the identifier assigned by the broker API. Entry is the fill price, Price is the current price. Stop, Trail, and Target are the 'real' stop / trail / takeprofit limits set by the script (the 'safety net' Stop Loss in the broker platform has normally a different value and is not used for the stopping the trade). Pool trades have no 'real' stops, therefore nothing is shown in that column.  Risk is the maximum possible loss when negative, and the minimum possible profit of the trade when positive. Profit is the current profit or loss, retrieved from the broker API for real trades, or calculated (including spread, rollover, and commission) for phantom trades. Pips is the volume-neutral profit or loss in pips. Due to slippage or lack of liquidity, trades can always close with a worse result than projected under Risk and Profit. Dependent on the broker plugin and on SET_PATCH parameters, the displayed entry or exit prices are not the open and close prices of the trade, but the market prices at trade entry or exit, which can slightly differ. While Zorro always records ask prices in the log, the entry and exit prices in the spreadsheet follow the usual notation of ask or bid prices dependent on trade type.

The displayed phantom lots do not necessarily sum up to the pool lots. The list can also contain phantom trades for the purpose of equity curve trading that do not contribute to the pool trades.

Below the trade list, a chart with the current price and the equity or balance curve is displayed in the status page. The chart is updated on every bar or every hour, whichever is least frequent. The update interval and further properties of the chart, such as indicator lines, colors, scale etc. can be set up in the script. If a different chart in [Test] mode is required, different chart properties can be set up with the conditions if(Live) or if(Test).

When the [Result] button is clicked and no specific action was assigned to that button, a chart and a performance report is immediately created in the Log folder, and a list of all open trades with entry, current, and stop prices and current profit/loss is also printed in the message window. The message window content is stored in the log file, so any event is recorded for later evaluation.

All closed trades are separately recorded in a CSV spreadsheet (see Data Export) for further evaluation or statistics. The spreadsheet can be read back by Zorro for simulating a test with real trades generated by another platform; the Simulate script can be used for that.  !!  Take care not to keep the trade spreadsheet open in Excel while Zorro is trading. Excel prevents any other program from accessing that file.

Normally, all trades statistics are reset at the begin of a session. They can be continued from the previous session when they were saved with setf(SaveMode,SV_STATS).

Manually closing trades

Normally you should refrain from tampering with the strategy, as it is (hopefully) already optimized and any manual intervention will most likely reduce the performance. Still, in some situations you might want to close individual trades, or lock an individual profit by placing a stop loss. You can do this directly in the broker platform. If supported by the broker API, Zorro will detect when a trade is manually closed, and also close it internally.

For closing all open trades in case of an emergency, click [Stop]. You'll then be asked first if you want to close all trades or not, and afterwards if you want to stop trading or not. Click first [Yes] for closing all trades, then [No] for continuing trading. All trades opened by this strategy will be closed; other open trades are not affected. If trades cannot be closed for any reason (the market is not open or there is no connection to the broker), they stay open, and must then be manually closed.

NFA compliance

US based accounts are restricted by NFA Compliance Rule 2-43(b). The rule requires that trades are served in a first-in, first-out order. You can not hedge positions or have more than one position per asset, you can not place stop loss or profit targets, and you can not close trades. Instead of closing a position, a new position must be opened in opposite direction. You're also not allowed to open more than 4 trades per week, dependent on your account deposit. All those restrictions are well-meant and intended to prevent US citizens from losing their money and suing their brokers (of course they do nevertheless).

Zorro can step around NFA restrictions - except for low leverage, unsupported assets, and limited trades - when the NFA flag is set. It must normally be set for US based accounts, except when trading through MT4. It must not be set for all other accounts. If it is set wrongly, you'll get error messages like "!Can't close trade" on US accounts, and you'll get a massive number of open trades on non-US accounts as no trade is ever closed. So make sure that you know whether you live in the US or not.

Stopping and resuming a live trading system

While trading, Zorro stores the currently open trades, the status of the sliders, and component specific variables (AlgoVar) in a .trd file in the Data folder. This way trading can be stopped and resumed without losing track of open trades. The name of the file is composed from the script name plus the selected asset plus a "_d" in case of a demo account. If a trade can not be resumed - for instance when it was externally closed - an error message will be printed at restarting the trading session, and Zorro will resume the other trades. Because any new trade overwrites the .trd file, its previous content is stored in a .bak file at session start. Trades are not resumed when the session has no lookback period and opens trades already in the first run.

The content of any .trd file can be loaded with loadStatus. For looking into its open trades, you can use a short script like this:

function run()
{
  if(Bar >= 1) {
    setf(SaveMode,SV_TRADES|SV_HTML);
    loadStatus("Data\\Z12_d.trd");
    quit();
  }
}

After a test run of this script, the open trades are printed in the HTML status page.

For migrating a live trading system to a different PC or VPS, stop trading without closing the open trades, then copy the whole Zorro folder over to the new PC. Open Zorro and start trading on the new PC. Slider settings and open trades will be resumed.

 !!  The .trd file can be simply copied over to the Data folder of a different Zorro installation for continuing the session from there. When stopping a trade session and starting a new session with the same script on a different broker or account, make sure to delete the .trd file so that open trades from the last session are not resumed with the new account or broker. This is especially important with an NFA account, since there is no way to automatically determine if trades were opened or closed on such an account.

Updating to a new software version

If strategy and .trd file format are unchanged in the new version:

If according to What's New the strategy or file format were changed:

If you want to run the old and new version a while in parallel:

Please read the What's New page for checking if the strategy or .trd format have changed, or if further actions are required for resuming sessions.

Trading without a broker

It is sometimes desired to run a Zorro script in [Trade] mode with no broker connection, for instance when prices are received from online price sources and when trades are not directly sent. For this the Offline plugin can be used. Alternatively, a live broker connection can be simulated with the Simulation plugin that's available in the Source folder. 

Re-training

While trading, Zorro S can re-train the system in regular intervals for adapting it to the current market situation. For comparing live with backtest results, it can also automatically run a backtest over the previous live trading period. For details see Retraining and Retesting.

Multiple accounts, multiple instances

The free Zorro version can only trade with a single Zorro instance. Zorro S is licensed to trade with multiple Zorro instances, brokers, and accounts simultaneously, either on the same PC or on several PCs or servers. Zorros trading on the same PC are automatically synchronized. That means that any instance waits until the other instance has finished its current task, such as sending an order or retrieving a price quote. This synchronization allows to trade even with a broker API that does not support many simultaneous connections, but it can also lead to long response times of the user interface, especially when lenghty operations - for instance, downloading large price histories - are performed on the same PC on which other Zorros are live trading. For preventing synchronization, set (on your own risk) the NOLOCK flag in the script.

When trading the same script on several Zorro instances, make sure to either copy the script to different names, or run it from different Zorro installations. Otherwise the Zorro instances would write into the same status, .log, or .trd files, which can cause undesired behavior such as garbled log files or 'deadlock' situations.

For trading with the same Zorro instance on multiple accounts of the same broker, make a copy of the broker plugin under a different name, and use it to define different assets for the different accounts. The procedure is described under Broker Arbitrage.

If you have several accounts with different brokers, you can use the ZStatus script for displaying the status of all accounts on a single panel (Zorro S required). Put your accounts in an account list, then enter the names as they appear in the scrollbox to the BROKERS definition at the begin of the script. Set NUM_BROKERS to the number of accounts you want to observe. If the accounts are in different currencies, you can set up conversion factors in the Convert array. Start the script in [Trade] mode. It will connect to your accounts in 1-minute intervals and display the profit since start, the profit of the last 2 days, the account value, the balance, the open trade value, and the total margin on a panel. Profits are displayed in green when positive, red when negative.

Note that with some brokers, such as IB, you'll need to add a dedicated user to your real money account for observing the account status, since you cannot run multiple sessions with the same user.

Switching or updating strategies

Zorro updates come out every couple of months, normally with improved versions of the Z strategies. When developing your own strategies, you'll also often get new ideas and improve the script so that is generates a few pips more than the previous version. For the transition of a live trading system from the old version to a new Zorro and/or strategy version, you have several options:

Errors and crash recovery

Zorro is designed to trade without human intervention. However, for keeping a program running for months and years, some special precautions are necessary. Obviously you should not switch off the active trading PC - not even in the interest of energy saving. Set the PC energy settings accordingly. Disable automatic Windows updates, as they normally reboot the PC. Look here for how to automatically restart a session if a reboot still happens. Screen savers won't harm.

Zorro will display an error message when the broker can not open or close a certain trade. There can be many reasons for this, such as an interruption of the connection to the broker, a server failure, lack of liquidity, or a closed market. If a trade was not opened, it is up to the script to cancel the trade or retry. If a trade can not be closed, Zorro will automatically close it again in regular intervals until the market opens or liquidity is available. If the close command repeatedly fails because the trade is unknown to the broker, Zorro will assume that it was already closed, stop the close attempts, and remove it from its internal trade list.

If you trip over the power cable, or if the broker's server goes offline, Zorro gets temporarily disconnected from the trade session. This can regularly happen on Saturdays due to broker server maintenance. It is normally no reason to worry: the session will continue automatically when the connection is established again. While disconnected, no prices are received, no bars are created, and Zorro cannot react on the market. If the situation persists, the colored square next to the server window will change from green to red, and the time in the server window will switch from server time to PC time. Zorro will then start reconnection attempts in increasing intervals, until the connection is established again. Any reconnection attempt prints a dot to the message window. Since there is no price data during the interruption, prices can suddenly jump to their current value at reconnecting. This might affect indicators. Trade strategies should consider disconnections when they are programmed to react on price jumps.

If the connection to the broker server cannot automatically be reestablished for some reason, click [Stop], then click [No] for not closing open trades and [No] for continuing trading. Zorro will then log out, close and re-open the broker API library, then log in again. This solves problems with a 'hung' broker interface or API.

If Zorro terminates due to a PC breakdown or software crash, start it again and click [Trade]. Zorro will then read its last trade state from the .trd file (see above) and continue trading at the point where it was shut down. The continued trades are displayed in the message window (f.i. [AUD/USD:CY:S4400] continuing...]. If you see an error message instead, a particular trade could not be continued. It might have hit a stop loss and been closed by the broker inbetween. For checking, open the broker's trade platform and compare the ID numbers of the open trades to the trades continued by Zorro.

If the PC is malfunctioning and you have to continue trading on a different PC, install Zorro there and then copy the *.trd file from the old to the new Zorro installation. Zorro will then resume the open trades on the new PC.

If a connected broker API or program, such as MT4, crashed due to a serious software or PC fault, it can become unresponsive and Zorro can appear 'frozen'. The safest way to resolve that is closing MT4, closing Zorro, rebooting the PC, then starting MT4 and Zorro again. If possible, find out the reason of the crash and fix it,

See also:

Bars, mode, log, testing, training, TradeMode ► latest version online Train mode

Training an algo trading system

Training can modify strategy parameters, generate trading rules, and assign optimal capital allocation factors for profit reinvestment or portfolio distribution. Here's a short introduction to algo system training.

For preparing a parameter based strategy to be trained, set the PARAMETERS flag and assign optimize calls to all optimizable parameters. The code for training the SMA time period of a simple moving average crossover system would look like this:

// n-bar Moving Average system (just for example - don't trade this!)
function run() 
{
  set(PARAMETERS);
  var TimePeriod = optimize(100,20,200,10);
  vars Prices = series(price());
  vars Averages = series(SMA(Prices,TimePeriod));
  if(crossOver(Prices,Averages)) 
    enterLong();
  else if(crossUnder(Prices,Averages)) 
    exitLong();
}

Now click [Train]. Zorro will now run through several optimization cycles for finding the most robust parameter combination. Zorro optimizes very fast; still, dependent on the number of parameters, assets, and trade algorithms, optimization can take from a few seconds up to several hours for a complex portfolio strategy. The progress bar indicates the time you have to wait until it's finished:

The displayed values per line are the loop and parameter number, the optimization step, the parameter value, the result of the objective function, and the number of winning and losing trades.

In [Train] mode, trades usually open 1 lot, regardless of Lots or Margin settings. The results are summed up over all sample cycles. Pool trades are not used, and phantom trades are treated like normal trades. This behavior can be modified for special cases with TrainMode flags.

The optimal parameters are stored in a *.par file in the Data folder, and are automatically used by Zorro when testing or trading the system. Parameters are normally optimized and stored separately per asset/algorithm component in a portfolio system; therefore all optimize calls must be located inside the asset/algo loop if there is any. When the FACTORS flag is set, capital allocation factors are also generated and stored in a *.fac file in the Data folder. When the strategy contains advise calls and the RULES flag is set, decision tree, pattern detection, or perceptron rules are generated and and stored in *.c files in the Data folder. If external machine learning functions are used, the trained models are stored in *.ml files in the Data Folder. In [Train] mode, trades usually open 1 lot, and pool and phantom trades are converted to normal trades (unless special TrainMode flags are set). Parameters, factors, and rules can be generated either together or independent of each other.

Training shows if the strategy is robust, i.e. insensitive to small parameter changes. Therefore it goes hand in hand with strategy development. When training a strategy in Ascent mode with the LOGFILE flag set, Zorro shows the performance variance over all parameter ranges in parameter charts on a HTML page, like this:

 

The charts are stored in the Log folder and end with the number of the parameter, f.i. _p1 for the first, _p2 for the second parameter and so on. The red bars are the result of the objective function, which is by default the profit factor of the training period, corrected by a factor for preferring a higher number of trades. The grey bars show the number of losing trades and the black bars show the number of winning trades. In walk forward optimization, the parameter charts display the average results over all WFO cycles. Genetic or brute force optimization does not generate parameter charts, but contour plots from the first two parameters as shown below.

Training is critical for almost all strategies, and should not be omitted even when a strategy appears to be already profitable with its default parameters. Most strategies only work within certain parameter ranges. Those ranges are usually different for every asset and also vary with the market situation - a bullish or bearish market requires different parameters than a market in a sidewards phase. Due to the asymmetry of many markets, long and short trades often also require different parameters. Naturally, parameters can only be optimized to a specific historical price curve, therefore they perform best with that particular price curve and will perform not as good with any other data - such as real time prices in live trading. This effect is called overfitting. It varies with the strategy, and is discussed below.

Select carefully the parameters that you train. Parameters that depend on external events - for instance, the market opening hour when the strategy exploits the overnight gap - must never be trained, even if training improves the test result. Likewise, parameters that should have no effect on strategy performance, such as price patterns from long ago or the start date of a backtest, must never be trained. Otherwise you're in danger to replace a real market inefficiency by a random training effect.

For avoiding overfitting bias when testing a strategy, parameter values are typically optimized on a training data set (the "in-sample" data), and then tested on a different data set (the "out-of-sample" or "unseen" data) to simulate the process of trading the system. If the optimized parameters do not perform well on out-of-sample data, the strategy is not suited for trading. Zorro offers several methods for optimizing and testing a strategy with unseen data:

Horizontal split optimization

This method divides the price data into one-week segments (a different segment length can be set up through DataSkip). Usually two of every three weeks are then used for the optimization, and every third week is skipped by the optimization and used for the strategy test. The LookBack period is the part of the price curve required for functions that use past price data as input, such as moving averages or frequency filters. Thus a preceding lookback period is always cut off in front of the data for both training and testing.

Simulation period
LookBack
Training
                 
Test
               


For setting up this method, set different SKIP1, SKIP2, SKIP3 flags for training and testing. Usually, SKIP3 is set in train mode and and SKIP1+SKIP2 in test mode; any other combination will do as well as long as two weeks are used for training and one week for testing. A script setup for horizontal split optimization looks like this:

function run() // horizontal split optimization
{
  if(Train) set(PARAMETERS+SKIP3);
  if(Test) set(PARAMETERS+SKIP1+SKIP2);
  ...
}

Horizontal split optimization is good for quick tests while developing the strategy, or for testing when you don't have enough price data or enough trades for walk-forward optimization. It is the fastest method, uses the largest possible data set for optimization and test, and gives usually quite realistic test results. For checking the consistency you can test and train also with different split combinations, f.i. SKIP1 for training and SKIP2+SKIP3 for testing. The disadvantage is that the test, although it uses different price data, runs through the same market situation as the training, which can make the result too optimistic.

Vertical split optimization

Alternatively, you can select only a part of the data period - such as 75% - for training. The rest of the data is then used exclusively for testing. This separates the test completely from the training period. Again, a lookback period is split off for functions that need past price data.

Simulation period
LookBack
Training
Test
           

For setting up this method, use DataSplit to set up the length of the training period. A script setup for vertical split optimization looks like this:

function run() // vertical split optimization
{
  set(PARAMETERS);
  DataSplit = 75; // 75% training, 25% test period
  ...
}

Vertical split optimization has the advantage that the test happens after the training period, just as in live trading. The disadvantage is that this restricts testing to a small time window, which makes the test quite inaccurate and subject to fluctuations. It also does not use the full simulation period for optimization, which reduces the quality of the parameters and rules.

Walk Forward Optimization

For combining the advantages of the two previous methods, an analysis and optimization algorithm named Walk Forward Optimization (WFO in short) can be used. This method is a variant of cross-validation and was first suggested for trading strategies by Robert Pardo. It trains and tests the strategy in several cycles using a data frame that "walks" over the simulation period:

WFOCycle
Simulation period
1
LookBack
Training
Test1
2
LookBack
Training
Test2
3
LookBack
Training
Test3
4
LookBack
Training
Test4
5
 
LookBack
Training
OOS Test
LookBack
Test5
WFO Test
Look
Back
Test1
Test2
Test3
Test4
Rolling Walk Forward Optimization
 
WFOCycle
Simulation period
1
LookBack
Training
Test1
2
LookBack
Training
Test2
3
LookBack
Training
Test3
4
LookBack
Training
Test4
5
LookBack
Training
Test
   
Test1
Test2
Test3
Test4
Anchored Walk Forward Optimization

The parameters and rules are stored separately for every cycle in files ending with "_n.par" or "_n.c", where n is the number of the cycle. This way the strategy can be tested repeatedly without having to generate all the values again. The test cycle is divided into (n-1) test periods that use the parameters and factors generated in the preceding cycle. The last training cycle has no test period, but can be used to generate parameters and rules for real trading.

For setting up this method, use DataSplit to set up the length of the training period in percent, and set either NumWFOCycles to the number of cycles, or WFOPeriod to the length of one cycle. A script setup for Walk Forward Optimization looks like this:

function run() // walk forward optimization
{
  set(PARAMETERS);
  DataSplit = 90; // 90% training, 10% test
  NumWFOCycles = 10; // 10 cylces, 9 test periods
  ...
}

The WFO test splits the test period into several segments according to the WFO cycles, and tests every segment separately. The test result in the performance report is the sum of all segments. The parameters from the last cycle are not included in the WFO test as they are only used for real trading. A special OOS test can be used for out-of-sample testing those parameters with price data from before the training period.

WFO can be used to generate a 'parameter-free' strategy - a strategy that does not rely on an externally optimized parameter set, but optimizes itself in regular intervals while trading. This makes optimization a part of the strategy. WFO simulates this method and thus tests not only the strategy, but also the quality of the optimization process. Overfitted strategies - see below - will show bad WFO performance, profitable and robust strategies will show good WFO performance. The disadvantage of WFO is that it's slow because it normally runs over many cycles. Like vertical split optimization, it uses only a part of the data for optimizing parameters and factors. Depending on the strategy, this causes sometimes worse, sometimes better results than using the full data range. Still, WFO is the best method for generating robust and market independent strategies.

Another advantage of WFO is that training is a lot faster than the other methods when using several CPU cores.

Combining rules and parameters

If a machine learning strategy also requires training of parameters, several training cycles in a certain order are required. There are several possible scenarios:

Training rules and parameters at the same time only works with single assets, not with a portfolio system that contains loops with asset or algo calls. If required, train all portfolio components separately and combine the resulting .c files. The Combine.c script can be used as a template for automatically combining parameters and rules from different assets or algos. Machine learning models (.ml files) can be combined with an appropriate R function. With Zorro S, the process can be automatized with a batch file.

Portfolio training

When training a portfolio of assets and algorithms, it is normally desired to train any asset/algo component separately, since their parameters and rules can be very different. Only in special cases it could make sense to train parameters that are common to all components, or use a combination of commonly and separately trained parameters. Examples for all 3 cases can be found under loop.

Training results

Before training a system the first time, make sure to run a [Test] with the raw, unoptimized script. Examine the log and check it for error messages, such as skipped trades or wrong parameters. You can this way identify and fix script problems before training the script. Training won't produce error messages - trades are just not executed when trade parameters are wrong.

Money management or profit reinvesting should not be active for training strategy parameters, because it would introduce artifacts from the different weights of trades. For this reason training uses fixed-size trades by default. If you want to train a money management algorithm or use different lot sizes, do that in a separate step using the TRADES flag after all other parameters have been trained.

After training, files are generated which contain the training results and the generated rules, parameters, and factors (see Export). A Zorro strategy can thus consist of up to 4 files that determine its trading behavior:

All 4 files, except for the executable *.x and the *.ml machine learning models, are normal text files and can be edited with any plain text editor.

Overfitting

The optimizing process naturally selects parameters that fare best with the used price data. Therefore, an optimized strategy is always more or less 'curve fitted' to the used price curve, even with Zorro's range-based optimizing. Neural networks, genetic algorithms, or brute force optimization methods tend to greatly overfit strategies. They produce panda bears that are perfectly adapted to a specific data set, but won't survive anywhere else. Some suggestions to reduce overfitting:

See also:

Testing, Trading, Result, WFO, DataSplit, optimize, Workshop5, Workshop 7

 

► latest version online Time series statistics

Time series statistics

Library of often used functions for evaluating properties of time series or data arrays. See also Normalization.

correl(vars Data, int Length, function): var

Spearman correlation of the Data series with an arbitrary function. Used for the CTI, CCYI, and CCYIR indicators. Source in indicators.c; usage and description on Financial Hacker

Correlation(vars Data1, vars Data2, int TimePeriod): var

Pearson's correlation coefficient between two data series over the given TimePeriod, in the range between -1..+1. A coefficient of +1.0, a "perfect positive correlation," means that changes in Data2 cause identical changes in Data1 (e.g., a change in the indicator will result in an identical change in the asset price). A coefficient of -1.0, a "perfect negative correlation," means that changes in Data2 cause identical changes in Data1, but in the opposite direction. A coefficient of zero means there is no relationship between the two series and that a change in Data2 will have no effect on Data1. This function can be also used to get the autocorrelation of a series by calculating the correlation coefficient between the original series and the same series lagged by one or two bars (series+1 or series+2). 

Covariance(vars Data1, vars Data2, int TimePeriod): var

Covariance between two data series. Can be used to generate a covariance matrix f.i. for the markowitz efficient frontier calculation.

Fisher(vars Data): var

Fisher Transform; transforms a normalized Data series to a normal distributed range. The return value has no theoretical limit, but most values are between -1 .. +1. All Data values must be in the -1 .. +1 range, f.i. by normalizing with the AGC, Normalize, or cdf function. The minimum Data length is 1; a single var can be passed with the & operator, f.i. Fisher(&MyVariable). Source in indicators.c.

FisherInv(vars Data): var

Inverse Fisher Transform; compresses the Data series to be between -1 and +1. The minimum length of the Data series is 1; a single var can be passed with the & operator, f.i. FisherInv(&MyVariable). Source in indicators.c.

FisherN(vars Data, int TimePeriod): var

Fisher Transform with normalizing; normalizes the Data series with the given TimePeriod and then transforms it to a normal distributed range. Similar to a Normalize filter (see below), but more selective due to the normal distribution of the output. The return value has no theoretical limit, but most values are in the -1.5 .. +1.5 range. The minimum length of the Data series is equal to TimePeriod. The function internally creates series (see remarks). Source code in indicators.c.

FractalDimension(vars Data, int TimePeriod): var

Fractal dimension of the Data series; normally 1..2. Smaller values mean more 'jaggies'. Can be used to detect the current market regime or to adapt moving averages to the fluctuations of a price series. Requires a lookback period of twice the TimePeriod. Source in indicators.c.

Gauss(vars Data, int TimePeriod): var

Gauss Filter, returns a weighted average of the data within the given time period, with the weight curve equal to the Gauss Normal Distribution. Useful for removing noise by smoothing raw data. The minimum length of the Data series is equal to TimePeriod, the lag is half the TimePeriod. Source in indicators.c.

Hurst (vars Data, int TimePeriod): var

Hurst exponent of the Data series; between 0..1. The Hurst exponent measures the 'memory' of a series. It quantifies the autocorrelation, i.e. the tendency either to revert to the mean (Hurst < 0.5) or to continue trending in a direction (Hurst > 0.5). This way the Hurst exponent can detect if the market is in a trending state. The TimePeriod window (minimum 20) must have sufficient length to catch the long-term trend; the lookback period must be at least twice the TimePeriod. The function internally creates series (see remarks). Source in indicators.c.

LinearReg(vars Data, int TimePeriod): var

Linear Regression, also known as the "least squares moving average" (LSMA). Linear Regression attempts to fit a straight trendline between several data points in such a way that the distance between each data point and the trendline is minimized. For each point, the straight line over the specified previous bar period is determined in terms of y = b + m*x, where y is the price and x the bar number starting at TimePeriod bars ago. The formula for calculating b and m is then

m = (nΣxy - ΣxΣy) / (nΣx² - (Σx)²)
b = (Σy - bΣx) / n


where n is the number of data points (TimePeriod) and Σ is the summation operator. The LinearReg function returns b+m*(TimePeriod-1), i.e. the y of the current bar.  
Source in ta-lib.zip. For higher order regression or for evaluating the regression value at past or future bars, use polyfit / polynom. For logistic regression with multiple variables, use the advise(PERCEPTRON,...).

LinearRegAngle(vars Data, int TimePeriod): var

Linear Regression Angle. Returns m converted to degrees. Source in ta-lib.zip. Due to the arbitrary x and y units of a price chart, the angle is a mostly useless parameter, although held in high esteem by Gann believers.

LinearRegIntercept(vars Data, int TimePeriod): var

Linear Regression Intercept. Returns b. Source in ta-lib.zip.

LinearRegSlope(vars Data, int TimePeriod): var

Linear Regression Slope. Returns m as data difference per bar. Source in ta-lib.zip

MatchIndex(vars Data, var Reference, int Period): int

Index of the Data value closest to Reference over the specified Period0 = best match is at current bar, 1 = at one bar ago, and so on. If the Data series was shifted (+N), add the offset N to the returned index for getting the number of bars passed since the best match. Source in indicators.c.

MaxIndex(vars Data, int Period): int

Index of highest Data value over the specified Period.. 0 = highest value is at current bar, 1 = at one bar ago, and so on. If the series was shifted (+N), add the offset N to the returned index for getting the number of bars passed since the lowest Data value. Source in ta-lib.zip.

MaxVal(vars Data, int Period): var

Highest Data value over the specified Period. Source in ta-lib.zip.

Median(vars Data, int Period): var

Median Filter; sorts the elements of the Data series and returns their middle value within the given Period. Useful for removing noise spikes by eliminating extreme values. The minimum length of the Data series is equal to Period, the lag is half the Period. Source in ta-lib.zip. See also Percentile.

MinIndex(vars Data, int Period): int

Index of the lowest value over a specified period; 0 = lowest value is at current bar, 1 = at one bar ago, and so on. If the series was shifted (+N), add the offset N to the returned index for getting the number of bars passed since the lowest Data value. Source in ta-lib.zip.  

MinVal(vars Data, int Period): var

Lowest value over a specified period. Source in ta-lib.zip.

MinMax(vars Data, int Period): var

Lowest and highest values over a specified period. Result in rMin, rMax. Source in ta-lib.zip.

MinMaxIndex(vars Data, int Period): int

Indexes of lowest and highest values over a specified period. Result in rMinIdx, rMaxIdx. 0 = current bar, 1 = one bar ago, and so on. Source in ta-lib.zip.

Mode(vars Data, int TimePeriod, int Steps): var

Most frequent Data value within TimePeriod. For this the Data range is divided into Steps separate ranges, and the number of values that fall into a range is counted. The function returns the value with the highest count. Source in indicators.c

Moment(vars Data, int TimePeriod, int N): var

The statistical moment N (1..4) of the Data series section given by TimePeriod. The first moment is the mean, second is variance, third is skewness, fourth ist kurtosis. The standard deviation is the square root of the second moment. The mean is stored in rMean. See also SemiMoment. Source in indicators.c.

NumAbove(vars Data, int Length, var Theshold): var

Number of Data elements above the given Threshold within the Length, from 0 to Length. Source in indicators.c

NumInRange(vars Low, vars High, var Min, var Max, int Length): var

Number of data ranges, given by their Low and High values, that lie completely inside the interval from Min to Max within the given Length. Can be used to calculate the distribution of prices or candles. Low and High can be set to the same value for counting all values in the interval, or swapped for counting all candles that touch the interval. Returns a value of 1..Length. Source in indicators.c. See also PercentRank.

NumRiseFall(vars Data, int TimePeriod): var

Length of the last streak of rising or falling values in the Data series, back to the given TimePeriod. For a rising sequence its length is returned, for a falling sequence the negative length (f.i. -3 when Data[3] < Data[2] > Data[1] > Data[0]). Range: 1..TimePeriod-1. See the RandomWalk script for an example. Source in indicators.c

NumUp(vars Data, int TimePeriod, var Theshold): var

NumDn(vars Data, int TimePeriod, var Theshold): var

Number of upwards or downwards Data changes by more than the given Threshold within the TimePeriod, from 0 to TimePeriod-1. See also SumUp, SumDn. Source in indicators.c

Percentile(vars Data, int Length, var Percent): var

Returns the given percentile of the Data series with given Length; f.i. Percent = 95 returns the Data value that is above 95% of all other values. Percent = 50 returns the Median of the Data series.

PercentRank(vars Data, int Length, var Value): var

The opposite of Percentile: Returns the percentage of Data values within the given Length that are smaller or equal than the given Value; returns 100 when Value is the greatest in the data range. Can transform any series to a range of 0..100. See also NumInRange.

R2(vars Data, int Length): var

Coefficient of determination (+1...-1); similarity of the Data series with a straight line through its end points. At 1 the data series is equivalent to a straight line, at 0 or below even the mean of the data is a better fit to the straight line than the real data series.

SemiMoment(vars Data, int TimePeriod, int N): var

The statistical moment N (1..4) of the Data series section given by TimePeriod, using only data points at or below the mean. The first moment is the mean, the second is the semivariance, third is downside skew, and fourth is downside kurtosis. The mean is stored in rMean. The semideviation (downside deviation) is the square root of the semivariance, and is often considered a better measure of risk than the standard deviation. See also Moment. Source in indicators.c.

ShannonGain(vars Data, int TimePeriod): var

Expected logarithmic gain rate of the Data series in the range of about +/-0.0005. The gain rate is derived from the Shannon probability P = (1 + Mean(Gain) / RootMeanSquare(Gain)) / 2, which is the likeliness of a rise or fall of a high entropy data series in the next bar period. A positive gain rate indicates that the series is more likely to rise, a negative gain rate indicates that it is more likely to fall. The zero crossover could be used for a trade signal. Algorithm by John Conover. Source in indicators.c.

ShannonEntropy(vars Data, int Length, int PatternSize): var

Entropy of patterns in the Data series, in bit; can be used to determine the 'randomness' of the data. PatternSize (2..8) determines the partitioning of the data into patterns of up to 8 bit. Each Data value is either higher than the previous value, or it is not; this is a binary information and constitutes one bit of the pattern. The more random the patterns are distributed, the higher is the Shannon entropy. Totally random data has a Shannon entropy identical to the pattern size. Algorithm explained on the Financial Hacker blog; source in indicators.c.

Spearman(vars Data, int TimePeriod): var

Spearman's rank correlation coefficient; correlation between the original Data series and the same series sorted in ascending order within TimePeriod (1..256). Returns the similarity to a steadily rising series and can be used to determine trend intensity and turning points. Range = -1..+1, lag = TimePeriod/2. For usage and details, see Stocks & Commodities magazine 2/2011. Source in indicators.c.

StdDev(vars Data, int TimePeriod): var

Standard Deviation of the Data series in the time period, from the ta-lib; accuracy = 0.0001. For long time periods, higher accuracy, or for downside deviation, use the square root of the second Moment or SemiMoment.

Sum(vars Data, int Length): var

Sum of all Data elements up to the given Length. Source in ta-lib.zip.

SumUp(vars Data, int Length): var

SumDn(vars Data, int Length): var

Sum of all upwards or downwards Data changes up to the given Length. See also NumUp, NumDn. Source in indicators.c.

SumSq(vars Data, int Length): var

Sum of the squares of all Data elements up to the given Length. Source code in indicators.c.

TSF(vars Data, int TimePeriod): var

Time Series Forecast. Returns b + m*(TimePeriod), the Linear Regression forecast for the next bar.

Variance(vars Data, int TimePeriod): var

Variance of the Data series in the time period, accuracy = 0.0001. Source in ta-lib.zip. For long time periods, higher accuracy, or for downside variance, use the second Moment or SemiMoment.
 

Standard parameters:

TimePeriod The number of bars for the time period of the function, if any; or 0 for using a default period.
Length The length of the Data series.
Data A data series, in reverse time order, usually  derived from the price functions price(), priceC() etc.. Alternatively a user created series or any other double float array with the given minimum length can be used. If not mentioned otherwise, the minimum length of the Data series is TimePeriod. Some functions require a second data series Data2

Usage example:

Volatility(Prices,20) calculates the standard volatility of a daily price series over the last 20 days.

Remarks:

Examples:

// plot some indicators
function run()
{
  set(PLOTNOW);
  var* Price = series(price());

// plot Bollinger bands BBands(Price,30,2,2,MAType_SMA);
plot("Bollinger1",rRealUpperBand,BAND1,0x00CC00);
plot("Bollinger2",rRealLowerBand,BAND2,0xCC00FF00);
plot("SAR",SAR(0.02,0.02,0.2),DOT,RED);
ZigZag(Price,20*PIP,5,BLUE);

// plot some other indicators
plot("ATR (PIP)",ATR(20)/PIP,NEW,RED);
plot("Doji",CDLDoji(),NEW+BARS,BLUE);
plot("FractalDim",FractalDimension(Price,30),NEW,RED);
plot("ShannonGain",ShannonGain(Price,40),NEW,RED);
}

See also:

Spectral filters, indicators, normalization, candle patterns, machine learning ► latest version online Troubleshooting

Troubleshooting. Bugs and fixing them. Support.

A script is a simple text file, so you can type anything in it, even complete nonsense. But even scripts that look perfectly fine at first glance can behave very different than the programmer intended. In the simplest case, the compiler throws an error message and displays the faulty line. You should be able to fix that unaided. Otherwise just go again through the C tutorial.

In less trivial cases you'll see an error message when the script is running, and it may be not as informative. The worst case is code that gives no error message at all, but just does not do what it should, or even crashes or freezes. Don't panic: Any programmer encounters such issues all the time. Beginners can often not imagine that they made a mistake, and suspect a Zorro bug. Seasoned programmers are used to bugs in their code and know how to find and fix them. So get out of beginner mode as soon as possible. Utilize the many books and online seminars about the C language and script writing with Zorro. If you cannof find a bug in your script, check also your historical data - maybe it has gaps, outliers, or irregular time stamps that cause strange results. If you're really stuck, we provide a script fixing service. But first try to solve the problem on your own.

Clean code

The best way to avoid bugs is good coding style that allows quick reading and understanding the code. This should be no problem with strategy scripts that are normally short and have a linear structure. Still, even a 20-line script can be written as a cluttered lump of spaghetti code. Some suggestions for avoiding bugs already when writing the script:

Error messages

Error messages at compiling the script are simple syntax errors. Typical examples:

The script and line in question is printed in the error message. This makes those errors easy to identify and fix, even for a beginner. You should not need help with syntax errors. If you do, start over with a C tutorial or book.

If a DLL does not load, check if it a) exists, b) can load all needed modules (=> Dependency Walker), and c) is not locked or access restricted by Windows.

If the script compiles with no error messages, it can still have bugs and produce a runtime error (like "Error 123: ...."). Under error messages you'll find a list of all such errors and warnings, and their likely reasons. Messages related to opening and closing positions are listed under Log; known issues with brokers are described on the related page (FXCM, IB, Oanda, MT4, etc). The answers to many typical issues of script development can be found in the FAQ list.

If you see a strange number like like "1.#J" or "1.#IND" in the log or message window, or got Error 037, Error 011, or similar errors, you're likely having a buggy expression. Such as a division by zero at the begin of the simulation when indicators are not yet initialized and price series are still a flat line. A quick workaround for division bugs is replacing A / B with A / fix0(B).

If you see "(Null)" in the log, it's usually printing an uninitialized string.

If you see a runtime error message in the message window, but have no clue at which part of your code the problem happens, the quickest way to find it is placing watch("?...") statements before suspicious places. The parameter is then displayed at the end of the error mesage. For instance, watch("?10") will add a (10) at the end of all subsequent error messages, and watch("?TEST") will add (TEST). You can remove the statements when the error is fixed.

If you trade live and see an error message beginning with an exclamation mark "!", it's a message from your broker, often giving the reason for not opening or closing a trade. For details, see Log about a list of all possible messages in a live trading session.

Unexpected results or differences

If backtest results seem not to be right, first look into the log (LOGFILE must be set). Select one or two trades and check them from begin to end. Script parameters can affect trade results in many different ways - make sure to check them all, and also read the remarks on their manual pages. Look for outlier trades with extremely high profits or losses. If needed, make sure that your script can deal with events such as price shocks or stock splits.

Most typical coding errors are easy to spot:

Other errors are not as easy to find. Typical problems:

Bugs

You want your script to do something, but it refuses to obey and does something else instead. That's a bug. The worst bugs are those that go unnoticed. That's why you should always carefully check the log, even if you don't get any error messages and all seems ok. If you notice something wrong in the log - for instance, the script trades too often, or not often enough, or at the wrong time - first go through your code. Sometimes you will directly see what's wrong, sometimes not. Then you must debug it.

Debugging strategy scripts is a special case of program debugging. You're normally interested in the behaviour of a variable or signal from bar to bar. There are several methods to observe the behavior of variables, either from code line to code line, or from bar to bar, or during the whole test run:

Single Step Debugging
                                 A strategy in the visual debugger

Crashes and freezes

They seem more serious, but are often easier to identify and fix than the bugs that only cause wrong behavior. You can encounter three types of crashes:

For fixing a freeze, first check all loops and recursions in your script and make sure that they terminate. Loops waiting for user input should check with a wait() call if the [Stop] button was hit. Most crashes are normally easy to fix when they happen regularly or always at the same bar or script position. Typical causes of crashes:

Crashes can be harder to find when they happen irregularly and their reason is not immediately obvious. Zorro has several mechanisms for detecting such problems:

If the problem is not caused by the script - for instance, when it happend while trading a Z system - you need to look for an external reason. Activate diagnostics mode as above. If the program then freezes or crashes again, you can see in the last line of the diag.txt at which place it happened. This can indicate which module on your PC is possibly malfunctioning. There are websites and forums on the Internet with more hints for PC troubleshooting.

Zorro bugs

The hopefully least likely reason of script troubles is a bug in Zorro. Look first in the bug list. If you think you've encountered a new Zorro bug that no one else has seen before, please report it to support@opgroup.de. Please describe what's happening under which circumstances and how to reproduce the problem; please include your script, the log, and all related data such as asset list and asset history. Do NOT send screenshots, videos, or photos that you took from your monitor (unless we ask for them). You need no support ticket for bug reports. If the bug can be confirmed, it will appear on the bug list and will normally be fixed within a few days.

Don't be disappointed when your bug report is quickly returned with the remark "no Zorro bug", since this happens with most of them. You can then safely assume that you'll be able to fix that bug. Use the methods described above, or hire our script fixing service.

Getting help

If you're really stuck, you can subscribe a support ticket and contact support@opgroup.de. There are two levels of support. Standard support will answer Zorro-related questions by email usually within 24 hours on business days, and suggest solutions for technical problems. Priority support helps with coding and with urgent problems. It includes reviewing small code snippets and providing answers by Skype chat, usually within 2 hours from 8:00 - 18:00 CET on business days. The subscriptions can be cancelled anytime. Licenses include free standard support periods.

You don't need a support ticket:

Zorro support does not cover teaching the C language or debugging, fixing, or programming your script. For C, there are lots of books, tutorials, and online courses available. For fixing your script, you can hire our script fixing service at support@opgroup.de. Depending on code complexity, it takes usually 1-2 hours to find and fix a bug. You can also hire a service for installing a ready-to-run system on your PC or VPS, or for writing specific asset lists and data conversion scripts. The most frequent support questions are listed here

See also:

watch, debugger, error messages, bug list
► latest version online Algorithmic Trading in C/C++ | Workshop 5

Previous: Trend Trading

Workshop 5: Counter-Trend Trading, Optimization, Walk Forward Analysis.

This is an example of a counter trend trading script (Workshop5):

function run()
{
  BarPeriod = 240;  // 4 hour bars
  LookBack = 500;   // maximum indicator time period
  set(PARAMETERS);  // generate and use optimized parameters
  StartDate = 2005;
  NumWFOCycles = 10;
  if(ReTrain) {
    UpdateDays = -1; 
    SelectWFO = -1; 
  }
 
// calculate the buy/sell signal with optimized parameters
  vars Prices = series(price(0));
  vars Filtereds = series(BandPass(Prices,optimize(30,20,40),0.5));
  vars Signals = series(FisherN(Filtereds,500));
  var Threshold = optimize(1,0.5,1.5,0.1);
 
// buy and sell
  Stop = optimize(4,2,10) * ATR(100);
  Trail = 4*ATR(100);
 
  if(crossUnder(Signals,-Threshold))
    enterLong(); 
  else if(crossOver(Signals,Threshold))
    enterShort();
 
// plot signals and thresholds
  plot("Filtered",Filtereds,NEW,BLUE);
  plot("Signal",Signals,NEW,RED);
  plot("Threshold1",Threshold,0,BLACK);
  plot("Threshold2",-Threshold,0,BLACK);
  PlotWidth = 600;
  PlotHeight1 = 300;
}

In this strategy, a bandpass filter with an optimized center period is fed with the price curve. The result is a clean curve that consists mostly of the medium-period peaks and valleys. It is normalized and Fisher transformed, and the resulting Signals series is finally compared with an upper and lower Threshold for generating trade signals. This trading method with frequency filters and transformations is superior to conventional indicators. It is explained in detail in the Black Book, and in even greater detail in the books by John Ehlers from the book list.

The threshold is optimized between 0.5 and 1.5 in steps of 0.1. Any Signal value that exceeds the threshold range triggers a trade. Before that, a stop loss is placed at an adaptive distance from the price, and a trail limit 4 average candles away from the current price. If the trade now goes in favorable direction by more than 4 average candles, the stop loss will follow the price at a distance of 8 candles. This ensures that all trades that reach an 8 candle profit are guaranteed to end with a win, regardless how the price further behaves. 

When the Signals curve crosses the negative threshold from above, the script assumes that the price is close to a bottom, and buys long. Short buying works the other way around. 

Training with Walk Forward Optimization

Training serves two purposes. At first, it improves the 'robustness' of a strategy. During the training run, strategy parameters are optimized and adapted to the market until the strategy returns stable profits with minimum deviation. The second purpose is finding out how sensitive the system is to small parameter changes. The more sensitive, the less likely is it that backtest results are reproduced in live trading.

Walk-Forward Optimization (WFO) is not merely an optimization, but an analysis method that tests the strategy together with its parameter ranges and optimization method. If a strategy fails in a walk forward analysis, it will also fail in real trading, even if it collected huge profits in backtests. For this reason, walk forward optimization is the most important process when developing a strategy.

WFO is activated by setting NumWFOCycles. It uses a data frame that is shifted in 10 cycles over the simulation period. The frame consists of a training period and a subsequent test as in the figure here. The lookback periods at the begin are needed to collect initial data for the functions. The training periods generate the parameters that are then tested in the subsequent test periods. This ensures that every test uses "unseen" price data that were not used for optimizing its parameters - just as in real trading. The data frame is then shifted over the simulation period for verifiying how the strategy would fare when started at different times. Because the test period is now much smaller than the whole simulation period, a far backStartDate = 2005 is used. This way enough data can be collected for getting a test period from 2010 to 2015.

For training the strategy, click [Train] and observe what the optimize calls do. It now takes a few minutes because the simulation period is about 10 years, and a full optimization is performed for any of the 10 cycles. The optimized parameters are stored in a separate parameter file for every WFO cycle. After the training phase, which can take about one minute depending on the PC speed, you'll see charts pop up in your Internet browser, like this:

Parameter 1 (bandpass time period)
 
Parameter 2 (Threshold)
 
Parameter 3 (Stop factor)

The parameter charts show how the parameter values affect the performance of the strategy. The red bars are the return ratio of the training period - that's basically the total win divided by the total loss, multiplied by a penalty factor for less trades. The dark blue bars are the number of losing trades and the light blue bars are the number of winning trades. We can see that the time period produces slightly increasing returns up to about 35, then the returns go down. Threshold has the best performance at about 1.0. The stop factor - the third parameter - slightly goes up and has a maximum at about 7. We can also see here that a distant stop, although it increases the risk and eventually reduces the profit, achieves a higher number of profitable trades and thus a better 'accuracy' of the strategy.

All red bars ending above 1.0 indicate a profitable parameter combination. In this case they stay above 1.0 over the whole range, which means that the strategy performance is quite robust and not very sensitive to parameter changes. The optimized parameters are stored in the file Data/Workshop5_EURUSD.par (the file name would be different for other assets).

After training, click [Test]. The data from the training is now used for a walk forward analysis. This is the equity curve:

We can see that the strategy still stays profitable with walk forward analysis, but the equity curve does not look smooth and the return in 2013 and 2015 was negative. In the next workshop we'll learn how to make strategy returns more steady and reliable so that one can really derive a regular income from them.

Real time optimizing a WFO strategy

When trading a walk forward optimized strategy, it should be regularly re-trained and adapted to the current market situation, just as in the WFO process. For this, the ReTrain clause was added to the script. ReTrain is nonzero when the [Train] button is clicked during live trading. UpdateDays is the time period for automatically downloading new price data for the current asset from the broker's server, which is then used for the subsequent training cycle. If set to -1, the price data is updated to the current date. SelectWFO tells Zorro not to optimize the whole simulation period, but only a certain WFO cycle; in this case, the last cycle (-1) that is used for live trading.

Clicking [Train] every couple of months (as indicated by "WFO test cycles" in the performance report) will continue the WFO process during trading, and make the strategy independent of external parameter settings. This way we have essentially a 'parameter-free' strategy.

Plotting signals

Since the trade rules are somewhat more complicated than the simple lowpass function of the previous lesson, let's see how the various series look like, for checking if everything works as supposed. This happens in the last lines at the end of the script.

plot("Filtered",Filtereds,NEW,BLUE);

This line generates a plot of the Filtereds series. It's plotted in a NEW chart window with color BLUE. We can use the plot function to plot anything into the chart, either in the main chart with the price and equity curve, or below the main chart in a new window. The Signal curve and the upper and lower Threshold are plotted in another new chart window below the chart:

The blue curve in the middle window is the plot of the Filtered series. It shows the price fluctuation in the range of about +/-0.005, equivalent to about 50 pips. The bottom window displays the Signals series. The black lines are the thresholds that trigger buy and sell signals when Signals crosses over or under them. Plotting variables and series in the chart greatly helps to understand and improve the trade rules. For examining a part of the chart in details, the PlotDate and PlotBars variables can be used to 'zoom into' a part of the chart.

What have we learned in this workshop?

Next: Portfolio Trading


Further reading: ► Training, plot, signal processing, optimize, NumWFOCycles

Algorithmic Trading in C/C++ | Workshop 6 Previous: Optimizing

Workshop 6: Portfolio strategies. Money management.

Diversification is an important factor for getting a regular income with algorithmic trading. Therefore, many successful trading strategies trade not only with a portfolio of assets, but also with a portfolio of strategies. Of course, any components of the portfolio - any assets and any algorithm - must be separately optimized. Here's a script that does that automatically. It trades with several assets on several time frames simultaneously, and uses both trend and counter-trend strategy algorithms (Workshop6.c):

// Counter trend trading function from Workshop 5
function tradeCounterTrend()
{
  TimeFrame = 4;	// 4 hour time frame
  vars Price = series(price(0));
  vars Filtered = series(BandPass(Price,optimize(30,25,35),0.5));
vars Signal = series(FisherN(Filtered,500));
var Threshold = optimize(1,0.5,2,0.1); Stop = optimize(4,2,10) * ATR(100);
Trail = 4*ATR(100);
if(crossUnder(Signal,-Threshold)) enterLong(); else if(crossOver(Signal,Threshold)) enterShort(); } // Trend trading function from Workshop 4 function tradeTrend() { TimeFrame = 1; // 1 hour time frame vars Price = series(price(0)); vars Trend = series(LowPass(Price,optimize(500,300,700)));
Stop = optimize(4,2,10) * ATR(100);
Trail = 0;
vars MMI_Raw = series(MMI(Price,300)); vars MMI_Smooth = series(LowPass(MMI_Raw,500)); if(falling(MMI_Smooth)) { if(valley(Trend)) enterLong(); else if(peak(Trend)) enterShort(); } } function run() { set(PARAMETERS,FACTORS,LOGFILE); BarPeriod = 60; LookBack = 2000; StartDate = 2005; NumWFOCycles = 10; Capital = 10000;
if(ReTrain) { UpdateDays = -1; SelectWFO = -1; reset(FACTORS); } // double portfolio loop while(asset(loop("EUR/USD","USD/JPY"))) while(algo(loop("TRND","CNTR"))) { Margin = 0.5 * OptimalF * Capital; if(Algo == "TRND") tradeTrend(); else if(Algo == "CNTR") tradeCounterTrend(); } }

For testing a portfolio strategy, you'll first need historical data for more assets, since the Zorro installation has only EUR/USD included. Go to the Zorro download page, and download the M1 price histories from 2002 on (that's normally 3 large files). Unzip the content in your History folder. You now have the recent history of the major index and Forex pairs. For this strategy we'll need the USD/JPY history from 2005 on.

The strategy is divided into 3 different functions: tradeTrend for trend trading, tradeCounterTrend for counter trend trading, and the run function that sets up the parameters, selects the assets, sets up the trade volume, and calls the two trade functions. The loop function lets the code walk through the various assets and trade functions.

The trade volume is controlled by this line:

Margin = 0.5 * OptimalF * Capital;

We're using a money management method developed by Ralph Vince. He published a computer algorithm that evaluates every component's balance curve for calculating the optimal percentage of the gained capital to be reinvested. This percentage is called the OptimalF factor (you can see it also in the OptF column in the performance report). Multiply OptimalF with the capital, and you'll get the maximum margin to be allocated to a certain trade. Normally, people try to stay well below this maximum margin. OptimalF is specific to the strategy component and calculated from historical performance, meaning there's no guarantee that this performance will continue in the future. Exceeding the maximum margin is worse than staying below it, therefore it's recommended to only invest about 50% of OptimalF.

Margin is one of the methods for determining the amount invested per trade. Other methods were giving the number of lots or the money at risk. Margin is only a fixed percentage of the real trade volume - for instance 1% at 1:100 leverage - that the broker keeps as a deposit. If Margin is left at its default value, Zorro always buys 1 lot, the minimum allowed trade size. The higher the margin, the higher the number of lots and the higher the profit or loss. Capital is a variable set to our initial investment ($10,000) for determining its annual growth (CAGR). For determining the optimal margin for the strategy component, this Capital is multiplied with the 50% of the OptimalF factor.

Now it's time to [Train] the strategy. Because we have now 4 portfolio components, 4x more bars, and twice as many cycles, the training process will take much longer than in the last workshop. As soon as it's finished, click [Test], then click [Result] (again, your equity curve can look different when testing a different time period).

You can see in the chart below that now two different algorithms trade simulaneously. The long green lines are from trend trading where positions are hold a relatively long time, the shorter lines are from counter-trend trading. The combined strategy does both at the same time. It generated about 40% annual CAGR in the walk forward test.

What happens when we reinvest our profits? Modify this line (changes in red):

Margin = 0.5 * OptimalF * Capital * sqrt(1 + ProfitClosed/Capital);

ProfitClosed is the sum of the profits of all closed trades of the portfolio component. 1 + ProfitClosed/Capital is our capital growth factor. Let's examine the strange formula with an example. Assume we started with $10000 capital and the component made $20000 profit. The inner term inside the parentheses is then 1 + $20000/$10000 = 3. This is the growth of the capital: we started with $10000 and have now $3000 on our account, so it grew by factor 3. The square root of 3 is ~1.7, and this is the factor used for reinvesting our profits.

Why the square root? Here's the background in short. In short term trading with high leverage, the required capital is mainly determined by the expected maximum drawdown. The maximum drawdown of any trade system increases over time. A system tested over 10 years has worse drawdowns than the same system tested over only 5 years. When modeling drawdown depth mathematically with a diffusion model, the maximum drawdown is proportional to the square root of the trading time. But the maximum drawdown is also proportional to the invested amount: When investing twice the volume you'll get twice the drawdown. Thus, if you would reinvest a fixed percentage of your balance, as suggested in most trading books, the maximum drawdown would grow by both effects proportionally to time to the power of 1.5. It will thus grow faster than your account balance. This means that at some point, a drawdown will inevitably exceed the balance, causing a margin call. There are several methods to overcome this problem, and one of them is to reinvest only an amount proportional to the square root of the capital growth. Thus when your capital doubles, increase the trade volume only by a factor of about 1.4 (the square root of 2), i.e. 40%. That's the famous square root rule that applies to all high leverage strategies. If you're interested, you can find the formula explained in detail in the Black Book, and also a comparison of several other reinvestment methods. For calculating the investment with the square root rule, use the InvestCalculator script.

Since only the investment method has changed in the script above, the system needs not be retrained. Clicking [Test] is enough. Reinvesting the capital by the square root rule increases the CAGR to about 50%.  

What have we learned in this workshop?

Next: Machine Learning


Further reading: ► while, asset, algo, loop, string, strstr

 

Algorithmic Trading in C/C++ | Workshop 4a

Previous: Trend trading

Workshop 4a: Indicator Implementation. More about series.

All the time, technical traders invent new indicators in their never ending hope to find one that really works. The article series Petra on Programming illustrates how to implement all sorts of complex and exotic indicators, and use them for trading systems. As an addendum to the previous workshop, we're going to implement the lowpass filter indicator. A second order lowpass filter is a bit more complex than its simple minded colleagues SMA, EMA, Stochastic, Ichimoku & Co. So it's a good exercise for serious indicator implementation.

When we're only interested in implementing an indicator from a ready formula, we can skip all math and start directly with the code. If you're interested in the theory, get the books by John Ehlers (book list) who introduced signal processing for traders. The Gaussian highpass filter formula below is from his book. For avoiding math, skip the following section.

>>> Math starts here

A lowpass filter suppresses the high frequency components in a data stream, a highpass filter suppresses the low frequency components. The higher the order of the filter, the sharper is the frequency cutoff, which is normally desired for indicators. The filter's formula usually expresses the filter gain - output divided by input - in Z-transform notation. Here's the gain of a second order Gaussian highpass filter that you can get from a digital filter design book:

GainHP = Out / In = (1 - a/2)2 · (1 - 2 z-1 + z-2) / (1 - (2 - 2a) z-1 + (1 - a)2 z-2)  

where a is the filter coefficient and the operator z-n applies n units of delay to the data. Z-transform notation is an elegant method to define arbitrary filters - if interested, read it up under the above link. For getting a lowpass filter, we can simply subtract the above highpass filter from a neutral filter with a gain of 1:

GainLP = Out / In = 1 - (1 - a/2)2 · (1 - 2 z-1 + z-2) / (1 - (2 - 2a) z-1 + (1 - a)2 z-2)

Converting to nominator / denominator form for separating input and output:

Out / In = (a - a2/4 + a2/2 z-1 - (a - 3a2/4) z-2) / (1 - (2 - 2a) z-1 + (1 - a)2 z-2)

Cross multiplication:

(1 - (2 - 2a) z-1 + (1 - a)2 z-2) · Out  = (a - a2/4 + a2/2 z-1 - (a - 3a2/4) z-2) · In

Applying the z operators to the input and output data; 1·Out = Out[0],  z-1·Out = Out[1], etc:

1 · Out[0] - (2 - 2a) · Out[1] + (1 - a)2 · Out[2] = (a - a2/4) · In[0] + a2/2 · In[1] - (a - 3a2/4) · In[2]

where Out[0] is the current output value, Out[1] is the output delayed by one bar, and so on. The final result:

Out[0] = (a - a2/4) · In[0] + a2/2 · In[1] - (a - 3a2/4) · In[2] + (2 - 2a) · Out[1] - (1 - a)2 · Out[2]

This is the formula that we can now directly implement as an indicator function.

>>> Math ends here

With the series function that we already used in the previous workshop, it's easy to implement the delayed data from the above formula. The filter coefficient a is converted from a more convenient time period by a 'smoothing formula', normally a = 2/(1+Period). The higher the Period, the smaller is a and the stronger is the high frequency attenuation effect of the filter.

That's the implementation (Workshop4a):

var lowpass(vars In,int Period)
{
  var a = 2./(1+Period);
  vars Out = series(In[0],3);
  return Out[0] = (a-0.25*a*a)*In[0]
    + 0.5*a*a*In[1]
    - (a-0.75*a*a)*In[2]
    + (2-2*a)*Out[1]
    - (1-a)*(1-a)*Out[2];
}

The first line converts the time period to the filter coefficient a. Mind the dot of the '2.'. The decimal point tells the compiler that it's a real number. A plain '2' would be an int. 1+Period is also an int, so we had an integer calculation that yields an integer result. Which will be normally 0 since the integer 2 divided by a higher integer is 0. This is one of the programming traps. If in doubt, put a decimal point to all numbers in any floating point formula in your scripts.

For the output we're creating a new series, but since we only need delay by of up to 2 bars, the series only needs a length of 3 elements. If we omitted the length parameter, the series had the length of the lookback period, which would be a waste of resources. We also initialize the series to the input data for preventing that it starts with 0.

Note that Period hs no effect on the amount of data. The filter uses only the last 3 bars. The plot of the lowpass function is the same as in the previous workshop, because the internal LowPass filter has the same formula:

You can now look into the file indicators.c in the Source folder that contains the code of many indicators, and check out how those indicators work.

What have we learned in this workshop?

Next: Walk Forward Analysis


Further reading: ► series, filters

Workshop 8 - Options trading

Previous: Machine Learning

Workshop 8: Options Trading

An option is a contract that gives its owner the right to buy (call option) or sell (put option) a financial asset (the underlying) at a fixed price (the strike price) at or before a fixed date (the expiration date). Options trading has the reputation to be more rewarding, but also more complex than trading currencies or stocks. At least for writiung the script, the latter is not true; Zorro allows easy and straightforward coding of options strategies. Here's an example of a simple options selling system (Workshop8.c):

#include <contract.c>

#define PREMIUM  3.00 // $300 per contract 
#define DAYS     6*7  // 6 weeks to expiration

void run() 
{
  set(PLOTNOW,LOGFILE);
  BarPeriod = 1440;

  History = ".t8"; // use options for unadjusted price history
  assetList("AssetsIB");
  asset("SPY");
  Multiplier = 100;

// load today's contract chain
  if(!contractUpdate(Asset,0,CALL|PUT)) return; 

  if(!NumOpenShort) { 
// find contracts with 6 weeks expiration and $2 premium
    if(combo(
      contractFind(CALL,DAYS,PREMIUM,2),1, 
      contractFind(PUT,DAYS,PREMIUM,2),1,
      0,0,0,0)) 
    { // sell a Strangle
      MarginCost = comboMargin(-1,3);
      enterShort(comboLeg(1));
      enterShort(comboLeg(2));
    }
  }
}

You need to download SPY options data for backtesting this system. There are a few differences to a 'normal' trading system. We're using an asset list for the broker IB that contains the underlying stock, SPY, a fund following the S&P500 index. Option contracts usually cover batches of N stocks, often 100, which must be set through Multiplier. The contractUpdate function loads the current options chain - the list of options with all available combinations of expirations and strikes - either from historical data, or from the broker API. If no positions are open, the strategy attempts to sell short a put and a call contract with 6 weeks expiration at a bid price of 2 dollars per stock. The contractFind function scans the options chain for fitting contracts.

The two contracts that match the desired bid price and expiration are now combined to a combo. Options and option combos can be traded with the usual enter functions, they must only be selected before with contract() or comboLeg() to distinguish the order from a trade with the underlying. The earned price - the premium, in total about 400 dollars minus commission - is booked on the account. That's not yet our profit, since we might be later obliged to sell or buy the underlying SPY stock at the strike price. The broker - or Zorro, respectively - will do that automatically when the underlying price at expiration date is not in our favor, and will deduct the difference of price and strike from our account. Otherwise, the contracts will expire worthlessly and we can keep the premium.

This combination of a put and a call contract is called a Strangle. Because we aimed for small premiums, our contracts start out of the money, meaning that the underlying price is below the call strike and above the put strike. This way we will normally win the premium, or most of it, when the SPY price does not move too far away until expiration. Otherwise, we'll lose. If the price moves a lot, we'll lose a lot.

Mind the MarginCost calculation with the comboMargin function. It is needed for a realictic backtest, since margin cost determines the required capital.

The result

We can see that in the test period 2012-2018 the strategy achieves not spectacular, but relatively constant annual returns in the 10% area. About 70% of trades are won. Still, it is not advisable to trade this system unchanged - you'll see that when you extend the test period beyond 2018. The system depends on a rising market. When the market tanks, it will fail bad since it has no mechanism for filtering unprofitable periods and limiting losses. We could place a stop loss just like for a normal trading system. The contracts are then automatically bought back when the underlying price moves dangerously far. Or we could buy 'insurance' in the form of two additional contracts with more distant strikes. This long/short combination of 4 contracts reduces the profit, but also restricts losses to a limit that can be determined with the strike prices. The Payoff script can display the risk and profit curves of arbitrary option combinations. In the Black Book you can find a different options selling system. It survives market crashes with a filter that prevents trading in volatile situations.

We're now at the end of the strategy coding course. For writing your own systems, it can save you a lot of time when you flip through this manual and make yourself familiar with Zorro's math and statistics functions. Often-used code snippets for your own scripts and strategies can be found on the Tips & tricks page. Writing good code and fixing bugs is described under Troubleshooting. If you worked with a different trade platform before, read the Conversion page about how to convert your old scripts or EAs to C. For serious strategy development, some knowledge of the leading data analysis software R can be of advantage - check out the R lectures.

What have we learned in this workshop?


Further reading: ►contract, combo

Algorithmic Trading in C/C++ | Workshop 7

Previous: Portfolio Trading

Workshop 7: Machine Learning

Zorro's advise function can be used for applying machine learning functions to candle patterns, and using the most profitable patterns for a trade signal. Here's a simple example (Workshop7.c):

function run()
{
  StartDate = 2010;
  EndDate = 2018;
  BarPeriod = 1440;   // 1 day
  NumWFOCycles = 5;   // WFO is mandatory for machine learning functions
 
  set(RULES,TESTNOW); // generate rules, test after training
  if(Train) Hedge = 2; // train with long + short trades
  if(!Train) MaxLong = MaxShort = 1; // only 1 open trade
  LifeTime = 1;        // 1 day prediction horizon
 
  if(adviseLong(PATTERN+2+RETURNS,0,
    priceH(2),priceL(2),priceC(2),
    priceH(1),priceL(1),priceC(1),
    priceH(1),priceL(1),priceC(1),
    priceH(0),priceL(0),priceC(0)) > 50)
    enterLong();
  if(adviseShort() > 50)
    enterShort();
}

Many lines in this code should be familiar, but there are also some new concepts. The adviseLong function takes price candles or other data, and generates a trade signal when the return value is above a threshold. It can be called like a normal indicator, but internally uses various machine learning training and prediction algorithms. The function is here called with the PATTERN classification method and the High, Low, and Close prices of the last 3 candles, split in two groups. The RETURNS flag uses the return of the following trade for training. Aside from PATTERN, other and more complex machine learning methods can be used, such as a deep learning neural net. A more detailed introduction in pattern detection with adviseLong/Short can be found in the Black Book. An application for deep learning is described in this article.

WFO or some other out-of-sample test method is mandatory for machine learning or pattern classification strategies. All machine learning systems tend to overfitting, so any in-sample result from price patterns, decision trees, or preceptrons would be far too optimistic and thus meaningless. The number 5 is a compromise: higher numbers produce more WFO cycles, ergo less bars for any cycle to train, so less patterns are found and the results deteriorate. Lower numbers produce more bars per cycle and more patterns are found, but they are from a longer time period - above one year - within which the market can have substantially changed. So the results can deteriorate, too.

The RULES flag is required for generating price patterns with the advise function. TESTNOW runs a test automatically after training - this saves a button click when experimenting with different pattern finding methods.

The next code line behaves differently in training and in test or trade mode:

if(Train) Hedge = 2;

Train is true in [Train] mode. In this mode we want to determine the profitability of a trade that follows a certain pattern. Hedge is set to 2, which allows long and short positions at the same time. This is required for training the patterns, otherwise the short trade after adviseShort would immediately close the long positions that was just opened after adviseLong, and thus assign a wrong profit/loss value to its candle pattern. Hedge is not set in test and trade mode where it makes sense that positions are closed when opposite patterns appear.

LifeTime sets the duration of a trade to 1 bar, i.e. 1 day. The trade results are also used for training the candle patterns and generating the trade rules. MaxLong/MaxShort limit the number of open trades in test or trade mode to 1.

The result

Click [Train]. Depending on the PC speed, Zorro will need a few seconds for running through the 5 WFO cycles and finding about 30 profitable long or short patterns in every cycle. Click [Result] for the equity curve:
 

The machine learning algorithm with daily candle patterns seems to give us a more or less rising equity curve and symmetric results in long and short trading. But can the same result be achieved in live trading? Or was it just a lucky setup? For finding out, you can apply a Reality Check with a Monte Carlo arlgorithm. There are several methods; a frequently used one is running the test many times (use NumTotalCycles) with a randomized price curve (Detrend = SHUFFLE), plotting a histogram of the results, and comparing it with the result from the real price curve. How to do such a reality check is covered in the Black Book. If you do that, you'll find that the system above will not pass the test.

What have we learned in this workshop?

Next: Options Trading


Further reading: ► advise, RULES

Algorithmic Trading in C/C++ | Workshop 4

Previous: Script basics

Algo Trading Workshop 4: Trend Trading, Time Series.

Prediction is difficult. Especially about the future.
- Niels Bohr

The point of trading is knowing the moment when it's good to buy, good to sell, or good to do nothing. A trading strategy uses market inefficiencies - deviations of the price curves from random data - for predicting future prices and finding the right buying and selling points. That will be the topic of the next workshops.

► All strategies presented here are meant for educational purposes. They all are designed for simplicity, not for maximum profit or robustness. For really trading such a strategy, you would normally use more entry filter rules, a more complex trade exit method than a simple stop, and a way to allocate capital. But we'll keep it easy in the workshops.

The backtest results included here can be different to the results you'll get when testing the scripts yourself. That's because you're likely using a more recent simulation time period, and different spread, commission, and rollover parameters which are updated when connecting to a broker. If not otherwise mentioned, the included scripts are set to a simulation period from 2010-2017; for a different time period modify the StartDate and EndDate lines in the script.

► A more comprehensive trading course, with different strategies, can be found in the Black Book.

Trend following

The most obvious way to make profits is going with the trend. An example (Workshop4):

function run()
{
  set(LOGFILE);
  
  vars Prices = series(price(0));
  vars Trends = series(LowPass(Prices,500));
 
  Stop = 4*ATR(100);

  vars MMI_Raws = series(MMI(Prices,300));
  vars MMI_Smooths = series(LowPass(MMI_Raws,300));
  
  if(falling(MMI_Smooths)) {
    if(valley(Trends))
      enterLong();
    else if(peak(Trends))
      enterShort();
  }
}

(If you're not yet familiar with scripts, start with Workshop 1.) We can see that the function is now named "run" and not "main". While a main function runs only once, a run function is called after every bar with the period selected with the scrollbars. By default, the bar period is 60 minutes. So this function runs once per hour when Zorro is trading.

The vars definition creates a series - a var 'with a history'. We define a price series and a trend series with the return value from the LowPass function, a second order lowpass filter. We will implement this filter as an exercise of indicator development in the next workshop. But for the moment you need only to keep in mind that this lowpass filter attenuates all the wiggles and jaggies of the Price series that are shorter than 4 weeks, but it does not affect the trend or long term cycles. You can see the frequency characteristic of the LowPass filter in the image on the Filter page.

Use a series (of type vars) when you also need the history of the variable, such as the value from 3 bars before. Know when you need a series and when a variable will do - confusing series and variables is one of the most frequent mistake in beginner's scripts.

In the image below, the black line is the original EUR/USD price and the red line is the result from the LowPass function:

A lowpass filter has a similar smoothing effect as a Moving Average function (see Indicators), but produces a better reproduction of the price curve and has less lag. This means the return value of a lowpass filter function isn't as delayed as the return values of Simple Moving Average or EMA functions that are normally used for trend trading. The script can react faster on price changes, and thus generate better profit.

We're setting a dynamic stop loss (Stop) that adapts to the market situation, and a filter for detecting if the market is trending or not. The Market Meanness Index (MMI) evaluates the self-correlation of the data and indicates its 'trendiness'. Its algorithm and usage can be found in the Black Book. The code calculates the MMI for the last 300 bars and smoothes it again with the LowPass filter.

Trades are entered at a trend peak or valley when the smoothed MMI is falling, indicating the begin of a trend. If a trade was already open in the opposite direction, it is automatically closed. 

An example trade triggered by this strategy:

The red line in the chart above is the Trends series. You can see that it has a peak at the end of September, so the peak(Trends) function returned true and enterShort was called. The tiny green dot is the moment where the short trade was entered. The Trends series continues down all the way until November 23, when a valley was reached. A long trade (not shown in this chart) was then entered and the short trade was automatically closed. The green line connects the entry and exit points of the trade. It was open almost 2 months, and made a profit of ~ 13 cents per unit, or 1300 pips.

Backtesting

Now let's just test how buying and selling works in that strategy. Start up Zorro, select the [Workshop4] script and the [EUR/USD] asset, leave the [Period] slider at 60 minutes, then click [Test]:

You'll likely get a different result when testing this strategy in a different time period or when your simulated account has different spread, rollover costs, commission, or slippage. By default, the simulation runs from 2012 until 2017. The strategy achieved an annual profit of ~400 pips, equivalent to about 30% annual return on capital. The average monthly income is 3 $ - quite modest, but Zorro simulated a microlot account and needed only ~100 $ capital for running the strategy. So you have about 3% return on capital per month. By the way, the '$' sign in Zorro's messages does not necessarily mean US-Dollars, it represents the account currency.

The equity curve can be seen by clicking [Result]:


In the image that pops up in the chart viewer, you can see a black curve and some green lines and red dots attached to the curve. In the background there's a jaggy blue area and a red area below. The black curve is the price of the selected asset - the EUR/USD. The price scale is on the left side of the chart. The green and red dots are winning and losing trades. The green lines connect the entry and exit point of a winning trade. You can see that there are far more red than green dots - about 80% of the trades are lost. However, the long-term trades all have green lines. So we have a lot of small losses, but several large wins. This is typical of a trend following strategy.

The most interesting part of the chart is the blue area that represents the equity curve. We can see that it's slightly below zero in the first years, then rises to about 300 $ in 2016. The red area below is it's evil counterpart, the "underwater equity" or drawdown curve that indicates losses on our account. The more blue and the less red, the better is the strategy. This one gives a mixed result. There are a few winning years, in the other years we had a loss or a tie. This shaky behavior is reflected in the low Sharpe Ratio and the high Ulcer Index (UI) of the strategy.

Analyzing the trades

Since we want to check the single trades of this strategy in detail, we have set the LOGFILE flag. Such flags are turned on with the set() function. If the flag is activated, [Test] stores a log of all events in the Log subfolder. The log is also opened with the script editor on clicking [Result]. It begins with a list similar to this one:

BackTest: Workshop4 EUR/USD 2008..2013
 
[139: Thu 10.01. 07:00]  1.46810
[140: Thu 10.01. 08:00]  1.46852
[141: Thu 10.01. 09:00]  1.46736
[142: Thu 10.01. 10:00]  1.46721
[EUR/USD::S4300] Short 1@1.4676 Risk 6 at 10:00
 
[143: Thu 10.01. 11:00]  0p 0/1
[EUR/USD::S4300] Reverse 1@1.4694: -1.52 at 11:00
[EUR/USD::L4400] Long 1@1.4694 Risk 6 at 11:00
 
[144: Thu 10.01. 12:00]  -20p 0/2
[145: Thu 10.01. 13:00]  -20p 0/2
[146: Thu 10.01. 14:00]  -20p 0/2
[EUR/USD::L4400] Reverse 1@1.4649: -3.54 at 14:00
[EUR/USD::S4700] Short 1@1.4649 Risk 6 at 14:00
 
[147: Thu 10.01. 15:00]  -67p 0/3
[EUR/USD::S4700] Stop 1@1.4729: -6.22 at 15:00
 
[148: Thu 10.01. 16:00]  -148p 0/3
[EUR/USD::L4900] Long 1@1.4744 Risk 6 at 16:00

The meaning of the cryptic messages is explained in the Log chapter. Checking the log is the first (or maybe, second) thing to do when testing a strategy, for determining if it trades correctly. You can see in the log that most trades are lost. Zorro seems to deliberately enter trades in the wrong direction; trading at random would only lose a little more than 50%, not 80%. But there's a method behind this madness. The algorithm wants to be in a favorable position when a long-term trend begins, and then keeps the position for a long time. That's why it wins in the long run despite losing most trades.

Aside from the log, [Result] also opens the performance report. The displayed parameters are described on the Performance page.

The trade distribution

For some more insight into the distribution of trades, we're plottoing the trade distribution. For this add the following line to the very begin of the script (before the run function):

#include <profile.c>

This is a command to the compiler to insert another script file from the include folder. profile.c is a script that contains functions for plotting price and trade statistics and seasonal analysis charts. For the trade distribution, call the following function from inside the run function:

plotTradeProfile(-50);

This generates a trade distribution chart in steps of 50 pips at the end of the test run. The generated chart looks similar to this one:

For generating the chart, all trades are sorted into buckets, depending on their profit. Every bucket is represented by a red and a blue bar. Trades with a loss between -200..-100 pips go into the first bucket at the left side, marked -200 at the x axis. The next buckets are for trades with loss or profit from -200..-150 pips, -150..-100 pips, -100..-50 pips, and so on. The height of the blue bar is the number of trades ending up in that bucket (right y axis), the height of the red bar is the sum of all profits in the bucket (left y axis). We can see that most trades end with a loss between 0 and -50 pips. The total profit of the system comes from relatively few profitable trades, some even with about 1000 pips profit.

Aside from a lowpass filter, several other filter algorithms are often used for detecting trend changes. You can find a comparison of trend filters in the Trend Indicators article series on Financial Hacker.

What have we learned in this workshop?

Next: Lowpass filter implementation


Further reading: ► series, price, filters, stop, buy, sell, plot, performance, LOGFILE, #include, profile

Trading essentials: ► Bars, Strategies

Algorithmic Trading in C/C++ | Workshop 1

Quick Tutorial: How To Code Algo Trading Strategies in C / C++

Strategy coding (or programming) is the act of writing the rules of an algorithmic trading system in code - a sort of simplified language. A simple algo trading script, coded in the language C, looks as below. The example strategy below is generated with ChatGPT from the follwing propt:

"Write a strategy that uses two moving averages (SMA) of the Close price. When the SMA(30) crosses over the SMA(100), open a long position; when the SMA(30) crosses under the SMA(100), open a short position. Use a stop at 10 pips distance."

function run()
{
  vars Prices = series(priceC());
  vars SMA100 = series(SMA(Prices,100));
  vars SMA30 = series(SMA(Prices,30));
  Stop = 10*PIP;

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

However, if you don't want to leave strategy creation to ChatGPT, but write them yourself, here's a quick tutorial. Pease be aware that simple indicator based strategies, like that one above, are not well suited for earning money in today's markets. Zorro uses C or C++ for algorithmic trading because they are the fastest and most widely used high-level languages. A script in C/C++ will need only a few seconds for a 10 years backtest. The same script in R or Python would need hours. Most software today is written in C or C++, and most trading platforms use C or a C variant for automated trading. Zorro gives you the choice to develop strategy scripts either in lite-C, a 'lightweight' version of the C programming language, or in 'full grown' C++

The lite-C compiler does not require a linkage process and thus compiles and starts a script 'on the fly'. It also includes a framework that hides away the complex stuff - like API calls, pointers, structs, etc. - that is difficult to beginners. It was originally developed for the computer game company Atari for controlling the artificial intelligence of agents in computer games. As you can imagine, a language powerful enough to control simultaneously ten thousands of agents in a virtual world is an excellent choice for trading strategies. Since most languages have a similar structure, after learning lite-C you'll also be able to understand simple code in Python, Pascal, or Tradestation's EasyLanguage, even though the latter declares a variable not with var x = 7, but with Vars: x(7).

The following workshops give a quick introduction into coding algorithmic trading strategies and indicators. They are for the language C, but the same scripts in C++ would look almost identical. The differences are listed under Coding in C++. For a more in-depth introduction to algorithmic trading, read the Black Book or register for the next Algo Bootcamp.

The example strategies are for educational purposes only and designed for simplicity, not for maximum profit and minimum risk. For trading with real money, better develop your own strategies.

Workshop 1: Variables

A script (or program) is a list of instructions in plain text format that tell the computer what to do under which circumstances. It consists of two types of objects, variables and functions (C++ has a third object type, classes, that are a combination of both). A variable is used to store numbers, text, or other information. It must be defined before it can be used, like this:

var Price;
int Days = 7;
string Wealth = "I am rich!";

Variables can have different types for different purposes, such as var (or double) for prices, vars for a time series, int for counting, or string for text. You can find details about the different types here. Any variable can receive an initial value at start.

A language that controls a certain platform, such as Zorro, has normally also predefined variables that affect the behavior of the platform. For instance, the Stop variable in the above SMA crossing example is used for setting up the stop loss distance. Predefined variables need not be defined by the user.

We can add comments to explain our code, using two slashes:

int CandleHeight; // the height of a candle in PIP

Often in testing a script we temporarily disable a line by placing two slashes in front of it. This is "commenting out" a line, and is used so frequently in programming that the script editor has two extra buttons for commenting out and commenting in.

Every definition or any command in C needs to end with a semicolon. If you forget to add ";" at the end of the line of code, you'll see an error message - not for the line with the missing semicolon, but for the following line!

The first script

Start Zorro, select Workshop1 in the Script dropdown menu, then press [Edit]. The script editor opens up and shows you this script:

// Tutorial Workshop 1: Variables
////////////////////////////////////////

function main()
{
  var a = 1;
  var b = 2;
  var c;

  c = a + b;
  printf("Result = %.f",c);
}

The script begins with a comment (the lines beginning with //). Then we have a function named main - anything that happens in a program must be inside a function - and three variables a, b, c. The action happens in the following line:

c = a + b;

This tells the computer to add the content of the variables a and b, and store the result in the variable c. The last line displays the content of c in the message window:

printf("Result = %.f",c);

Now press [Test] on Zorro's panel and watch the script running.

Workshop 2: Functions

Functions can be defined using the word function (or void, or a variable type, more about that later) followed by the name of the function and a pair of parentheses ( ). The parentheses are used to pass additional variables to the function, if required. The body of the function is written inside a pair of curly brackets { }. The body consists of one or more lines of C code that end with a semicolon. Programmers usually indent the code in the function body by some spaces or a tab, for making clear that it is inside something.

Zorro scripts often use the convention of lowercase names for functions (except for common indicators such as EMA), 'CamelCase' for variables (except for 1-letter variables with no particular meaning like a, b, c, or indices like i, j, k), and UPPERCASE for #defines and flags (which we'll explain later). An example of a function that computes the number of days spent by me (or you) on Earth:

function compute_days()
{
  var MyAge = 33;
  var DaysPerYear = 365.25;
  var NumberOfDays = MyAge * DaysPerYear;
  printf("I am %.0f days old!",NumberOfDays);
}

Enter this code in a new empty script, and save it (File / Save As) into the Strategy folder under a name like Myscript.c (don't forget the ".c" at the end - it means that this file contains C code). You should now find Myscript in the Script scrollbox. When you now select it and click [Test], you 'll get an error message "No main or run function!".

If a function is named main, it will automatically start when we start the script. Just like predefined variables, there are also predefined functions. The most often used is run that normally contains the trade strategy and is automatically run once for every bar period. If a script has neither a main nor a run function, Zorro assumes that you made a mistake and will give you this error message.

So enter a main function at the end of the script:

function main()
{
   compute_days();
}

This code calls (meaning it executes) our compute_days function. Any function can be called from another function, here from the main function. For calling a function, we write its name followed by a pair of parenthesis and the ubiquitous semicolon. Write the code for your functions first and and the code that calls them later. The computer reads code from the top of the script down to the bottom, line by line. Don't call or refer to something that was not defined before, or else you will get an error message. The printf function is a special function that displays something in the message window.

You will likely encounter compiler errors when you write scripts - even experienced programmers make little mistakes all the time. Sometimes it's a forgotten definition, sometimes a missing semicolon or bracket. Get used to compiler errors and don't be helpless when you see one. The computer usually tells you what's wrong and at which line in the script, so you won't need rocket science for fixing it.

Passing variables to and from functions

A function can also get variables or values from the calling function, use them for its calculation, and give the resulting value back in return. Example:

var compute_days(var Age)
{
   return Age * 356.25;
}

The var age between the parentheses is for passing a value to the function. This is our new script with passing variables to and from functions:

var compute_days(var Age)
{
  return Age * 365.25;
}
 
function main()
{
  var MyAge = 33;
  var NumberOfDays = compute_days(MyAge);
  printf("I am %.f days old!",NumberOfDays);
}

The "I am %.f days old!",NumberOfDays are the two variables that we pass to the printf function. The first variable is a string, used for displaying some text: "I am %.f days old!". The second variable is a var: NumberOfDays. Check out the printf function in the manual.

When a function returns a var, we can just place a call to this function in stead of the var itself - even inside the parentheses of another function. We'll save one variable and one line of script this way::

function main()
{
  var MyAge = 33;
  printf("I am %.f days old!",compute_days(MyAge));
}

 !!  Important tip when you call functions in your code that return something. Do not forget the parentheses - especially when the parameter list is empty! In C, a function name without parentheses means the address of that function in the computer memory. x = add_numbers; and x = add_numbers(); are both valid syntax and normally won't give an error message! But they are something entirely different.

Workshop 3: Branches and Loops

You will use "if" statements when you want your script to make decisions - meaning that it behaves differently depending on some conditions, like user input, a random number, the result of a mathematical operation, a crossing of two indicators, etc. Here's the simplest form of the "if" statement:

if(some condition is true)
    do_something(); // execute this command (a single command!)

or:

if(some condition is true)
{
    // execute one or several commands that are placed inside the curly brackets
} else {
    // execute one or several commands that are placed inside these curly brackets
}

A practical example:

function main()
{
  var MyProfit = slider(3,50,0,200,"Profit",0);
  if(MyProfit > 100)
    printf("Enough!");
  else
    printf("Not enough!");
}

When you click [Test] to run that script, you'll notice that the bottom slider gets the label "Profit". Move it all the way to the right, so that 200 appears in the small window, and click [Test] again. You can see that the result is now different. The slider() function has put its return value - which is the value from the bottom slider - into the MyProfit variable, and thus the if(..) condition became true as the value was bigger than 100. You can see details of the slider function in the manual.

While loops

An example of a "while loop":

while(MyMoney < 1000000)
    trade(MyMoney);

The while statement has the same syntax as the if statement. A "loop" is called so because the program runs in a circle. Here's the form of a while loop:

while (some condition is true)
{
    // execute all commands inside the curly brackets repeatedly until the condition becomes false
}

Whatever those commands do, they must somehow affect the while condition, because when the condition never changes, we have an "infinite loop" that never ends! Run this in Zorro:

function main()
{
  int n = 0; 
  while(n < 10) { 
    n = n + 1;
    printf("%i ",n);
  }
} 

This little program adds 1 to the variable n, prints the variable to the message window, and repeats this until n is 10.

Loops are very useful when something has to be done repeatedly. For instance, when we want to execute the same trade algorithm several times, each time with a different asset. This way we can create a strategy that trades with a portfolio of assets.

And there's more...

We are now at the end of the basic workshops that teach general coding. But we haven't touched yet concepts such as macros, pointers, arrays, structs, classes, or the Windows API. You can learn 'real' programming beyond the scope of trade strategies with a C book or a free online C tutorial, such as Sam's Teach Yourself C in 24 Hours. You can also join the Gamestudio community that uses the lite-C language for programming small or large computer games. Check out how a 'real' C program looks like: open and run the included script Mandelbrot. It has nothing to do with trading and you will probably not understand yet much of the code inside. It is a normal Windows graphics program. Don't worry, trading strategies won't be this complicated - it just shows that programming can be a lot more fun even without earning money with it.

In the next workshops we'll begin developing trading strategies.

Next: Trading


Further reading: ► Variables. Expressions. Functions, return, printf. if, for, slider.

 

Verbose

Verbose

Determines the verbosity of trade, error, and diagnostics messages (see Log). The more verbose, the slower the execution of large backtests.

Range:

0 Few messages. Less important warnings are suppressed. Bars are only printed to the log in [Trade] mode or when trades are open.
1 More messages (default). All bars and all major events, such as entering or closing a trade, are printed to the log and in [Trade] mode also displayed in the window..
2 Even more messages, including the trade parameters at any trade entry and exit. In [Trade] mode the daily profit, drawdown, and CBI is printed once per day. In [Test] mode with TICKS flag the tick preceding a trade is displayed. The prices of the selected asset are printed at any bar in the form Open/High\Low/Close.
3 Even more messages, including skipped trades, possible price outliers, and function parameter errors. Prices are displayed with more digits. In [Trade] mode all open trades are listed once per day.
7 Extensive diagnostics messages, including all memory allocations that exceed 2 MB, all BrokerTrade calls, and API execution times.
+DIAG (+8) Additional flag to activate a 'black box recorder' for diagnostics. A ...diag.txt file is created in the Log folder. It contains a list with the 4000 last events and can be used to determine the reason of a crash or other problem that leads to the termination of the script. For details see troubleshooting. Black box recording strongly affects the program speed, so do not use this flag unnecessarily.
+ALERT (+16) Additional flag to display critical messages, such as suspicious asset parameters, possibly orphaned trades, broker API errors, or print(TO_ALERT,..) calls, in a separate alert box. DIAG recording is stopped at the first critical event, so the details leading to the situation can be evaluated from the black box log (Log\...diag.txt). Recording continues when the alert box is closed.
+SILENT (+32) Suppress all printf messages. Messages by print(TO_WINDOW,...) are still printed.
+LOGMSG (+512) Additional flag to print the log also to the message window.

Type:

int

Remarks:

Example:

function run()
{
  Verbose = 7+DIAG; // extensive messages plus black box recorder 
  ...
}

See also:

LOGFILE, LogNumber, -diag, log, troubleshooting

 

► latest version online version

version(): var

Returns Zorro's version number with two digits behind the decimal.

require(var Version): int

Returns nonzero when Zorro's version number is equal or higher than Version. Otherwise prints an error message, terminates the session, and returns 0. Pass a negative Version number when Zorro S is also required.

Examples:

if(version() < 2.14)
  return quit("Zorro 2.14 or above needed for this script!");

if(!require(-2.14)) return; // need Zorro S 2.14 or above

See also:

printf, run, wait, quit, SPONSORED

► latest version online

watch

watch (string text, ...)

Prints the given text and up to 8 following bool, int, var, float, or string variables to the message window or the log, or add text to exception or crash messages (lite-C only). Can enter single step debugging mode. Allows to quickly debug into functions and watch variable behavior.

Parameters:

text

Text string to be displayed, followed by the variables. If the string begins with an exclamation mark "!...", script execution stops at that line and Zorro changes to debugging mode. This way the behavior of variables inside a loop or function can be debugged step by step. If the string begins with a "#" character, the text is not displayed in the message window, but printed in all modes to the log or - in diagnostics mode - to the diag.txt file (see Verbose). If the string begins with a "?", the text is displayed together with the next exception or crash message. If the string contains a decimal point '.', float, double, or var variables are printed in higher precision.

... Up to 8 variables, function calls, or expressions to be watched. Supported are bool, int, var, double, float, or string. Floating point types are displayed with 5 decimals.

Remarks:

Examples:

int i;
for(i=0; i<10; i++)
  watch("!i",i,"twice",2*i,"square",i*i);

See also:

Verbose, debugging, printf, troubleshooting

► latest version online

while, do

while (comparison) { instructions... }

do { instructions... } while (comparison) ;

Repeats all instructions between the winged brackets as long as the comparison between the round brackets is true or evaluates to non-zero. This repetition of instructions is called a loop. The while statement evaluates the comparison at the begin, the do..while statement at the end of each repetition.

Remarks:

Example:

x = 0;
while(x < 100) // repeat while x is lower than 100
  x += 1; 

See also:

if, for, break, continue, comparisons, loop

► latest version online

window

window(string title) : HWND

Returns a handle to the active window when its title bar contains the title string. Can be used to wait until a certain window or dialog becomes active.

Parameters:

title - part of the window title (case sensitive), or 0 for returning the handle to the window that recently became active.

Returns:

HWND of the found active window. Otherwise 0.

Remarks:

Example:

See keys.

See also:

keys, order, exec, mouse, HWnd

► latest version online Trade statistics

Trade statistics

The following system variables can be used to obtain trade statistics separately per asset, algorithm, and long/short trade direction. They can be evaluated in real time while trading, or at the end of a simulation cycle for calculating statistics in [Test] mode. All parameters are read/only. Most come in three flavors:

...Long: Results of all long trades with the current asset and algorithm. Including phantom trades, but not including pool trades.
...Short: Results of all short trades with the current asset and algorithm, including phantom trades, but no pool trades.
...Total: Results of all trades with all assets and algorithms, not including phantom trades.

In [Test] mode the ...Long and ...Short results are from the current sample cycle only, which allows to produce statistics distributions of sample cycles. If the ALLCYCLES flag is set, the ...Long and ...Short results are summed up from the all sample cycles. The ...Total results are always summed up.

A set of overall strategy statistics is available after the end of a simulation, and can be evaluated in the objective or the evaluate function. For calculating various statistics of the last N trades, use the results function.
 

WinLong

WinShort

WinTotal

Sum of profits of all trades won so far. When using oversampling or phantom trades, WinLong or WinShort can be higher than WinTotal

PipsTotal

Profit of all won trades minus loss of all lost trades, in volume neutral PIP units. For converting a component profit to a profit in average PIP units, multiply it with PipsTotal/(WinTotal-LossTotal).

LossLong

LossShort

LossTotal

Sum of losses by all trades lost so far. The accumulated balance, i.e. the return of all closed trades is WinTotal - LossTotal. WinTotal or LossTotal can be modified by script for simulating additional wins or losses in the backtest. The current profit factor, clipped at 10, is ifelse(LossTotal > 0,WinTotal/LossTotal,10).

LossGlobal

Sum of losses by all trades of all Zorro instances that have set the ScholzBrake and are trading real accounts on the same PC. This variable is only available in [Trade] mode. It is increased by any loss, and updated to the other Zorro instances once per bar and on any trade.

WinValLong

WinValShort

WinValTotal

Open profit of all currently winning trades.

LossValLong

LossValShort

LossValTotal

Open loss amount of all currently losing trades. The accumulated equity, i.e. the current profit of all open and closed trades is WinTotal - LossTotal + WinValTotal - LossValTotal.

PipsValTotal

Open profit of all open trades in volume neutral PIP units. For converting a component open profit to a profit in average PIP units, multiply it with PipsValTotal/(WinValTotal-LossValTotal)

ProfitClosed

Realized component profit so far; WinLong-LossLong+WinShort-LossShort.

ProfitOpen

Unrealized component profit so far; WinValLong-LossValLong+WinValShort-LossValShort.

ProfitTotal

Realized and unrealized total profit so far; WinTotal-LossTotal+WinValTotal-LossValTotal.

BalanceLong

BalanceShort

Sum of returns of all closed trades of the current component; WinLong-LossLong or WinShort-LossShort.

EquityLong

EquityShort

Sum of returns of all closed, plus value of all open trades of the current component; BalanceLong+WinValLong-LossValLong or BalanceShort+WinValShort-LossValShort.

WinMaxLong

WinMaxShort

WinMaxTotal

Maximum profit of a trade so far.

LossMaxLong

LossMaxShort

LossMaxTotal

Maximum loss of a trade so far.

NumWinLong

NumWinShort

NumWinTotal

Number of profitable trades so far. The average return per winning trade is WinTotal/NumWinTotal.

NumLossLong

NumLossShort

NumLossTotal

Number of lost trades so far. The average return per trade is (WinTotal-LossTotal)/(NumWinTotal+NumLossTotal). The number of closed trades is NumWinTotal+NumLossTotal.

LossStreakLong

LossStreakShort

LossStreakTotal

Current number of losses in a row, or 0 if the last trade was a winner. Can be reset by script.

WinStreakLong

WinStreakShort

WinStreakTotal

Current number of wins in a row, or 0 if the last trade was lost. Can be reset by script.

LossStreakValLong

LossStreakValShort

LossStreakValTotal

Accumulated loss of the current loss streak, or 0 if the last trade was a winner.

WinStreakValLong

WinStreakValShort

WinStreakValTotal

Accumulated profit of the current win streak, or 0 if the last trade was lost.

NumWinningLong

NumWinningShort

Number of currently open winning trades with the current asset and algorithm, including phantom trades.

NumLosingLong

NumLosingShort

Number of currently open losing trades with the current asset and algorithm, including phantom trades.

NumOpenLong

NumOpenShort

Number of currently open trades with the current asset and algorithm, including phantom trades.

NumLongTotal

NumShortTotal

NumOpenTotal

NumOpenPhantom

Numbers of currently open trades with all assets and algorithms.

NumPendingLong

NumPendingShort

NumPendingTotal

Number of currently pending trades, i.e. trades that have just been entered, or that have not yet reached their Entry Stop or Limit within their EntryTime period. NumPendingTotal includes pending phantom trades in Virtual Hedging mode, as they also trigger real trades.

NumRejected

Number of rejected open or close orders in live trading, due to lack or market liquidity, broker connection failure, market closures, holidays, or other reasons.

LotsPool

LotsVirtual

LotsPhantom

Open position of the current asset, positive when long and negative when short. The variables hold the difference of long and short open lots of real trades (LotsPool), virtual trades in virtual hedging mode (LotsVirtual), and phantom trades (LotsPhantom). Only for the underlying, not for positions of options and combos. Open account positions can be read from the broker API with the GET_POSITION command, 

 

Type:

int for numbers that count something, otherwise var.

Remarks:

Example:

// suspend trading after 4 losses in a row
if(LossStreakShort >= 4 || LossStreakLong >= 4)
  setf(TradeMode,TR_PHANTOM); // phantom trades
else   resf(TradeMode,TR_PHANTOM); // normal trading

See also:

Trade parameters, Balance, Lots, for(trades), strategy statistics, performance report, results

 

► latest version online