简介
MetaTrader 5 提供了一款强大的工具,允许您快速检查各种交易理念。那就是基于现有的交易策略并使用 MQL5 向导来生成 EA 交易。
通过 MQL5 向导创建的 EA 交易基于四大支柱,即四个基本类:
图 1. CExpert 基类的结构
- CExpert 类(或其子类)是 EA 交易的主要“引擎”。CExpert 的实例包含下列每个类的一个副本:CExpertSignal、CExpertMoney 和 CExpertTrailing(或其子类):
- CExpertSignal 构成了交易信号生成器的基础。CExpert 包含的一个 CExpertSignal 派生类实例提供了 EA 交易以及市场准入可能性、准入水平和基于内置算法下达保护性订单方面的相关信息。EA 交易决定了是否进入市场。有关 CExpertSignal 类及其用法的更多详情请参见《MQL5 向导:如何创建交易信号模块》一文。
- CExpertMoney 类构成了风险和资金管理机制的基础。CExpert 包含的一个 CExpertMoney 派生类实例提供了 EA 交易以及敞口持仓的可能交易量和基于内置算法下达挂单方面的相关信息。EA 交易决定了交易量。
- CExpertTrailing 类构成了开仓支持机制的基础。CExpert 包含的一个 CExpertTrailing 派生类实例提供了 EA 交易以及基于内置算法修改持仓保护性订单的可能性方面的相关信息。EA 交易决定了是否修改订单。CExpertTrailing 类及其使用的更多详情将在另一章节中进行介绍。
此外,CExpert 类的成员还是以下类的实例:
- CExpertTrade(用于交易)
- CIndicators(用于控制 EA 交易中涉及的指标和时间序列)
- CSymbolInfo(用于获取工具的信息)
- CAccountInfo(用于获取交易账户状态的相关信息)
- CPositionInfo (用于获取持仓的相关信息)
- COrderInfo (用于获取挂单的相关信息)
在下文中,EA 交易指的是 CExpert 或其子类的实例。
CExpert 及其使用的更多详情将在另一章节中进行介绍。
1. CExpertMoney 基类
正如前文所述,CExpertMoney 类构成了风险和资金管理机制的基础。CExpertMoney 类采用了一套公共虚拟方法,用于同“外界”进行通信:
初始化 |
说明 |
虚拟 Init |
类实例的初始化可实现模块数据和 EA 交易数据的同步 |
Percent |
设置参数“风险百分比”的值 |
虚拟 ValidationSettings |
参数设置验证 |
虚拟 InitIndicators |
创建并初始化操作风险和资金管理机制所需的所有指标和时间序列 |
检查开仓/转仓/平仓必要性的方法 |
|
虚拟 CheckOpenLong |
确定买入持仓开仓交易量 |
虚拟 CheckOpenShort |
确定卖出持仓开仓交易量 |
虚拟 CheckReverse |
确定反向持仓的交易量 |
虚拟 CheckClose |
确定平仓的必要性 |
方法说明
1.1. 初始化方法
1.1.1 Init
Init() 方法在类实例添加至 EA 交易后立即被自动调用。无需覆盖该方法。
virtual bool Init(CSymbolInfo* symbol, ENUM_TIMEFRAMES period, double adjusted_point);
1.1.2 Percent
Percent() 方法被调用以配置合适的参数。它的值可以从 0.0 到 100.0(包括 0.0 和 100.0 在内)。默认值为 100.0。无需覆盖该方法。
void Percent(double percent);
1.1.3 ValidationSettings
在设置完所有参数后,ValidationSettings() 方法会从 EA 交易中直接调用。如果存在其他设置参数,您必须覆盖该方法。
virtual bool ValidationSettings();
如果所有选项都有效(可用),则覆盖方法必须返回 true。如果至少有一个参数不正确(导致无法进一步操作),则必须返回 false。覆盖方法必须调用基类方法并检查结果。
1.1.4 InitIndicators
InitIndicators () 方法用于创建和初始化所有必要的指标和时间序列。在设置完所有参数且成功验证其正确性后,该方法会从 EA 交易中调用。如果风险和资金管理机制使用了至少一个指标或时间序列,应应覆盖该方法。
virtual bool InitIndicators(CIndicators* indicators);
应通过标准库的适当类来使用指标和/或时间序列。所有指标和/或时间序列的指针均应添加至 EA 交易的指标集(指向其的指针作为参数传递)。
如果对指标和/或时间序列的所有操作均已成功(表明其适合使用),则覆盖方法必须返回 true。如果对指标和/或时间序列的操作至少有一次失败(导致无法进一步操作),则覆盖方法必须返回 false。
1.2. 确定持仓量的方法
1.2.1 CheckOpenLong
CheckOpenLong() 方法用于计算买入持仓开仓的交易量。该方法通过 EA 交易调用,以确定买入持仓开仓的交易量。如果希望根据一定的算法(该算法不同于基类中实现的算法)计算买入持仓开仓的交易量,则必须覆盖该方法。
\virtual double CheckOpenLong(double price, double sl);
该方法必须实现检查买入持仓开仓交易量的算法。该方法必须返回计算得出的交易量。
1.2.2 CheckOpenShort
CheckOpenShort() 方法用于计算卖出持仓开仓的交易量。该方法通过 EA 交易调用,以确定卖出持仓开仓的交易量。如果希望根据一定的算法(该算法不同于基类中实现的算法)计算卖出持仓开仓的交易量,则必须覆盖该方法。
virtual double CheckOpenShort(double price, double sl);
该方法必须实现检查卖出持仓开仓交易量的算法。该方法必须返回计算得出的交易量。
1.2.3 CheckReverse
CheckReverse() 方法用于计算反向持仓的交易量。该方法通过 EA 交易调用,以确定反向持仓交易操作的交易量。如果希望根据一定的算法(该算法不同于基类中实现的算法)计算反向持仓的交易量(例如两倍交易量反向),则必须覆盖该方法。
virtual double CheckReverse(CPositionInfo* position, double sl);
该方法必须实现计算反向持仓交易量的算法,其相关信息可通过 position 指针获知。该方法必须返回计算得出的反向持仓交易量。
1.2.4 CheckClose
CheckClose() 方法用于检查是否有必要平仓(从资金管理和风险管理的角度来看)。该方法通过 EA 交易调用,以确定是否有必要平仓。如果希望根据一定的算法(该算法不同于基类中实现的算法)来平仓(例如部分平仓),则必须覆盖该方法。
virtual double CheckClose(CPositionInfo* position);
该方法必须实现定义平仓必要性的算法,其相关信息可通过 position 指针获知。该方法必须返回计算得出的平仓交易量。
2. 创建资金和风险管理机制
了解 CExpertMoney 基类的结构后,现在您就可以开始创建自己的风险和资金管理机制了!在下文中,风险和资金管理机制被称为“资金管理器”。
如上所述,CExpertMoney 类是一套公共虚拟方法,EA 交易通过其了解资金管理器在有关市场准入方向交易量方面的“意见”。
因此,我们的主要目标是创建自己的资金管理器的类,令其派生于 CExpertMoney 类,覆盖相应的虚拟方法,并实现所需算法。
我们的第二个问题(具有同等重要性)是让我们的类对于 MQL5 向导呈“可视”状态。先说最重要的吧。
2.1. 创建交易信号生成器类
我们开始吧。
首先创建(例如,使用同一 MQL5 向导)一个扩展名为 mqh 的包含文件。
在 File(文件)菜单中选择 Create(创建)(或按 Ctrl+N 组合键),并指明创建一个包含文件:
图 2. 使用 MQL5 向导创建一个包含文件
应该注意的是,为使文件随后能够被 MQL5 向导识别为资金管理器,应将文件创建于文件夹 Include\Expert 中。
为了避免与标准库发生冲突,应创建自己的文件夹 Include\Expert\Money\MyMoneys,我们在其中创建文件 SampleMoney.mqh,并在 MQL5 向导中指定这些参数:
图 3. 设置包含文件的位置
MQL5 向导的运行结果显示如下情形:
//+------------------------------------------------------------------+ //| SampleMoney.mqh | //| Copyright 2010, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2010, MetaQuotes Software Corp." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| defines | //+------------------------------------------------------------------+ // #define MacrosHello "Hello, world!" // #define MacrosYear 2010 //+------------------------------------------------------------------+ //| DLL imports | //+------------------------------------------------------------------+ // #import "user32.dll" // int SendMessageA(int hWnd,int Msg,int wParam,int lParam); // #import "my_expert.dll" // int ExpertRecalculate(int wParam,int lParam); // #import //+------------------------------------------------------------------+ //| EX5 imports | //+------------------------------------------------------------------+ // #import "stdlib.ex5" // string ErrorDescription(int error_code); // #import //+------------------------------------------------------------------+
接下来完全是“手工”操作。删除不必要部分,并加入所需部分 - 标准库的包含文件 ExpertMoney.mqh 和空的类说明。
//+------------------------------------------------------------------+ //| SampleMoney.mqh | //| Copyright 2010, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2010, MetaQuotes Software Corp." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include <Expert\ExpertMoney.mqh> //+------------------------------------------------------------------+ //| Class CSampleMoney. | //| Purpose: Class for risk and money management. | //| It is derived from the CExpertMoney class. | //+------------------------------------------------------------------+ class CSampleMoney : public CExpertMoney { }; //+------------------------------------------------------------------+
现在有必要选择算法了。
我们采用下列算法作为打造资金管理器的基础:在“正常”条件下,建议使用预先设定的固定交易量。不过,如果上一个仓位平仓时发生亏损,就建议以两倍的交易量开仓。
这反映在我们的文件中。
//+------------------------------------------------------------------+ //| SampleMoney.mqh | //| Copyright 2010, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2010, MetaQuotes Software Corp." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include <Expert\ExpertMoney.mqh> //+------------------------------------------------------------------+ //| Class CSampleMoney. | //| Purpose: Class for risk and money management | //| doubling the volume after a loss deal. | //| It is derived from the CExpertMoney class. | //+------------------------------------------------------------------+ class CSampleMoney : public CExpertMoney { }; //+------------------------------------------------------------------+
为资金管理器定义一个设置列表。实际上,这样的列表是不存在的。所有设置都包含在一个参数中,在“正常”情况下该参数将确定交易量的大小。
该参数将存储在类的受保护数据成员中。将通过相应的公共方法实现对参数的访问。在类构造函数中,该参数将通过一个默认值进行初始化。要检查这些参数,可根据基类说明来覆盖虚拟方法 ValidationSettings。
让我们将这些更改包含在文件中:
//+------------------------------------------------------------------+ //| SampleMoney.mqh | //| Copyright 2010, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2010, MetaQuotes Software Corp." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include <Expert\ExpertMoney.mqh> //+------------------------------------------------------------------+ //| Class CSampleMoney. | //| Purpose: Class for risk and money management | //| doubling the volume after a loss deal. | //| It is derived from the CExpertMoney class. | //+------------------------------------------------------------------+ class CSampleMoney : public CExpertMoney { protected: //--- setup parameters double m_lots; // deal volume for "normal" conditions public: CSampleMoney(); //--- methods to set the parameters void Lots(double lots) { m_lots=lots; } }; //+------------------------------------------------------------------+ //| Constructor CSampleMoney. | //| INPUT: no. | //| OUTPUT: no. | //| REMARK: no. | //+------------------------------------------------------------------+ void CSampleMoney::CSampleMoney() { //--- setting the default values m_lots=0.1; } //+------------------------------------------------------------------+
另外,我们考虑下如何实现 ValidationSettings() 方法。需要指出的是,该基类已经有了一个配置参数,而该参数同样需要验证。
ValidationSettings() 方法的实现:
//+------------------------------------------------------------------+ //| Validation of the setup parameters. | //| INPUT: no. | //| OUTPUT: true if the settings are correct, otherwise false. | //| REMARK: no. | //+------------------------------------------------------------------+ bool CSampleMoney::ValidationSettings() { //--- Call the base class method if(!CExpertMoney::ValidationSettings()) return(false); //--- Validation of parameters if(m_lots<m_symbol.LotsMin() || m_lots>m_symbol.LotsMax()) { printf(__FUNCTION__+": the deal volume must be in the range %f to %f",m_symbol.LotsMin(),m_symbol.LotsMax()); return(false); } if(MathAbs(m_lots/m_symbol.LotsStep()-MathRound(m_lots/m_symbol.LotsStep()))>1.0E-10) { printf(__FUNCTION__+": the volume of the deal must be multiple of %f",m_symbol.LotsStep()); return(false); } //--- Successful completion return(true); }
设置已就绪,现在我们来继续操作资金管理器。需要采用一种方法来确定上一笔交易是否亏损,并在必要时确定其交易量。在类的说明中对其进行声明:
class CSampleMoney : public CExpertMoney { protected: //--- Setup parameters double m_lots; // deal volume for "normal" conditions public: CSampleMoney(); //--- Methods to set parameters void Lots(double lots) { m_lots=lots; } //--- Methods to validate parameters virtual bool ValidationSettings(); protected: double CheckPrevLoss(); };
该方法的实现:
//+------------------------------------------------------------------+ //| Defines whether the prev. deal was losing. | //| INPUT: no. | //| OUTPUT: volume of the prev. deal if it's losing, otherwise 0.0 | //| REMARK: no. | //+------------------------------------------------------------------+ double CSampleMoney::CheckPrevLoss() { double lot=0.0; //--- Request the history of deals and orders HistorySelect(0,TimeCurrent()); //--- variables int deals=HistoryDealsTotal(); // Total number of deals in the history CDealInfo deal; //--- Find the previous deal for(int i=deals-1;i>=0;i--) { if(!deal.SelectByIndex(i)) { printf(__FUNCTION__+": Error of deal selection by index"); break; } //--- Check the symbol if(deal.Symbol()!=m_symbol.Name()) continue; //--- Check the profit if(deal.Profit()<0.0) lot=deal.Volume(); break; } //--- Return the volume return(lot); }
让我们再仔细考虑下这些算法(虽然已经比较详细了)。
跳过细枝末节,我们注意到,资金管理器在接收到上一笔交易亏损的信息后会提议增大交易量的大小。如果上一笔交易没有亏损,我们将提供具有固定交易量的开仓,该固定交易量通过具体参数进行定义。
为此,我们覆盖了虚拟方法 CheckOpenLong 和 CheckOpenShort,并赋予其相应功能。
类的说明:
//+------------------------------------------------------------------+ //| Class CSampleMoney. | //| Purpose: Class for risk and money management | //| doubling the volume after a loss deal. | //| It is derived from the CExpertMoney class. | //+------------------------------------------------------------------+ class CSampleMoney : public CExpertMoney { protected: //--- Setup parameters double m_lots; // Deal volume for "normal" conditions public: CSampleMoney(); //--- Methods to set the parameters void Lots(double lots) { m_lots=lots; } //--- Methods to validate the parameters virtual bool ValidationSettings(); //--- Methods to define the volume virtual double CheckOpenLong(double price,double sl); virtual double CheckOpenShort(double price,double sl); protected: double CheckPrevLoss(); };
CheckOpenLong 和 CheckOpenShort 方法的实现几乎完全相同。两种方法都通过调用之前实现的 CheckPrevLoss 方法来确定增大交易量的必要性。
接下来我们要考虑这一点,即不能无限制地增大交易量。有两个限制适用于持仓量:
- 交易品种单笔交易的最大交易量在服务器设置 (SYMBOL_VOLUME_MAX) 中进行了指定。
- 所需自由资金额在存款金上的可用性。
CheckOpenLong 和 CheckOpenShort 方法的实现:
//+------------------------------------------------------------------+ //| Defining the volume to open a long position. | //| INPUT: no. | //| OUTPUT: lot-if successful, 0.0 otherwise. | //| REMARK: not. | //+------------------------------------------------------------------+ double CSampleMoney::CheckOpenLong(double price,double sl) { if(m_symbol==NULL) return(0.0); //--- Select the lot size double lot=2*CheckPrevLoss(); if(lot==0.0) lot=m_lots; //--- Check the limits double maxvol=m_symbol.LotsMax(); if(lot>maxvol) lot=maxvol; //--- Check the margin requirements if(price==0.0) price=m_symbol.Ask(); maxvol=m_account.MaxLotCheck(m_symbol.Name(),ORDER_TYPE_BUY,price,m_percent); if(lot>maxvol) lot=maxvol; //--- Return the trade volume return(lot); } //+------------------------------------------------------------------+ //| Defining the volume to open a short position. | //| INPUT: no. | //| OUTPUT: lot-if successful, 0.0 otherwise. | //| REMARK: no. | //+------------------------------------------------------------------+ double CSampleMoney::CheckOpenShort(double price,double sl) { if(m_symbol==NULL) return(0.0); //--- Select the lot size double lot=2*CheckPrevLoss(); if(lot==0.0) lot=m_lots; //--- Check the limits double maxvol=m_symbol.LotsMax(); if(lot>maxvol) lot=maxvol; //--- Check the margin requirements if(price==0.0) price=m_symbol.Bid(); maxvol=m_account.MaxLotCheck(m_symbol.Name(),ORDER_TYPE_SELL,price,m_percent); if(lot>maxvol) lot=maxvol; //--- Return the trade volume return(lot); }
至此,我们已解决了第一个问题。上述代码是资金管理器的类的“源代码”,可满足主要任务的要求。
2.2. 为 MQL5 向导创建所生成资金管理器类的说明
我们现在开始解决第二个问题。资金管理器应能被 MQL5 向导的交易策略生成器所识别。
我们已完成了第一个必要条件:我们将文件放置在 MQL5 向导能够找到的位置。但这还不够。MQL5 向导不仅要能找到文件,还要能识别它。为此,我们必须根据 MQL5 向导的要求将类描述符添加至原始文本。
我们来看一下这些规则:
1. 注释块应以下面的代码行开头:
// wizard description start //+------------------------------------------------------------------+ //| Description of the class |
2. 下一行是一个文本描述符(我们会在 MQL5 向导中选择信号时见到),格式为 "//| Title=<Text> |"。如果代码文本过多而无法显示在一行,您可以在后面再添加一行代码(但不能超过此数)。
在本例中,即下述代码行:
//| Title=Trade with a doubling of lot after a loss |
3. 接下来是以格式 "//| Type=<Type> |" 指定类的类型的代码行。<Type> 字段必须具有资金值(除了资金管理器,MQL5 向导也能识别其他类的类型)。
输入:
//| Type=Money |
4. 接下来一行呈 "//| Name=<Name> |" 格式的代码是信号的简称(MQL5 向导用其来生成 EA 交易全局变量的名称)。
我们得到下列内容:
//| Name=Sample |
5. 类的名称是说明中的一个重要要素。在呈 "//| Class=<ClassNameа> |" 格式的代码行中,<ClassName> 参数必须与类的名称相匹配:
//| Class=CSampleMoney |
6. 我们不会在此行填入任何内容,但该行必须存在(这是到语言参考部分的链接):
//| Page= |
7. 此外还有关于信号设置参数的说明。
这是一组代码行(行数等于参数的个数)。
每一行的格式均为 "//| Parameter=<NameOfMethod>,<TypeOfParameter>,<DefaultValue> |"。
下面是我们的参数集:
//| Parameter=Lots,double,0.1 | //| Parameter=Percent,double,100.0 |
8. 注释块应以下面的代码行结束:
//+------------------------------------------------------------------+ // wizard description end
2-7 我们需要进一步说明 2-7 项。类描述符部分包含关键字(Title、Type、Name、Class、Page、Parameter)。遗憾的是,MQL5 向导无法识别作为类说明一部分的字符的全部可能组合。
因此,为避免不必要的错误,应按如下格式编写代码:<说明> 仅可为关键字 Title 保留空格。第 1 项和第 8 项应“按原样”复制。
类描述符(第一行)必须位于文件的前 20 行。让我们将描述符添加到源代码。
//+------------------------------------------------------------------+ //| SampleMoney.mqh | //| Copyright 2010, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2010, MetaQuotes Software Corp." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include <Expert\ExpertMoney.mqh> #include <Trade\DealInfo.mqh> // wizard description start //+------------------------------------------------------------------+ //| Description of the class | //| Title=Trading with lot doubling after a loss | //| Type=Money | //| Name=Sample | //| Class=CSampleMoney | //| Page= | //| Parameter=Lots,double,0.1 | //| Parameter=Percent,double,100.0 | //+------------------------------------------------------------------+ // wizard description end //+------------------------------------------------------------------+ //| Class CSampleMoney. | //| Purpose: Class for risk and money management | //| doubling the volume after a loss deal. | //| It is derived from the CExpertMoney class. | //+------------------------------------------------------------------+ class CSampleMoney : public CExpertMoney { protected: //--- Setup parameters double m_lots; // Deal volume for "normal" conditions public: CSampleMoney(); //--- Methods to set the parameters void Lots(double lots) { m_lots=lots; } //--- Methods to validate the parameters virtual bool ValidationSettings(); //--- Methods to define the volume virtual double CheckOpenLong(double price,double sl); virtual double CheckOpenShort(double price,double sl); protected: double CheckPrevLoss(); }; //+------------------------------------------------------------------+ //| Constructor CSampleMoney. | //| INPUT: no. | //| OUTPUT: no. | //| REMARK: no. | //+------------------------------------------------------------------+ void CSampleMoney::CSampleMoney() { //--- Setting default values m_lots=0.1; } //+------------------------------------------------------------------+ //| Validation of the setup parameters. | //| INPUT: no. | //| OUTPUT: true if the settings are correct, otherwise false. | //| REMARK: no. | //+------------------------------------------------------------------+ bool CSampleMoney::ValidationSettings() { //--- Call the base class method if(!CExpertMoney::ValidationSettings()) return(false); //--- Validating the parameters if(m_lots<m_symbol.LotsMin() || m_lots>m_symbol.LotsMax()) { printf(__FUNCTION__+": The deal volume must be in the range %f to %f",m_symbol.LotsMin(),m_symbol.LotsMax()); return(false); } if(MathAbs(m_lots/m_symbol.LotsStep()-MathRound(m_lots/m_symbol.LotsStep()))>1.0E-10) { printf(__FUNCTION__+": The deal volume must be multiple of %f",m_symbol.LotsStep()); return(false); } //--- Successful completion return(true); } //+------------------------------------------------------------------+ //| Defining the volume to open a long position. | //| INPUT: no. | //| OUTPUT: lot-if successful, 0.0 otherwise. | //| REMARK: no. | //+------------------------------------------------------------------+ double CSampleMoney::CheckOpenLong(double price,double sl) { if(m_symbol==NULL) return(0.0); //--- Select the lot size double lot=2*CheckPrevLoss(); if(lot==0.0) lot=m_lots; //--- Check the limits double maxvol=m_symbol.LotsMax(); if(lot>maxvol) lot=maxvol; //--- Check the margin requirements if(price==0.0) price=m_symbol.Ask(); maxvol=m_account.MaxLotCheck(m_symbol.Name(),ORDER_TYPE_BUY,price,m_percent); if(lot>maxvol) lot=maxvol; //--- Return the trade volume return(lot); } //+------------------------------------------------------------------+ //|Defining the volume to open a short position. | //| INPUT: no. | //| OUTPUT: lot-if successful, 0.0 otherwise. | //| REMARK: no. | //+------------------------------------------------------------------+ double CSampleMoney::CheckOpenShort(double price,double sl) { if(m_symbol==NULL) return(0.0); //--- Select the lot size double lot=2*CheckPrevLoss(); if(lot==0.0) lot=m_lots; //--- Check the limits double maxvol=m_symbol.LotsMax(); if(lot>maxvol) lot=maxvol; //--- Check the margin requirements if(price==0.0) price=m_symbol.Bid(); maxvol=m_account.MaxLotCheck(m_symbol.Name(),ORDER_TYPE_SELL,price,m_percent); if(lot>maxvol) lot=maxvol; //--- Return the trade volume return(lot); } //+------------------------------------------------------------------+ //| Defines whether the prev. deal was losing. | //| INPUT: no. | //| OUTPUT: Volume of the prev. deal if it's losing, otherwise 0.0 | //| REMARK: no. | //+------------------------------------------------------------------+ double CSampleMoney::CheckPrevLoss() { double lot=0.0; //--- Request the history of deals and orders HistorySelect(0,TimeCurrent()); //--- variables int deals=HistoryDealsTotal(); // Total number of deals in the history CDealInfo deal; //--- Find the previous deal for(int i=deals-1;i>=0;i--) { if(!deal.SelectByIndex(i)) { printf(__FUNCTION__+": Error of deal selection by index"); break; } //--- Check the symbol if(deal.Symbol()!=m_symbol.Name()) continue; //---Check the profit if(deal.Profit()<0.0) lot=deal.Volume(); break; } //--- Return the volume return(lot); } //+------------------------------------------------------------------+
以上便是全部内容。资金管理器已经可以使用了。
为了让 MQL5 向导的交易策略生成器能够使用资金管理器,应重启 MetaEditor(MQL5 向导仅在启动时才会扫描文件夹 Include\Expert)。
重启 MetaEditor 后,创建的资金管理器模块就可以在 MQL5 向导中使用了:
图 5. MQL5 向导中创建的资金管理器
现在可以使用资金管理器参数说明部分指定的输入参数了:
图 6. MQL5 向导中创建的资金管理器的输入参数
可使用 MetaTrader 5 终端的策略测试程序找出所实现交易策略的输入参数的最佳值。
图 7 显示了根据本资金管理系统进行交易的 EA 交易的测试结果(EURUSD H1,测试周期:01.01.2010-05.01.2011)。
图 7. 在损失后使用本资金管理模块并采用两倍交易量策略得出的历史数据测试结果
在创建 EA 交易时,我们使用了在《MQL5 向导:如何创建交易信号模块》一文中实现的交易信号模块。该 EA 交易的参数如下所示:(PeriodMA=12, ShiftMA=0, MethodMA=MODE_EMA, AppliedMA=PRICE_CLOSE, Limit=-70, StopLoss=145, TakeProfit=430, Expiration=10, Lots=0.1, Percent=100).
总结
MQL5 向导的交易策略生成器极大简化了交易理念的检验过程。生成的 EA 交易的代码基于标准库的交易策略类,用于实现某些交易信号类、资金和风险管理类以及持仓支持类。
本文介绍了如何创建自定义风险和资金管理模块以及如何在 MQL5 向导中启用该模块。我们将使用一个资金管理算法作为示例,在该算法中交易量规模取决于上一笔交易的结果。本文还介绍了为 MQL5 向导创建的类的说明的结构和格式。