下载MetaTrader 5

在通用EA交易中发送交易信号

1 二月 2016, 13:02
Igor Kim
0
1 272

简介


一段时间以前我决定在"通用EA交易的开发"这一主题下尝试把EA交易的开发过程通用化和统一. 这就意味着需要设计出开发主单元模块的某种标准, 之后可以像使用施工包一样用来构造EA交易. 任务已经部分实现了. 我提供了一个用于通用EA交易的结构, 还有一个统一使用不同指标信号的想法. 在本文中, 我会继续此工作 - 我将尝试使用通用的过程来构造和发送交易信号以及管理EA交易中的项目. 此处项目的意思是一个操作 - 买入(BUY)或者卖出(SELL), 之后的讨论所指的都是这类操作. 本文并不讲述挂单, 例如限价买入(BUYLIMIT), 止损买入(BUYSTOP), 限价卖出(SELLLIMIT)和止损卖出(SELLSTOP), 但是在文章末尾我会说明, 我的方法可以很容易地应用其中.

交易信号的分类


有几种类型的交易信号:

  1. 买入;
  2. 卖出;
  3. 再次买入(做平均);
  4. 再次卖出 (做平均);
  5. 全部关闭买入;
  6. 全部关闭卖出;
  7. 部分关闭买入;
  8. 部分关闭卖出.

如果我们把做平均和部分关闭从交易信号构造部分调换出来放到订单管理单元的话, 这个列表将缩减如下:

  1. 买入;
  2. 卖出;
  3. 关闭买入;
  4. 关闭卖出;
  5. 什么都不做 (EA的常规操作需要这个信号).

如果是这样, EA交易的操作应该如下:

  1. 一个信号项目创建一个交易信号;
  2. 交易信号进入仓位管理单元, 在单元中会决定是新建仓位, 做平均, 部分还是全部关闭仓位, 然后再把缩短的交易信号发送到程序的交易信号处理单元;
  3. 交易信号处理单元直接进行交易操作.

在本文中, 我将研究使用不同的方式创建交易信号, 并使用不同方法, 也就是之前第一点和第二点提到的单元间的接口, 把它们的信号发送到仓位管理单元 .


单个仓位上的摇摆交易


这种交易方法如下: 在建立一个新的仓位之前, 前面的仓位需要关闭. 新的仓位和前面的是反向的. 如果它是买入, 我们就建立卖出仓位, 以此类推. 在这种情况下, 建仓和平仓是同时进行的, 这就是为什么关闭信号可以不用. 所以摆动交易的实现只需要发送三种交易信号: 买入, 卖出和什么都不做. 这些信号可以使用一个整数变量发送. 例如:

1 - 买入;
0 - 什么都不做;
-1 - 卖出.

而分析市场条件和创建交易信号的部分可以写在一个独立的函数中, 例如 GetTradeSignal() 函数,

//+----------------------------------------------------------------------------+
//|  返回交易信号:                                                               |
//|     1 - 买入                                                                |
//|     0 - 什么都不做                                                           |
//|    -1 - 卖出                                                                |
//|  参数:                                                                      |
//|    sym - 资产名称 ("" - 当前交易品种)                                         |
//|    tf  - 时间框架                ( 0 - 当前时间框架)                          |
//+----------------------------------------------------------------------------+
int GetTradeSignal(string sym="", int tf=0) 
  {
   int bs=0;
   if (sym=="") sym=Symbol();
 
   // 分析模块把一个数值赋予bs变量
 
   return(bs);
  }
//+----------------------------------------------------------------------------+

它会返回以上所述的整数值. 这个函数的激活最好是直接在仓位管理单元进行.


