English Русский Español Deutsch 日本語 Português
preview
您需要了解的有关MQL5程序结构的所有信息

您需要了解的有关MQL5程序结构的所有信息

MetaTrader 5交易 | 19 二月 2024, 10:49
796 0
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

概述

使用任何编程语言的每一个软件都有一个结构,在理解了这个结构之后,我们就可以顺利地创建或开发我们的软件。MQL5语言的程序与任何其他编程语言都是一样的。它有自己的结构,开发者应该理解它,以顺利有效地实现项目的目标。在本文中,我们将提供这方面的信息,以尽可能轻松地交付其内容。我们将通过涵盖以下主题来学习任何MQL5程序的结构:

在前面的主题之后,假设您将非常好地理解任何MQL5程序的结构,并且您可以顺利有效地基于此结构创建或开发任何软件。

免责声明:所有按原样提供的信息仅用于教育目的,不用于交易目的或建议。这些信息不能保证任何结果。如果您选择在任何交易账户上使用这些材料,您将自行承担风险,并且您将承担唯一责任。


预处理器

在这一部分中,我们将详细了解预处理器作为一个编程概念。预处理器是编译过程中至关重要的一步。它发生在程序的实际编译之前。在预处理步骤中,将执行各种操作,例如包括文件、确定软件属性、定义常量和导入函数。

