Your undocumented features when writing EAs in MQL4
The magazine publishes many reviews advisor programs and their performance: some are more successful, while others are immediately regarded as unprofitable, which is also a result in itself. But the effectiveness of an Expert Advisor, when you look at it closely, is a complex outcome:
1) applied trading strategy (algorithm for generating open-close signals);
2) the capital management technique used (lot size management algorithm);
3) The accuracy of the program written in MQL to the descriptions of the first two points (presence of logical errors) and the absence of unintended side effects in it.
Much has been written on the first two positions, but now I would like to focus on some subtle issues of using the MQL languagewhich have not found any display or mention in the language descriptions (e.g., in the documentation set out here: _http://docs.mql4.com/ru/).
This is important for two reasons:
? first, knowing the behavior of the language constructs, not reflected in the documentation, prevents the occurrence of little understood side effects in the program;
? Secondly, it allows you to write more efficient code, less prone to hidden errors. You don't need any special qualifications to use these features in your EAs, you just need to name them and know that they can be used. And if in the following text any formulations seem a little complicated to you... feel free to skip them, they are made only for completeness.
Passing parameters to a function by reference (by address)
This thing is well known to programmers in any of the universal programming languages, such as C, but even more so in C++. Let's write the simplest Expert Advisor (file t2.mq4 - here and further the names of the test examples in the attached archive), and put it on the chart of any currency pair:
int start() {
static int nTick = 0;
Inc( nTick );
Alert( «новый тик № » + nTick );
}
//—————————————
void Inc( int n ) { n++; }
The result of execution is a continuous repetition of the same result: "new tick #0"... The value n passed to the function changes there, but this has no effect on the external environment of the function (which called the start() function) - into the function is passed copy of the nTick variable, so usually function call works in MQL4. Let's add one character (&) to the Inc() function description :
void Inc( int& n ) { n++; }
The execution of the advisor will change radically:
new tic #1
new tic #2
new tic #3
…
Badge &. pointed out that it is not a copy that should be passed to the function, but original the nTick variable, and all changes made to that variable within the function will be reflected in the value of that variable outside functions. This behavior is called side effect of the function.
Conclusions: What are the consequences of this little curiosity? Very far reaching. Functions in MQL are very limited, they can return only one value of the simplest type: int, string, datetime, ... Due to a side effect, a function with many parameters can make changes to all of them, actually producing a return (after its execution) of more than one result (or it can be regarded as a return of structured data). For example, a function like this:
double Func( int& n, string& s, datetime& t ) { ... }
Passing an array by reference as a parameter to a function
The next example (file t3.mq4) is close to the previous one, but here the side effects and associated features are significantly larger:
extern int N = 7;
int A[];
//——————————————————————
int init() {
ArrayResize( A, N );
Alert( «размер массива » + ArraySize( A ) );
for( int i = 0; i < N; i++ ) A[ i ] = i + 1;
}
//——————————————————————
int start() {
static nBars = 0; // not to particle, now on bars, not ticks
if( nBars != Bars ) { // so as not to wait, run on M1
nBars = Bars;
IncArray( A );
ShowArray( A );
}
}
//——————————————————————
void IncArray( int& Array[] ) {
int n = ArraySize( Array );
for( int i = 0; i < n; i++ ) Array[ i ]++;
}
void ShowArray( int Array[] ) {
int n = ArraySize( Array );
string Msg = «»;
for( int i = 0; i < n; i++ ) {
Msg = Msg + Array[ i ];
if( i < n — 1 ) Msg = Msg + » , «;
}
Alert( Msg );
}
Transfer the advisor to the chart (preferably M1 time frame) and you will have the pleasure to observe the output of such messages:
2, 3, 4, 5, 6, 7, 8
3, 4, 5, 6, 7, 8, 9
4, 5, 6, 7, 8, 9, 10
…
Conclusions: Now each element of the passed array is modified separately.
Note: I'm curious, how will the program's behavior change if the array is passed into IncArray() not by reference? Will a copy of the array be created for working with it inside the function (like in the previous example for the single value)? We can achieve this by rewriting the header of this function like this (that's all):
void IncArray( int Array[] ) {
…
The result is unexpected even for C or C++ programmers! At the compilation stage, before reaching execution, we will get an error message like this:
‘Array’ — array item cannot be assigned C:\Program Files\MetaTrader — Alpari\experts\t3.mq4 (22, 33)
This means that the L-value control (assignment availability) for array elements is still done by the compiler! And this is not true for single (scalar) parameters.
Changing the dimensionality of an array passed by reference
The following trick can amaze even a seasoned programmer of classical programming languages. The possibility to change the size of the array at any time is in itself a significant feature of MQL4:
int A[ 5 ];
for( i = 0; i < ArraySize( A ); i++ ) A[ i ] = 333;
ShowArray( A );
ArrayResize( A, 7 );
for( i = 0; i < ArraySize( A ); i++ ) A[ i ] = 333;
ShowArray( A );
After doing this we get, oddly enough: 333, 333, 333, 333, 333, 0, 0
It turns out that arrays are not repartitioned completely, but "completed" to the required dimension - all previously assigned values of elements are preserved! But without such a trick, it would be probably impossible for MQL4 developers to implement time series representation in MT4.
And now for the promised trick itself - changing the dimensionality of the array inside the function.
To do this, just rewrite one function from the previous example:
void IncArray( int& Array[] ) {
int n = ArraySize( Array );
ArrayResize( A, n + 1 );
for( int i = 0; i <= n; i++ ) Array[ i ] = i + 1;
}
Execution result (copied from the MetaTrader 4 terminal log (https://mr-trader.com/forex-terminals/torgovyj-terminal-metatrader-4-skachat), which unfolds the results from bottom to top):
…
2011.02.10 19:54:00 @t3 EURUSD,M1: alert: 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14
2011.02.10 19:53:02 @t3 EURUSD,M1: alert: 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13
2011.02.10 19:52:08 @t3 EURUSD,M1: alert: 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12
…
Note: Let's go back to the question we discussed above: how does an array in MQL4 get passed to a function in the first place (by copy or by original)? To do this, let's rewrite it one more time:
void IncArray( int Array[] ) {
int n = ArraySize( Array );
ArrayResize( A, n + 1 );
}
And we get a rather unexpected result:
2011.03.07 20:17:02 t4 EURUSD,M1: Alert: 1 , 2 , 3 , 4 , 5 , 6 , 7 , 0 , 0 , 0
2011.03.07 20:16:01 t4 EURUSD,M1: Alert: 1 , 2 , 3 , 4 , 5 , 6 , 7 , 0 , 0
2011.03.07 20:15:44 t4 EURUSD,M1: Alert: 1 , 2 , 3 , 4 , 5 , 6 , 7 , 0
As a result, you can argue that the array is always passed to the function by reference, but unless you explicitly specify it (&), Compiler will keep track and prevent attempts to change array elements.
Не понятное. Если Вы программист, так сказать — заядлый?! Зачем нарушаете и учите этому маневру других? Где-то там в коде есть функция, где внутри создана другая функция — это(если я не ошибаюсь), ошибка и очень плохая. Вот Ваш код нарушения прописных истин:
void IncArray( int& Array[] ) {
int n = ArraySize( Array );
for( int i = 0; i
I'm sorry! I didn't notice the parenthesis right away.
I'm learning to program. And so, it helped me a lot to understand how to write code.
Сейчас как-раз пытаюсь научиться формировать массивы и заполнять их значения с помощью циклов, правда цикл while у меня на компе почему-то(написанный лично мной) — виснет, и я так думаю, значит я что-то делаю не так?! И руки пока не доходят до разбора этого цикла по порядку его понимания. Это все еще впереди?! И вот теперь статические переменные немного не понятны?! То есть, если она инициализирована, то по сути ее значение должно быть константой и Ask, и Bid, как константы разнятся с моим пониманием — констант?! Тут нужен аргумент, который поставит все на свои места. А пока — это все находится в хаотичном состоянии. Так бывает. Всему свое время и место.