//+----------------------------------------------------------------------------+
//|  仓位管理                                                                   |
//+----------------------------------------------------------------------------+
void ManagePositions() 
  {
   double sl=0, tp=0;
   int    bs=GetTradeSignal();
 
   if(bs>0) 
     {
      if(ExistPositions("", OP_SELL)) 
          ClosePositions("", OP_SELL);
      if(!ExistPositions("", OP_BUY)) 
        {
          if(StopLoss!=0) 
              sl=Ask-StopLoss*Point;
          if(TakeProfit!=0) 
              tp=Ask+TakeProfit*Point;
          OpenPosition("", OP_BUY, sl, tp);
        }
     }
   if(bs<0) 
     {
       if(ExistPositions("", OP_BUY)) 
           ClosePositions("", OP_BUY);
       if(!ExistPositions("", OP_SELL)) 
         {
           if(StopLoss!=0) 
               sl=Bid+StopLoss*Point;
           if(TakeProfit!=0) 
               tp=Bid-TakeProfit*Point;
           OpenPosition("", OP_SELL, sl, tp);
         }
     }
  }
//+----------------------------------------------------------------------------+

在这个实例中, 一个整数类型的局部变量bs作为两个程序单元间的连接器. 单个仓位摇摆交易的例子代码全部文本都在 e-SampleSwing.mq4 文件之中.



单个仓位的简单交易


这个例子稍微难一点. 尽管在市场上每个时间点只有一个仓位, 它的关闭与另外一个仓位的建立不是直接联系的. 所以一个成功的仓位管理需要使用全部五个信号: 买入, 卖出, 关闭买入仓位, 关闭卖出仓位, 以及什么都不做. 他们可以使用一个整型变量赋予以下数值发送:

  • 2 - 关闭卖出仓位;
  • 1 - 买入;
  • 0 - 什么都不做;
  • -1 - 卖出;
  • -2 - 关闭买入仓位.

仓位管理单元可以写在一个函数之内:

//+----------------------------------------------------------------------------+
//|  仓位管理                                                                   |
//+----------------------------------------------------------------------------+
void ManagePositions() 
  {
    double sl=0, tp=0;
    int    bs=GetTradeSignal();
 
    if(ExistPositions()) 
      {
        if(bs==2) 
            ClosePositions("", OP_SELL);
        if(bs==-2) 
            ClosePositions("", OP_BUY);
      } 
    else 
      {
        if(bs==1) 
          {
            if(StopLoss!=0) 
                sl=Ask-StopLoss*Point;
            if(TakeProfit!=0) 
                tp=Ask+TakeProfit*Point;
            OpenPosition("", OP_BUY, sl, tp);
          }
        if(bs==-1) 
          {
            if(StopLoss!=0) 
                sl=Bid+StopLoss*Point;
            if(TakeProfit!=0) 
                tp=Bid-TakeProfit*Point;
            OpenPosition("", OP_SELL, sl, tp);
          }
      }
  }
//+----------------------------------------------------------------------------+

单个仓位简单交易实例源代码的全部文本都在e-SampleSimple.mq4文件中.

使用一个变量发送交易信号的主要不便之处在于您不能同时发送多个信号(串行接口的缺点) - 例如, 您不能在两个方向上建仓, 或者在建仓同时关闭一个已有的仓位, 或者关闭所有的仓位. 这个缺点可以通过把一个信号分成两个整数而部分解决.

有三种方法来分开一个信号:

  1. 一个变量用于建仓, 另一个用于平仓. 您可以组合建仓和平仓, 即摇摆交易, 但是您不能在两个方向上减仓或者同时平掉逆向的仓位;
  2. 买入仓位(建立和关闭)使用一个变量, 卖出仓位(建立和关闭)使用另外一个变量. 您可以在两个方向上同时建仓, 也可以一起关闭全部仓位, 但是您不能同时建立和关闭, 例如买入或者卖出仓位, 也就是您不能重新建立仓位;
  3. 建立买入仓位和关闭卖出仓位使用一个变量, 建立卖出和关闭买入仓位使用另外一个. 您可以在同一方向上建立或者关闭多个仓位, 但是您不能组织摇摆交易.

让我们尝试第二种方法, 因为重新建仓比较少见. 没有人愿意在点差上丢钱. 第二种方法可以使用几种方法实现.

