简介
在本文中,我们将增强EA交易,使用指标值来检查建仓条件。为了增加点乐趣,我们会在外部参数中创建一个下拉列表,可以从三个交易指标中选择一个。
这里提醒一下:我们会继续修改MQL5 Cookbook系列中前文里的EA交易,最新版本的EA交易可以从叫做“MQL5 Cookbook: 交易历史和取得仓位属性的函数库”一文中下载。
另外,本文中我们会特别创建一个函数来检查交易操作是否能够进行,建仓函数将被修改,会让EA交易决定交易模式(即时执行 和 市场执行)。
因为EA交易的代码在前面的文章中经过增强和提高以后已经超过了1500行,每个新功能增加以后都会变得越来越不方便,所以,逻辑上的解决方案是把它分成几类,分别作为独立的库文件。现在,目标已经设定,让我们开始吧。
EA 交易开发
我们把前文中EA交易的源代码文件(*.mq5)放到一个独立的文件夹中,TestIndicatorConditions,我们还需要在其中创建Include子文件夹,在这个文件夹中我们会创建包含文件 (*.mqh)。他们可以用MQL5向导来生成(Ctrl+N) 或者人工创建,在所需目录中创建标准文本文件(*.txt) 然后重命名为 *.mqh。
以下是所有创建的包含文件的名称和注释:
- Enums.mqh 将包含所有的枚举;
- InfoPanel.mqh 将包含设置信息面板,以及创建和删除图形对象的函数;
- Errors.mqh 将包含所有返回错误代码和去初始化原因的函数;
- TradeSignals.mqh 包含用于把价格和指标值复制到数组的函数,以及信号模块;
- TradeFunctions.mqh 将包含交易函数;
- ToString.mqh 将包含用于把数字值转换为字符串的函数;
- Auxiliary.mqh 将用于其它的辅助函数.
为了把这些函数库包含到主文件中,我们使用#include指令。因为EA交易的主文件和包含文件夹 (Include) 在同一个文件夹中,包含文件的代码将如下:
//--- 包含自定义库 #include "Include/Enums.mqh" #include "Include/InfoPanel.mqh" #include "Include/Errors.mqh" #include "Include/TradeSignals.mqh" #include "Include/TradeFunctions.mqh" #include "Include/ToString.mqh" #include "Include/Auxiliary.mqh"
然后,我们就可以打开和修改这些文件,把EA交易主文件的部分代码转移到其中了。
为了正确浏览代码, 需要在每个头文件中增加对邻近头文件以及EA交易的主文件的引用,比如,我们的交易函数库,TradeFunctions.mqh, 看起来是这样的:
//--- 连接EA交易的主文件 #include "../TestIndicatorConditions.mq5" //--- 包含自定义库 #include "Enums.mqh" #include "InfoPanel.mqh" #include "Errors.mqh" #include "TradeSignals.mqh" #include "ToString.mqh" #include "Auxiliary.mqh"
作为同一层目录中的文件,只需要指定文件名称就足够了,为了到上一层目录,您需要在文件路径的反斜杠之前加上两个点。
让我们在Enums.mqh文件中增加指标的枚举。为了指导的目的,在这个EA交易中我们将使用两个标准指标(移动平均(MV)和商品通道指数(CCI)) 和一个自定义指标 (MultiRange_PCH)。枚举定义如下:
//--- 指标 enum ENUM_INDICATORS { MA = 0, // 移动平均 CCI = 1, // CCI PCH = 2 // 价格通道 };
外部参数修改如下:
//--- EA交易的外部参数 sinput long MagicNumber=777; // 幻数 sinput int Deviation=10; // 滑点 input ENUM_INDICATORS Indicator=MA; // 指标 input int IndicatorPeriod=5; // 指标周期数 input int IndicatorSegments=2; // 一个方向上的指标段数 input double Lot=0.1; // 手数 input double VolumeIncrease=0.1; // 仓位交易量增加值 input double VolumeIncreaseStep=10; // 交易量增加步长 input double StopLoss=50; // 止损 input double TakeProfit=100; // 获利 input double TrailingStop=10; // 跟踪止损 input bool Reverse=true; // 仓位反转 sinput bool ShowInfoPanel=true; // 是否显示信息面板
如上文所述,您将可以从指标参数的下拉列表中从三个指标中选择一个。
还有一个应用于所有指标的参数 – IndicatorPeriod,它用于设置指标周期数。来自以往版本EA交易的NumberOfBars 参数已经被重命名为IndicatorSegments,现在用于说明指定的指标必须向上/向下以满足建仓条件的柱数。
进而,我们还增加了另外一个外部参数,VolumeIncreaseStep,它可以用于设置交易量增加点数的步长。
AllowedNumberOfBars 变量的值(现在是AllowedNumberOfSegments)将在 GetBarsData()自定义函数中做调整,它现在被放到一个独立的函数中并且只是在初始化时被调用。
因为仓位建仓条件现在是使用指标值做检查,其赋值总是大于2。换句话说,如果IndicatorSegments外部变量被赋值为1,AllowedNumberOfSegments变量将赋值为3,因为为了满足条件 (比如买入) ,在已完成柱的指标值必须比前一柱大。为了这个目的,我们需要读取最后三个指标值。
以下是CorrectInputParameters() 函数的代码:
//+------------------------------------------------------------------+ //| 调整输入参数 | //+------------------------------------------------------------------+ void CorrectInputParameters() { //--- 调整开启仓位条件中柱的数量 if(AllowedNumberOfSegments<=0) { if(IndicatorSegments<=1) AllowedNumberOfSegments=3; // 至少需要3个柱 if(IndicatorSegments>=5) AllowedNumberOfSegments=5; // 但是不能超过7 else AllowedNumberOfSegments=IndicatorSegments+1; // 总是大于2 } }
在我们处理指标之前,我们创建一个函数来检查是否允许交易 – CheckTradingPermission()。如果因为函数中列出的任何原因不允许交易,将会返回0值,这说明下一次尝试必须在下一个柱进行。
//+------------------------------------------------------------------+ //| 检查是否允许交易 | //+------------------------------------------------------------------+ bool CheckTradingPermission() { //--- 对于实时模式 if(IsRealtime()) { //--- 检查服务器连接 if(!TerminalInfoInteger(TERMINAL_CONNECTED)) return(1); //--- 在运行程序级别上的交易许可 if(!MQL5InfoInteger(MQL5_TRADE_ALLOWED)) return(2); //--- 终端级别上的交易许可 if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) return(3); //--- 当前账户的交易许可 if(!AccountInfoInteger(ACCOUNT_TRADE_ALLOWED)) return(4); //--- 当前账户的自动交易许可 if(!AccountInfoInteger(ACCOUNT_TRADE_EXPERT)) return(5); } //--- return(0); }
让我们现在开始文章的重点部分,为了访问指标值,我们需要获得它的句柄,这可以通过使用特别函数做到,它们的名字由指标的短名称和之前的’i‘字符组成。
例如,移动平均(MA)指标有对应的iMA()函数,所有MetaTrader 5终端中的标准指标句柄可以通过这些函数获得,完整的列表在MQL5参考的技术指标部分。如果您需要取得一个自定义指标的句柄,使用iCustom()函数.
我们将实现GetIndicatorHandle()函数,根据在指标参数中所选择的指标,对应指标的句柄将被赋予indicator_handle全局变量。此函数的代码可以在我们交易信号函数库(/Include/TradeSignals.mqh文件)中找到,指标句柄的变量位于EA交易的主文件中。
//+------------------------------------------------------------------+ //| 取得指标句柄 | //+------------------------------------------------------------------+ void GetIndicatorHandle() { //--- 如果选择了移动平均指标 if(Indicator==MA) indicator_handle=iMA(_Symbol,Period(),IndicatorPeriod,0,MODE_SMA,PRICE_CLOSE); //--- 如果选择了CCI指标 if(Indicator==CCI) indicator_handle=iCCI(_Symbol,Period(),IndicatorPeriod,PRICE_CLOSE); //--- 如果选择了MultiRange_PCH指标 if(Indicator==PCH) indicator_handle=iCustom(_Symbol,Period(),"MultiRange_PCH",IndicatorPeriod); //--- 如果指标句柄无法获得 if(indicator_handle==INVALID_HANDLE) Print("获取指标句柄失败!"); }
下一步,我们创建GetDataIndicators() 函数使用这些获得的指标句柄, 然后我们可以获得它们的值。这可以使用CopyBuffer()函数做到,与文章“MQL5 Cookbook: 在MetaTrader 5策略测试器中分析仓位属性”中使用 CopyTime(), CopyClose(), CopyOpen(), CopyHigh() 和CopyLow() 函数读取柱值方式类似。
因为指标可能含有几个缓冲区(几行数值),缓冲区索引作为第二个参数传给CopyBuffer()函数。标准指标的缓冲区索引可以在MQL5参考中找到。对于自定义指标,如果提供了源代码,缓冲区索引可以在代码中找到。如果没有代码,您需要通过试验,在策略测试器的可视化模式下观察条件如何被满足,然后再找到索引。
在那之前,我们需要在EA交易的主文件中为指标缓冲区值创建动态数组:
//--- 指标值数组 double indicator_buffer1[]; double indicator_buffer2[];
GetIndicatorsData() 的代码在下面提供:
//+------------------------------------------------------------------+ //| 取得指标值 | //+------------------------------------------------------------------+ bool GetIndicatorsData() { //--- 如果已经获得了指标句柄 if(indicator_handle!=INVALID_HANDLE) { //--- 对于移动平均或者CCI指标 if(Indicator==MA || Indicator==CCI) { //--- 反转索引顺序 (... 3 2 1 0) ArraySetAsSeries(indicator_buffer1,true); //--- 取得指标值 if(CopyBuffer(indicator_handle,0,0,AllowedNumberOfSegments,indicator_buffer1)<AllowedNumberOfSegments) { Print("复制数值失败 ("+ _Symbol+"; "+TimeframeToString(Period())+") (至 indicator_buffer1 数组)错误 ("+ IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError())); return(false); } } //--- 对于 MultiRange_PCH 指标 if(Indicator==PCH) { //--- 反转索引顺序 (... 3 2 1 0) ArraySetAsSeries(indicator_buffer1,true); ArraySetAsSeries(indicator_buffer2,true); //--- 取得指标值 if(CopyBuffer(indicator_handle,0,0,AllowedNumberOfSegments,indicator_buffer1)<AllowedNumberOfSegments || CopyBuffer(indicator_handle,1,0,AllowedNumberOfSegments,indicator_buffer2)<AllowedNumberOfSegments) { Print("复制数值失败 ("+ _Symbol+"; "+TimeframeToString(Period())+") (至 indicator_buffer1 或 indicator_buffer2 数组)错误 ("+ IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError())); return(false); } } //--- return(true); } //--- 如果指标句柄没有获得,重试 else GetIndicatorHandle(); //--- return(false); }
GetTradingSignal()函数已经从本质上改变了,在有无持仓的情况下,条件都已改变。对于移动平均和CCI指标,条件是相同的。对于MultiRange_PCH指标,它们被分配到不同的区块中。为了使程序有更好的可读性以及避免重复,我们创建了一个辅助函数,GetSignal(),它返回开启仓位信号,或者在已有持仓并且外部参数允许仓位反转的条件下返回相反信号。
以下是GetSignal() 函数的代码:
//+------------------------------------------------------------------+ //| 检查条件并返回信号 | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE GetSignal() { //--- 检查针对移动平均和CCI指标的条件 if(Indicator==MA || Indicator==CCI) { //--- 卖出信号 if(AllowedNumberOfSegments==3 && indicator_buffer1[1]<indicator_buffer1[2]) return(ORDER_TYPE_SELL); //--- if(AllowedNumberOfSegments==4 && indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer1[2]<indicator_buffer1[3]) return(ORDER_TYPE_SELL); //--- if(AllowedNumberOfSegments==5 && indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer1[2]<indicator_buffer1[3] && indicator_buffer1[3]<indicator_buffer1[4]) return(ORDER_TYPE_SELL); //--- if(AllowedNumberOfSegments==6 && indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer1[2]<indicator_buffer1[3] && indicator_buffer1[3]<indicator_buffer1[4] && indicator_buffer1[4]<indicator_buffer1[5]) return(ORDER_TYPE_SELL); //--- if(AllowedNumberOfSegments>=7 && indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer1[2]<indicator_buffer1[3] && indicator_buffer1[3]<indicator_buffer1[4] && indicator_buffer1[4]<indicator_buffer1[5] && indicator_buffer1[5]<indicator_buffer1[6]) return(ORDER_TYPE_SELL); //--- 买入信号 if(AllowedNumberOfSegments==3 && indicator_buffer1[1]>indicator_buffer1[2]) return(ORDER_TYPE_BUY); //--- if(AllowedNumberOfSegments==4 && indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer1[2]>indicator_buffer1[3]) return(ORDER_TYPE_BUY); //--- if(AllowedNumberOfSegments==5 && indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer1[2]>indicator_buffer1[3] && indicator_buffer1[3]>indicator_buffer1[4]) return(ORDER_TYPE_BUY); //--- if(AllowedNumberOfSegments==6 && indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer1[2]>indicator_buffer1[3] && indicator_buffer1[3]>indicator_buffer1[4] && indicator_buffer1[4]>indicator_buffer1[5]) return(ORDER_TYPE_BUY); //--- if(AllowedNumberOfSegments>=7 && indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer1[2]>indicator_buffer1[3] && indicator_buffer1[3]>indicator_buffer1[4] && indicator_buffer1[4]>indicator_buffer1[5] && indicator_buffer1[5]>indicator_buffer1[6]) return(ORDER_TYPE_BUY); } //--- 用于检查 MultiRange_PCH 指标条件的区块 if(Indicator==PCH) { //--- 卖出信号 if(close_price[1]<indicator_buffer2[1] && open_price[1]>indicator_buffer2[1]) return(ORDER_TYPE_SELL); //--- 买入信号 if(close_price[1]>indicator_buffer1[1] && open_price[1]<indicator_buffer1[1]) return(ORDER_TYPE_BUY); } //--- 没有信号 return(WRONG_VALUE); }
GetTradingSignal()函数的代码如下:
//+------------------------------------------------------------------+ //| 判断交易信号 | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE GetTradingSignal() { //--- 如果没有持仓 if(!pos.exists) { //--- 卖出信号 if(GetSignal()==ORDER_TYPE_SELL) return(ORDER_TYPE_SELL); //--- 买入信号 if(GetSignal()==ORDER_TYPE_BUY) return(ORDER_TYPE_BUY); } //--- 如果有持仓 if(pos.exists) { //--- 读取仓位类型 GetPositionProperties(P_TYPE); //--- 取得最后一笔交易的价格 GetPositionProperties(P_PRICE_LAST_DEAL); //--- 检查移动平均和CCI指标条件的区块 if(Indicator==MA || Indicator==CCI) { //--- 卖出信号 if(pos.type==POSITION_TYPE_BUY && GetSignal()==ORDER_TYPE_SELL) return(ORDER_TYPE_SELL); //--- if(pos.type==POSITION_TYPE_SELL && GetSignal()==ORDER_TYPE_SELL && close_price[1]<pos.last_deal_price-CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_SELL); //--- 买入信号 if(pos.type==POSITION_TYPE_SELL && GetSignal()==ORDER_TYPE_BUY) return(ORDER_TYPE_BUY); //--- if(pos.type==POSITION_TYPE_BUY && GetSignal()==ORDER_TYPE_BUY && close_price[1]>pos.last_deal_price+CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_BUY); } //--- 检查 MultiRange_PCH 指标条件的区块 if(Indicator==PCH) { //--- 卖出信号 if(pos.type==POSITION_TYPE_BUY && close_price[1]<indicator_buffer2[1] && open_price[1]>indicator_buffer2[1]) return(ORDER_TYPE_SELL); //--- if(pos.type==POSITION_TYPE_SELL && close_price[1]<pos.last_deal_price-CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_SELL); //--- 买入信号 if(pos.type==POSITION_TYPE_SELL && close_price[1]>indicator_buffer1[1] && open_price[1]<indicator_buffer1[1]) return(ORDER_TYPE_BUY); //--- if(pos.type==POSITION_TYPE_BUY && close_price[1]>pos.last_deal_price+CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_BUY); } } //--- 没有信号 return(WRONG_VALUE); }
现在我们只需要仔细研究交易品种属性部分中的即时执行(Instant Execution)和市场执行(Market Execution)模式, 进而修改对应的OpenPosition()开启仓位函数了。它们的意义可以通过自己的名称来解释了,也可以在MQL5 参考中找到:
- 即时执行
- 市场执行
请注意,在处理市场执行模式时, 您在开启仓位的时候不能设置止损和获利价位:您需要首先开启仓位再修改它,来设置那些价位。
让我们在交易品种属性结构中增加执行模式:
//--- 交易品种属性 struct symbol_properties { int digits; // 价格中的小数点位数 int spread; // 点差点数 int stops_level; // 止损级别 double point; // 点值 double ask; // 买价 double bid; // 卖价 double volume_min; // 交易的最小交易量 double volume_max; // 交易的最大交易量 double volume_limit; // 仓位以及单方向订单的最大允许交易量 double volume_step; // 交易中交易量改变的最小步长 double offset; // 事务中价格的最大可能偏移 double up_level; // 止损价位上限 double down_level; // 止损价位下限 ENUM_SYMBOL_TRADE_EXECUTION execution_mode; // 执行模式 };
相应地,我们需要修改ENUM_SYMBOL_PROPERTIES枚举
//--- 仓位属性的枚举 enum ENUM_SYMBOL_PROPERTIES { S_DIGITS = 0, S_SPREAD = 1, S_STOPSLEVEL = 2, S_POINT = 3, S_ASK = 4, S_BID = 5, S_VOLUME_MIN = 6, S_VOLUME_MAX = 7, S_VOLUME_LIMIT = 8, S_VOLUME_STEP = 9, S_FILTER = 10, S_UP_LEVEL = 11, S_DOWN_LEVEL = 12, S_EXECUTION_MODE = 13, S_ALL = 14 };
还有GetSymbolProperties() 函数:
case S_EXECUTION_MODE: symb.execution_mode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_EXEMODE); break; //--- case S_ALL : symb.digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); symb.spread=(int)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD); symb.stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); symb.point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); symb.ask=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),symb.digits); symb.bid=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),symb.digits); symb.volume_min=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN); symb.volume_max=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX); symb.volume_limit=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_LIMIT); symb.volume_step=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP); symb.offset=NormalizeDouble(CorrectValueBySymbolDigits(lot_offset*symb.point),symb.digits); symb.up_level=NormalizeDouble(symb.ask+symb.stops_level*symb.point,symb.digits); symb.down_level=NormalizeDouble(symb.bid-symb.stops_level*symb.point,symb.digits); symb.execution_mode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_EXEMODE); break; //---
最终,OpenPosition() 函数的代码现在如下:
//+------------------------------------------------------------------+ //| 开启一个仓位 | //+------------------------------------------------------------------+ void OpenPosition(double lot, ENUM_ORDER_TYPE order_type, double price, double sl, double tp, string comment) { //--- 设置交易结构的幻数 trade.SetExpertMagicNumber(MagicNumber); //--- 设置滑点点数 trade.SetDeviationInPoints(CorrectValueBySymbolDigits(Deviation)); //--- 即时执行模式 // 开启仓位时可以同时设置止损和获利 if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_INSTANT) { //--- 如果仓位开启失败,打印相关信息 if(!trade.PositionOpen(_Symbol,order_type,lot,price,sl,tp,comment)) Print("开启仓位错误: ",GetLastError()," - ",ErrorDescription(GetLastError())); } //--- 市场执行模式 // 首先开启一个仓位,然后设置止损和获利价位 // *** 从 build 803 开始,止损和获利可以在仓位开启时设置 *** if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_MARKET) { //--- 如果没有仓位,首先开启仓位,然后设置止损和获利 if(!pos.exists) { //--- 如果仓位开启失败,打印相关信息 if(!trade.PositionOpen(_Symbol,order_type,lot,price,0,0,comment)) Print("开启仓位错误: ",GetLastError()," - ",ErrorDescription(GetLastError())); //--- 取得有无仓位的标志 pos.exists=PositionSelect(_Symbol); //--- 如果有持仓 if(pos.exists) { //--- 设置止损和获利 if(!trade.PositionModify(_Symbol,sl,tp)) Print("修改仓位出错: ",GetLastError()," - ",ErrorDescription(GetLastError())); } } //--- 如果仓位存在,增加交易量,不改变止损和获利价位 else { //--- 如果仓位开启失败,打印相关信息 if(!trade.PositionOpen(_Symbol,order_type,lot,price,sl,tp,comment)) Print("开启仓位错误: ",GetLastError()," - ",ErrorDescription(GetLastError())); } } }
我们还必须把最后的非常重要的部分加到事件处理函数中:
- OnInit
//+------------------------------------------------------------------+ //| EA初始化函数 | //+------------------------------------------------------------------+ int OnInit() { //--- 调整输入参数 CorrectInputParameters(); //--- 取得指标句柄 GetIndicatorHandle(); //--- 初始化新柱 CheckNewBar(); //--- 取得属性 GetPositionProperties(P_ALL); //--- 设置信息面板 SetInfoPanel(); //--- return(0); }
- OnDeinit
//+------------------------------------------------------------------+ //| EA去初始化函数 | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- 在日志中打印去初始化原因 Print(GetDeinitReasonText(reason)); //--- 当从图表上移除时 if(reason==REASON_REMOVE) { //--- 从图表上删除所有有关信息面板的对象 DeleteInfoPanel(); //--- 删除指标句柄 IndicatorRelease(indicator_handle); } }
- OnTick
//+------------------------------------------------------------------+ //| EA订单函数 | //+------------------------------------------------------------------+ void OnTick() { //--- 如果柱不是新的,退出 if(!CheckNewBar()) { if(IsVisualMode() || IsRealtime()) { //--- 取得属性并更新信息面板上的数值 GetPositionProperties(P_ALL); //--- 设置/更新信息面板 SetInfoPanel(); } return; } //--- 如果有新柱 else { //--- 如果允许交易 if(CheckTradingPermission()==0) { if(!GetIndicatorsData()) return; GetBarsData(); // 取得柱数据 TradingBlock(); // 检查条件和交易 ModifyTrailingStop(); // 修改跟踪止损价位 } } //--- 取得属性 GetPositionProperties(P_ALL); //--- 更新信息面板 SetInfoPanel(); }
现在所有函数都完成了,我们可以优化参数了。请记住,您需要从主程序文件编译代码。
优化参数和测试EA交易
策略测试器需要如下设置:
图 1. 策略测试器设置.
进一步,我们设置EA交易用于优化的参数 (参见附件中的 *.set 设置文件):
图 2. EA交易的设置.
优化在双核处理器上大约需要40分钟。优化图表使您能够基于利润区域的结果部分评估交易系统的质量:
图 3. 优化图表
最大采收率测试结果如下:
图 4. 最大采收率测试结果
结论
文章的附件中是可以下载的EA交易代码存档,展开以后,您需要把/TestIndicatorConditions 文件目录放到<Metatrader 5 terminal>/MQL5/Experts下面,为了保证EA能够正确运行,MultiRange_PCH 指标应该被下载和放置到<Metatrader 5 terminal>/MQL5/Indicators目录下。
本文译自 MetaQuotes Software Corp. 撰写的俄文原文
原文地址: https://www.mql5.com/ru/articles/645
MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!
免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。