English Русский Español Deutsch 日本語 Português
通用的 Expert Advisor 模板

通用的 Expert Advisor 模板

MetaTrader 4交易系统 | 16 三月 2016, 12:44
2 202 0
Вадим Андреевич
Вадим Андреевич

简介

很多交易者面临编写自己的 Expert Advisor 的问题。第一要务是什么?如何在 EA 代码中设置获利、止损或追踪止损?如何检验策略的功能性?本文将详述用于创建 Expert Advisor 的主要函数。或许有人会发现追踪代码很有用。



Expert Advisor 变量

那么,什么变量在所有 Expert Advisor 中都是不可或缺的?要让测试程序也检查由布尔型变量设置的参数,应该做些什么?第一,我将展示一段作用于所有的货币对的通用 EA 代码,为了获得更高的速度,建议根据你自己的货币类型进行调节。第二,0-禁用,1-启用(因为我们希望在优化程序中检查所有的参数。如果最终代码极少优化,建议更改该参数)。第三,水平(止损)变量初始为整数,在代码中,必要时更改为分数。在现实生活中,我们交易不同的货币,这仅是一个模板!第四,如果止损参数等于零,则止损不工作。



定义变量

现在开始定义变量。让我们从容易优化的变量——外部变量开始。

extern double MaxLot;
extern double TakeProfit;
extern double TrailingStop;
extern double StopLoss;
extern double MinProfit;
extern double ProfitPoints;
extern int    Slippage=0;
extern int Condition1=1;
extern double LotSpliter=1.0;
extern int CloseByOtherSideCondition;

MaxLot 变量设置最大手数,即我们希望限制的最大使用手数(手数也受服务器限制,但我们稍后对其进行讨论)。
当 TakeProfit、StopLoss 和 TrailingStop 大于零时,在代码中作用。
当 ProfitPoints 大于零,MinProfit 和 ProfitPoints 根据原则:价格达到 ProfitPoints 并返回至 MinProfit 而作用。
ConditionX 激活进入条件。
LotSpliter 是手数分割器。仅使用部分可用手数,例如 0.1 仅包含总保证金可用比例的十分之一。
CloseByOtherSideCondition 在出现相反条件时关闭订单。

让我们设置内部变量,在描述 EA 时对其进行讨论。

double Lot;
double PP=0;
double slu,sld,a,b;
double tp,sl;

初始化代码

现在来看一下,仅启动 Expert Advisor 可以计算哪些变量并在代码中进一步使用。

int init()
  {
   tp=TakeProfit;
   sl=StopLoss;
   return(0);
  }

我们获取这些变量的值,用于止损位更改的情况。如果打算始终以相同的量进行交易,我们也可以计算手数并显示额外量(本文稍后分析手数计算)。我们也可以创建包含 EA 描述或版权的可显示的注释。做法如下:

Comment("cloud trade \n v2.0.11");

"\n" - 意思是切换至下一个显示行“carriage return”。


Framework 代码

现在让我们查看没有订单的代码:

   if(OrdersTotal()==0)
   {   
      preinit();
      if(U()==1)
      {
         OrderBuy();
         return(0);
      }
      if(U()==2)
      {
         OrderSell();
         return(0);
      }
      return(0);
   }

这些函数的一部分将稍后分析。原则如下:初始化参数、检查是否有进入条件、按条件输入。

现在让我们查看有一个订单的代码:


      if(OrderType()==OP_BUY)
        {
         if((slu)>PP)
           {
            PP=slu;
           }
         if(((slu)>0.001) '' (OrderStopLoss()<(b-TrailingStop))
          '' (OrderOpenPrice()<(b-TrailingStop))
           '' (OrderProfit()>MathAbs(OrderSwap())))
           {
            if(TrailingStop!=0)
              {
               OrderModify(OrderTicket(), 0, b-TrailingStop, 0, 0, 0);
              }
           }
        }
      if(OrderType()==OP_SELL)
        {
         if((sld)>PP)
           {
            PP=sld;
           }
         if(((sld)>0.001) '' (OrderStopLoss()>(a+TrailingStop))
          '' (OrderOpenPrice()>(a+TrailingStop)))
           {
            if(TrailingStop!=0)
              {
               OrderModify(OrderTicket(), 0, a+TrailingStop, 0, 0, 0);
              }
           }
        }
      if(ProfitPoints!=0)
        {
         if(OrderType()==OP_BUY '' PP>=ProfitPoints '' (slu)<=MinProfit)
           {
            CloseOnlyOrder(OrderTicket());
            return(0);
           }
         if(OrderType()==OP_SELL '' PP>=ProfitPoints '' (sld)<=MinProfit)
           {
            CloseOnlyOrder(OrderTicket());
            return(0);
           }
        }
      if(CloseByOtherSideCondition==1)
        {
         if(OrderType()==OP_BUY '' U()==2)
           {
            CloseOnlyOrder(OrderTicket());
            return(0);
           }
         if(OrderType()==OP_SELL '' U()==1)
           {
            CloseOnlyOrder(OrderTicket());
            return(0);
           }
        }