1. 两个函数返回两个局部变量. 一个函数创建买入信号, 第二个函数用于卖出.

//+----------------------------------------------------------------------------+
//|  返回买入仓位信号:                                                           |
//|     1 - 买入                                                               |
//|     0 - 什么都不做                                                          |
//|    -1 - close BUY                                                          |
//|  参数:                                                                      |
//|    sym - 资产名称 ("" - 当前交易品种)                                         |
//|    tf  - 时间框架                ( 0 - 当前时间框架)                          |
//+----------------------------------------------------------------------------+
int GetTradeSignalBuy(string sym="", int tf=0) 
  {
    int bs=0;
    if(sym=="") 
        sym=Symbol();
 
    // 分析模块把一个数值赋予bs变量
 
    return(bs);
  }
 
//+----------------------------------------------------------------------------+
//|  返回卖出仓位的交易信号:                                                      |
//|     1 - sell                                                               |
//|     0 - 什么都不做                                                          |
//|    -1 - close SELL                                                         |
//|  参数:                                                                      |
//|    sym - 资产名称 ("" - 当前交易品种)                                         |
//|    tf  - 时间框架                ( 0 - 当前时间框架)                          |
//+----------------------------------------------------------------------------+
int GetTradeSignalSell(string sym="", int tf=0) 
  {
    int bs=0;
    if(sym=="") 
        sym=Symbol();
 
    // 分析模块把一个数值赋予bs变量
 
    return(bs);
  }
 
//+----------------------------------------------------------------------------+
//|  仓位管理                                                                   |
//+----------------------------------------------------------------------------+
void ManagePositions() 
  {
    double sl=0, tp=0;
    int    bs=GetTradeSignalBuy();
    int    ss=GetTradeSignalSell();
 
    if(ExistPositions()) 
      {
        if(bs<0) 
            ClosePositions("", OP_BUY);
        if(ss<0) 
            ClosePositions("", OP_SELL);
      }
    if(!ExistPositions()) 
      {
        if(bs>0) 
          {
            if(StopLoss!=0) 
                sl=Ask-StopLoss*Point;
            if(TakeProfit!=0) 
                tp=Ask+TakeProfit*Point;
            OpenPosition("", OP_BUY, sl, tp);
          }
        if(ss>0) 
          {
            if(StopLoss!=0) 
                sl=Bid+StopLoss*Point;
            if(TakeProfit!=0) 
                tp=Bid-TakeProfit*Point;
            OpenPosition("", OP_SELL, sl, tp);
          }
      }
  }
//+----------------------------------------------------------------------------+

这是一个简单而方便的创建并发送信号的方法, 但是一个信号单元被分到两个函数中, 可能会减慢它的运行.



2. 两个全局变量在一个函数内取得数值.

//+----------------------------------------------------------------------------+
BuySignal=0;
SellSignal=0;
 
//+----------------------------------------------------------------------------+
//|  创建交易信号.                                                               |
//|  参数:                                                                      |
//|    sym - 资产名称 ("" - 当前交易品种)                                         |
//|    tf  -  时间框架              ( 0 - 当前时间框架)                           |
//+----------------------------------------------------------------------------+
void FormTradeSignals(string sym="", int tf=0) 
  {
    if(sym=="") sym=Symbol();
 
    // 分析模块, 把值赋予BuySignal和SellSignal变量
  }
 
//+----------------------------------------------------------------------------+
//|  仓位管理                                                                   |
//+----------------------------------------------------------------------------+
void ManagePositions() 
  {
    double sl=0, tp=0;
 
    FormTradeSignals();
    if(ExistPositions()) 
      {
        if(BuySignal<0) 
            ClosePositions("", OP_BUY);
        if(SellSignal<0) 
            ClosePositions("", OP_SELL);
      }
    if(!ExistPositions()) 
      {
        if(BuySignal>0) 
          {
            if(StopLoss!=0) 
                sl=Ask-StopLoss*Point;
            if(TakeProfit!=0) 
                tp=Ask+TakeProfit*Point;
            OpenPosition("", OP_BUY, sl, tp);
          }
        if(SellSignal>0) 
          {
            if(StopLoss!=0) 
                sl=Bid+StopLoss*Point;
            if(TakeProfit!=0) 
                tp=Bid-TakeProfit*Point;
            OpenPosition("", OP_SELL, sl, tp);
          }
      }
  }
