Download MetaTrader 5

Common Errors in MQL4 Programs and How to Avoid Them

25 March 2014, 13:58
MetaQuotes Software Corp.
4
42 433

Introduction

Some older programs can return errors in the new version of the MQL4 of compiler.

To avoid critical completion of programs, the previous version compiler handled many errors in the runtime environment. For example, division by zero or array out of range are critical errors and usually lead to application crash. Such errors occur only in some states for certain values ​​of variables. Read this article to know how to handle such cases.

The new compiler can detect actual or potential sources of errors and improve code quality.

In this article, we discuss possible errors that can be detected during compilation of old programs and ways to fix them.

  1. Compilation Errors
  2. Runtime Errors
  3. Compiler Warnings

1 Compilation Errors

If a program code contains errors, it cannot be compiled.

To fully control all errors, it is recommended to use strict compilation mode, which is set by the following directive:

#property strict

This mode greatly simplifies troubleshooting.


1.1. Identifier coincides with a reserved word

If the name of a variable or function coincides with one of the reserved words

int char[];  // incorrect
int char1[]; // correct
int char()   // incorrect
{
 return(0);
}

the compiler returns an error messages:

Figure 1. Errors "unexpected token" and "name expected"

Figure 1. Errors "unexpected token" and "name expected"

To fix this error, you need to use the correct name of the variable or function.


1.2. Special characters in the names of variables and functions

If names of variables or functions contain special characters ($, @, point):

int $var1; // incorrect
int @var2; // incorrect 
int var.3; // incorrect
void f@()  // incorrect
{
 return;
}

the compiler returns an error messages:

Figure 2. Errors "unknown symbol" and "semicolon expected"

Figure 2. Errors "unknown symbol" and "semicolon expected"

To fix this error, you need to use correct function or variable names.


1.3. Errors using the switch operator

In the old version of the compiler you could use any values ​​in expressions and constants of the switch operator:

void start()
  {
   double n=3.14;
   switch(n)
     {
      case 3.14: Print("Pi");break;
      case 2.7: Print("E");break;
     }
  }

In the new compiler, constants and expressions of the switch operator must be integers, so errors occur when you try to use such constructions:

Figure 3. Errors "illegal switch expression type" and "constant expression is not integral"

Figure 3. Errors "illegal switch expression type" and "constant expression is not integral"

In such cases, you can use explicit comparison of numerical values, for example:

void start()
  {
   double n=3.14;
   if(n==3.14) Print("Pi");
   else
      if(n==2.7) Print("E");
  }

1.4. Return values ​​of functions

All functions except void should return the declared type value. For example:

int function()
{
}

In strict compilation mode an error occurs:


Figure 4. Error "not all control paths return a value"

Figure 4. Error "not all control paths return a value"

In default compilation mode, the compiler returns a warning:

Figure 5. Warning "not all control paths return a value"

Figure 5. Warning "not all control paths return a value"

If the return value of the function does not match the declared one:

int init()                         
  {
   return;                          
  }

an error is detected during strict compilation:

Figure 6. Error "function must return a value"

Figure 6. Error "function must return a value"

In default compilation mode, the compiler returns a warning:

Figure 7. Warning 'return - function must return a value"

Figure 7. Warning 'return - function must return a value"

To fix such errors, add the return operator with the return value of the corresponding type to the function code.



1.5. Arrays in function arguments

In function arguments, arrays are now passed by reference only.

double ArrayAverage(double a[])
{
 return(0);
}
In strict compilation mode, this code will cause an error:

Figure 8. Compiler error "arrays passed by reference only"

Figure 8. Compiler error "arrays passed by reference only"

In default compilation mode, the compiler returns a warning:

Figure 9. Compiler warning "arrays passed by reference only"

Figure 9. Compiler warning "arrays passed by reference only"

To fix this error, you must explicitly specify that the array is passed by reference by adding the prefix & before the name of the array:

double ArrayAverage(double &a[])
{
 return(0);
}

It should be noted that now constant arrays (Time[], Open[], High[], Low[], Close[], Volume[]) cannot be passed by reference. For example, the following call:

ArrayAverage(Open);

regardless of the compilation mode leads to an error:

