Updated MQL4
MQL4的新变化
从版本600开始,MQL4编程语言进行了全面修订,达到了MQL5的水平——现在你可以使用统一的MetaEditor开发环境、统一的风格、库和调试工具来开发MQL4/5交易机器人。
由于易于学习以及多年来使用MetaTrader 4终端生成的大量代码,MQL4在自动化系统开发者中非常流行。然而,这种语言也有一些缺点,其主要优势——简单的编程语言不允许开发复杂的系统,并且阻碍了从高级语言移植已调试过的库。因此,我们决定在其中实现尽可能多的MQL5语言功能和特性,同时完全保留MQL4的功能。换句话说,所有强大的MQL5功能,包括P和原生代码编译器,都将出现在MQL4中。
为了实现这一点,我们开发了一个统一的编译器,能够自动支持MQL4和MQL5语言。MetaEditor也将成为同时适用于MetaTrader 4和MetaTrader 5平台的统一应用程序。因此,可以从任何版本编译MQL4和MQL5。MQL5存储也变得可用。
MQL4应用程序的保护水平提升到了MQL5级别。新的EX4/EX5文件提供了严格且完全修订过的保护。这意味着安全的EX4应用程序市场也可供MetaTrader 4使用。
此外,MQL4现在具有新的图形对象和用于处理图表的新功能。MQL5标准库将移植到MQL4,为开发者提供创建自己的图形界面和交易库的新可能性。现在,你可以使用这些资源在MetaTrader 4中创建完整的应用程序。
MQL4语言的变更
增加了新的char、short、long、uchar、ushort、uint、ulong和double数据类型。这将允许从其他C++类似语言转移代码。不同类型的数据处理速度不同。整数数据是最快处理的类型。使用特殊的协处理器来处理双精度数据。然而,由于浮点数据内部表示的复杂性,其处理速度比整数慢。类型转换也已实现。
字符串现在以Unicode格式表示,之前是ANSI格式(单字节的)。如果程序使用DLL并将字符串变量传递给它们,应考虑这一点。
预定义的Volume变量现在是长类型。访问卷量的时间序列也由长类型数组组成。建议在旧的MQL4程序中显式地将此类型的数据转换为目标类型,以避免类型溢出错误。
结构类和对象指针、void类型和this关键字,允许对象接收对自身的引用,已添加进来。所有面向对象编程标准都得到支持:
P允许使用类开发程序。这促进了大型应用程序的调试和开发,同时也由于继承提供了多次重用先前生成的代码的能力。然而,这并不意味着你不能像以前那样以过程导向的风格生成MQL4代码。如果你不需要新功能,你可以像过去一样开发程序。
init()、deinit()和start()预定义函数为了兼容性而保留下来,但现在可以使用OnInit()、OnDeinit()、OnStart()、OnCalculate()和OnTick()函数代替。此外,还实现了新的预定义OnTimer()、OnChartEvent()和OnTester()处理函数。在旧的MQL4中,预定义函数可以有任何参数和任何返回类型,并且可以通过名称而不是签名来调用。在新的MQL4中,所有预定义函数必须严格符合其签名。换句话说,它们应该具有精确定义的参数集和返回类型。
现在,变量名不能包含特殊字符和小数点,新的MQL4关键字也不能用作名称。旧的MQL4程序可以使用新编译器重新编译,以轻松纠正所有此类错误,同时遵循编译器的提示。
优先级规则现在符合C语言标准。如果你不确定,可以在旧的MQL4应用程序中插入括号,以明确指示优先级,提高可靠性。
条件检查现在在逻辑运算中使用,与旧版本的MQL4不同,在那里所有表达式都先计算然后再进行检查。假设使用逻辑AND有两个条件的检查:
if(condition1 && condition2)
{
// some block of statements
}如果condition1表达式为假,则不执行condition2表达式的计算,因为false && true的结果仍然等于false。
ArrayCopyRates()函数的行为已经改变。在之前的MQL4版本中,此函数用于将价格序列复制到double[][6]数组。现在,如果你需要接收时间序列,请使用MqlRates结构元素的数组:
//--- Structure that stores information about the prices, volumes and spread.
struct MqlRates
{
datetime time; // Period start time
double open; // Open price
double high; // The highest price of the period
double low; // The lowest price of the period
double close; // Close price
long tick_volume; // Tick volume
int spread; // Spread
long real_volume; // Trade volume
};此外,新的函数格式可以用于虚拟复制,当没有实际复制时,访问复制的值实际上就是价格数据。
int ArrayCopyRates(
MqlRates& rates_array[], // MqlRates array, passed by reference
string symbol=NULL, // symbol
int timeframe=0 // timeframe
);为了与旧的MQL4程序兼容,之前的调用格式也保留下来,但现在它会导致数据实际复制到双精度型数组中。
int ArrayCopyRates(
void& dest_array[][], // destination array, passed by reference
string symbol=NULL, // symbol
int timeframe=0 // timeframe
);这意味着当时间序列中的值发生变化(添加了新条线、重构、最后一条线的收盘价更新)时,必须重新将所需数据复制到dest_array[]中。即使它是静态声明的,接收数组也会自动分配所需的复制条数。
RateInfo历史数据存储格式已更改。旧版本中的RateInfo结构如下所示:
struct RateInfo
{
unsigned int ctm; // bar open date
double open; // Open price
double low; // Low price
double high; // High price
double close; // Close price
double vol; // volume
};在新格式中,RateInfo结构具有存储价差和交易量的字段:
//--- Standard quote presentation in the new terminal version
struct RateInfo
{
INT64 ctm; // open date and time
double open; // Open price (absolute value)
double high; // Low price
double low; // High price
double close; // Close price
UINT64 vol; // tick volume
INT32 spread; // spread
UINT64 real; // trade volume
};因此,如果MQL4程序包含用于传递/接受价格数据的DLL,源代码中相应的函数应重写并重新编译,以考虑格式变化,确保正确运行。
| 基于旧RateInfo格式的旧EX4应用程序和DLL将在新终端中无法使用。需要转换为新格式。 |
在文件操作中,同时打开的文件数量现在可以达到64个,而在旧的MQL4中最多只有32个。直到最近,文件总是以FILE_SHARE_READ或FILE_SHARE_WRITE模式打开。现在,应明确指定所需的打开模式。
对于FileWrite()、FileWriteArray()、FileWriteDouble()、FileWriteInteger()和FileWriteString()函数,返回值的类型已从int改为uint。这些函数返回实际写入的字节数,或在出错时返回0(在旧版本的MQL4中,出错时返回负数)。
对函数、变量作用域和局部数组内存释放的处理也发生了变化。由于变化足够多,引入了新的 #property strict属性,以最大限度地与开发MQL4程序的先前方法兼容。当使用MQL向导创建新的MQL4应用程序时,此属性总是添加到模板中。
datetime类型的字符串表示取决于编译模式:
datetime date=D'2014.03.05 15:46:58';
string str="mydate="+date;
//--- str="mydate=1394034418" - old compiler/new compiler without #property strict
//--- str="mydate=2014.03.05 15:46:58" - new compiler with #property strict
下表显示了MQL4、不使用严格模式的新型MQL4以及具有指定严格编译模式的新型MQL4之间的差异:
| #propertystrict |
在严格模式下编译库时,每个导出的函数都应添加export修饰符,否则该函数将无法从外部访问。
编译器之间的差异表:
| 旧MQL4编译器 | 新型MQL4编译器 | 具有#property strict的新型MQL4 |
|---|---|---|
| init()、start()和deinit()入口点可以有任何参数和任何返回类型 | init()、start()和deinit()为了兼容性而保持不变, 而新的OnInit()、OnStart()、OnCalculate()、OnTick()、OnTimer()、OnChartEvent()、OnTester()和OnDeinit()应严格符合其签名 | 同上 |
| init()函数返回的结果不会由运行时子系统分析 | init()和OnInit()函数返回的结果不会由运行时子系统分析 | 如果OnInit()返回非零值,则专家顾问或指示器的操作将停止,程序将被卸载 |
| 任何变量名(保留字除外)都是可能的,包括特殊字符和小数点 | 变量名不能包含特殊字符和小数点。 保留字列表已扩展。因此,像short、long、const等广泛使用的词不能用作名称 | 同上 |
| 变量作用域从声明开始(即使在嵌套块中)到函数结束 | 同上 | 变量作用域从声明开始到声明变量的块结束 |
| 所有变量(全局和局部)都隐式初始化为零 | 同上 | 只有全局变量被初始化。在局部变量中,只有字符串被隐式初始化 |
| 当函数退出时,局部数组不会释放 | 当函数退出时,局部数组会被释放 | 当退出{}块时,局部数组会被释放 |
| “数组超出范围”不会导致严重错误 | 同上,除了结构和类的数组,对于这些数组,此错误是严重的 | “数组超出范围”是一个导致程序停止的严重错误 |
| 没有结构和类 | 结构和类存在。实现了额外的数据类型 | 同上 |
| 字符串是单字节的。 datetime是32位整数 预定义的Volume变量是双精度类型 | 字符串是Unicode的。 datetime是64位整数 预定义的Volume变量是长类型 | 同上 |
| ArrayCopyRates()执行到double[][]数组的虚拟复制 | ArrayCopyRates()执行到MqlRates[]数组的虚拟复制。为了兼容性,复制到double[][]数组的行为保持不变,但是这种复制是真实的,不是虚拟的。 | 同上 |
| 即使函数有类型,也不能返回值。为此,编译器会在函数末尾自动插入return(0) | 同上 | 任何类型的函数都应返回值 |
| 同时打开的文件数量是32 | 同时打开的文件数量是64 | 同上 |
| 文件始终以FILE_SHARE_READ和FILE_SHARE_WRITE模式打开 ** | 应明确指定FILE_SHARE_READ和/或FILE_SHARE_WRITE | 同上 |
| extern变量的名称在输入参数窗口中显示给脚本 | extern和input变量的名称在show_inputs模式下的输入参数窗口中显示给脚本 | 在show_inputs模式下的输入参数窗口中,字符串评论而不是extern和input变量名称显示在脚本中 |
- 请特别注意“数组超出范围”错误——许多旧的自定义指标在新编译器的严格模式下在图表上运行时会显示此错误。建议找出原因并消除它。
** 在新的MQL4和MQL5中,FILE_SHARE_READ和FILE_SHARE_WRITE标志负责文件的共享使用模式。在旧的MQL4中没有这样的文件。
文件结构的变化
在之前的MetaTrader 4客户端终端版本(509及更早版本)中,所有MQL4应用程序都存储在<terminal_installation_folder>\experts\根目录下的以下子目录中:
- \experts - 专家顾问(交易机器人),
- \experts\indicators - 自定义指标,
- \experts\scripts - 脚本(在图表上单次运行的MQL4应用程序),
- \include - 实现到其他程序中的源代码MQH和MQ4文件,
- \libraries - 以MQ4源代码和从它们编译的EX4可执行文件形式的库。它们用于其他MQL4程序动态调用其中的函数,
- \files - 特殊的“文件沙箱”。MQL4应用程序仅在此目录内执行文件操作。
在新的MQL4版本中,存储源代码的文件结构已经改变。现在,所有MQL4应用程序应位于<data_folder>\MQL4\目录下的适当文件夹中:
- \Experts - 专家顾问(交易机器人),
- \Indicators - 自定义指标,
- \Scripts - 脚本(在图表上单次运行的MQL4应用程序),
- \Include - 实现到其他程序中的源代码MQH和MQ4文件,
- \Libraries - 以MQ4源代码和从它们编译的EX4可执行文件形式的库。它们用于其他MQL4程序动态调用其中的函数,
- \Images - 用于资源的图像文件,
- \Files - 特殊的“文件沙箱”。MQL4应用程序仅在此目录内执行文件操作。
当从版本509更新MetaTrader 4终端到新版本时,所有来自以前版本的标准根目录的MQ4、MQH和EX4文件都会自动复制并移动到适当的文件夹。用户创建的子文件夹以及其中的文件不会处理。如果需要,应手动将它们移动到新的位置。
| 在更新过程中不会删除任何文件或文件夹!所有文件复制操作包括使用的文件路径都在更新期间的终端日志中固定。 |
在更新过程中不会自动重新编译旧的EX4文件到新版本。用户可以自由决定哪些源代码应该编译到新的EX4版本。所有旧的EX4都将在新的MetaTrader 4终端中工作。由新编译器编译的EX4库只能从也在新版本中编译的EX4程序中调用。
在某些情况下,您可能需要编辑 #include中的包含文件的路径(如果相对路径已更改)。请注意,MetaEditor的根目录现在是<data_folder>\MQL4\。所有程序都应位于正确的子目录中。
您可以通过终端菜单或MetaEditor中的“文件 - 打开数据文件夹”找到计算机上每个MetaTrader 4终端的数据文件夹(<data_folder>)。