//+----------------------------------------------------------------------------+

从源代码可以明显看出, 使用全局变量的实现与第一种方法相比差别不大. 显著的优点是一个函数内设置信号模式.



3. 通过引用在一个函数内传送两个局部变量.

//+----------------------------------------------------------------------------+
//|  返回交易信号.                                                               |
//|  参数:                                                                      |
//|    bs  - 买入信号                                                           |
//|    ss  - 卖出信号                                                           |
//|    sym - 资产名称 ("" - 当前交易品种)                                         |
//|    tf  - 时间框架                ( 0 - 当前时间框架)                          |
//+----------------------------------------------------------------------------+
void GetTradeSignals(int& bs, int& ss, string sym="", int tf=0) 
  {
    if(sym=="") 
        sym=Symbol();
 
    // 分析模块, 给变量bs和ss赋值
  }
 
//+----------------------------------------------------------------------------+
//|  仓位管理                                                                   |
//+----------------------------------------------------------------------------+
void ManagePositions() 
  {
    double sl=0, tp=0;
    int    bs=0, ss=0;
 
    GetTradeSignals(bs, ss);
    if(ExistPositions()) 
      {
        if(bs<0) 
            ClosePositions("", OP_BUY);
        if(ss<0) 
            ClosePositions("", OP_SELL);
      }
    if(!ExistPositions()) 
      {
        if(bs>0) 
          {
            if(StopLoss!=0) 
                sl=Ask-StopLoss*Point;
            if(TakeProfit!=0) 
                tp=Ask+TakeProfit*Point;
            OpenPosition("", OP_BUY, sl, tp);
          }
        if(ss>0) 
          {
            if(StopLoss!=0) 
                sl=Bid+StopLoss*Point;
            if(TakeProfit!=0) 
                tp=Bid-TakeProfit*Point;
            OpenPosition("", OP_SELL, sl, tp);
          }
      }
  }
//+----------------------------------------------------------------------------+

4. 一个两个元素的数组. 如果它是全局的, 它就在函数内被初始化. 如果它是局部的, 它通过引用而传递. 这里都很简单 - 一个数组替换了两个变量. 就像之前讨论的, 全局元素以及使用引用的局部元素. 没有新的内容.

并且, 最终我们可以把它分成四个变量, 组织包含全部数值的并行接口来发送数据. 对于仓位管理, 每个信号有两个状态: 存在还是不存在. 所以我们最好使用逻辑型变量. 您可以组合信号, 使用四个变量没有限制地发送它们. 下面是使用含有四个逻辑型变量的数组的代码示例.

//+----------------------------------------------------------------------------+
//|  返回交易信号.                                                               |
//|  参数:                                                                      |
//|    ms  - 信号数组                                                           |
//|    sym - 资产名称 ("" - 当前交易品种)                                         |
//|    tf  - 时间框架                ( 0 - 当前时间框架)                          |
//+----------------------------------------------------------------------------+
void GetTradeSignals(bool& ms[], string sym="", int tf=0) 
  {
    if(sym=="") 
        sym=Symbol();
 
    // 分析模块, 填充ms数组:
    // ms[0]=True;   // 买入
    // ms[1]=True;   // 卖出
    // ms[2]=True;   // 关闭买入
    // ms[3]=True;   // 关闭卖出
  }
 