Figure 10. Error 'Open' - constant variable cannot be passed as reference

Figure 10. Error 'Open' - constant variable cannot be passed as reference

To avoid these errors, copy the required data from the constant array:

   //--- an array that stores open price values
   double OpenPrices[];
   //--- copy the values of open prices to the OpenPrices[] array
   ArrayCopy(OpenPrices,Open,0,0,WHOLE_ARRAY);
   //--- function call
   ArrayAverage(OpenPrices);



2. Runtime Errors

Errors that occur during the execution of the program code are called runtime errors. Such errors usually depend on the state of a program and are associated with incorrect values ​​of the variables.

For example, if a variable is used as an index of array elements, its negative values ​​will inevitably lead to the Array out of Range error.


2.1. Array out of Range

This error often occurs in indicators when accessing indicator buffers. The IndicatorCounted() function returns the number of bars unchanged since the last indicator call. Indicator values on previously calculated bars do not need recalculation, so for faster calculations you only need to process the last few bars.

Most indicators that use this method of calculation optimization look like this:

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
   //--- some calculations require no less than N bars (e.g. 100)      
   if (Bars<100) // if less bars are available on a chart (for example on MN timeframe)    
     return(-1); // stop calculation and exit

   //--- the number of bars that have not changed since the last indicator call
   int counted_bars=IndicatorCounted();
   //--- exit if an error has occurred
   if(counted_bars<0) return(-1);
      
   //--- position of the bar from which calculation in the loop starts
   int limit=Bars-counted_bars;

   //--- if counted_bars=0, reduce the starting position in the loop by 1,   
   if(counted_bars==0) 
     {
      limit--;  // to avoid the array out of range problem when counted_bars==0
      //--- we use a shift of 10 bars back in history, so add this shift during the first calculation
      limit-=10;
     }
   else //--- the indicator has been already calculated, counted_bars>0
     {     
      //--- for repeated calls increase limit by 1 to update the indicator values for the last bar
      limit++;
     } 
   //--- the main calculation loop
   for (int i=limit; i>0; i--)
   {
     Buff1[i]=0.5*(Open[i+5]+Close[i+10]) // values of bars 5 and 10 bars deeper to history are used
   }
}

Often the case of counted_bars==0 is handled incorrectly (the initial limit position should be reduced by the value equal to 1 + maximum index relative to the loop variable).

Also, remember that at the time of the start() function execution we can access elements of arrays of indicator buffers from 0 to Bars ()-1. If you need to work with the arrays that are not indicator buffers, increase their size using the ArrayResize() function in accordance with the current size of indicator buffers. The maximum index of the element to address can also be obtained by calling ArraySize() with one of the indicator buffers used as an argument.


2.2. Zero Divide

The Zero Divide error occurs when a divisor in a division operation is equal to zero:

void OnStart()
  {
//---
   int a=0, b=0,c;
   c=a/b;
   Print("c=",c);
  }

When you run this script, an error message appears in the Experts tab and the program shuts down:

Figure 11. Error message "zero divide"

Figure 11. Error message "zero divide"

Usually this error occurs when the value of the divisor is determined by the values ​​of any external data. For example, if trade parameters are analyzed, the value of the used margin is equal to 0 if there are no open orders. Another example: if the analyzed data are read from a file, we can not guarantee correct operation if the file is not available. So you should take into account such cases and process them correctly.

The easiest way is to check the divisor before the division operation and report an incorrect parameter value:

void OnStart()
  {
//---
   int a=0, b=0,c;
   if(b!=0) {c=a/b; Print(c);}
   else {Print("Error: b=0"); return; };
  }

This does not cause a critical error, but a message about an incorrect parameter value appears and program shuts down:

Figure 12. Incorrect divider message

Figure 12. Incorrect divider message


2.3. Use of 0 instead of NULL for the current character

In the old version of the compiler 0 (zero) could be used as an argument in functions that require specification of a financial instrument.

For example, the value of the Moving Average technical indicator for the current symbol could be requested as follows:

AlligatorJawsBuffer[i]=iMA(0,0,13,8,MODE_SMMA,PRICE_MEDIAN,i);    // incorrect

In the new compiler you should explicitly specify NULL to specify the current symbol:

