面向初学者的 MQL4 语言系列之1——MQL4 语言入门

0
回复
6340
查看
[复制链接]

41

主题

32

回帖

248

积分

中级会员

积分
248
来源: 2019-8-3 10:07:20 显示全部楼层 |阅读模式
本帖最后由 量子量化 于 2019-8-3 10:18 编辑

简介

本系列的文章主要针对的是完全不懂编程,但想要在最短的时间内花费最少的精力尽快了解 MQL4 语言的交易者。如果您看到“面向对象”或“三维数组”这类词语就觉得头痛,那么这篇文章正是您需要的良方。这些课程的设计旨在最快出成果。内容也通俗易懂。我们在理论方面不会有太深的研究,但从第一课起就已能获得实际的收益了。


建议

如果您之前从未做过编程工作,第一次阅读某些案例时,您会发现很难懂其中的意思。慢慢再看一遍文本,仔细思考每个句子。您终会豁然开朗,因为这实际上真的不难。在理解之前的信息后,方可进行下一步。研究代码示例。根据您学到的内容编写自己的代码示例。


首先要了解 MQL4

让我们先来看一下可以用这种语言做什么。它主要用于创建脚本、自定义指标、EA 和库:

  • 脚本就是命令序列,即您每请求一次才运行一次的程序。它们可替代您每天在交易中执行的操作。例如当您开订单时。它们也可执行特定功能,例如分析图表和生成统计信息。
  • 定制指标是技术指标,主要作为内置指标的补充。它们用于开发图表或其他可视信息。和脚本不同的是,自定义指标在每次价格变动(即每跳动一次)时运行一次。显示什么指标只取决于您。它可以是一个没什么作用的窦道图,也可以是帮助您探寻市场方向的强大工具。例如,如果您确切地知道市场在什么时候、哪些情况下会趋于平盘态势,您可以将其编写到一个指标里。
  • Expert Advisor是绑定到任何金融工具上的机械交易系统。与自定义指标类似,每跳动一次,expert advisor 就作用一次,其与指标的不同之处在于,它们可以通知您市场情况(例如,提供某些买入或卖出建议)或自己进行交易,无需您的帮助。终端可支持策略测试,从而快速评估您的 expert advisor 的盈利能力。您可以用 MQL4 语言描述您的策略,而终端会坚定不移地遵循您的所有指示。
  • 是用于执行特定任务的函数集合。例如,您的某个 EA 可能使用了特殊的数学函数来决定买入和卖出的时机。


本文中我们将学习编写通用脚本。为此我们使用了一个特殊的程序 - MetaEditor 4。要启动它,在打开的自定义终端上按 F4 键。要新建一个脚本,单击 MetaEditor 4 菜单中的“文件”->“新建”按钮,用用“Ctrl+N”按钮:
01.png

在显示的窗口中,选择要创建的项目。选择“脚本”,然后单击“下一步”:

file:///D:/%25E5%2585%25B6%25E4%25BB%2596%25E8%25BD%25AF%25E4%25BB%25B6/%25E6%259C%2589%25E9%2581%2593%25E8%25B5%2584%25E6%2596%2599/gyshssl@126.com/620f4b5f36fb43bbab835f7b6d0ef935/screen2__1.png
02.png
在下一个窗口中,在名称字段中名称字段中键入脚本名称。在作者字段中添加您的姓名并在链接字段中键入脚本名称。然后单击确定


03.png


file:///D:/%25E5%2585%25B6%25E4%25BB%2596%25E8%25BD%25AF%25E4%25BB%25B6/%25E6%259C%2589%25E9%2581%2593%25E8%25B5%2584%25E6%2596%2599/gyshssl@126.com/c97fec91fbe14b04933e80539fc9bdf0/screen3_1.png
之后会看到一个新窗口,这是您将使用的最重要的工具。它显示原始文本

04.png


请注意,即便是一个空白的、不可运行的脚本也包含着一个代码。现在万事俱备,开始编程。但很遗憾,接下来该怎么做,您一点头绪都没有。让我们试着改变这种状况。


源代码、编译和其他

您应该了解一件重要的事情。在 MetaEditor 中编写的是源代码。即,它是一个命令序列,而终端将一个个往下执行这些命令。但终端无法执行源代码。源代码对您来说是可以理解的,但 MetaTrader 理解不了。要终端可以理解源代码,应将源代码“翻译”成合适的“语言”。要进行“翻译”,在 MetaEditor 中按下F5键。之后,源代码将被编译成一个运行文件编译是一种将您编写并可理解的源代码“翻译”成 MetaTrader 可理解并可执行的特殊文件的过程。自己试一试。新建一个名为“Test1”的脚本,但不要编译。使用“浏览器”打开终端,进入“脚本”文件夹。您会看到并没有名为“Test1”的脚本

05.png


现在编译此脚本(F5 键)
06.png


再次进行终端。“Test1”脚本出现了

07.png
在终端浏览器中双击该脚本名称,该脚本将打开。但什么都不会发生,因为脚本是空的。

现在您应该已经了解编写脚本的流程是什么样的了:编写源代码,编译脚本,在终端中打开脚本,查看结果,更改特定代码,编译,查看...就这么循环,直到得到想要的结果。

应该在哪里编写脚本?

您应该已经看到,空脚本包含一个特定代码。但应该将源代码写到哪里?什么源代码有用?应该将源代码写在int start(){行与 return(0);}行之间,如图中所示

08.png
在这里编写我会提供给您的所有东西,这些都会起到作用。稍后我们将详细说明这些行的意义。

变量

什么是变量?试着自己找到答案,我会提供一些帮助。您现在几岁?五年后您几岁?看,您的年龄就是个变量。您的年龄随着时间在变化,就像任何其他变量那样。也就是说,变量的第一个特性是它会随着时间而发生变化。另一个示例:您五岁时多高?很可能比现在矮得多。身高又是一个变量示例。但有一个很重要的区别点。注意,年龄是用整数来算的。身高一般要算到小数位(“浮点数”)。年龄: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() 函数接受两个参数:第一个是消息文本,第二个是标题文本。示例如下:
CalculateCurrentOrders要执行一个函数,首先写入其名称。别忘了区分大小写!然后在括号中写入参数,用逗号隔开。我们示例中的参数是字符串类型的参数。正如我们记得的那样,所有行都用引号(“”)括起来的。在任何指令的末尾加一个分号。为了正确理解,我们来看图。它显示了代码和结果之间的关联

09.png
当然,一切正常。但我们如何展示其他类型的变量呢?很简单 - 牢记在心里:
int a = 50;
int b = 100;   
MessageBox("It is very simple. a+b=" + (a + b), "a+b=?")

得到的结果是:
file:///D:/%25E5%2585%25B6%25E4%25BB%2596%25E8%25BD%25AF%25E4%25BB%25B6/%25E6%259C%2589%25E9%2581%2593%25E8%25B5%2584%25E6%2596%2599/gyshssl@126.com/5d26c9ce42fb454c9529ea3d52e7bd21/screen7__1.png

如您所料,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.

要正确理解内置数组和指数,看看这个:

10.png
最后一根条柱的指数(编号)为 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 语言的各个不同特点,并了解其他一些集成函数,这些函数将为编程带来更多的可能性。
上涨-黄金.jpg


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 免费注册
关注微信