首先,我们仅选择一个订单,以便后续操作(本文将在单独的部分分析该代码)。然后给价格分配变量,以免更改订单,例如,错误方向的追踪止损或无利润的新价格。首先检查订单的追踪止损可能性,同时为下一个函数(最小获利)收集数据,前面已对其使用进行了描述。然后是在相反条件出现时关闭订单及在相反方向打开订单的函数。



本文中分析的函数

现在让我们查看这些函数,用于缩短代码并在程序块中加入最常用的命令,以便随后调用完整的程序块。尝试设置这些条件并检验:

//+------------------------------------------------------------------+
//|  returns a signal to buy or to sell                              |
//+------------------------------------------------------------------+
int U()
  {
      if((U1()==2 '' Condition1==1)
       || (U2()==2 '' Condition2==1)){return(2);}
      if((U1()==1 '' Condition1==1)
       || (U2()==1 '' Condition2==1)){return(1);}
   return(0);
  }
//+------------------------------------------------------------------+
//|  returns a signal based on stochastic values                     |
//+------------------------------------------------------------------+
int U1()
  {
   if(iStochastic(Symbol(),Period(),Kperiod,Dperiod,Slowing, Method,PriceUsing,MODE_SIGNAL,1)>=80)
     {
      if(iStochastic(Symbol(),Period(),Kperiod,Dperiod,Slowing,Method,PriceUsing,MODE_SIGNAL,2)
           <=iStochastic(Symbol(),Period(),Kperiod,Dperiod,Slowing, Method,PriceUsing,MODE_MAIN,2))
        {
         if(iStochastic(Symbol(),Period(),Kperiod,Dperiod,Slowing,Method,PriceUsing,MODE_SIGNAL,1)
           >=iStochastic(Symbol(),Period(),
              Kperiod,Dperiod,Slowing,Method,PriceUsing,MODE_MAIN,1))
           {
            return(2);
           }
        }
     }
   if(iStochastic(Symbol(),Period(),Kperiod,Dperiod,Slowing,Method,PriceUsing,MODE_SIGNAL,1)<=20)
     {
      if(iStochastic(Symbol(),Period(),Kperiod,Dperiod,Slowing,Method,PriceUsing,MODE_SIGNAL,2)
           >=iStochastic(Symbol(),Period(),Kperiod,Dperiod,Slowing,Method,PriceUsing,MODE_MAIN,2))
        {
         if(iStochastic(Symbol(),Period(),Kperiod,Dperiod,Slowing, Method,PriceUsing,MODE_SIGNAL,1)
              <=iStochastic(Symbol(),Period(),Kperiod,Dperiod,Slowing,Method,PriceUsing,MODE_MAIN,1))
           {
            return(1);
           }
        }
     }
   return(0);
  }
//+------------------------------------------------------------------+
//| find trend direction using fractals                              |
//+------------------------------------------------------------------+
int U2()
  {
   double fu=0,fd=0;
   int f=0,shift=2;
   while(f<2)
     {
      if(iFractals(Symbol(),Period(),MODE_UPPER,shift)>0)
        {
         fu=fu+1;
         f=f+1;
        }
      if(iFractals(Symbol(),Period(),MODE_LOWER,shift)>0)
        {
         fd=fd+1;
         f=f+1;
        }
      shift=shift+1;
     }
   if(fu==2){return(2);}
   if(fd==2){return(1);}
   return(0);
  }

第一个函数检查条件,后面两个设置条件。

现在来查看函数,如果止损位设置错误则进行计算,并定义手数值:

//+------------------------------------------------------------------+
//| preliminary initialization of variables                          |
//+------------------------------------------------------------------+
int preinit()
  {
   Lot=NormalizeDouble(MathFloor(LotSpliter*AccountBalance()*AccountLeverage()
      /Ask/MathPow(10,Digits+1)*10)/10,1);
   if(MaxLot>0 '' Lot>MaxLot){Lot=MaxLot;}
   if(Lot>MarketInfo(Symbol(),MODE_MAXLOT)){Lot=MarketInfo(Symbol(),MODE_MAXLOT);}
   PP=0;
   StopLoss=sl;
   TakeProfit=tp;
   if(TakeProfit!=0 '' TakeProfit<(MarketInfo(Symbol(),MODE_STOPLEVEL)))
     {
      TakeProfit=MarketInfo(Symbol(),MODE_STOPLEVEL);
     }
   if(StopLoss!=0 '' StopLoss<(MarketInfo(Symbol(),MODE_STOPLEVEL)))
     {
      StopLoss=MarketInfo(Symbol(),MODE_STOPLEVEL);
     }
   return(0);
  }

现在设置函数,根据预设的止损位打开订单:

