开发MQL5程序之前,让我们想花几分钟时间,了解一下MQL5的程序结构。
所有MQL5程序,具有相同的基本结构。
顶部,是预处理声明;接着是输入变量和全局变量。最后是函数,类,以及定义的程序处理器。
让我们从#property开始吧,几乎所有MQL5内我们都见得到。
#property关键字用来定义程序属性,比如描述信息,指标含义,脚本和类库属性。
可以使用MQL5向导创建程序,link,version属性会指定插入。我们可以手动添加 description属性。这些将会展示在EA属性对话框内的常用选项卡内。当你需要分发程序时,这个挺有用的。
#property声明放置在程序的最顶部。必须在主程序内定义。其他引入文件的property声明会被忽略。下面是一个例子。
#property copyright "mql45ea"
#property link "http://www.sample.com"
#property version "1.07"
#property description "Dual moving average cross with trailing stop"
#define指令用于定义贯穿程序的长常量。第二章我们介绍过常量。惯例是用大写字母作为常量的标识符。下面是一些例子:
#define PI 3.14159265
#define MAX_BARS 100
#define COMPANY_NAME "GitHub"
程序中,直接引用变量名,就可以使用了。像下面这样:
double diameter = 5;
double circumference = PI * diameter;
Print(circumference);
上述代码,用直径计算圆周。使用了PI常量。
还有一种#define指令,是参数类型。可以传参,就像函数那样。该指令接收最多八个参数,表达式直接计算,并可在程序内使用。
#define PI 3.14159265;
#define CIRC(dia) PI * dia;
这种形式,类似函数,在程序中这样用:
double diameter = 5.56;
double circumference = CIRC(diameter);
上面的CIRC函数直接调用#define内的表达式 PI * dia,并将变量 diameter 传递给 dia,计算并返回值。
如果有一些简单的数学运算,且频繁使用,定义为传参型的常量,比定义为函数要好用,且程序不易杂乱。为什么?因为面向对象,你要再使用公用函数等等,显得与编程风格大相径庭,这不符合最佳实践。
该关键字用于引入文件,并包含到程序内。引入的文件可包含变量,函数,类。有两种形式的include指令。
#include <Trade.mqh>
#define "Trade.mqh"
第一种,包含在中括号内,编译器会搜寻默认的include目录,也就是 、MQL5\Include子目录。这种是推荐的用法,所有章节均采用此写法。
第二种使用双引号引用,告诉编译器在当前程序文件所在的目录内查找。同级目录的引用。
第二章已经介绍过了。请往前翻阅。
主要目的是声明允许用户输入的变量,还有全局使用的变量列表。
在本程序文件内声明,或使用引入文件的类。
事件处理器内的程序,在特定事件触发时执行。例如,系统收到传入的新报价,就会触发NewTick事件。然后触发OnTick()事件。把需要处理的程序,写到OnTick内就可以了。
每个程序都有其自身的事件处理器。EA和指示器使用OnInit()处理Init事件,仅在程序初始化时执行一次。同样,OnStart()处理Start事件。OnCalculate()处理Calculate事件。后续内容我们会详细讨论。
下面我们用一个实例程序,把上面讲到的概念,都过一遍。让大家对其用法,有个初步的立体的概念。
事件处理,我们仅举例OnInt()和OnTick()两个。
//+------------------------------------------------------------------+
//| circumference.mq5 |
//| ann |
//| https://mql5.com/ |
//+------------------------------------------------------------------+
#property copyright "ann"
#property link "https://mql5.com/"
#property version "1.00"
#define PI 3.14159265
input double Radius = 1.5;
double RadiusSq;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
RadiusSq = MathPow(Radius,2);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
double area = CalcArea();
Print(StringFormat("Radius = %f, Area = %f", Radius, area));
}
//+------------------------------------------------------------------+
double CalcArea()
{
double rs = PI * RadiusSq;
return(rs);
}
上述代码,使用系统提供的格式化函数StringFormat,操作格式化后的数据,更为自由。加入运行序列,输出面板的大致如下:
2019.12.16 20:53:52.015 circumference (EURUSD,H1)
Radius = 56.900000, Area = 10171.251780
读者可以将上述代码自行在编辑器内编译运行测试一下。