AlligatorJawsBuffer[i]=iMA(NULL,0,13,8,MODE_SMMA,PRICE_MEDIAN,i); // correct

In addition, the current symbol and chart period can be specified using the Symbol() and Period() functions.

AlligatorJawsBuffer[i]=iMA(Symbol(),Period(),13,8,MODE_SMMA,PRICE_MEDIAN,i); // correct


2.4. Unicode strings and their use in a DLL

Strings are now represented as a sequence of Unicode characters.

Remember this and use appropriate Windows functions. For example, when using the wininet.dll library instead InternetOpenA() and InternetOpenUrlA(), you should call InternetOpenW() and InternetOpenUrlW().

The internal structure of strings has changed in MQL4 (now it takes 12 bytes), and the MqlString structure should be used when passing strings to DLL:

#pragma pack(push,1)
struct MqlString
  {
   int      size;       // 32 bit integer, contains the size of the buffer allocated for the string
   LPWSTR   buffer;     // 32 bit address of the buffer that contains the string
   int      reserved;   // 32 bit integer, reserved, do not use
  };
#pragma pack(pop,1)


2.5. File sharing

In the new MQL4, FILE_SHARE_WRITE and FILE_SHARE_READ flags should explicitly be specified for shared use when opening files.

If the flags are absent, the file is opened in exclusive mode and cannot be opened by anyone else till it is closed by the user who opened it.

For example, when working with offline charts share flags should be explicitly specified:

   // 1-st change - add share flags
   ExtHandle=FileOpenHistory(c_symbol+i_period+".hst",FILE_BIN|FILE_WRITE|FILE_SHARE_WRITE|FILE_SHARE_READ);

For more details please read Offline Charts in the New MQL4.


2.6. Datetime Conversion

Conversion of datetime to a string now depends on the compilation mode:

  datetime date=D'2014.03.05 15:46:58';
  string str="mydate="+date;
//--- str="mydate=1394034418" - old compiler, no directive #property strict in the new compiler
//--- str="mydate=2014.03.05 15:46:58" - new compiler with the directive #property strict

For example, trying to work with files whose name contains a colon causes an error.


3. Compiler Warnings

Compiler Warnings are informative and are not error messages, but they indicate possible error sources.

A clear code should not contain warnings.


3.1. Names of global and local variables coincide

If variables on the global and local levels have similar names:

int i; // a global variable
void OnStart()
  {
//---
   int i=0,j=0; // local variables
   for (i=0; i<5; i++) {j+=i;}
   PrintFormat("i=%d, j=%d",i,j);
  }

the compiler displays a warning showing the line number on which the global variable is declared:

Figure 13. Warning "declaration of '%' hides global declaration at line %"

Figure 13. Warning "declaration of '%' hides global declaration at line %"

To fix such warnings correct names of global variables.


3.2. Mismatch of Types

The new compiler has a new typecasting operation.

#property strict
void OnStart()
  {
   double a=7;
   float b=a;
   int c=b;
   string str=c;
   Print(c);
  }

In strict compilation mode the compiler shows warnings if types mismatch:

Figure 14. Warnings "possible loss of data due to type conversion" and "implicit conversion from 'number' to 'string'

Figure 14. Warnings "possible loss of data due to type conversion" and "implicit conversion from 'number' to 'string'

In this example, the compiler warns about the possible loss of accuracy for different data types assigned and implicit conversion from int to string.

To fix the warning use explicit typecasting:

#property strict
void OnStart()
  {
   double a=7;
   float b=(float)a;
   int c=(int)b;
   string str=(string)c;
   Print(c);
  }

3.3. Unused Variables

The presence of variables that are not used in the program code (superfluous entities) is not a good habit.

void OnStart()
  {
   int i,j=10,k,l,m,n2=1;
   for(i=0; i<5; i++) {j+=i;}
  }

Reports of such variables are displayed regardless of the compilation mode:

Figure 15. Warning "variable '%' not used'

Figure 15. Warning "variable '%' not used'

To fix it, remove unused variables from your code.


Conclusions

The article describes common problems that may occur during compilation of old programs that contain errors.

In all cases it is recommended to use strict compilation mode for program debugging.