所有预处理器指令都以(#)开头。这些指令不被视为语言语句,因此,它们不应以分号(;)结尾。在预处理器指令的末尾包含分号可能会导致基于指令类型的错误

换句话说,我们可以说预处理器是指在编译过程之前准备好程序源代码。有许多类型的预处理器指令基于参数,我们需要在MQL5程序中确定这些参数,如下所示:

  • 宏替换(#define)
  • 程序属性(#property)
  • 包含文件(#include)
  • 导入函数(#import)
  • 条件编译(#ifdef、#ifndef、#else、#endif)

宏替换(#define):

#define预处理器指令可用于创建符号常量或定义要在程序中使用的常量。如果您不知道什么是常量,那么它就是一个具有不变值的标识符。我们还可以说,#define指令可以用于为常量分配助记符名称,因为我们将为特定标识符使用替换值。此预处理器指令的第一种格式与以下格式相同:

#define 标识符 替换值

因此,我们在程序中有这行代码,这意味着在编译程序之前,标识符将被替换值替换。此格式是没有参数的#define指令或无参数格式,MQL5中还有另一种格式,它是参数格式,最多允许八个参数与#define指示一起使用,如下所示:

#define 标识符(参数1,参数2,…参数5)

变量的相同规则支配常量标识符:

  • 该值可以是任何类型,如整数、双精度或字符串
  • 表达式可以是多个标记,并且在该行结束时结束,并且不能移动到下一行代码

以下是一个例子:

//Parameter-free format
#define INTEGER               10                                     //int
#define DOUBLE                10.50                                  //double
#define STRING_VALUE      "MetaQuotes Software Corp."                //str
#define INCOMPLETE_VALUE INTEGER+DOUBLE                              //Incomlete
#define COMPLETE_VALUE (INTEGER+DOUBLE)                              //complete
//Parametic format
#define A 2+3
#define B 5-1
#define MUL(a, b) ((a)*(b))
double c=MUL(A,B);
//function to print values
void defValues()
  {

   Print("INTEGER Value, ",INTEGER);         //result: INTEGER Value, 10
   Print("DOUBLE Value, ",DOUBLE);           //result: DOUBLE Value, 10.50
   Print("STRING Value, ",STRING_VALUE);     //result: STRING Value, MetaQuotes Software Corp.
   Print("INCOMPLETE Value, ",INCOMPLETE_VALUE*2);     //result: INCOMPLETE Value, 31
   Print("COMPLETE Value, ",COMPLETE_VALUE*2);     //result: STRING Value, 41
   Print("c= ",c);                                  //result: c= 41
  }

还有(#undef)预处理器指令,它还取消了之前声明或定义的内容。

程序属性(#property):

当我们创建软件时,我们可能会发现自己需要指定额外的参数,我们可以通过使用#property来完成。必须在主mql5文件中指定这些属性,而不是在include文件中指定,并且在include中指定的属性将被忽略。因此,我们可以说#property指令为程序指定了额外的属性。如果你问我们在这种情况下需要指定什么,我们可以回答这个问题,我们有很多东西,例如,指示符、脚本、描述性信息和库属性。与其他预处理器指令一样,#property 将在源代码的顶部指定,执行时将显示在程序窗口的公共选项卡上。

以下是此类预处理器指令的示例:

#property copyright "Copyright 2023, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property description "Property preprocessor"

我们可以在程序窗口中看到这些值,如下图所示:

属性

正如我们在前一张图片中看到的那样,我们在执行EA时在公共选项卡中定义了我们需要的属性,以及版权2023,MetaQuotes有限公司的文本。是一个超链接。当悬停它时,我们可以看到它,当按下它时,它将指向链接属性的链接。

包含文件(#include):

通常情况下,所有#include指令都将放在程序的开头。它指定了一个包含在特定软件中的包含文件,这意味着包含的文件成为软件的一部分,我们可以使用它的内容,如变量、函数和类。

有两种格式可以通过#include指令包含文件:

#include <File_Name.mqh>
#include "File_Name.mqh"

这两种格式的区别在于我们需要编译器查找要包含的文件的位置,第一种是让编译器在MetaTrader 5安装的Include文件夹或标准库头文件中查找文件,第二种是让编译程序在与程序文件相同的目录中查找文件。

导入函数(#import):

#import指令用于将已编译的MQL5模块(*.ex5文件)和操作系统模块(*.dll文件)中的函数导入软件。必须完整描述该功能,其格式如下:

#import "File_Name"
    func1 define;
    func2 define;
    ...
    funcN define;
#import

条件编译 (#ifdef, #ifndef, #else, #endif):

条件编译允许我们除了控制程序的编译之外,还控制预处理指令的执行。它使我们能够根据特定条件控制编译或跳过程序代码的一部分,该特定条件可以是以下格式之一:

#ifdef identifier
   //If the identifier has been defined, the code here will be compiled.
#endif
#ifndef identifier
   // If the identifier is not defined, the code here will be compiled.
#endif

正如我们之前提到的,如果我们移动到一个新行,预处理器指令将不会继续,但在这里,通过使用#else和#endif,这种类型的指令后面可以跟任意数量的行。如果条件为true,则忽略这两个#else和#endif之间的行,但如果条件不满足,则忽略检查条件和#else(或#endif,如果前者不存在)之间的行。

您可以从MQL参考中了解有关MQL5中预处理器的更多信息。


输入和全局变量

在这一部分中,我们将在预处理器指令之后识别MQL5程序结构的其他组件,这些指令是输入和全局变量。我们将从输入变量开始,在写入指定数据类型的输入修饰符后,输入变量定义外部变量。因此,我们有输入修饰符和输入变量的值,输入修饰符不能在mql5程序中修改,但值只能由程序的用户从程序属性的输入窗口或选项卡中更改。当我们通过输入修饰符定义这些外部变量时,总是在调用OnInIt()之前重新初始化。

以下是输入变量的格式:

input int            MA_Period=20;
input int            MA_Shift=0;
input ENUM_MA_METHOD MA_Method=MODE_SMA;

之后,我们可以找到用户要确定的输入窗口,如下图所示:

输入参数

正如我们所看到的,我们可以定义MA周期、MA偏移和MA的类型。我们还可以通过在窗口中放置我们需要看到的内容的注释来确定输入参数在输入选项卡中的外观,与前面的示例相同:

input int            MA_Period=20;        //Moving Average Period 
input int            MA_Shift=0;          //Moving Average Shift
input ENUM_MA_METHOD MA_Method=MODE_SMA;  //Moving Average Type

我们可以在输入选项卡中找到与以下相同的参数:

输入参数1

正如我们所看到的,这些参数看起来与我们在前一张图片中看到的不同。您可以从MQL5参考中了解有关输入变量的更多信息。

全局变量必须在事件处理程序之外创建,或者在同一级别的函数中创建,如果我们想查看这些全局变量的示例,我们可以看到如下所示:

int Globalvar;   // Global variable before or outside the event handler and functions
int OnInit()
  {
   ...
  }

因此,我们可以说全局变量的作用域是整个程序,它们可以从程序中的所有函数访问,在加载程序时和OnInit事件处理或OnStart()事件处理之前初始化一次,我们稍后将讨论事件处理程序,但在这里我提到它们是为了显示全局变量在MQL5程序结构中的位置。

您可以从MQL5参考中了解有关全局变量的更多信息。


函数,类

讨论了预处理器、输入和全局变量之后,在这一部分中,我们将讨论MQL5程序结构的其他组件,即函数和类。前面有一篇关于函数的详细文章,您可以阅读它,通过了解应用程序中MQL5中的函数的文章来详细了解这些有趣的主题。如果你需要在理解MQL5中的面向对象编程(OOP)的背景下阅读一些关于类的内容,你也可以通过理解MQL5-面向对象编程的文章阅读我之前的文章。我希望你觉得它们很有用。

在这里,我们将提到这个重要组件在任何软件中的位置,就像自定义类一样,因为我们可以在软件中的任何地方定义它们,并且它可以在包含文件中定义,这些文件可以通过使用#include指令来包含,就像我们在预处理器主题中提到的一样。它们可以放在事件处理程序之前或之后,也可以放在输入变量和全局变量之下。

函数的格式如下所示:

returnedDataType functionName(param1, param2)
{
        bodyOfFunction
}

类的格式如下所示:

class Cobject
{
   int var1;       // variable1
   double var2;    // variable1
   void method1(); // Method or function1
};

您可以从MQL5参考中了解有关函数的更多信息。


事件处理函数

在这一部分中,我们将分享有关事件处理函数的信息,它们是mql5程序中非常重要的组件。事件处理函数是一个可执行的函数,当特定事件发生时,如接收到新的报价时,即专家顾问发生的新报价事件,则OnTick()事件处理程序将是可执行的,因为此事件处理程序具有可在接收到新价格或报价发生时运行的代码体。

根据MQL5程序的类型,有不同的事件处理函数,以下是针对这些事件处理函数的:

事件处理函数 描述 格式 
OnStart 此处理函数可用于脚本类型程序,以便在启动事件发生时调用函数。
  • 具有返回值的版本: 
int  OnStart(void);
  • 没有返回值的版本:
void  OnStart(void);
OnInit 它可以在EA交易和指标程序中使用,以便在初始化程序时调用函数
  •  具有返回值的版本:
int  OnInit(void);
  • 没有返回值的版本:
void  OnInit(void);

OnDeinit 它可以在EA和指标程序中使用,以在去初始化程序时调用函数
void  OnDeinit(
   const int  reason         // deinitialization reason code
   );
OnTick 它可以在EA和指标中使用,以便在接收新报价时调用函数
void  OnTick(void);
OnCalculate 它可以在指标中用于在发送Init事件时以及在价格数据发生任何变化时调用函数
  • 基于数据数组的计算
int  OnCalculate(
   const int        rates_total,       // price[] array size
   const int        prev_calculated,   // number of handled bars at the previous call
   const int        begin,             // index number in the price[] array meaningful data starts from
   const double&    price[]            // array of values for calculation
   );
  • 基于当前时间段时间序列的计算
int  OnCalculate(
   const int        rates_total,       // size of input time series
   const int        prev_calculated,   // number of handled bars at the previous call
   const datetime&  time{},            // Time array
   const double&    open[],            // Open array
   const double&    high[],            // High array
   const double&    low[],             // Low array
   const double&    close[],           // Close array
   const long&      tick_volume[],     // Tick Volume array
   const long&      volume[],          // Real Volume array
   const int&       spread[]           // Spread array
   );
OnTimer 当交易终端发生Timer周期性事件时,它可以在EA和指标中用于调用函数
void  OnTimer(void);
OnTrade 它可以在EA中用于在交易服务器上完成交易操作时调用函数
void  OnTrade(void);
OnTradeTransaction 它可以在EA中用于在对交易账户执行某些特定操作时调用函数
void  OnTradeTransaction()
   const MqlTradeTransaction&    trans,     // trade transaction structure
   const MqlTradeRequest&        request,   // request structure
   const MqlTradeResult&         result     // response structure
   );
OnBookEvent 它可以在EA中用于在市场深度发生变化时调用函数
void  OnBookEvent(
   const string&  symbol         // symbol
   );
OnChartEvent 当用户使用图表时,可以在指标中使用它来调用函数
void  OnChartEvent()
   const int       id,       // event ID 
   const long&     lparam,   // long type event parameter
   const double&   dparam,   // double type event parameter
   const string&   sparam    // string type event parameter
   );
OnTester 当EA交易对历史数据的测试结束时,它可以在EA中用于调用函数
double  OnTester(void);
OnTesterInit 它可以在EA中用于在第一次优化通过之前在策略测试器中开始优化时调用函数
  • 具有返回值的版本
int  OnTesterInit(void);
  • 没有返回值的版本
void  OnTesterInit(void);
OnTesterDeinit 它可以在EA中用于在策略测试器中的EA交易优化结束后调用函数
void  OnTesterDeinit(void);
OnTesterPass 当接收到新的数据帧时,它可以在EA中用于调用函数
void  OnTesterPass(void);

您可以从MQL5参考中了解有关事件处理的更多信息。


MQL5程序示例

在这一部分中,我们将应用我们现在学到的知识,使用正确的MQL5结构创建一个简单的应用程序。我们提到,我们可以根据程序类型和所需任务使用MQL5结构的组件,因为没有义务使用其中的一些组件,例如使用#include预处理器,因为它可能不需要包括与#property相同的任何外部文件,因为使用或不使用它是可选的。此外,它可能需要或不需要在程序中创建自定义类或函数。无论如何,您将使用程序所需的内容,以下是一些简单的应用程序,用于基于不同的程序类型呈现所有所需的结构组件。

脚本类型:

以下是脚本MQL5程序的一个简单示例,该程序能够通过输入计算和添加用户输入的两个数字,并通过使用打印功能在“专家”选项卡中打印结果。我在这里需要提到的是,在这个脚本程序中,我们将添加一个#property,该属性允许显示脚本输入以由用户输入数字。

//+------------------------------------------------------------------+
//|                                       Script program example.mq5 |
//|                                   Copyright 2023, MetaQuotes Ltd.|
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
//property preprocessor
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property script_show_inputs
//inputs
input int userEntryNum1;
input int userEntryNum2;
//global variable
int result;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
//event handler
void OnStart()
  {
   result=userEntryNum1+userEntryNum2;
   Print("Result: ", result);
  }
//+------------------------------------------------------------------+

如果我们想创建另一个EA或指标程序,我们需要根据程序类型使用不同的事件处理程序。例如,EA程序可以使用OnTick()事件处理程序在接收到新的分时数据时执行操作

现在我们已经确定了MQL5程序的结构,我们看到某些组件根据程序类型及其目标或任务而有所不同。这种理解有助于我们确定软件中每个组件的位置

如前所述,要应用这些知识,我们可以从一个简单的脚本程序开始


结论

在我们通过本文主题提到的内容之后,假设您了解了任何MQL5程序的结构,并且能够根据其类型确定创建MQL5软件所需的组件,因为我们了解了构建MQL5结构所需的知识,该结构与以下序列相同:

  • 预处理器
    • 宏替换(#define)
    • 程序属性(#property)
    • 包含文件(#include)
    • 导入函数(#import)
    • 条件编译(#ifdef、#ifndef、#else、#endif)
  • 输入和全局变量
  • 函数和类
  • 事件处理函数
    • OnStart
    • OnInit
    • OnDeinit
    • OnTick
    • OnCalculate
    • OnTimer
    • OnTrade
    • OnTradeTransaction
    • OnBookEvent
    • OnChartEvent
    • OnTester
    • OnTesterInit
    • OnTesterDeinit
    • OnTesterPass

我希望您发现这篇文章对构建MQL5程序有帮助。了解其背景对于顺利有效的进程至关重要。如果你想了解更多关于使用流行的技术指标创建交易系统的信息,你可以参考我以前发表的关于这个主题的文章

此外,我还写过关于在任何EA中创建和使用自定义指标的文章,以及MQL5编程中的其他重要主题,如面向对象编程(OOP)和函数。我相信这些文章将对你的学习和交易之旅有价值。

本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/13021

附加的文件 |
RSI深三步交易技巧 RSI深三步交易技巧
在MetaTrader 5中展示RSI深三步交易技术。本文基于一系列新的研究,这些研究展示了一些基于RSI的交易技术,RSI是一种技术分析指标,用于衡量股票、货币或商品等证券的强度和动量。
MQL5 中的范畴论 (第 11 部分):图论 MQL5 中的范畴论 (第 11 部分):图论
本文是以 MQL5 实现范畴论系列的续篇。于此,我们验证在开发交易系统的平仓策略时,图论如何与幺半群和其它数据结构集成。
MQL5 中的范畴论 (第 12 部分):秩序(Orders) MQL5 中的范畴论 (第 12 部分):秩序(Orders)
本文是范畴论系列文章之以 MQL5 实现图论的部分,深入研讨秩序(Orders)。我们通过研究两种主要的秩序类型,实测秩序论的概念如何支持幺半群集合,从而为交易决策提供信息。
使用格兹尔算法的循环分析 使用格兹尔算法的循环分析
在本文中,我们介绍了在Mql5中实现格兹尔算法(Goertzel algorithm)的代码实用程序,并探讨了将该技术用于分析报价的两种方法,以制定可能的策略。