//+----------------------------------------------------------------------------+
//|  仓位管理                                                                   |
//+----------------------------------------------------------------------------+
void ManagePositions() 
  {
    bool   ms[4]={False, False, False, False};
    double sl=0, tp=0;
 
    GetTradeSignals(ms);
    if(ExistPositions()) 
      {
        if(ms[2]) 
            ClosePositions("", OP_BUY);
        if(ms[3]) 
            ClosePositions("", OP_SELL);
      }
    if(!ExistPositions()) 
      {
        if(ms[0]) 
          {
            if(StopLoss!=0) 
                sl=Ask-StopLoss*Point;
            if(TakeProfit!=0) 
                tp=Ask+TakeProfit*Point;
            OpenPosition("", OP_BUY, sl, tp);
          }
        if(ms[1]) 
          {
            if(StopLoss!=0) 
                sl=Bid+StopLoss*Point;
            if(TakeProfit!=0) 
                tp=Bid-TakeProfit*Point;
            OpenPosition("", OP_SELL, sl, tp);
          }
      }
  }
//+----------------------------------------------------------------------------+

不论是使用局部变量, 在一个函数内使用引用来传递参数, 还是使用全局变量在一个函数内初始化, 都是没有问题的. 然而, 使用四个不同函数来根据数值初始化四个局部变量的话, 那是不合理的.



支持主仓位


当信号来临时, 建立一个仓位. 它就变成主仓位. 所有其他与主仓位无关的信号将被忽略. 另外根据信号建立的仓位都是对应着主仓位的. 在关闭主仓位时, 所有额外的仓位也会被关闭.

发送此类信号的方法可以使用一个, 两个或者四个变量来实现. 这些之前都讲述过了. 您可能遇到的困难是提供连接: 一个信号- 一个仓位. 如果在一个仓位上进行交易, 这个问题就可以通过使用ExistPositions()函数对是否存在仓位做个简单检测而解决. 如果有了仓位, 进场信号就略过, 没有仓位 - 执行进场信号.

所以, 可以采用不同方法:

  1. 在进场之间提供暂停功能. 最简单的实现;
  2. 1个柱 - 1次进场. 第一种的变通方法. 这种实现同样简单;
  3. 给信号以及信号和订单编号组成的控制数组编号. 这是最困难的实现并且还可能不稳定.

这是第一种方法的代码示例:

//+----------------------------------------------------------------------------+
extern int PauseBetweenEntrys=3600;    // 进场之间的暂停秒数
 
//+----------------------------------------------------------------------------+
//|  仓位管理                                                                   |
//+----------------------------------------------------------------------------+
void ManagePositions() 
  {
    bool   ms[4]={False, False, False, False};
    double sl=0, tp=0;
 
    GetTradeSignals(ms);
    if(ExistPositions()) 
      {
        if(ms[2]) 
            ClosePositions("", OP_BUY);
        if(ms[3]) 
            ClosePositions("", OP_SELL);
      }
    if(SecondsAfterOpenLastPos()>=PauseBetweenEntrys) 
      {
        if(ms[0]) 
          {
            if(StopLoss!=0) 
                sl=Ask-StopLoss*Point;
            if(TakeProfit!=0) 
                tp=Ask+TakeProfit*Point;
            OpenPosition("", OP_BUY, sl, tp);
          }
        if(ms[1]) 
          {
            if(StopLoss!=0) 
                sl=Bid+StopLoss*Point;
            if(TakeProfit!=0) 
                tp=Bid-TakeProfit*Point;
            OpenPosition("", OP_SELL, sl, tp);
          }
      }
  }
 
//+----------------------------------------------------------------------------+
//|  返回前一个仓位建立后的秒数                                                    |
//|  参数:                                                                      |
//|    sym - 资产名称 ("" - 当前交易品种)                                         |
//|    op  - operation              (-1 - any position)                        |
//|    mn  - MagicNumber            (-1 - any magic number)                    |
//+----------------------------------------------------------------------------+
datetime SecondsAfterOpenLastPos(string sym="", int op=-1, int mn=-1) 
  {
    datetime oot;
    int      i, k=OrdersTotal();
 
    if(sym=="") sym=Symbol();
        for(i=0; ik; i++) 
          {
            if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) 
              {
                if(OrderSymbol()==sym) 
                  {
                    if(OrderType()==OP_BUY || OrderType()==OP_SELL) 
                      {
                        if(op0 || OrderType()==op) 
                          {
                            if(mn0 || OrderMagicNumber()==mn) 
                              {
                                if(ootOrderOpenTime()) 
                                    oot=OrderOpenTime();
                              }
                          }
                      }
                 }
             }
        }
      return(CurTime()-oot);
  }