Translated from Russian by MetaQuotes Software Corp.
Original article: https://www.mql5.com/ru/articles/1391

Last comments | Go to discussion (4)
MQL4 Comments
MQL4 Comments | 27 Mar 2014 at 12:05

these enum data identifiers are NOT recognized in mql4, even though they are documented in the MQL4 Reference

ENUM_ACCOUNT_STOPOUT_MODE, ENUM_ACCOUNT_TRADE_MODE, ENUM_APPLIED_VOLUME, ENUM_DRAW_TYPE, ENUM_SYMBOL_TRADE_EXECUTION, ENUM_SYMBOL_TRADE_MODE, ENUM_TRADE_RETURN_CODES

for example if I compile a script with this line

Print("MQL_PROGRAM_TYPE = "+MQLInfoInteger(MQL_PROGRAM_TYPE)+" = "+EnumToString((ENUM_PROGRAM_TYPE)MQLInfoInteger(MQL_PROGRAM_TYPE)));

it's all correct and Script prints: MQL_PROGRAM_TYPE = 1 = PROGRAM_SCRIPT

but, if i try to compile this other line

Print("SYMBOL_TRADE_MODE = "+SymbolInfoInteger(Symbol(), SYMBOL_TRADE_MODE)+" = "+EnumToString((ENUM_SYMBOL_TRADE_MODE)SymbolInfoInteger(Symbol(), SYMBOL_TRADE_MODE)));

there are errors! 'NUM_SYMBOL_TRADE_MODE' - undeclared identifier

and so on with 'ENUM_SYMBOL_TRADE_EXECUTION' - undeclared identifier, 'ENUM_ACCOUNT_TRADE_MODE' - undeclared identifier, ..., ...




madmaxbd
madmaxbd | 15 Jun 2015 at 02:02

Help me this not working 

 <code removed>

Alain Verleyen
Alain Verleyen | 15 Jun 2015 at 12:31
madmaxbd:

Help me this not working 

 <code removed>

Please attach your code as a file, it is too big.
mranda
mranda | 14 Jun 2016 at 10:53

Hi, with the new version I am unable to compile my code, receiving the message "expression of 'void' type is illegal" with reference to line 1 column 1 (see attached pictures). The line 1 and column 1 as shown on other attached pict. Any idea what I have to look for?

Thanks for help 

Upgrade to MetaTrader 4 Build 600 and Higher Upgrade to MetaTrader 4 Build 600 and Higher

The new version of the MetaTrader 4 terminal features the updated structure of user data storage. In earlier versions all programs, templates, profiles etc. were stored directly in terminal installation folder. Now all necessary data required for a particular user are stored in a separate directory called data folder. Read the article to find answers to frequently asked questions.

Data Structure in MetaTrader 4 Build 600 and Higher Data Structure in MetaTrader 4 Build 600 and Higher

MetaTarder 4 build 600 features the new structure and location of the client terminal files. Now, MQL4 applications are placed in separate directories according to the program type (Expert Advisors, indicators or scripts). In most cases, the terminal data is now stored in a special data folder separated from the terminal installation location. In this article, we will describe in details how data is transferred, as well as the reasons for introducing the new storage system.

Why Is It Important to Update MetaTrader 4 to the Latest Build by August 1? Why Is It Important to Update MetaTrader 4 to the Latest Build by August 1?

From August 1, 2014, MetaTrader 4 desktop terminals older than build 600 will no longer be supported. However, many traders still work with outdated versions and are unaware of the updated platform's features. We have put a lot of effort into development and would like to move on with traders and abandon the older builds. In this article, we will describe the advantages of the new MetaTrader 4.

How we developed the MetaTrader Signals service and Social Trading How we developed the MetaTrader Signals service and Social Trading

We continue to enhance the Signals service, improve the mechanisms, add new functions and fix flaws. The MetaTrader Signals Service of 2012 and the current MetaTrader Signals Service are like two completely different services. Currently, we are implementing A Virtual Hosting Cloud service which consists of a network of servers to support specific versions of the MetaTrader client terminal. Traders will need to complete only 5 steps in order to rent the virtual copy of their terminal with minimal network latency to their broker's trade server directly from the MetaTrader client terminal.