How to Make the Detection and Recovery of Errors in an Expert Advisor Code Easier
Introduction
The development of trading EAs in the MQL4 language is not an easy matter from the points of view of several aspects:
- first, algorithmization of any more are less difficult trading system is already a problem itself, because one needs to take into account many details, from the peculiarities of an algorithmized EA and till the specific MetaTrader 4 environment;
- second, even the presence of further EA algorithm does not eliminate difficulties that appear when transferring the developed algorithm into the programming language MQL4.
We should do justice to the trading platform MetaTrader 4 - the existence of the
programming language for writing trading Expert Advisors is already a large step
forward as compared to earlier available alternatives. The compiler is of great
help in writing correct EAs. Immediately after clicking 'Compile' it will report
about all syntax errors in your code. If we dealt with an interpreted language,
such errors would be found only during EA's operation, and this would have increased
the difficulty and period of development. However, except syntax errors any EA
can contain logical errors. Now we will deal with such errors.
Errors in Using Built-In Functions
While no trading EA can do without using built-in functions, let us try to ease
our life when analyzing errors, returned by such functions. First let us view the
operation results of functions, directly connected with trading operations, because
ignoring errors in such functions may lead to critical for an EA effects. However
further arguments refer also to other built-in functions.
Unfortunately using MQL4 options one cannot write a generalized library for processing
all possible error situations. In each separate case we need to process errors
separately. Still there is good news - we do not need to process all errors, many
of them are simply eliminated at the stage of EA development. But for doing this
they should be detected in time. As an example let us view 2 typical EA errors
in MQL4:
1) Error 130 - ERR_INVALID_STOPS
2) Error 146 - ERR_TRADE_CONTEXT_BUSY
One of the cases, when the first error appears, is the attempt of an EA to place
a pending order too close to market. Its existence may seriously spoil the EA characteristics
in some cases. For example, suppose the EA, having opened a profitable position,
cuts profit every 150 points. If at the next attempt error 130 occurs and the price
returns to the previous stop level, this may deprive you of your legal profit.
Despite such possibility, this error can be eliminated from the very beginning,
inserting into the EA code the function of taking into account the minimally acceptable
distance between a price and stops.
The second error 'trade context is busy' cannot be fully eliminated. When several
EAs operate in one terminal we can face the situation, when one of the Expert Advisors
is trying to open a position, when the second one is doing the same. Consequently,
this error must be always processed.
So we always need to know, if any of built-in functions returns error during EA operation. It can be achieved using the following simple additional function:
#include <stderror.mqh> #include <stdlib.mqh> void logError(string functionName, string msg, int errorCode = -1) { Print("ERROR: in " + functionName + "()"); Print("ERROR: " + msg ); int err = GetLastError(); if(errorCode != -1) err = errorCode; if(err != ERR_NO_ERROR) { Print("ERROR: code=" + err + " - " + ErrorDescription( err )); } }
In a simplest case it should be used the following way:
void openLongTrade() { int ticket = OrderSend(Symbol(), OP_BUY, 1.0, Ask + 5, 5, 0, 0); if(ticket == -1) logError("openLongTrade", "could not open order"); }
The first parameter of the function logError() shows the function name, in which
an error was detected, in our case it is the function openLongTrade(). If our Expert
Advisor calls the function OrderSend() in several places, we will have the opportunity
to determine exactly in which case the error occurred. The second parameter delivers
the error description, it enables to understand in which exact place inside the
function openLongTrade the error was detected. This can be a short description,
or a detailed one, including values of all parameters, passed into the built-in
function.
I prefer the last variant, because if an error appears, one can immediately get all the information, necessary for analysis. Suppose, before calling OrderSend() the current price greatly moved aside from the last known price. As a result an error will occur and in the EA log file the following lines will appear:
ERROR: in openLongTrade()
ERROR: could not open order
ERROR: code=138 - requote
I.e. it will be clear:
- in what function the error occurred;
- to what it refers (in our case to the attempt to open a position);
- exactly what error appeared (error code and its description).
Now let us view the third, optional, parameter of the function logError(). It is needed, when we want to process a certain error type, and other errors will be included into a log file, as earlier:
void updateStopLoss(double newStopLoss) { bool modified = OrderModify(OrderTicket(), OrderOpenPrice(), newStopLoss, OrderTakeProfit(), OrderExpiration()); if(!modified) { int errorCode = GetLastError(); if(errorCode != ERR_NO_RESULT ) logError("updateStopLoss", "failed to modify order", errorCode); } }
Here in the function updateStopLoss() the built-in function OrderModify() is called. This function slightly differs from OrderSend() in terms of errors processing. If no parameter of a changed order differs from its current parameters, the function will return the error ERR_NO_RESULT. If such a situation is acceptable in our EA, we should ignore this error. For this purpose we analyze the value, returned by GetLastError(). If an error with the code ERR_NO_RESULT occurred, we do not write anything into the log file.
However, if another error occurred, we should report about it as we did it earlier. Exactly for this purpose we save the result of the function GetLastError() into a intermediate variable and pass it using the third parameter into the function logError(). Actually the built-in function GetLastError() automatically zeros the last error code after it is called. If we do not pass the error code into logError(), the EA log file would contain an error with the code 0 and description "no error".
The similar actions should be done when processing other errors, for example, requotes.
The main idea is to process only errors, that need to be processed and to pass
other ones into the function logError(). In this case we will know, if an unexpected
error occurred during EA operation. After analyzing the log file, we will know
whether this error needs a separate processing or it can be eliminated improving
the EA code. Such approach makes our life easier and shortens time, spent for bug
fixing.
Diagnosing Logic Errors
Logic errors in An Expert Advisor code may be trouble enough. The absence of EA
stepping-through option makes combating such errors a quite unpleasant task. The
main tool of diagnosing such errors at the present moment is the built-in function
Print(). Using it we may print current values of important variables and record
the EA operation flow. When debugging an EA during testing with visualization,
the built-in function Comment() can also be helpful. As a rule, when the wrong
work of an EA is confirmed, we have to add temporary calling of the function Print()
and record the EA inner state in the assumed places of error appearance.
But for the detection of difficult error situations we sometimes need to add dozens of such diagnostic calls of the function Print(). And after the problem detection and recovery, the function calls have to be deleted or commented, in order not to overcharge the EA log file and not to make its testing slower. The situation is even worse, if the EA code already includes the function Print() for periodic recording of different states. Then the temporal Print() function calls cannot be deleted by a simple search of 'Print' in EA code. One has to think whether to delete a function, or not.
For example, when recording errors of functions OrderSend(), OrderModify() and OrderClose() it is useful to print into a log file the current value of the variables Bid and Ask. This makes finding reasons for such errors as ERR_INVALID_STOPS and ERR_OFF_QUOTES easier.
For writing this diagnostic information into a log file, I recommend using the following additional function:
void logInfo(string msg) { Print("INFO: " + msg); }
because:
- first, now such function will not be confused with 'Print' during search;
- second, this function has one more useful peculiarity, which will be discussed later.
It takes much time to add and delete temporary diagnostic calls of the function Print(). That is why I suggest one more approach, which is efficient when detecting logic errors in a code and helps saving our time. Let us analyze the following simple function:
void openLongTrade(double stopLoss) { int ticket = OrderSend(Symbol(), OP_BUY, 1.0, Ask, 5, stopLoss, 0); if(ticket == -1) logError("openLongTrade", "could not open order"); }
In this case, while we open a long position, it is clear, that in a correct EA operation the value of he parameter stopLoss cannot be larger or equal to the current Bid price. I.e. correctis the statement that when calling the function openLongTrade(), the condition stopLoss < Bid is always fulfilled. As we know it already on the stage of writing the analyzed function, we can use it the following way:
void openLongTrade( double stopLoss ) { assert("openLongTrade", stopLoss < Bid, "stopLoss < Bid"); int ticket = OrderSend(Symbol(), OP_BUY, 1.0, Ask, 5, stopLoss, 0); if(ticket == -1) logError("openLongTrade", "could not open order"); }
I.e. we insert our statement into the code using the new additional function assert(). The function itself is quite simple:
void assert(string functionName, bool assertion, string description = "") { if(!assertion) Print("ASSERT: in " + functionName + "() - " + description); }
The first parameter of the function is the name of the function, in which our condition is checked (by analogy with the function logError()). The second parameter shows the results of this condition checking. And the third parameter denotes its description. As a result, if an expected condition is not fulfilled, the EA log file will contain the following information:
- the name of the function, in which the condition was not fulfilled;
- description of this condition.
As a description we may display, for example, the condition itself, or a more detailed
description, containing the values of the controlled variables at the moment of
checking the condition, if this can help to find the error causes.
Of course the given example is maximally simplified. But I hope it reflects the
idea quite clearly. As the EA functionality grows, we see clearly how it should
work and what conditions and input parameters are acceptable, and which ones are
not. Inserting it into an EA code using the function assert() we get a useful information
about the place, in which EA operation logic is broken. Moreover, we partially
eliminate the necessity to add and delete temporary calls of the function Print(),
because the function assert() generates diagnostic messages into the EA log file
only at the moment of detecting discrepancies in expected conditions.
One more useful method is using this function before each division operation. Actually, this or that logic error can sometimes result in division by zero. In such cases the Expert Advisor stops operating, and a single line appears in the log file: 'zero divide'. And if the division operation is used many times in the code, it may be rather difficult to detect the place where the error occurred. And here the function assert() may be very helpful. We simply need to insert the corresponding checking before each division operation:
assert("buildChannel", distance > 0, "distance > 0"); double slope = delta / distance;
And now in case of a division by zero, we just look through the log file to find where exactly the error appeared.
Analyzing the EA Log File for Detecting Errors
The suggested functions for errors recording help to find them easily in the log file. We just need to open the log file in text mode and search by words "ERROR:" and "ASSERT:". However, sometimes we face situations, when during development we omit the call results of this or that built-in function. Sometimes division by zero is omitted. How can we detect messages about such errors among thousands of lines, containing the information about opened, closed and changed positions? If you face such a problem, I recommend you the following way out.
Open Microsoft Excel and download the EA operation log file as a CSV-file, indicating
that as a separator a space or several spaces should be used. Now enable "Autofilter".
This gives you a convenient opportunity to look through the filter options in two
neighboring columns (you will easily understand, what columns), to understand if
the log file contains errors, written into it by a terminal, or not. All entries,
generated by functions logInfo(), logError() and assert(), start with the prefix
("INFO:", "ERROR:" and "ASSERT:"). The entries of
the terminal about errors can also be easily seen among a few variants of typical
entries about working with orders.
This task can be solved in a more elegant way. For example if you are well acquainted with tools of processing text files, you can write a small script, which will display only those EA log file lines, which refer to EA operation errors. I strongly recommend running each Expert Advisor through the tester on a large period and then analyze the log file of its operation using the described methods. This allows detecting the majority of possible errors before you run your EA on a demo-account. After that the same from-time-to-time analyzing its operation log file will help you detect in time errors, peculiar to EA operation only on accounts in real time.
Conclusion
The described additional functions and simple methods allow simplifying the process of errors detection and recovery in EA code, written in the programming language MQL4. For your convenience the above described functions are included into attached files. Simply add the file to your EA using the directory #include. Hope, the described methods will be successfully used by traders, who worry about the robustness and correctness of the Expert Advisors.
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/1473
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
is good.
nice article - more like this needed. assert is good. logging is good - black hole without. gives edge to coder and need all can get when coding/debug/runtime crazys ;)
i use assert long time - 4get where is in lots code, is good function param checker also, use all over, not trust self or mt ;) - is bit of magic when assert 'pops up' out of blue some times ;)
also take error logging step further - have one () for *all* mt error codes. only need call - it log to terminal and sends timestamped+callerLoc logfile entry - all use same msg format. is big lump code but now never mess with "msg..." anymore in mainline code. it return 'wat do next' suggest/status and caller make decision if retry,stop,refresh,abort,..
just ideas ok? not criticissms
nice read, yes.