//+----------------------------------------------------------------------------+

这种支持主仓位方法的实例代码全文本在e-SampleMain.mq4文件中.


使用平均的摇摆交易


当进场信号来临时, 建立一个仓位. 根据第一个仓位的方向, 所有之后同方向的信号都会建立新的仓位. 当来临的信号与已经存在的仓位方向相反时, 全部仓位关闭并建立一个另外方向的仓位. 信号可以是可以重复的.

发送并执行在一个仓位上摇摆交易的信号已经讨论过了. 让我们采用这个例子来实现建立额外仓位的选项. 为了提供1个信号-1个仓位的连接, 让我们把进场局限为一个柱只能一次. 1个柱 - 1次进场.

//+----------------------------------------------------------------------------+
//|  仓位管理                                                                   |
//+----------------------------------------------------------------------------+
void ManagePositions() 
  {
    double sl=0, tp=0;
    int    bs=GetTradeSignal();
 
    if(bs>0) 
      {
        if(ExistPositions("", OP_SELL)) 
            ClosePositions("", OP_SELL);
        if(NumberOfBarLastPos()>0) 
          {
            if(StopLoss!=0) 
                sl=Ask-StopLoss*Point;
            if(TakeProfit!=0) 
                tp=Ask+TakeProfit*Point;
            OpenPosition("", OP_BUY, sl, tp);
          }
      }
    if(bs<0) 
      {
        if(ExistPositions("", OP_BUY)) 
            ClosePositions("", OP_BUY);
        if(NumberOfBarLastPos()>0) 
          {
            if(StopLoss!=0) 
                sl=Bid+StopLoss*Point;
            if(TakeProfit!=0) 
                tp=Bid-TakeProfit*Point;
            OpenPosition("", OP_SELL, sl, tp);
          }
      }
  }
 
//+----------------------------------------------------------------------------+
//|  返回最新建立的仓位的柱编号或者 -1.                                            |
//|  参数:                                                                     |
//|    sym - 资产名称 ("" - 当前交易品种)                                         |
//|    tf  - 时间框架                ( 0 - 当前时间框架)                          |
//|    op  - 操作                (-1 - 任意仓位)                                 |
//|    mn  - 幻数              (-1 - 任何幻数)                                   |
//+----------------------------------------------------------------------------+
int NumberOfBarLastPos(string sym="", int tf=0, int op=-1, int mn=-1) 
  {
    datetime oot;
    int      i, k=OrdersTotal();
 
    if(sym=="") 
        sym=Symbol();
    for(i=0; ik; i++) 
      {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) 
          {
            if(OrderSymbol()==sym) 
              {
                if(OrderType()==OP_BUY || OrderType()==OP_SELL) 
                  {
                    if(op0 || OrderType()==op) 
                      {
                        if(mn0 || OrderMagicNumber()==mn) 
                          {
                            if(ootOrderOpenTime()) oot=OrderOpenTime();
                          }
                      }
                  }
             }
         }
     }
   return(iBarShift(sym, tf, oot, True));
  }
//+----------------------------------------------------------------------------+

使用平均的摇摆交易实例代码的全文在e-SampleSwingAdd.mq4文件中.


组合策略


对于每个交易信号, 建立一个仓位而不论其他.