//+------------------------------------------------------------------+
//| returns true in case of a successful opening of Buy              |
//+------------------------------------------------------------------+
bool OrderBuy()
  {
   bool res=false;
   if(StopLoss!=0 '' TakeProfit!=0)
     {
      res=OrderSend(Symbol(), 0, NormalizeDouble(Lot,1), Ask, Slippage,
       NormalizeDouble(Ask-StopLoss,4),
        NormalizeDouble(Ask+TakeProfit,4), 0, 0, 0, 0);
      return(res);
     }
   if(StopLoss==0 '' TakeProfit!=0)
     {
      res=OrderSend(Symbol(), 0, NormalizeDouble(Lot,1), Ask, Slippage, 0,
       NormalizeDouble(Ask+TakeProfit,4), 0, 0, 0, 0);
      return(res);
     }
   if(StopLoss==0 '' TakeProfit==0)
     {
      res=OrderSend(Symbol(), 0, NormalizeDouble(Lot,1), Ask,
       Slippage, 0, 0, 0, 0, 0, 0);
      return(res);
     }
   if(StopLoss!=0 '' TakeProfit==0)
     {
      res=OrderSend(Symbol(), 0, NormalizeDouble(Lot,1), Ask, Slippage,
       NormalizeDouble(Ask-StopLoss,4), 0, 0, 0, 0, 0);
      return(res);
     }
   return(res);
  }
//+------------------------------------------------------------------+
//|   returns true in case of a successful opening of Sell           |
//+------------------------------------------------------------------+
bool OrderSell()
  {
   bool res=false;
   if(StopLoss!=0 '' TakeProfit!=0)
     {
      res=OrderSend(Symbol(), OP_SELL, NormalizeDouble(Lot,1), Bid, Slippage,
       NormalizeDouble(Bid+StopLoss,4),
        NormalizeDouble(Bid-TakeProfit,4), 0, 0, 0, 0);
      return(res);
     }
   if(StopLoss==0 '' TakeProfit!=0)
     {
      res=OrderSend(Symbol(), OP_SELL, NormalizeDouble(Lot,1), Bid, Slippage,
       NormalizeDouble(Bid+StopLoss,4),
        NormalizeDouble(Bid-TakeProfit,4), 0, 0, 0, 0);
      return(res);
     }
   if(StopLoss==0 '' TakeProfit==0)
     {
      res=OrderSend(Symbol(), OP_SELL, NormalizeDouble(Lot,1), Bid, Slippage,
       NormalizeDouble(Bid+StopLoss,4),
        NormalizeDouble(Bid-TakeProfit,4), 0, 0, 0, 0);
      return(res);
     }
   if(StopLoss!=0 '' TakeProfit==0)
     {
      res=OrderSend(Symbol(), OP_SELL, NormalizeDouble(Lot,1), Bid, Slippage,
       NormalizeDouble(Bid+StopLoss,4),
        NormalizeDouble(Bid-TakeProfit,4), 0, 0, 0, 0);
      return(res);
     }
   return(res);
  }

下一个函数关闭具有所示订单号、交易量和所示价格的订单。

//+-------------------------------------------------------------------------+
//|  returns true in case of a successful closing of an order with Ticket   |
//+-------------------------------------------------------------------------+
bool CloseOnlyOrder(int Ticket, double Lots ,double priceClose)
  {
   bool res=false;
   res=OrderClose(Ticket, Lots, priceClose, Slippage, 0);
   return(res);

现在来查看根据位置编号选择订单的函数,以对其进一步操作:

//+--------------------------------------------------------------------------------+
//| returns true in case of a successful choosing of an order in the position pos  |
//+--------------------------------------------------------------------------------+
bool SelectOnlyOrder(int pos)
  {
   bool res=false;
   res=OrderSelect(pos,SELECT_BY_POS,MODE_TRADES);
   return(res);
  }
//+------------------------------------------------------------------+


一些编码建议

第一,设置诸如 0 和 1而非“真”和“假”的选项。这将帮助你更好的优化 Expert Advisor。第二,当市场沿着跟条件相反的方向移动时,不要忽视限制可能损失的止损。第三,不要在没有止损的情况下测试自动交易系统——这容易导致保证金快速亏损。第四,使用函数和程序块,使代码更容易理解。



总结

创建 Expert Advisor 并不难。为了使其更加容易,附件包含了本文分析的 Expert Advisor。


本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/1495

附加的文件 |
template.mq4 (8.07 KB)
面向初学者的 MQL4 语言。技术指标和内置函数 面向初学者的 MQL4 语言。技术指标和内置函数
这是&ldquo;面向初学者的 MQL4 语言&rdquo;系列的第三篇文章。现在我们将学习使用内置函数和用于技术指标的函数。后者对于以后开发你自己的 Expert Advisor 和指标至关重要。另外,我们将通过一个简答的例子,解释如何追踪进入市场的交易信号,以及如何正确使用指标。在文章的末尾,你将学到一些关于语言本身的新鲜有趣的内容。
突破策略测试程序在测试对冲 EA 时的限制 突破策略测试程序在测试对冲 EA 时的限制
一种使用策略测试程序测试对冲 Expert Advisor 的思路。
如何实施你自己的优化标准 如何实施你自己的优化标准
本文给出了一个为标准 Expert Advisor 准备的获利/亏损标准优化示例(结果返回文件) - 移动平均线。
群集指标在嘉盛市场中的实际应用 群集指标在嘉盛市场中的实际应用
群集指标是一系列将货币对分成独立的货币的指标。指标允许跟踪相对货币波动,确定形成新的货币趋势的潜能,接收交易信号,以及跟踪中期和长期仓位。