在上一节课中我们新建了一个脚本,介绍了一些基本的知识。本节课我们要新建我们的第一个EA,了解一下EA的运行方式,学会用一些常用的语句。
1、新建第一个EA
创建好EA后,我们会发现新的EA代码主要有三部分组成:OnInit()函数、OnDeint()函数、OnTick()函数,如以下代码:
#property copyright “Copyright 2019, MetaQuotes Software Corp.”#property link “https://www.mql5.com”#property version “1.00”#property strictint OnInit () { return(INIT_SUCCEEDED); }void OnDeint (const int reason) { }void OnTick () { }
这三部分分别是什么意思呢,第一个函数OnInit()是初始化函数,只在EA刚加载的时候运行一次,一些前期的工作可以放到这里来做,比如判断账户是否可以使用该EA等,如果初始化成功就会返回INIT_SUCCEEDED,此时EA就会转移到OnTick()函数运行,如果不成功则程序结束。
OnTick()函数在当前品种每个Tick报价来的时候会运行一次,也就是说只要当前品种价格变动,OnTick()函数就会运行一次,在一个Tick下,EA可以做很多事,比如瞬时开平仓、挂单、移动挂单价格、移动止损止盈等,这也是为什么EA能做到很多人很难做到的事,也是其优点之一。
OnDeint()函数在EA退出的时候会运行一次,我们运行EA时,很多时候会让EA在图表上显示很多信息,也会在图上画线之类的,当我们退出EA时往往想把这些线去掉以方便看图表,这时就要用到该函数来删除图表上的内容。
目前我们还用不到OnInit()函数和OnDeinit()函数,可以暂时将他们删除。接下来我们在EA中写一些常用的语句,试试我们的新EA。
2、输出语句
在上节课中我们知道了Print()函数的使用方法,下面我们在EA中试一下:
int a=1;void OnTick (){ int b=2; a ; Print(“a b=” (string)(a b));}
编译运行后我们可以看到EA的输出如下图:
可以看到,由于a是全局变量,它只初始化一次,因此每个Tick过来之后,a的数值不会回到原来的数值而是加了1,所以a b的数值也会加1。
如果我们需要在同一行输出多个内容怎么办?我们可以在后面用” ”号将两个输出连起来,如下:
int a=1;void OnTick (){ int b=2; a ; Print(“a b=” (string)(a b) ” a-b=” (string)(a-b) “\na-b=” (string)(a-b));}
编译运行后我们可以看到EA的输出如下图:
我们将两个内容连起来了。注意,对于Print函数,在其他语言中都有用的换行符号”\n”是不起作用的,Print的输出只能在一行内。
除了Print,我们还可以用Comment函数实现输出,如以下代码所示:
int a=1;void OnTick (){ int b=2; a ; Comment(“a b=” (string)(a b) ” a-b=” (string)(a-b) “\na-b=” (string)(a-b));}
以上语句的输出如下图所示:
可以看到在图表的左上角显示了我们的输出结果,而且我们注意到,”\n”起到了换行的作用。
第三种输出方式,我们可以用MessageBox来输出,代码如下:
int a=1;void OnTick (){ int b=2; a ; MessageBox(“a b=” (string)(a b) ” a-b=” (string)(a-b) “\na-b=” (string)(a-b));}
输出结果如下:
可以看到我们输出了一个弹窗,这对于一些半自动交易系统来说是非常有用的,当达到一定的条件时,程序可以自动提示,然后由人工判断是否可以入场。
接下来我们来试着改一下弹窗的标题和弹窗的选项,代码如下:
int a=1;void OnTick (){ int b=2; a ; MessageBox(“a b=” (string)(a b),”计算结果”,1);}
输出结果如下:
可以看到,我们输出的标题变为了”计算结果”,选项中加了一个”取消”的选项,因此MesageBox这个函数第一部分是输出的内容,第二部分是弹窗的标题,第三部分是弹窗的选项。
那么弹窗都有哪些选项呢?我们打开MQL4帮助文件,搜索MessageBox函数,如下图:
可以看到,若函数的第三部分是0,则代表仅有一个”确定”选项;1代表有一个”确定”选项以及一个”取消”选项;2代表”中止”/”重试”/”忽略”,有三个选项;3代表”是”/”否”/”取消”,也有三个选项;4代表”是”/”否”,只有两个选项,5代表”重试”/”取消”,6代表”取消”/”重试”/”继续”。至于怎么使用它们,会在以后的课程中介绍给大家。
3、逻辑判断
接下来介绍一下简单且最常用的逻辑判断函数,if和else,if的作用是,当条件成立时就执行if内的语句,如以下代码:
int a=1;void OnTick (){ int b=3;int c=1;if (a<5){ a=a b;}else if (a>=5 && a<=7){ a=a c;}else{ a=a-c;} Print(“a=” (string)(a));}
以上代码的意思是,如果a<5,那么当有报价过来的时候会激发OnTick()函数,将a b的值重新赋给a;否则的话(即a>=5)如果a>=5且a<=7,那么将a c的值赋给a;再不然(即a>7),则将a-c的值赋给a。根据这个逻辑,那么第一的输出应该是1 3=4,第二次是4 3=7,第三次是7 1=8,第四次是8-1=7,往后都在7和8之间来回输出。
我们看一下我们的EA输出是否符合我们的逻辑预期,输出如下:
输出没有问题。
上例中已经讲了两个条件需要同时成立时才能执行语句的”与”逻辑,即以上代码中的”&&”,另外有的时候我们需要判断,如果两个条件中的任意一个条件成立那我们就执行,或说条件不成立我们就执行。以下代码做个示例:
int a=1;void OnTick (){ int b=1;int c=1;if (b<1 || !(c>1)){ a=a 1;} Print(“a=” (string)(a));}
以上的语句的意思是,只要满足“b<1”或者“非c>1”(即c<=1)的条件,则将a加1并输出,我们可以看到“或”逻辑用的符号是“||”,“非”逻辑用的符号是“!”,看一下输出结果:
可以看到输出结果是符合我们的预期的。
虽然逻辑判断语句很简单,但这是写EA时最重要的函数之一,很多时候进行入场条件的判断以及出场条件的判断都要用到它,因此这个是最基本的东西。
4、字符串处理
字符串处理函数也是一类比较重要的函数,这一类函数在判断交易品种的时候特别有用。比如说,我们要做交叉盘,计算交叉盘的盈亏的时候又需要用到直盘货币对的汇率,这个时候我们怎么自动查询直盘货币对的汇率呢?
这个时候我们需要把交叉货币对分成前后两个部分,比如EURGBP,我们要把它先分解成EUR以及GBP,然后再将字符串与USD拼接,再查询新拼接到的产品的汇率,比如说将EUR与USD拼接成EURUSD,再在所有产品中查询名字叫做”EURUSD”的品种的价格。
那么首先,我们先实现将产品的字符串分成两部分,第一部分字符串,3个字符,第二部分字符串也是三个字符,采用StringSubstr ()函数来截取字符串。打开帮助文件我们可以看一下这个函数有什么功能
如上图,这个函数需要输入的第一个变量是要处理的字符串;第二个变量是截取的开始位置编号注意字符串中的第一个字符的位置编号为0,第二个字符编号为1,往后顺延;第三个变量是截取的字符的个数。
下面我们将字符串”EURGBP”截取为两段,代码如下:
void OnTick (){string a=”EURGBP”;string b=””;string c=””;b=StringSubstr(a, 0, 3);c=StringSubstr(a, 3, 3);Print(“b=” b ” c=” c);}
这段代码的意思是在字符串a中,从其位置编号为0的字符往后数3个字符,将这3个字符赋给b,既将a的前三个字符赋给b。由于a的第四个字符位置编号为3,那么我们就从位置编号为3的字符往后数3个,将这些字符赋给c,最终输出的结果如下:
可以看出我们做到了截取字符串的处理。
接下来,我们要组合字符串,将其组合成直盘的货币对,这时可以简单地使用加法将字符串粘合,比如我们的字符串为a=”EUR”,那么我们直接将它加上”USD”,即a=a ”USD”,a就成为了”EURUSD”。
我们知道,有些直盘货币对是美元在前的,比如说”USDJPY”,如果交叉货币对里面出现了日元,那么直接加上”USD”你输出的结果就成了”JPYUSD”了,对于一般的平台,这个货币对是不存在的。
于是很苦恼,现在我们想要一个随便输入一个交叉货币对,都能找到直盘货币对的程序,怎么做呢?
我们可以事先定义好直盘货币对,然后再判断我们粘合后的货币对和事先定义好的是否一致,这就要用到循环语句了。
5、循环
循环语句在任何一门计算机语言中都是最基本最重要的内容,MQL4也不例外。MQL4里的循环语句和C语言非常接近,一般用到的语句有for循环,while循环以及do while循环,控制循环的语句也有break和continue。接下来以一个案例介绍一下MQL4中如何使用这些循环,代码如下:
void OnTick (){int a=0;int b=0;int c=0;int d=1;int i;//for循环for(i=0;i<=10;i ){a=a d;}//while循环i=0;while(i<=10){b=b d;i ;}//do while循环i=0;do{b=b d;i ;} while(i<=10)}
由上例可以看出,采用for循环是需要输入循环次数的,但是相对来说for循环简介一点。while循环中的条件是该循环继续下去的判断条件,当该条件不成立时则跳出循环,如上例,这个while循环在i<=10时会继续循环,当进行到i>10为时,循环终止了。do while循环也是一样的。
for循环不怎么方便的地方在于它本身必须要指定循环的次数,如果面对的是一个在接近无穷多的数据中查找想要的数据的问题,那么这个循环的次数就不好指定了,设小了就很容易因为循环次数到了而跳出了循环导致查询的结果不对。而while和do while不会,这两个可以根据你的查找条件来跳出循环,不用指定循环次数。一般来说循环采用for循环,结构清晰,但是涉及到查询类的问题建议用while和do while循环。
知道了循环的用法,接下来我们便可以解决在字符串处理一节中遗留下来的问题了,即输入任意一个交叉货币对名称,将其分解成两个直盘货币对的名称。代码如下:
void OnTick (){string symbol[7];symbol[0]=”EURUSD”;symbol[1]=”GBPUSD”;symbol[2]=”AUDUSD”;symbol[3]=”NZDUSD”;symbol[4]=”USDCHF”;symbol[5]=”USDJPY”;symbol[6]=”USDCAD”;string a=”EURJPY”;string b=””;string c=””;int i;int kaiguan=0;b=StringSubstr(a, 0, 3);c=StringSubstr(a, 3, 3);string d= b ”USD” ;for(i=0;i<=6;i ){if(d==symbol[i]){ kaiguan=1; b=b ”USD”; break;}}if(kaiguan==0){b=”USD” b;}kaiguan=0;string e= c ”USD” ;for(i=0;i<=6;i ){if(e==symbol[i]){ kaiguan=1; c=c ”USD”; break;}}if(kaiguan==0){c=”USD” c;}Print(“第一个货币对为” b);Print(“第二个货币对为” c);}
以上代码中先定义了所有的直盘货币对的名称,然后再将输入的交叉货币对分解成两部分,然后在后面加上”USD”并和之前定义好的直盘货币对比对,如果比对是正确的,那么也就是说我们在后面加上的”USD”是对的,反之则在前面加上”USD”。
输出的结果如下图:
以上便是MQL4的一些基本语法,在以后这些语法将经常用到,如果把学习MQL4类比于学英语,那这几个语法就是英文字母了,学会了后面就容易多了。