您需要了解的有关MQL5程序结构的所有信息
概述
使用任何编程语言的每一个软件都有一个结构,在理解了这个结构之后,我们就可以顺利地创建或开发我们的软件。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
我们可以在输入选项卡中找到与以下相同的参数:
正如我们所看到的,这些参数看起来与我们在前一张图片中看到的不同。您可以从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程序中非常重要的组件。事件处理函数是一个可执行的函数,当特定事件发生时,如接收到新的报价时,即专家顾问发生的新报价事件,则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