While syntax may look on a first look
somewhat similar to C,
the MQL4 language is by far a different cup of tea,
it lives inside a rather specific eco-system
with many un-paralleled and dual-edge sword features.
TL;DR; at least, you've been warned.
Since the early state of the original MQL4 language, there were important concepts, that go well beyond the classics of C.
The units of MQL4 code ( be it any of { EA: Expert Advisor | CI: Custom Indicator | Script: Script } ) are being executed in several different modes of the code-execution eco-system, either:
- having been "put on" a graph, being that during live-market hours or weekends
- having been "setup in" a trivial mode in a [Strategy Tester] back-testing facility of the Terminal
- having been "setup in" a complex mode of an optimisation module in a [Strategy Tester]
While this may seem obvious for hard-core MetaTrader Quant Modellers, one may also agree this is a hidden part of the O/P context, which was readable just from decoding a bit cryptic second sentence ( cit. orig. ) :
"just a simple EA in MT4".
So, what are the MQL4-ways to use external variables?
All of the { extern | input | sinput }
declarations ought be somewhere near the top of the MQL4-code, having a file-level of the scope-of-validity.
Placing the same extern
declarations right to the bottom of the file will still work, compiler is aware of the proper scoping ( as it by-design also has to carefully handle all the potential variable name(s) masking(s) / un-masking(s) in case the same names were also used in some call-interface(s) declaration(s) or somewhere "inside" some deeper embedded code-block(s) ).
Such un-orthodox practice should be considered rather in-humane, as we also strive to create a good practice for human-understanding of the code-unit(s) we design, irrespective of compiler's ability to process the tail-definitions the very same way it does for the head-definitions, right?
Besides the trivialities of a variable-definition, belonging to a certain scope-of-validity ( inside a relevant { code-block }
-scope ), there are some major implications for extern
, input
and sinput
MQL4-specific declaration modifiers.
//+------------------------------------------------------------------+
//| StackOverflow__test_EA_extern.mq4 |
//| Copyright © 1987-2017 [MS] |
//| nowhere.no |
//+------------------------------------------------------------------+
#property copyright "Copyright © 1987-2017 [MS]"
#property link "nowhere.no"
#property version "1.00"
#property strict // New-MQL4.56789 <--- this CHANGES SOME RUN-TIME FEATURES OF THE SAME SOURCE-CODE (!!!)
//--- extern parameters --- MQL4 declared <extern>-variables have at least DUAL-INTERFACE ROLE
extern double ama_red; // _RED /*| (1) is a LIVE, knowingly bidirectional INTERFACE */
extern double ama_blue; // _BLUE /*| between a code-execution ecosystem */
extern double ama_purple; // _PURPLE /*| & MT4 GUI user-interactions */
// ^ (2) is an ITERATOR, unidirectional INTERFACE ROLE
// ^ from MetaTrader Terminal 4 [StrategyTester]
// ^ into MetaTrader Terminal 4 Optimisation Tool
// ^
// comment^-strings are DISPLAYED in GUI dialogue boxes ( on #property strict // mode )
// + each user-interaction, done via GUI, RESETs the STATE of the so far running EA (!!!)
// + variables are always RE-INITIALISED immediately before the OnInit() is called
// ^^^^^^^^^^^^^^^^^^^^^
//
// - Arrays[] and variables of complex types can't act as <extern>-variables.
/*
//--- input parameters --- New-MQL4.56789 Build 1065+ EA-templates started to be proposed as <input>-based
input double ama_red; // can never be assigned a value to <input>-defined variables
input double ama_blue; // can never be used for a man-machine user-interaction
input double ama_purple; //
*/
#property show_inputs
int aTracePointNUMBER(){ static int aTracePointORDINAL = EMPTY; return( ++aTracePointORDINAL ); }
void aTracePointREPORT( const string aTracePointCALLER,
const double aTracePointDOUBLE_VALUE,
const string aTracePointDOUBLE_NAME
){
PrintFormat( "[%d] In(%s): <_%s_> == %f", // PrintFormat( "[%d] In(%s): <_%s_> == %f",
aTracePointNUMBER(), // aTracePointNUMBER(),
aTracePointCALLER, // __FUNCTION__,
aTracePointDOUBLE_NAME, // "ama_red"
aTracePointDOUBLE_VALUE // ama_red
); // );
}
/*
#include MQL4_Project_common_HEADERS_FILE.mqh // may help with #define-s, but not that much with { extern | input | sinput }-s in MQL4
*/
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit() {
aTracePointREPORT( __FUNCTION__, ama_red, "ama_red" );
ama_red = iMA( NULL, 0, 6, 0, MODE_EMA, PRICE_CLOSE, 0 );
aTracePointREPORT( __FUNCTION__, ama_red, "ama_red" );
ama_red = EMPTY;
aTracePointREPORT( __FUNCTION__, ama_red, "ama_red" );
return( INIT_SUCCEEDED ); }
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit( const int reason ) {
// aTracePointREPORT( __FUNCTION__, ama_red, "ama_red" );
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick() {
// uponEntry: // EACH TIME anExternalFxEventSTREAM QUOTE.ARRIVAL
aTracePointREPORT( __FUNCTION__, ama_red, "ama_red" );
// ... EA code ...
ama_red = iMA( NULL, 0, 6, 0, MODE_EMA, PRICE_CLOSE, 0 );
// ...
}
//+------------------------------------------------------------------+
/*
2017.04.15 14:40:45.030 2013.01.01 00:00:00 StackOverflow__test_EA_extern inputs: ama_red=-123456789; ama_blue=-987654321; ama_purple=918273645;
2017.04.15 14:40:45.030 2013.03.21 13:20:00 StackOverflow__test_EA_extern XAUUSD,M1: [0] In(OnInit): <_ama_red_> == -123456789.000000
2017.04.15 14:40:45.030 2013.03.21 13:20:00 StackOverflow__test_EA_extern XAUUSD,M1: [1] In(OnInit): <_ama_red_> == 1608.298571
2017.04.15 14:40:45.030 2013.03.21 13:20:00 StackOverflow__test_EA_extern XAUUSD,M1: [2] In(OnInit): <_ama_red_> == -1.000000
2017.04.15 14:40:45.030 2013.03.21 13:20:00 StackOverflow__test_EA_extern XAUUSD,M1: [3] In(OnTick): <_ama_red_> == -1.000000
2017.04.15 14:43:14.764 2013.03.21 13:20:02 StackOverflow__test_EA_extern XAUUSD,M1: [4] In(OnTick): <_ama_red_> == 1608.298571
2017.04.15 14:43:16.827 2013.03.21 13:20:05 StackOverflow__test_EA_extern XAUUSD,M1: [5] In(OnTick): <_ama_red_> == 1608.296000
2017.04.15 14:43:18.889 2013.03.21 13:20:07 StackOverflow__test_EA_extern XAUUSD,M1: [6] In(OnTick): <_ama_red_> == 1608.293428
2017.04.15 14:43:20.952 2013.03.21 13:20:10 StackOverflow__test_EA_extern XAUUSD,M1: [7] In(OnTick): <_ama_red_> == 1608.295142
2017.04.15 14:43:23.014 2013.03.21 13:20:12 StackOverflow__test_EA_extern XAUUSD,M1: [8] In(OnTick): <_ama_red_> == 1608.296857
2017.04.15 14:43:25.077 2013.03.21 13:20:15 StackOverflow__test_EA_extern XAUUSD,M1: [9] In(OnTick): <_ama_red_> == 1608.293428
*/
By tracing this trivial code, you may realise a few facts:
- While one cannot, for obvious compile-time reasons, declare an
ema_red
et al with being assigned to a non-(compile-time)-constant initialisation, there still is a way to achieve such aimed target and one may use for this a mandatory call to an OnInit(){...}
event-handler code-block, to provide such a non-constant value into the intended extern
variable.
Listed are the trace-reports from such constructed EA, showing each change of extern
-declared inputs, before and after the code-execution reached the event-triggered OnTick(){...}
handler ( original start()
handler is not used anymore, yet historically still present a lot in MQL4 code-base ):
2017.04.15 14:40:45.030 2013.01.01 00:00:00 StackOverflow__test_EA_extern inputs: ama_red=-123456789; ama_blue=-987654321; ama_purple=918273645;
2017.04.15 14:40:45.030 2013.03.21 13:20:00 StackOverflow__test_EA_extern XAUUSD,M1: [0] In(OnInit): <_ama_red_> == -123456789.000000
2017.04.15 14:40:45.030 2013.03.21 13:20:00 StackOverflow__test_EA_extern XAUUSD,M1: [1] In(OnInit): <_ama_red_> == 1608.298571
2017.04.15 14:40:45.030 2013.03.21 13:20:00 StackOverflow__test_EA_extern XAUUSD,M1: [2] In(OnInit): <_ama_red_> == -1.000000
2017.04.15 14:40:45.030 2013.03.21 13:20:00 StackOverflow__test_EA_extern XAUUSD,M1: [3] In(OnTick): <_ama_red_> == -1.000000
2017.04.15 14:43:14.764 2013.03.21 13:20:02 StackOverflow__test_EA_extern XAUUSD,M1: [4] In(OnTick): <_ama_red_> == 1608.298571
2017.04.15 14:43:16.827 2013.03.21 13:20:05 StackOverflow__test_EA_extern XAUUSD,M1: [5] In(OnTick): <_ama_red_> == 1608.296000
2017.04.15 14:43:18.889 2013.03.21 13:20:07 StackOverflow__test_EA_extern XAUUSD,M1: [6] In(OnTick): <_ama_red_> == 1608.293428
2017.04.15 14:43:20.952 2013.03.21 13:20:10 StackOverflow__test_EA_extern XAUUSD,M1: [7] In(OnTick): <_ama_red_> == 1608.295142
2017.04.15 14:43:23.014 2013.03.21 13:20:12 StackOverflow__test_EA_extern XAUUSD,M1: [8] In(OnTick): <_ama_red_> == 1608.296857
2017.04.15 14:43:25.077 2013.03.21 13:20:15 StackOverflow__test_EA_extern XAUUSD,M1: [9] In(OnTick): <_ama_red_> == 1608.293428
Beware, extern
is a BI-DIRECTIONAL INTERFACE (!!!)
Given the above, on condition a user uses the GUI to modify once setup extern to a new value, the code-execution of the currently live-running Expert Advisor is reset to a new initialised values. That may cause a lot of problems in distributed-system's processing and one ought take due care to propagate such "invisible" reset to proper handling of going back to square [ 1 ].
The impact of the live-Market session Expert Advisor code-execution reset could be indeed disastrous, so be always careful, whether your code is being designed as being robust against such unintentional event(s), causing a Deus Ex Machina mystical changes of behaviour, very similar to a sudden thunder from a clear, blue sky.
If one does not expect such habit, a hidden remark in the system journal will typically not suffice for one to realise what happened to the live-session and what cascade of additional side-effects was triggered by such event.
Epilogue:
Upon mastering both the trivial extern
and also it's external-iterator alternative for the complex-setup, used in [Strategy Tester] optimiser ( be it a fully-orthogonal brute-force or a somewhat magically labeled Genetic-mode one ), the fully bi-directional interface role of the extern
-variables are a very powerful tool for Man-Machine-Interactions during MQL4 Quant Modelling.