乍一看来, 这是使用软件实现交易信号执行中最困难的方法. 除了要发送交易信号, 您还需要发送信号所属的某个组合. 但是, 如果您把信号分成组, 而建立一个组合, 您将看到, 每个组就是一个简单的单个仓位交易. 把组分配到组合中的方法是显而易见的: 为每个组设置一个唯一编号, 然后在循环中搜索全部组. 这里是一个四个信号组的例子:

//+----------------------------------------------------------------------------+
//|  返回交易信号.                                                               |
//|  参数:                                                                      |
//|    ms  - 信号数组                                                           |
//|    ns  - 信号编号                                                           |
//|    sym - 资产名称 ("" - 当前交易品种)                                         |
//|    tf  -  时间框架              ( 0 - 当前时间框架)                           |
//+----------------------------------------------------------------------------+
void GetTradeSignals(bool& ms[], int ns, string sym="", int tf=0) 
  {
    if (sym=="") sym=Symbol();
 
    // 使用switch或者if操作符来切换组
    // 分析模块, 填充ms数组:
    // ms[0]=True;   // 买入
    // ms[1]=True;   // 卖出
    // ms[2]=True;   // 关闭买入
    // ms[3]=True;   // 关闭卖出
  }
 
//+----------------------------------------------------------------------------+
//|  仓位管理                                                                   |
//+----------------------------------------------------------------------------+
void ManagePositions() 
  {
    bool   ms[4];
    double sl=0, tp=0;
    int    i;
 
    for(i=0; i4; i++) 
      {
        ArrayInitialize(ms, False);
        GetTradeSignals(ms, i);
        if(ExistPositions("", -1, MAGIC+i)) 
          {
            if(ms[2]) ClosePositions("", OP_BUY, MAGIC+i);
            if(ms[3]) ClosePositions("", OP_SELL, MAGIC+i);
          }
        if(!ExistPositions("", -1, MAGIC+i)) 
          {
            if(ms[0]) 
              {
                if(StopLoss!=0) 
                    sl=Ask-StopLoss*Point;
                if(TakeProfit!=0) 
                    tp=Ask+TakeProfit*Point;
                OpenPosition("", OP_BUY, sl, tp, MAGIC+i);
              }
            if(ms[1]) 
              {
                if(StopLoss!=0) 
                    sl=Bid+StopLoss*Point;
                if(TakeProfit!=0) 
                    tp=Bid-TakeProfit*Point;
                OpenPosition("", OP_SELL, sl, tp, MAGIC+i);
              }
          }
      }
  }
//+----------------------------------------------------------------------------+

使用组合策略的代码示例全文位于e-SampleCase.mq4文件中.


订单的交易信号


让我们看一下用于操作挂单的交易信号列表.

  1. 设置限价买入(BuyLimit);
  2. 设置限价卖出(SellLimit);
  3. 设置止损买入(BuyStop);
  4. 设置止损卖出(SellStop);
  5. 删除限价买入;
  6. 删除限价卖出;
  7. 删除止损买入;
  8. 删除止损卖出;
  9. 修改限价买入;
  10. 修改限价卖出;
  11. 修改止损买入;
  12. 修改止损卖出.

您可以轻松猜到, 使用挂单您会想要同时发送四种或者更多的交易信号. 如果EA交易的逻辑要求可以同时运作仓位和订单呢?

这里是扩展过的全部交易信号列表.

  1. 建立买入(Buy);
  2. 建立卖出(Sell);
  3. 设置限价买入(BuyLimit);
  4. 设置限价卖出(SellLimit);
  5. 设置止损买入(BuyStop);
  6. 设置止损卖出(SellStop);
  7. 关闭买入;
  8. 关闭卖出;
  9. 删除限价买入;
  10. 删除限价卖出;
  11. 删除止损买入;
  12. 删除止损卖出;
  13. 修改限价买入;
  14. 修改限价卖出;
  15. 修改止损买入;
  16. 修改止损卖出.

