虽然语法可能乍一看
有点类似于 C,
MQL4 语言是一种截然不同的茶,
它存在于一个相当特定的生态系统中
许多无与伦比的双刃剑功能。
TL;DR;至少,你已经被警告过了。
自从原始 MQL4 语言的早期状态以来,就有一些重要的概念远远超出了 C 的经典。
MQL4 代码的单位(可以是 { EA: Expert Advisor | CI: Custom Indicator | Script: Script } 中的任何一个)正在代码执行生态系统的几种不同模式下执行:
- 已“放入”图表,即在实时市场时段或周末
- 已在终端的 [Strategy Tester] 回测工具中“设置为”普通模式
- 已在 [Strategy Tester] 中“设置”为优化模块的复杂模式
虽然这对于核心 MetaTrader Quant Modellers 来说似乎很明显,但人们也可能同意这是 O/P 上下文的隐藏部分,仅通过解码有点神秘的第二句话就可以阅读 ( cit. orig. ) :
“只是一个简单的 MT4 EA”。
那么,MQL4 使用外部变量的方法是什么?
所有 { extern | input | sinput } 声明都应位于 MQL4 代码顶部附近,具有有效范围的文件级别。
将相同的extern 声明放在文件的底部仍然有效,编译器知道正确的范围(因为它在设计上还必须小心处理所有潜在的变量名称(s)掩码(s) ) / un-masking(s),以防在某些调用接口声明或“内部”某些更深的嵌入式代码块中也使用了相同的名称。
这种非正统的做法应该被认为是相当不人道的,因为我们还努力创造一种良好的做法,让人们能够理解我们设计的代码单元,而不管编译器处理尾部定义的能力如何与头部定义一样,对吧?
除了变量定义的琐碎性之外,属于某个有效范围(在相关的{ code-block }-范围内),对于extern,还有一些主要含义,input 和 sinput 特定于 MQL4 的声明修饰符。
//+------------------------------------------------------------------+
//| 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
*/
通过跟踪这个微不足道的代码,您可能会意识到一些事实:
- 虽然一个不能,出于明显的编译时原因,声明一个
ema_red 等并分配给非(编译时)常量初始化, 仍然有一种方法可以实现这样的目标,为此可以使用对 OnInit(){...} 事件处理程序代码块的强制调用,以提供这样的将非常量值添加到预期的 extern 变量中。
列出了来自这种构造的 EA 的跟踪报告,显示了在代码执行到达事件之前和之后,extern 声明的输入的每次更改触发的 OnTick(){...} 处理程序(原来的start() 处理程序已不再使用,但历史上仍然大量存在于 MQL4 代码库中):
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
注意,extern 是双向接口 (!!!)
鉴于上述情况,如果用户使用 GUI 将设置 extern 修改为新值,则当前实时运行的“EA 交易”的代码执行将重置为新的初始化值。这可能会在分布式系统的处理中引起很多问题,并且应该适当注意传播这种“不可见”的重置以正确处理返回到正方形 [1]。
实时市场会话“EA 交易”代码执行重置的影响确实可能是灾难性的,因此请务必小心,您的代码是否被设计为对此类意外事件具有鲁棒性,从而导致 Deus Ex Machina 神秘的行为变化,非常类似于来自晴朗蓝天的突然雷声。
如果人们没有预料到这种习惯,系统日志中的隐藏评论通常不足以让人们了解实时会话发生了什么以及此类事件触发了哪些额外的副作用。
结语:
掌握了简单的 extern 以及复杂设置的外部迭代器替代方案,用于 [Strategy Tester] 优化器(无论是完全正交的蛮力还是有点神奇地标记为遗传模式一),extern-变量的完全双向接口角色是 MQL4 量化建模期间人机交互的非常强大的工具。