简介
本系列的文章主要针对的是完全不懂编程,但想要在最短的时间内花费最少的精力尽快了解 MQL4 语言的交易者。如果您看到“面向对象”或“三维数组”这类词语就觉得头痛,那么这篇文章正是您需要的良方。这些课程的设计旨在最快出成果。内容也通俗易懂。我们在理论方面不会有太深的研究,但从第一课起就已能获得实际的收益了。
建议
如果您之前从未做过编程工作,第一次阅读某些案例时,您会发现很难懂其中的意思。慢慢再看一遍文本,仔细思考每个句子。您终会豁然开朗,因为这实际上真的不难。在理解之前的信息后,方可进行下一步。研究代码示例。根据您学到的内容编写自己的代码示例。
首先要了解 MQL4
让我们先来看一下可以用这种语言做什么。它主要用于创建脚本、自定义指标、EA 和库:
- 脚本就是命令序列,即您每请求一次才运行一次的程序。它们可替代您每天在交易中执行的操作。例如当您开订单时。它们也可执行特定功能,例如分析图表和生成统计信息。
- 定制指标是技术指标,主要作为内置指标的补充。它们用于开发图表或其他可视信息。和脚本不同的是,自定义指标在每次价格变动(即每跳动一次)时运行一次。显示什么指标只取决于您。它可以是一个没什么作用的窦道图,也可以是帮助您探寻市场方向的强大工具。例如,如果您确切地知道市场在什么时候、哪些情况下会趋于平盘态势,您可以将其编写到一个指标里。
- Expert Advisor是绑定到任何金融工具上的机械交易系统。与自定义指标类似,每跳动一次,expert advisor 就作用一次,其与指标的不同之处在于,它们可以通知您市场情况(例如,提供某些买入或卖出建议)或自己进行交易,无需您的帮助。终端可支持策略测试,从而快速评估您的 expert advisor 的盈利能力。您可以用 MQL4 语言描述您的策略,而终端会坚定不移地遵循您的所有指示。
- 库是用于执行特定任务的函数集合。例如,您的某个 EA 可能使用了特殊的数学函数来决定买入和卖出的时机。
本文中我们将学习编写通用脚本。为此我们使用了一个特殊的程序 - MetaEditor 4。要启动它,在打开的自定义终端上按 F4 键。要新建一个脚本,单击 MetaEditor 4 菜单中的“文件”->“新建”按钮,用用“Ctrl+N”按钮:
在显示的窗口中,选择要创建的项目。选择“脚本”,然后单击“下一步”:
在下一个窗口中,在名称字段中名称字段中键入脚本名称。在作者字段中添加您的姓名并在链接字段中键入脚本名称。然后单击确定:
之后会看到一个新窗口,这是您将使用的最重要的工具。它显示原始文本:
请注意,即便是一个空白的、不可运行的脚本也包含着一个代码。现在万事俱备,开始编程。但很遗憾,接下来该怎么做,您一点头绪都没有。让我们试着改变这种状况。
源代码、编译和其他
您应该了解一件重要的事情。在 MetaEditor 中编写的是源代码。即,它是一个命令序列,而终端将一个个往下执行这些命令。但终端无法执行源代码。源代码对您来说是可以理解的,但 MetaTrader 理解不了。要终端可以理解源代码,应将源代码“翻译”成合适的“语言”。要进行“翻译”,在 MetaEditor 中按下F5键。之后,源代码将被编译成一个运行文件。编译是一种将您编写并可理解的源代码“翻译”成 MetaTrader 可理解并可执行的特殊文件的过程。自己试一试。新建一个名为“Test1”的脚本,但不要编译。使用“浏览器”打开终端,进入“脚本”文件夹。您会看到并没有名为“Test1”的脚本:
现在编译此脚本(F5 键):
再次进行终端。“Test1”脚本出现了。
在终端浏览器中双击该脚本名称,该脚本将打开。但什么都不会发生,因为脚本是空的。
现在您应该已经了解编写脚本的流程是什么样的了:编写源代码,编译脚本,在终端中打开脚本,查看结果,更改特定代码,编译,查看...就这么循环,直到得到想要的结果。
应该在哪里编写脚本?
您应该已经看到,空脚本包含一个特定代码。但应该将源代码写到哪里?什么源代码有用?应该将源代码写在int start(){行与 return(0);}行之间,如图中所示:
在这里编写我会提供给您的所有东西,这些都会起到作用。稍后我们将详细说明这些行的意义。
变量
什么是变量?试着自己找到答案,我会提供一些帮助。您现在几岁?五年后您几岁?看,您的年龄就是个变量。您的年龄随着时间在变化,就像任何其他变量那样。也就是说,变量的第一个特性是它会随着时间而发生变化。另一个示例:您五岁时多高?很可能比现在矮得多。身高又是一个变量示例。但有一个很重要的区别点。注意,年龄是用整数来算的。身高一般要算到小数位(“浮点数”)。年龄:20 岁,30 岁。身高:1.8米,1.9米。这是个非常重要的特点:每个变量都属于特定的类型。我们来看看还有哪些其他类型的变量。您可以用数字来描述很多参数,但怎么描述文本呢?特殊字符串类型就用于这个用途。此类变量类型仅包含行。现在我们来看看如何用 MQL4 语言创建和描述变量。示例:
int age = 25;
这里我们看到一个整型变量 (int - integer)。Int 是 MQL4 语言里的一个关键字,即我们使用一个整数类型。然后我们写入“年龄”- 这是变量的名称,即此变量中存储的用于表达意义的词。之后我们使用符号“=”将值 25 分配给此变量。每条指令之后都应有一个“;”。注意,任何变量的声明和初始化都遵循以下形式:
[ 变量类型] [ 变量名称] = [ 变量值];
此外,不必将值分配给变量(初始化),你可以这么写:
int age;
再举一个例子:
double height = 1.95;
这里我们声明一个名为“身高”的变量,此变量存储 double 类型值(浮点数、小数),并用“=”运算符分配值 1.95。
现在我们来看看字符串变量:
string name = "Janet";
字符串是一种变量类型,名称是一个变量名称,“Janet”是一个变量值。注意,字符串类型变量的值要放在双引号(“”)中间。
还有一种非常有用的变量类型 - 布尔型。这种变量仅可接受两个值:true 或 false。示例:
bool trend = false;
现在您应该记住一些简单的东西。MQL4 语言是一种区分大小写的语言,即用编写代码时用大写还是小写字母有很大的区别。例如,如果您声明几个变量时使用相同的名称但不同的大小写,那将得到完全不同的变量:
double HIGHTPRICE;
double hightprice;
double HightPrice;
double hightPrice;
上述代码将创建四个完全不同的变量。还请注意,MQL4 语言的所有关键字都是小写的。
下一个示例:
DOUBLE hightPrice1;
Double hightPrice2;
上述代码无法正常工作,因为“double”将不会被接受为 MQL4 语言的一个关键字。还有个更重要的注意事项,变量名称不能用数字或特殊符号(*、&、%、$)开头。例如:
double 1price;
double %price;
注释也是一个语言元素。如果在一行开头写入“//”,那么整行都是注释。这意味着编译期间,这行将被忽略。例如:
// this is a comment
现在您可以看到,一个空脚本代码包括对一个资料性字符的很多注释。注释您的代码。有时它会帮助您节省大量时间。
使用变量
现在我们来看看在声明这些变量后该对它们做什么。来一个简单的例子:
double a = 50.0;// declare a value with a floating point and
// assign the value 50 to it
double b = 2.0;
double c;
c = a + b; // assign to the variable c sum of variables
// a and b. Now the value is equal to 52. Like after
// any other instruction put a semicolon (“;”)
c = a - b; // diminution, c = 48
c = a*b; // multiplication, c = 100
c = a / b; // division, c = 25
c = (a + b)*a; // place the operations that should be performed
// first inside the brackets. In our case
// first we get the sum of a and b
// after that this sum will be multiplied by a and assigned to c
c = (a + b) / (a - b); // if there are several operations in brackets,
// they will be performed
c = a + b*3.0; // according to mathematic rules first you will get
// multiplication b and 3, and then the sum
如果需要用某个变量执行一项运算,并分配一个结果给它,例如加 5,可以采用以下方式之一:
int a = 5;
a = a + 5; // add 5
a += 5; // analogous
a = a*5;
a *= 5; // multiply a by 5 and assign to it
a /= 5; // divide and assign
如要加 1 或减 1,用以下方法:
int a = 5;
a++; // add1, it is called increment
а--; // subtract 1, it is decrement
这都可以,但用这种脚本的话,您无法确定这一切是否能正常运行,因为屏幕上没有任何反应。
这就是显示结果会很方便的原因。为此,我们需要使用一个集成函数 MessageBox()。
MessageBox()
一个函数就是一套指令,它接受参数,并根据参数显示结果。在我们的示例中,MessageBox() 函数接受两个参数:第一个是消息文本,第二个是标题文本。示例如下:
MessageBox("Hello, World! There is some text.","caption");
要执行一个函数,首先写入其名称。别忘了区分大小写!然后在括号中写入参数,用逗号隔开。我们示例中的参数是字符串类型的参数。正如我们记得的那样,所有行都用引号(“”)括起来的。在任何指令的末尾加一个分号。为了正确理解,我们来看图。它显示了代码和结果之间的关联。
当然,一切正常。但我们如何展示其他类型的变量呢?很简单 - 牢记在心里:
int a = 50;
int b = 100;
MessageBox("It is very simple. a+b=" + (a + b), "a+b=?")
得到的结果是:
如您所料,MQL4 就是这么设计的,当我们尝试向某行中添加其他数字类型时,它会自动将数字传递到行中并合并它们。这真是一个美妙的特性!您也可以对字符串变量进行这种运算:
int a = 50;
int b = 100;
string str1 = "a + b =";
str1 += a + b; // now str1 = "a + b = 150"
// now use the variable str1 as
// a first parameter
MessageBox(str1, "a + b = ?");
现在您知道如何使用 MessageBox() 函数提取不同的数据了。但是,能够显示简单的数学运算结果又算什么?我们对 MQL4 的要求可不仅仅是算个加法和乘法,不是吗?
数组
别怕。这很简单。看一看。假设您要记住五个价格。我们该怎么做?好吧,我们这么做:
double price1 = 1.2341;
double price2 = 1.2321;
double price3 = 1.2361;
double price4 = 1.2411;
double price5 = 1.2301;
我们得到五个变量,它们只有一个数据类型,且描述同一个参数 - 价格。我们可以换种方法,用一个数组。一个数组就是一组变量,指数不同,但名称相同。从五个元素声明一个数组,方式如下:
double price[5];
常用形式:
(数组类型)(数组名称) [元素数量];
在我们的示例中:数组类型 - double(双类型),名称 - price(价格),元素数量 - 5。我们来看如何引用这些数组元素:
double price[5]; // declare an array of 5 elements
price[0] = 1.2341; // refer to the first element of the array and
// assign a price to it. Note
// that the index of the first element starts with 0
// It is an important feature,
// you should get used to it.
price[1] = 1.2321; // refer to the second element
price[2] = 1.2361; // and so on
price[3] = 1.2411;
price[4] = 1.2301;
就像常用变量一样,我们可以对数组元素执行任何运算。实际上,数组的元素就是常用变量。
double price[2];
price[0] = 1.2234;
price[1] = 1.2421;
MessageBox("Middle price is " + (price[0] +
price[1]) / 2.0,"middle price");
声明一个数组时,可以向所有元素分配初始值。
double price[2] = {1.2234, 1.2421};
我们简单地列举元素的初始值(在大括号中用逗号隔开)。在这种情况下,您可让编译器自动放上元素的数量,而不是您手动写入。
double price[] = {1.2234, 1.2421};
毫无疑问这些都是可以的,不过,遗憾的是,这根本没什么用处。我们总得弄到一点实际的数据吧!例如,当前价格、时间、可用金额等。
集成或内置的数组和变量
没有实际的数据,我们当然什么都做不了。要获得实际数据,我们只需参考对应的内置数组。以下是几个内置数组:
High[0]; // refer to the last maximal price,
// that the bar has achieved in the current timeframe
// and current currency pair. Currency pair and
// the timeframe depend on the chart, on which you
// have started a script. It is very important to remember!
Low[0]; // minimal price of the last bar
// in the current chart.
Volume[0]; // value of the last bar in the current chart.
要正确理解内置数组和指数,看看这个:
最后一根条柱的指数(编号)为 0,旁边一根为 1,以此类推。
还有内置常用变量。例如,Bars 显示当前图表中的条柱数量。它是个常用变量,但它早就已经声明了,并非在您的脚本中进行的声明。此变量像其他内置数组和变量一样始终存在。
循环
假设您决定要计算图表中所有条柱的最大价格的平均值。为此,您将各个元素轮流添加到一个变量,如下所示:
double AveragePrice = 0.0;
AveragePrice += High[0];
AveragePrice += High[1];
AveragePrice += High[2];
AveragePrice += High[3];
AveragePrice += High[4]; // ... and so on
AveragePrice /= Bars;
我只能告诉您一点:这种方法可行,但有些可笑。循环专门适用此类用途。注意,所有运算都是完全类似的,只有指数从 0 更改为变量 Bars-1 值。确定计数器并用其引用数组元素某种程度上来说就已经非常方便了。我们可以用循环来解决这个任务:
double AveragePrice = 0.0;
for(int a = 0; a < Bars; a++)
{
AveragePrice += High[a];
}
我们看看各行:
double AveragePrice = 0.0; // everything is clear
// this is a cycle.
or(int a = 0; a < Bars; a++)
循环用关键字 for 开头。(还有其他类型的循环,例如while,但我们现在不讨论它们)。然后在引号中指明用分号隔开的计数器、循环计算条件、计数器增加运算。通常它以以下方式呈现:
for(declaration of a counter; the cycle operation conditions;
counter changes)
{
// the source code, that will be repeated is in braces,
}
让我们更近距离地了解循环声明的各个阶段。
计数声明int 类型用于此计数器。变量计数器的名称无关紧要。您应初始化主值,例如初始化为 0。
计数器计算条件:这很简单。在此处确定一个条件,如果它为 true,循环继续进行。否则,循环终止。例如在我们的示例中:
a < Bars
很明显,当变量计数器小于变量 Bars 时,循环将继续进行。假设变量 Bars=10,则在循环上每移动一次,变量增加 1,直至达到 10。之后,循环将停止。
计数器更改:如果我们不更改计数器(在我们的示例中是增加它),将发生什么情况?循环将永不停止,因为条件永不会满足。为了更好地理解循环的意义,我编写了一段可执行循环的代码,并提供了注释:
// the cycle:
// double AveragePrice=0.0;
//
// for(int a=0;a>
// {
// AveragePrice+=High[a];
// }
//
// will be performed in this way:
//
double AveragePrice=0.0;
int a=0;
AveragePrice+=High[a];
a++; // now a=1, suppose Bars=3.
// then the cycle goes on, because Bars > a
AveragePrice+=High[a];
a++; // a=2
AveragePrice+=High[a];
а++; // a=3
// the conditions is not fulfilled any more, so the cycle
// stops, because a=3 and Bars=3
现在您应该了解循环的工作方式了。但还应该再了解一些细节。
循环计算条件各有不同。如下例中所示:
a>10 // the cycle works while a>10
a!=10 // the cycle works while a is not equal to 10
a==20 // while a is not equal to 20
a>=2 // while a is more or equal to 2
a<=30 // while a is less or equal to 30
计数器可用不同的方式进行更改。例如,您不必每次都加 1。您可以这么做:
a-- // the counter will each time decrease by 1
a += 2 // the counter will each time increase by 2
此外,您可将计数器更改放到循环主体内。如下例中所示:
for(int a=0; a<Bars;)
{
AveragePrice+=High[a];
a++; // the counter changes inside the cycle body
}
>
也不必在循环中声明变量计数器。您可以换一种方式:
int a = 0;
for(;a < Bars;)
{
AveragePrice += High[a];
a++; // the counter changes inside the cycle body
}
如果循环主体仅包含一个操作符,如下所示:
for(int a = 0; a < Bars; a++)
{
AveragePrice += High[a];
}
然后也不必使用发括号:
for(int a = 0; a < Bars; a++)
AveragePrice += High[a];
这就是目前为止有关循环的所有内容了。还有其他的循环类型,我们会在下一课中讨论。现在您应该知道何时使用循环并记住语法。尝试编写几个循环,以通过 MessageBox() 函数显示计数器值。尝试编写一个非连续循环,看看启动后会发生什么。
条件
还有一个您始终会用到的重要项目 - 条件。我们的生活充满着大量的条件以及根据这些条件进行的活动。我们往往采用的是条件式的思考方式。例如:“如果我有充足的时间,我会读这本书。如果没有,还是读本杂志吧”。您可以生成数以百计的此类条件和行动。但我们如何用 MQL4 来编写它们呢?示例如下:
// of course the conditionsn should be written in MQL4
if( I have enough time )
{
// here we place any actions, directions in MQL4
will read this book;
}
// if the first condition is not fulfilled,
// the second is fulfilled
else
{
read a magazine; // code
}
现在您应可理解这种条件式语法了。我们看看用 MQL4 语言完整编写的条件:
int a = 10;
int b = 0;
if(a > 10 )
{
b = 1;
}
else
{
b = 2;
}
MessageBox("b=" + b,".");
这一切都很简单。完成后,b 的值是多少?当然 b=2,因为条件 a > 10 并未满足。这很简单。另外也不必使用其他关键字:
int a = 10;
int b = 0;
if(a > 10)
{
b = 1;
}
在这种情况下,如果某个条件未满足,遵循大括号中该条件的代码块将被忽略。在我们的示例中是在满足 b = 0 之后。同时还要注意条件的构建方式。我们知道,不同类型的变量会获得不同的值,例如:
- int - 整数(1、60、772);
- double - 浮点数(1.0021、0.221);
- string - 仅行(“字”、“一些文本”);
- bool - 仅 true 或 false(true、false)。
因此,得出的结论是:在条件中比较仅带可接受值的变量,例如:
int Integer=10;
double Double=1.0;
string String="JustWord";
bool Bool=true;
if(Integer<10) // the condition is correct
{
MessageBox("It works!","Really!");
}
if(Double!=1.0) // the condition is correct
{
MessageBox("works!","Simple!");
}
if(String==10) // this is nonsense!!
// we found the variable
//String with string type, but 10 is int
{
MessageBox("Wrong type","u can't c this");
}
if(String!="Word") // ok
{{
// ...
}
if(Bool==true) // correct
{
// ...
}
注意,我们使用条件运算符(==, !=, >, <, >=, <=).对于字符串类型和布尔类型,仅使用 == 和 != 进行比较。
现在我们来了解一下其中内容的含义。是的,您可以在循环中使用条件,也可以在条件中使用循环;您可以在条件中使用其他条件,等等。例如:
int a=0;
double b=0.0;
bool e;
if(a==0)
{
for(int c=0;c<Bars;c++)
{
b+=High[c];
}
if(b>500.0)
{
e=true;
}
else
{
e=false;
}
}
下例显示的是条件的另一种使用方式:
int a=0;
int result;
if(a==0)
{
result=1;
}
else if(a==1)
{
result=2;
}
else if(a==2)
{
result=3;
}
else
{
result=4;
}
如果某个条件默认有另一个条件,在关键字“else”后编写该条件,具体参考上述编码。这也是可行的。其他条件的数量不受限制。如果用于满足条件的行动符合一项运算,您可忽略循环中的大括号。
if(a==1)
{
b=2;
}
// or this way:
if(a==1)
b=2;
复杂条件
通常一个条件是不够的。您需要比较很多参数;这时就需要使用复杂条件。例如,如果我有足够的时间和耐心,我会好好学习 MQL4 语言。我们可以将它编写成代码:
if((enough time) && (enough oatience))
{
I will learn MQL4 language;
}
这意味着您首先应将复杂条件细分为简单条件,将它们放到括号中,并在它们之间加上 &&(逻辑 AND)或 ||(逻辑 OR)。如果两个条件都为真,添加 && (AND)。如果只有一个条件为真,添加 || (OR)。示例如下:
nt a=10;
int b=20;
if((a>5) && (b<50))
MessageBox("Its works!","Yes");
除此以外,您可根据需要任意添加和组合条件:
int a=10;
int b=20;
if ( ((a>5) || (a<15)) && ( b==20))
MessageBox("It works, too!","Yes");
混合使用
要在循环中同时使用复杂条件和简单条件,您可能要编写一段非常复杂的代码。几乎所有机制都可以用 MQL4 语言的这些浅显结构来表示。如果理解了这些简单代码的编写和操作原理,您也就弄懂了一半的 MQL4 或任何其他编程语言!这其实是非常简单的!您所需要的只是多练习。试着尽可能多地编写脚本,以记住语法和获取实践经验。此外,查看附件 examples.mq4 中的示例,尝试理解它们。
其他内置变量和数组
您已了解了 High[]、Low[]、Volume[] 等数组以及 Bars 这个变量。以下是一些其他的有用变量:
double Open[] // the array of prices of the current
// chart opening bars
double Close[] // the array of prices of the current
// chart closing bars
double Bid // the last known buying price in
// the current currency pair
double Ask // the last known selling price in
// the current currency pair
总结
您已经学了不少东西。也许现在您脑子里乱糟糟一团。重读本文,加深记忆,进行练习,尝试理解含义。我敢保证,不用多久一切都将会豁然开朗。本文介绍了整个 MQL4 语言的基础知识。对文中内容理解地越深,后续的学习就越轻松。再多说一句 - 后续的学习为何为简单得多?因为本文的内容就已经是最难的部分了。下一篇文章中,我们将学习 MQL4 语言的各个不同特点,并了解其他一些集成函数,这些函数将为编程带来更多的可能性。