MQL5 Cookbook: 在EA交易中使用指标来设置交易条件
简介
在本文中,我们将增强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 Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/645
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
sergeev:
......但您所问的问题与经纪人无关,而是与符号有关。
一个符号是市场执行符号,另一个是证券交易所符号。
它们有不同的条件、不同的成交类型和许多其他东西......
谢尔盖耶夫,你没 注意我刚才写的:
我画了一个简单的脚本。在该脚本中,我从不同的经纪商处获得了以下有关 EURUSD 符号的信息:
经纪商 "A":
GI 0 22:39:37 test_symbol (EURUSD,H1) Плавающий спред: 1
KQ 0 22:39:37 test_symbol (EURUSD,H1) Режим заключения сделок: SYMBOL_TRADE_EXECUTION_INSTANT
OK 0 22:39:37 test_symbol (EURUSD,H1) Режим заливки оредеров: 1
DS 0 22:39:37 test_symbol (EURUSD,H1) Путь в дереве символов: Forex\EURUSD
经纪商 "B":
EQ 0 22:45:00 test_symbol (EURUSD,H1) Плавающий спред: 0
RN 0 22:45:00 test_symbol (EURUSD,H1) Режим заключения сделок: SYMBOL_TRADE_EXECUTION_INSTANT
LS 0 22:45:00 test_symbol (EURUSD,H1) Режим заливки оредеров: 3
OK 0 22:45:00 test_symbol (EURUSD,H1) Путь в дереве символов: Forex-Fix\EURUSD
您可以注意到,在相同的 交易完成模式 下,填写订单的模式是不同的。是的,点差模式是不同的(我在说明经纪商拥有相同类型的账户时忽略了这一点)....。问题是,这两种模式(达成交易和填写订单)是否有某种联系?
问题是,这两种模式(做交易和倒读者)有什么联系吗?
当然没有。
到期时间和利润计算方式也与 "做交易 "模式无关。
非常好的文章。
但我在下载 Zip 文件时遇到困难,文件似乎已损坏。
没问题!
我在另一台电脑上用另一个导航仪试了试,运行正常!
对不起
您好、
不知道为什么,当我尝试用策略测试器测试 EA 时,TP 和 SL 从未出现。当我不使用跟踪止损时,它们也不会被设置。
当使用 TrailingStop 参数时,SL 会出现,但这似乎是一个修改,而不是仓位的初始设置。(使用移动止损允许在使用移动止损时设置 SL)。
同时,当调试允许自动交易时(在模拟账户 上),SL 和 TP 会立即设置(当首次在 TradingBlock() 中设置头寸时;无需等待 ModifyTrailingStop() 函数即可看到 SL 和 TP)。
有人能解释一下为什么吗?
是经纪商(Alpari UK)的问题吗?
在这种情况下,很难评估 EA 在设置 SL 和 TP 时是如何工作的。
如果有人能解释一下,非常感谢。
M.