I see this tripping up MT4 programmers enough that it warrants a post.
A quick background for the uninitiated, Metaquotes structured MT4 EAs with 3 main event processes.
init(), start() and deinit()
This is clean, minimal and logically sound.
- init() runs ONCE on starting up, or when editing your EA properties, allowing you to do the necessary polling of your trade server details, and whatever variables you want to assign ONCE. Basically anything static throughout the run of your session.
- start() runs on every BID tick. (Misc: it abandons any subsequent ticks until it completes, therefore it is ideal to avoid processes like long loops/arrays, e.g. if you expect 20 ticks per second, i.e. a new tick comes in every 50 milliseconds, and your start() takes 100 milliseconds, you should expect your EA to not process half of your ticks; this is possible if you are pinging an external site, for news or authentication, etc.. although a typically well written EA will run a pass in the sub-millisecond range)
- deinit() runs ONCE on removing the EA, or when editing your EA properties, or shutting down MT4. This is typically for report generation, deleting chart objects that were generated by the EA, etc..
The horse’s mouth:
http://docs.mql4.com/runtime/
http://book.mql4.com/programm/execution
To save some cycles and make start() run faster, the programmer will want to avoid unnecessary calculations, particularly on things that do not need to be done on every tick, largely because the information is not expected to change for a while, e.g. high of the previous bar.
So naturally he’ll want to make it run only once on every new bar, and he could put this into start():
start(){ if(Bars>BarsCount){ //logic to be run on new bar BarsCount=Bars; } ... }
The problem arises when an extern variable is called by the logic being run only on the new bar. E.g.
extern int ManualGMTOffset = 0; start(){ if(Bars>BarsCount){ GMTOffset=ManualGMTOffset; BarsCount=Bars; } ... }
Learning point:
As extern variables are variables declared on a global scope, they retain their values in the MT4 platform/session until some logic changes them.
(However, they do get removed from memory if the EA is removed, or updated if the EA is reattached.)
So the net result for the above example is that even though extern int ManualGMTOffset is edited from the properties menu, int GMTOffset does not update correctly until a new bar is formed.
You have to be conscious of the variables that you want updating per tick, as would be the case for variables polling extern variables, versus those updating less frequently.
(do not confuse variables declared on a global scope with MT4-specific “GlobalVariables”, which persist even after shutdown for up to 4 weeks from last access unless actively removed, and are typically used for cross-EA/script communication, etc)
So one way is to reattach the EA whenever you edit any extern parameter that you know other variables which update on a non-tick basis will call. Or, sacrifice the speed improvement and update per tick.
But, since init() runs once on editing the extern parameters, you can simply put an extra copy of the variable assignment that calls the extern variable in init(). Thereby retaining the speed improvement and have the non-tick updated variables be refreshed to the latest extern parameter.
extern int ManualGMTOffset = 0; init(){ GMTOffset=ManualGMTOffset; } start(){ if(Bars>BarsCount){ GMTOffset=ManualGMTOffset; BarsCount=Bars; } ... }
Post end. Don’t read further.
Down the rabbit hole:
You may ask, why not just have it in init() and leave it out of start()? It becomes a big hairy deal(see what I did there?) if you turn on the EA over the weekend when the market is closed and your Market Watch time is not updated, e.g on launching, init() runs and you poll TimeCurrent() to get Market Watch time and TimeLocal() to get your OS’s time. Because TimeCurrent() remains the Friday close time, and TimeLocal() becomes a snapshot of your OS’s time at init() on the Sunday or Monday pre-market hours you activate the EA, whatever further calculations to get GMTOffset, e.g. via GetTimeZoneInformation() in Window’s kernel32.dll. (that in itself has its own issues), becomes completely skewed.
It has to reside somewhere in start(). But as mentioned above, to optimise for speed, we do not want it running on per tick basis but frequently enough as to get a correct value while infrequently to minimise the calculations.
And further down:
But why go through so much bother for a simple 1+1 arithmetic? Because it’s not, particularly when generating time strings, effectively arrays, for execution/monitoring time windows, which can be computationally expensive. Note that when I say computationally expensive, I mean you might lose a few hundred microseconds per tick, which may or may not be important, depending on your perspective or strategy. They do add up during backtesting though. More on that in another post, possibly.
On a semi-related note, you cannot access the EA’s Properties(F7) when it is running through start(), either due to some unnecessarily long loop or array calculation, or sleep().
*The lovely photo of Miss Piggy is property of The Muppets Studio, LLC, a wholly-owned subsidiary of The Walt Disney Company
Discussion
No comments yet.