16个交易信号!两个字节!还有一种方法以二进制数发送信号, 但是使用字符串类型. 例如, 00101..., 其中的0或者1将都负责一种确定的信号. 但是这样会使子字符串操作混乱, 还需要信号解码. 所以, 您可以看到, 最方便的方法还是使用一个数字元素组成的数组, 其长度等于信号的数量. 另外, 为了更加方便地参照数组元素, 索引可以使用常数来定义. MQL4 已经包含了 OP_BUY, OP_SELL 之类. 我们可以简单继续:

#define OP_CLOSEBUY         6
#define OP_CLOSESELL        7
#define OP_DELETEBUYLIMIT   8
#define OP_DELETESELLLIMIT  9
#define OP_DELETEBUYSTOP   10
#define OP_DELETESELLSTOP  11
#define OP_MODIFYBUYLIMIT  12
#define OP_MODIFYSELLLIMIT 13
#define OP_MODIFYBUYSTOP   14
#define OP_MODIFYSELLSTOP  15

然后使用如下方式引用数组元素: ms[OP_BUY] 或者 ms[OP_MODIFYSELLLIMIT] 而不是 ms[0] 和 ms[13]. 当然, 这还是偏好问题. 我更喜欢清晰和简单的代码, 所以我选择数字.

让我们继续. 所有事情看起来简单明了!但是我们还是需要设置订单的价格水平, 仓位的止损和获利水平, 因为每个交易信号可能会包含某个止损, 获利, 以及设置订单的价格水平. 但是我们怎样才能把这些信息一起随交易信号传递呢?我提供了如下解决方法:

  1. 声明一个二维数组, 行数等于信号的数量, 有3列;
  2. 第一列如果为0, 说明没有信号, 如果不为0 - 则有信号或仓位(设置订单的价格水平);
  3. 第二列 - 止损;
  4. 第三列 - 获利水平.

使用订单进行交易的代码示例全文位于e-SampleOrders.mq4文件中.

还有, 如果我们需要设置几个限价买入或者止损卖出呢?还有建立更多仓位?如果是这样, 我们就将按照本文中"组合策略"部分描述的那样, 也就是在仓位和订单中使用幻数来进行组合管理.

结论


是时间做一下总结了:

  1. 使用一个变量来发送信号有串行接口的一些不便 - 每个时间点只能发送一个信号;
  2. 实现并行接口会引入大量的变量并使它们的管理变得复杂, 但是允许同时发送多个信号.

所以, 是使用第一个还是其他的方法发送信号要权衡利害来选择.

本文译自 MetaQuotes Software Corp. 撰写的俄文原文
原文地址: https://www.mql5.com/ru/articles/1436

附加的文件 |
b-Positions.mqh (20.13 KB)
e-SampleCase.mq4 (7.12 KB)
e-SampleMain.mq4 (7.94 KB)
e-SampleOrders.mq4 (14.71 KB)
e-SampleSimple.mq4 (6.88 KB)
如何不陷入优化陷阱? 如何不陷入优化陷阱?

本文讲述了怎样更好地理解测试器优化结果的方法. 它也给出了一些避免"有害优化"的技巧.

在Linux桌面系统运行MetaTrader 4客户终端 在Linux桌面系统运行MetaTrader 4客户终端

本文讲述了使用非模拟器wine软件在Linux桌面系统上运行MetaTrader 4客户终端的详细设置步骤.

交易者的个人心理学 交易者的个人心理学

一幅金融市场上交易者的行为画像. 作者自己的菜单是来自于A.Elder的书 "怎样在股票交易中投机取胜" ("How to speculate on stock exchange and win").

创建自动交易系统 创建自动交易系统

您现在已经成为程序的幸运拥有者——该程序可以在几分钟内为您建立一套可盈利的自动交易系统(ATC),不得不说这听起来很诱人。 您需要做的只是输入想要的数值并按回车键。 此刻,赶紧测试您的自动交易系统并获取预期的收益吧。 成千上万人花费数千小时的时间开发了这套独特的自动交易客户端,从此将能一劳永逸,这听起来多少有些空洞。 一方面,这听起来的确有点不实际。 但是,我认为这个问题可以解决。