基于大众交易系统和交易机器人优化点金术的 Expert Advisor
简介
大部分外汇交易方面的书籍通常提供最简单的交易系统作为教材。但时至今日,这种系统仅作为一般性说明而存在,并没有以现成 Expert Advisor 形式正确实现此类交易策略的案例。因此,现在无法评估此类示例是否存在任何实用价值。如果我们遍览各种专门讨论 EA 编写的论坛,我们可以得出一个结论:几乎所有新手阶段的 EA 编写者都必须做无谓的重复工作,从零开始基于最简单的交易系统开发其第一个 Expert Advisor。
我很怀疑这种工作的价值,认为有必要让任何交易新手都能使用基于这些交易系统正确开发的 EA,这样就不必凡事都从头开始了。本文中,我想要提供我自己的解决方案。对于 EA 构建,我将使用我自己的库中的指标,文章延迟最小的有效平均算法:在指标中的使用。
通用 EA 结构方案
在进入正文之前,我希望你注意到一个事实,即本文中所有 EA 都是根据同一个方案构建的:
基于移动方向变化的交易系统
现在我将试着通过一个现有 Expert Advisor 的示例(包含使用该 EA 时的所有小细节)来详细阐明文中所述 EA 结构方案的理念。在本系统中,买入信号是行情方向从下跌变为上升:
卖出信号是行情方向从上升变为下跌:
我们使用第三、第二和第一个柱上的移动值计算趋势信号。零柱上的移动值忽略不计,即系统仅处理已闭合的柱。自定义指标 JFATL 用作移动指标 - 它是一个简单的数字 FATL 过滤器,带额外的 JMA 平滑处理。在互联网上你常常会看到一些说法,称在 FATL 方向发生变化时入场交易必能到手几个点的利润,而人们很轻易地相信这个策略在现实中也是那么的高效。以下是 Expert Advisor 形式的系统实现版本:
Expert Advisor 代码
//+==================================================================+ //| Exp_1.mq4 | //| Copyright © 2007, Nikolay Kositsin | //| Khabarovsk, farria@mail.redcom.ru | //+==================================================================+ #property copyright "Copyright © 2007, Nikolay Kositsin" #property link "farria@mail.redcom.ru" //---- EA INPUT PARAMETERS FOR BUY TRADES extern bool Test_Up = true;//filter of trade calculations direction extern int Timeframe_Up = 240; extern double Money_Management_Up = 0.1; extern int Length_Up = 4; // smoothing depth extern int Phase_Up = 100; // parameter changing in the range //-100 ... +100, influences the quality of the transient process; extern int IPC_Up = 0;/* Selecting prices, on which the indicator will be calculated (0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL, 6-WEIGHTED, 7-Heiken Ashi Close, 8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW, 11-Heiken Ashi Low, 12-Heiken Ashi High, 13-Heiken Ashi Open, 14-Heiken Ashi Close.) */ extern int STOPLOSS_Up = 50; // stoploss extern int TAKEPROFIT_Up = 100; // takeprofit extern bool ClosePos_Up = true; // forced position closing allowed //---- EA INOUT PARAMETERS FOR SELL TRADES extern bool Test_Dn = true;//filter of trade calculations direction extern int Timeframe_Dn = 240; extern double Money_Management_Dn = 0.1; extern int Length_Dn = 4; // smoothing depth extern int Phase_Dn = 100; // parameter changing in the range // -100 ... +100, influences the quality of the transient process; extern int IPC_Dn = 0;/* Selecting prices, on which the indicator will be calculated (0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL, 6-WEIGHTED, 7-Heiken Ashi Close, 8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW, 11-Heiken Ashi Low, 12-Heiken Ashi High, 13-Heiken Ashi Open, 14-Heiken Ashi Close.) */ extern int STOPLOSS_Dn = 50; // stoploss extern int TAKEPROFIT_Dn = 100; // takeprofit extern bool ClosePos_Dn = true; // forced position closing allowed //---- Integer variables for the minimum of counted bars int MinBar_Up, MinBar_Dn; //+==================================================================+ //| Custom Expert functions | //+==================================================================+ #include <Lite_EXPERT1.mqh> //+==================================================================+ //| Custom Expert initialization function | //+==================================================================+ int init() { //---- Checking the correctness of Timeframe_Up variable value if (Timeframe_Up != 1) if (Timeframe_Up != 5) if (Timeframe_Up != 15) if (Timeframe_Up != 30) if (Timeframe_Up != 60) if (Timeframe_Up != 240) if (Timeframe_Up != 1440) Print(StringConcatenate("Timeframe_Up parameter cannot ", "be equal to ", Timeframe_Up, "!!!")); //---- Checking the correctness of Timeframe_Dn variable value if (Timeframe_Dn != 1) if (Timeframe_Dn != 5) if (Timeframe_Dn != 15) if (Timeframe_Dn != 30) if (Timeframe_Dn != 60) if (Timeframe_Dn != 240) if (Timeframe_Dn != 1440) Print(StringConcatenate("Timeframe_Dn parameter cannot ", "be equal to ", Timeframe_Dn, "!!!")); //---- Initialization of variables MinBar_Up = 4 + 39 + 30; MinBar_Dn = 4 + 39 + 30; //---- end of initialization return(0); } //+==================================================================+ //| Expert Advisor deinitialization function | //+==================================================================+ int deinit() { //----+ //---- End of EA deinitialization return(0); //----+ } //+==================================================================+ //| Custom Expert iteration function | //+==================================================================+ int start() { //----+ Declaration of local variables int bar; double Mov[3], dMov12, dMov23; //----+ Declaration of static variables static int LastBars_Up, LastBars_Dn; static bool BUY_Sign, BUY_Stop, SELL_Sign, SELL_Stop; //----++ CODE FOR LONG POSITIONS if (Test_Up) { int IBARS_Up = iBars(NULL, Timeframe_Up); if (IBARS_Up >= MinBar_Up) { if (LastBars_Up != IBARS_Up) { //----+ Initialization of variables BUY_Sign = false; BUY_Stop = false; LastBars_Up = IBARS_Up; //----+ CALCULATING INDICATOR VALUES AND UPLOADING THEM TO BUFFERS for(bar = 1; bar <= 3; bar++) Mov[bar - 1]= iCustom(NULL, Timeframe_Up, "JFatl", Length_Up, Phase_Up, 0, IPC_Up, 0, bar); //----+ DEFINING SIGNALS FOR TRADES dMov12 = Mov[0] - Mov[1]; dMov23 = Mov[1] - Mov[2]; if (dMov23 < 0) if (dMov12 > 0) BUY_Sign = true; if (dMov12 < 0) BUY_Stop = true; } //----+ EXECUTION OF TRADES if (!OpenBuyOrder1(BUY_Sign, 1, Money_Management_Up, STOPLOSS_Up, TAKEPROFIT_Up)) return(-1); if (ClosePos_Up) if (!CloseOrder1(BUY_Stop, 1)) return(-1); } } //----++ CODE FOR SHORT POSITIONS if (Test_Dn) { int IBARS_Dn = iBars(NULL, Timeframe_Dn); if (IBARS_Dn >= MinBar_Dn) { if (LastBars_Dn != IBARS_Dn) { //----+ Initialization of variables SELL_Sign = false; SELL_Stop = false; LastBars_Dn = IBARS_Dn; //----+ CALCULATING INDICATOR VALUES AND UPLOADING THEM TO BUFFERS for(bar = 1; bar <= 3; bar++) Mov[bar - 1]= iCustom(NULL, Timeframe_Dn, "JFatl", Length_Dn, Phase_Dn, 0, IPC_Dn, 0, bar); //----+ DEFINING SIGNALS FOR TRADES dMov12 = Mov[0] - Mov[1]; dMov23 = Mov[1] - Mov[2]; if (dMov23 > 0) if (dMov12 < 0) SELL_Sign = true; if (dMov12 > 0) SELL_Stop = true; } //----+ EXECUTION OF TRADES if (!OpenSellOrder1(SELL_Sign, 2, Money_Management_Dn, STOPLOSS_Dn, TAKEPROFIT_Dn)) return(-1); if (ClosePos_Dn) if (!CloseOrder1(SELL_Stop, 2)) return(-1); } } //----+ return(0); } //+------------------------------------------------------------------+
为了获取买入和卖出信号,我们使用了两个完全相似的独立算法;每个算法都有其自己的外部参数用于优化。我自身的经验证明,使用这种方法编写 EA 所能得到的利润远大于仅用一种算法检测买入和卖出信号的方法。如果你对这个针对空头和多头都使用同一个算法的方法感兴趣,可以在 EXP_0.mq4 EA 中学习此算法;我们将继续讨论包含两个算法的 EA。Expert Advisor 可以同时在买入方向和卖出方向上各建一个仓位,作为一个交易对。EA 执行市场中的交易。通过 StopLoss(止损)和 TakeProfit(获利)订单平仓。如果趋势信号的方向与 EA 中所建仓位方向相反,EA 允许强行平仓。接收出场信号的方法与获取入场信号的方法类似,只是性质相反而已。
Lite_EXPERT1.mqh 文件的内容
为了最大程度挖掘自身劳动的经济效益,在编写 Expert Advisor 时试着尽可能多地使用通用的用户定义函数。之后用不同的部分组合成一个 EA 代码,就像在不同的设备制造厂中装配一个产品一样,其中,每个新产品都包含最大数量的统一和标准的细节、块和模块。这就是为何所有 EA 中的“交易执行”块中都会使用多个通用的用户定义函数的原因;这些函数通过 #include <Lite_EXPERT1.mqh> 指令包括到 EA 代码中:
bool OpenBuyOrder1 ( bool BUY_Signal, int MagicNumber, double Money_Management, int STOPLOSS, int TAKEPROFIT ) bool OpenSellOrder1 ( bool SELL_Signal, int MagicNumber, double Money_Management, int STOPLOSS, int TAKEPROFIT ) CountLastTime ( int lastError ) bool CloseOrder1 ( bool Stop_Signal, int MagicNumber ) int StopCorrect ( string symbol, int Stop ) bool MarginCheck ( string symbol, int Cmd, double& Lot ) double GetFreeMargin() bool DeleteOrder1 ( bool& CloseStop, int MagicNumber ) bool OpenBuyLimitOrder1 ( bool& Order_Signal, int MagicNumber, double Money_Management, int STOPLOSS, int TAKEPROFIT, int LEVEL, datetime Expiration ) bool OpenBuyStopOrder1 ( bool& Order_Signal, int MagicNumber, double Money_Management, int STOPLOSS, int TAKEPROFIT, int LEVEL, datetime Expiration ) bool OpenSellLimitOrder1 ( bool& Order_Signal, int MagicNumber, double Money_Management, int STOPLOSS, int TAKEPROFIT, int LEVEL, datetime Expiration ) bool OpenSellStopOrder1 ( bool& Order_Signal, int MagicNumber, double Money_Management, int STOPLOSS, int TAKEPROFIT, int LEVEL, datetime Expiration ) bool OpenBuyOrder2 ( bool BUY_Signal, int MagicNumber, double Money_Management, int STOPLOSS, int TAKEPROFIT ) bool OpenSellOrder2 ( bool SELL_Signal, int MagicNumber, double Money_Management, int STOPLOSS, int TAKEPROFIT ) bool Make_TreilingStop ( int MagicNumber, int TRAILINGSTOP )
调用 OpenBuyOrder1() 函数时,将建立多头仓,如果外部变量 BUY_Signal 的值等于 true 且没有未平仓位,则其标识号(幻数)等于 MagicNumber 变量的值。外部变量 STOPLOSS 和 TAKEPROFIT 的值定义对应的 StopLoss 和 TakeProfit 的点数值。Money_Management 变量值的可变范围为 0 到 1。此变量指示可用保证金的哪个部分用于交易执行。如果此变量的值小于 0,OpenBuyOrder1() 函数将把其值用作手数!调用 OpenSellOrder1() 函数时,使用类似方式建立空头仓。两个函数都是独立建仓,但每 11 秒内仅可有一个交易执行命令被发送到服务器。除了执行交易,OpenBuyOrder1() 和 OpenSellOrder1() 函数还将有关尚未结束的交易的信息记录到日志文件中。
如果 Stop_Signal 变量获得 true 值,CloseOrder1() 函数(被调用时)会在幻数等于 MagicNumber 变量的值时平仓。
StopCorrect() 函数接受 StopLoss 或 TakeProfit 值作为 Stop 参数,检查此值与最小允许值的一致性,必要时将此值更改为最小允许值并返回它(将可能的修正考虑在内)。
MarginCheck() 函数的赋值表示在可用保证金不足以买入当前手数时,最大程度减少尚未关闭的交易中使用的手数,直至可用保证金足以开始交易。
OpenBuyOrder1()、OpenSellOrder1() 和 CloseOrder1() 在 start() 函数内使用,而 StopCorrect() 和 MarginCheck() 函数在 OpenBuyOrder1() 和 OpenSellOrder1() 的代码中使用。
正确完成 OpenBuyOrder1()、OpenSellOrder1() 和 CloseOrder1() 时,其中任一函数返回“true”,如果函数执行期间出现任何错误,则返回值“false”。所有这三个函数:OpenBuyOrder1()、OpenSellOrder1() 和 CloseOrder1() 在交易执行期间将其外部变量 BUY_Signal、SELL_Signal 和 Stop_Signal 的值发送到“false”中!
GetFreeMargin() 函数返回当前帐户的可用保证金金额,留作可用于建仓的利润或亏损。此函数用于计算手数。
CountLastTime() 函数对 LastTime 变量执行初始化,以说明交易执行期间发生的错误。应在交易执行之后立即调用此函数,例如:
//----+ Open Buy position ticket = OrderSend(Symb, OP_BUY, Lot, ask, 3, Stoploss, TakeProfit, NULL, MagicNumber, 0, Lime); //---- Calculating a pause between trade operations CountLastTime(GetLastError());
除了上述列举的函数,文件 Lite_EXPERT.mqh 还包含另外四个用于下挂单的函数以及一个用于删除挂单的函数:OpenBuyLimitOrder()、OpenBuyStopOrder1()、OpenSellLimitOrder1()、OpenSellStopOrder1()、DeleteOrder1()。这些函数的外部变量赋值方式完全相似,可以很容易从其名称上理解含义。需要新变量 LEVEL 和 Expiration 以将下挂单的价位与当前价格的距离点数以及相应的挂单到期日期发送到函数中。
除了所有讨论的函数之外,此文件还包含其他两个函数:OpenBuylOrder2() 和 OpenSellOrder2(),它们完全类似于 OpenBuyOrder1() 和 OpenSellOrder1(),只有一个细节有所不同。这些函数首先在没有 StopLoss 和 TakeProfit 订单的情况下建仓,之后设置 StopLoss 和 TakeProfit 来修改已建仓位。在 EA 中操作时,这些函数是必须用到的,它们适用于因“市场观察”执行类型的缘故而不允许客户在市场上建仓时下止损或获利订单的经纪人。在此类经纪公司中,止损和获利订单是通过修改已建立的仓位来追加的。
此文件包括的最后一个函数是 Make_TreilingStop()。此函数执行标准追踪止损。
除了函数,Lite_EXPERT.mqh 还包含全变量 LastTime,此变量在全局层次声明,因为它用于开立订单的所有函数。
依我看来,此函数集非常适用于实际的 EA 编写,可为新手阶段的 EA 编写者节省很多时间,无需再编写此代码。举个例子,你可从提供的函数集中挑选任意函数:
//+==================================================================+ //| OpenBuyOrder1() | //+==================================================================+ bool OpenBuyOrder1 (bool& BUY_Signal, int MagicNumber, double Money_Management, int STOPLOSS, int TAKEPROFIT) { //----+ if (!BUY_Signal) return(true); //---- Checking the expiration of minimal time interval //between two trade operations if (TimeCurrent() < LastTime) return(true); int total = OrdersTotal(); //---- Checking the presence of an opened position //with the magic number equal to Value of MagicNumber variable for(int ttt = total - 1; ttt >= 0; ttt--) if (OrderSelect(ttt, SELECT_BY_POS, MODE_TRADES)) if (OrderMagicNumber() == MagicNumber) return(true); string OrderPrice, Symb = Symbol(); int ticket, StLOSS, TkPROFIT; double LOTSTEP, MINLOT, MAXLOT, MARGINREQUIRED; double FreeMargin, LotVel, Lot, ask, Stoploss, TakeProfit; //----+ calculating lot size for position opening if (Money_Management > 0) { MARGINREQUIRED = MarketInfo(Symb, MODE_MARGINREQUIRED); if (MARGINREQUIRED == 0.0) return(false); LotVel = GetFreeMargin() * Money_Management / MARGINREQUIRED; } else LotVel = MathAbs(Money_Management); //---- LOTSTEP = MarketInfo(Symb, MODE_LOTSTEP); if (LOTSTEP <= 0) return(false); //---- fixing lot size for the nearest standard value Lot = LOTSTEP * MathFloor(LotVel / LOTSTEP); //----+ checking lot for minimally accepted value MINLOT = MarketInfo(Symb, MODE_MINLOT); if (MINLOT < 0) return(false); if (Lot < MINLOT) return(true); //----+ checking lot for maximally accepted value MAXLOT = MarketInfo(Symb, MODE_MAXLOT); if (MAXLOT < 0) return(false); if (Lot > MAXLOT) Lot = MAXLOT; //----+ checking if free margin is enough for lot size if (!MarginCheck(Symb, OP_BUY, Lot)) return(false); if (Lot < MINLOT) return(true); //---- ask = NormalizeDouble(Ask, Digits); if (ask == 0.0) return(false); //---- StLOSS = StopCorrect(Symb, STOPLOSS); if (StLOSS < 0) return(false); //---- Stoploss = NormalizeDouble(ask - StLOSS * Point, Digits); if (Stoploss < 0) return(false); //---- TkPROFIT = StopCorrect(Symb, TAKEPROFIT); if (TkPROFIT < 0) return(false); //---- TakeProfit = NormalizeDouble(ask + TkPROFIT * Point, Digits); if (TakeProfit < 0) return(false); Print(StringConcatenate ("Open for ", Symb, " a Buy position with the magic number ", MagicNumber)); //----+ Open Buy position ticket = OrderSend(Symb, OP_BUY, Lot, ask, 3, Stoploss, TakeProfit, NULL, MagicNumber, 0, Lime); //---- Calculating pause between trade operations CountLastTime(GetLastError()); //---- if(ticket > 0) { if (OrderSelect(ticket, SELECT_BY_TICKET)) { BUY_Signal = false; OpderPrice = DoubleToStr(OrderOpenPrice(), Digits); Print(StringConcatenate(Symb, " BUY order with the ticket No", ticket, " and magic number ", OrderMagicNumber(), " opened with the price ",OpderPrice)); return(true); } else { Print(StringConcatenate("Failed to open ", Symb, " BUY order with the magic number ", MagicNumber, "!!!")); return(true); } } else { Print(StringConcatenate("Failed to open", Symb, " BUY order with the magic number", MagicNumber, "!!!")); return(true); } //---- return(true); //----+ }
对于 MQL4 新用户来说,编写这种没有明显和不明显错误的代码是一项相当困难而耗时的任务。而在自己的 Expert Advisor 中使用这样的现成通用代码(由专家编写)则非常方便:
//----+ EXECUTION OF TRADES if (!OpenBuyOrder1(BUY_Sign, 1, Money_Management_Up, STOPLOSS_Up, TAKEPROFIT_Up)) return(-1); if (ClosePos_Up) if (!CloseOrder1(BUY_Stop, 1)) return(-1);
只需几行通用的用户定义函数调用,代码就完成了!你要做的只是理解函数调用的编写方式。更重要的是,EA 代码变得相当简单易懂,因此你可以在自己的 Expert Advisor 中实施任何交易策略。EA Exp_1.mq4 中未使用用于打开挂单和追踪止损位的函数,因此在 EA 编译期间,MetaEditor 将显示有关从 EA 中删除这些函数的警告:
有关 EA 代码的其他说明
现在我们可以开始讨论其余 EA 代码。EA 包含两个几乎完全相同的算法,所以,要正确了解详情,只需分析建立多头仓的 EA 部分就可以了。EA 代码中已包含解释单独代码片段含义的注释。所以,我们来分析未加注释的内容。在初始化块中,对 MinBar_Up 变量进行初始化:
//---- Initialization of variables MinBar_Up = 4 + 39 + 30;
此变量的目的是在 EA 内存中存储柱的最小数量;如果该数量再减少,则 EA 无法在多头方向上进行操作。此值是根据自定义指标 JFATL.mq4 的算法定义的。要仅计算 FATL 数字过滤器的一个值,需要 39 个图表柱。要获取由 JMA 算法进行了平滑处理的 JFATL,至少需要 30 个 FATL 值,而 EA 针对交易的信号检测算法使用三个倒数第二的图表柱,外加第四个柱 - 零柱。检查柱数是否足以用于进一步的计算,如下所示:
if (IBARS_Up >= MinBar_Up)
必须检查
if (LastBars_Up != IBARS_Up)
以避免 EA 在每次价格变动时都要重新计算信号以入市;EA 应仅在柱变化时执行此计算,这样可以最大程度节省计算机资源和 EA 优化时间。这就是 LastBars_Up 变量被声明为静态变量的原因 - 为了记住 int start() 函数在上一次价格变动时的柱数。仅在柱变化时才执行一次 BUY_Sign 和 BUY_Stop 的初始化以入场或退场;它们应保留其值不变,直至交易执行或关闭,或直至柱再次发生变化。这就是这些变量被声明为静态变量的缘故。我想其他 EA 细节都已经相当明确,从代码中就能了解。
更换 Expert Advisor 中的移动
现在我想要详细说明 EA 修改以使用另一个移动的可能性。举个例子,让我们使用我的库中的自定义指标 J2JMA.mq4,其调用方式如下:
//----+ CALCULATING INDICATOR VALUES AND UPLOADING THEM INTO BUFFERS for(bar = 1; bar <= 3; bar++) Mov[bar - 1]= iCustom(NULL, Timeframe_Up, "J2JMA", Length1_Up, Length2_Up, Phase1_Up, Phase2_Up, 0, IPC_Up, 0, bar);
此任务包含略微更改 EA 外部参数块(示例中仍是仅说明一半算法):
//---- EA INPUT PARAMETERS FOR BUY TRADES extern bool Test_Up = true;//filter of trade calculations direction extern int Timeframe_Up=240; extern double Money_Management_Up = 0.1; extern int Length1_Up = 4; // depth of the first smoothing extern int Phase1_Up = 100; // parameter of the first smoothing, //changing in the range -100 ... +100, influences the quality //of the transient process of averaging; extern int Length2_Up = 4; // depth of the second smoothing extern int Phase2_Up = 100; // parameter of the second smoothing, //changing in the range -100 ... +100, influences the quality //of the transient process of averaging;
应在初始化块中更改变量值:
//---- Initialization of variables MinBar_Up = 4 + 30 + 30;
现在我们有两个连续的 JMA 平滑处理,每个都至少需要 30 个柱,还需要 4 个柱用于 EA 的计算算法。之后,在获取源值的块中,自定义指标参考应更改为段落开头处的参考。对于第二个部分的算法,所有流程都是相同的。因此,我们不仅可以使用移动,还可使用振荡指标,这个指标有时候是非常有用的。基于 J2JMA 的已完成 EA 代码包括在 EXP_2.mqh 文件中。
EA 优化
现在以给定 EA 为例,我们来讨论这些文章中将包含的所有 EA 的一些优化细节。如前所述,EA 中有两个独立算法,各自用于处理多头仓和空头仓。这种方式自然可以每次仅针对买入或卖出方向优化 EA,从而实现更方便更快捷的优化。为此也相应地使用了两个外部变量 EA - Test_Up 和 Test_Dn。
将“false”值分配到其中一个逻辑变量便可排除相关方向上的所有计算,从而使优化耗时更少。在一个方向上优化 EA 参数之后,我们可以将 Test_Up 和 Test_Dn 变量的值互换,以优化另一个方向上的 EA。之后才可将“true”同时分配给这两个变量并测试结果。优化和测试流程在文章在 MetaTrader 4 客户终端中测试 Expert Advisor:外部一览中已有相当详尽的说明,此 EA 的优化流程可参考该文章。启动策略测试程序,上传 EA,选择交易对、对周期、优化方法和优化时间:
之后在测试程序中选择“Expert 属性”,并转到“测试”:
这里我们定义保证金金额,选择一个优化方向(多头或空头),并选择遗传优化算法。之后转到“输入”选项卡:
这里我们将“true”分配给外部变量 Test_Up 或 Test_Dn,并将“false”分配给剩下的那个。然后,将优化所用的图表周期的值分配给 Timeframe_Up 和 Timeframe_Dn 变量,在 Money_Management_Up 和 Money_Management_Dn 中相应地定义用于执行买入和卖出交易的保证金部分。对于其余的外部变量,设置优化期间的变动限制。之后,标记已优化的外部变量。单击“确定”关闭选项卡,在测试程序中单击“启动”开始优化。优化结束后,
转至测试程序中的“结果”选项卡:
并将满意的优化结果上传到策略测试程序。之后,在相反方向执行同一程序。结果,我们获得了使用上传参数的 EA,此 EA 在优化时段内具备盈利能力。注意,MetaTrader 4 中的 EA 属性选项卡与上述选项卡不同:
此选项卡并不太方便使用这种形式的窗口,而是比较适用于最大化的窗口。客户终端不允许最大化该窗口,因此创建了一个用于最大化的额外文件 (OpenExp.exe)。此文件可最大化文件名以 Exp_c(区分大小写)开头的 EA 的属性窗口。要使用此文件,必须从某个目录启动该文件,之后程序模块会等待 EA 属性窗口显示并更改窗口大小;此时不建议移动鼠标。
但任何刚开始使用测试的 EA 编写者都可能有很多关于各种测试细节的问题。首先,我们要根据具体情况决定使用哪个图表周期进行测试和优化。在我看来,这个问题并没有什么固定的答案。通常我们认为,EA 操作的图表周期越长,交易系统中使用的可见规律就越稳定。这类似于真实交易。但长远来看,还是可通过认真分析以及不同测试和优化方式之间的比较得出的结果最好。选择进行优化的证券也是如此。每个证券都有其独具的特性。现在说说建模。经验表明,根据控制点建模时,在第一个柱变化时接收交易执行信号的 EA 时得到了很好的优化,优化期间没有任何质与量方面的重大损失。一般来说,如何看待此类表述,具体还看各人的经验。我确信,优化此类 EA 时对所有价格变动都建模是无益的。现在我们讨论另一个重要参数 - 优化的时间周期:
这里可能会有不同的值,具体取决于 EA 优化的目的。重要的是,这个周期不能超过可用历史数据,否则可能会出现以下情况:
报价存档中所有可用历史数据的下限如下:
以下这种错误一般会出现在优化和测试期间操作 Expert Advisor 和其中包括的指标之时:
这些错误与 EA 自身并无关联!只是优化周期应从可用历史中选择,而不是想选什么就选什么!
现在我将提供一些有关 Expert Advisor 的“输入”选项卡的说明。我们还是只挑一部分进行分析 - 用于多头仓。前面我已经提过 Test_Up。Timeframe_Up 参数的含义很清楚了。Money_Management_Up 也已作说明。。现在我们来分析一下 Length_Up 参数。此参数的含义与简单移动平均线的 Period 参数相似。此参数值范围为 1 到无穷大。将此参数上限设置为 150 以上并无意义,大部分情况下可以很容易移至更高的时间范围。此参数的优化期间,要注意,此参数越大,JFATL 指标检测到的稳定长期趋势就越多。因此,在此交易系统中,Length_Up 参数与 STOPLOSS_Up 和 TAKEPROFIT_Up 之间有非显性的依赖关系。逻辑上讲,止损和获利直接取决于 Length_Up。当然,我们下单时可以无视 Length_Up,但这种情况下,交易系统的盈利能力就不是取决于系统属性,而是取决于当前市场状况了,但这可没有在交易系统中定义过!我们可以从以下示例中理解这一说法的含义。假设我们要使用以下参数在此 EA(货币对 EUR/USD)的优化期间获得相当优秀的 EA 测试结果:
extern bool Test_Up = true; extern int Timeframe_Up = 1; extern double Money_Management_Up = 0.1; extern int Length_Up = 4; extern int Phase_Up = 100; extern int IPC_Up = 0; extern int STOPLOSS_Up = 100; extern int TAKEPROFIT_Up = 200; extern bool ClosePos_Up = false;
事实是,Length_Up 等于 4 时,JFATL 是一个极快趋势的指标;这和 EA 操作的分钟图一结合,使这种系统甚至可以确定 10 到 15 个点的价格变化幅度,所以,得出这样大的止损和获利值的优异测试结果只说明了一个事实,即市场在优化期间表现出强劲的趋势,而这个趋势并未在系统内检测到。所以我们应该明白,上传这些参数后,EA 很难表现出这么好的结果。但是,如果你可以使用其他一些工具检测市场中存在的强劲趋势,那么使用此类参数就说得过去了。
变量 Phase_Up 值的变化范围为 -100 至 +100。当值等于 -100 时,JFATL 移动的瞬变过程具有最小的特性,但我的经验表明,最佳的优化结果是在值为 +100 时取得的。IPC_Up 变量定义了 JFATL 算法进行进一步处理时使用的价格。如果出现了与已建仓位方向相反的趋势,可使用 ClosePos_Up 变量强行平仓。应该考虑到,如果获利位与止损位与市场价位相差过远,应基于移动信号进行所有平仓,并且如果 ClosePos_Up 的值等于“true”,TP 和 SL 将不会影响到交易!
总结
到这里我必须结束这段说明了,本文结尾处讨论的优化主题太大了。所以还是留到下一篇文章中再作讨论吧。第一篇文章的主要意图是向读者展示我自己的 Expert Advisor 编写方法,并向拥有很少 EA 编写经验的新手提供更简单且相当通用的 EA 代码构建方法。我认为本文已完成了这个任务。在下一篇文章中,我将解释一些用于分析优化结果的功能,并提供一个用于交易系统的功能。至于本文包括的示例 EA,请注意,它们都过于简单,无法真正用于全赋值自动交易。但是,当交易者需要暂时离开其客户终端一段时间时,它们在作为工作工具实现单独交易操作的自动化方面颇为有效。
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/1516