下载MetaTrader 5

按记录过滤

19 二月 2016, 11:18
Andrey Khatimlianskii
0
528

简介


有不同的过滤器:指标值、市场波动性、时间和工作日。 它们全部可以用来过滤亏损交易。 将过滤器添加至 Expert Advisor 非常容易——只要在开始程序块之前另加一个条件即可。

但是,如果要使用 EA 记录作为过滤器,应如何做呢? 如果在数次不成功的交易后关闭交易系统,则随后不会生成记录,因此没有可以分析的内容。 要解决这个问题,我们需要教会 Expert Advisor 虚拟交易,即模拟开仓、修改和平仓而无需真实交易活动。

这是本文要讲述的内容。

试验策略


为了系统的实施,我们将在 Expert Advisor CrossMACD_DeLuxe.mq4 进行一些改动:
  • 在每个头寸的开仓/修改/平仓点,改动将写在虚拟头寸的数组内;
  • 添加虚拟头寸的 StopLoss 和 TakeProfit 的启动跟踪;
  • 添加过滤标准——现实交易不会进行的条件。
我将力图详尽的描述 EA 修改的每个步骤。 如果你不感兴趣,可以下载现成的 Expert Advisor 并前往“游戏是否得不偿失?”部分。

解释虚拟头寸


出现开仓的信号。 计算 StopLoss 和 TakeProfit 参数,完全做好调用 OrderSend() 函数的准备。 我们正是在此刻开始虚拟交易——只要将所有必要参数保存在合适的变量:
void OpenBuy()
  {
    int _GetLastError = 0;
    double _OpenPriceLevel, _StopLossLevel, _TakeProfitLevel;
    _OpenPriceLevel = NormalizeDouble(Ask, Digits);
 
    if(StopLoss > 0)
        _StopLossLevel = NormalizeDouble(_OpenPriceLevel - 
                                     StopLoss*Point, Digits); 
    else
        _StopLossLevel = 0.0; 
    if(TakeProfit > 0)
        _TakeProfitLevel = NormalizeDouble(_OpenPriceLevel + 
                                   TakeProfit*Point, Digits); 
    else
        _TakeProfitLevel = 0.0; 
 
    //---- open the virtual position
    virtOrderSend(OP_BUY, _OpenPriceLevel, _StopLossLevel, 
                  _TakeProfitLevel);
 
    if(OrderSend(Symbol(), OP_BUY, 0.1, _OpenPriceLevel, 3, 
       _StopLossLevel, _TakeProfitLevel, "CrossMACD", 
       _MagicNumber, 0, Green) < 0)
      {
        _GetLastError = GetLastError();
        Alert("Error OrderSend № ", _GetLastError);
        return(-1);
      }
  }
 
//---- Save parameters of the opened position in main variables
void virtualOrderSend(int type, double openprice, double stoploss,
                      double takeprofit)
  {
    virtTicket = 1;
    virtType = type;
    virtOpenPrice = openprice;
    virtStopLoss = stoploss;
    virtTakeProfit = takeprofit;
  }

可以看到,我们只用了五个变量:

int       virtTicket     = 0;   
// determines, if there is an open virtual position
int       virtType       = 0;   // position type
double    virtOpenPrice  = 0.0; // position opening price
double    virtStopLoss   = 0.0; // position StopLoss
double    virtTakeProfit = 0.0; // position TakeProfit

我们任务的完成并不需要其他特征。 如果要拓展该示例的功能性,仅需添加必要的变量。

为了追踪平仓和头寸修改,我们需要更多操作。 复制 Expert Advisor 内未平仓头寸控制的程序块,并更改订单特征为虚拟:
int start()
  {
    // skipped...
 
    //+------------------------------------------------------------------+
    //| Control block of "virtual" positions                             |
    //+------------------------------------------------------------------+
    if(virtTicket > 0)
      {
        //---- if BUY-position is open,
        if(virtType == OP_BUY)
          {
            //---- if MACD crossed 0-line downwards,
            if(NormalizeDouble(MACD_1 + CloseLuft*Point*0.1, 
               Digits + 1) <= 0.0)
              {
                //---- close position
                virtOrderClose(Bid);
              }
            //---- if the signal did not change, accompany the position by 
            //     TrailingStop
            else
              {
                if(TrailingStop > 0)
                  {
                    if(NormalizeDouble(Bid - virtOpenPrice, 
                       Digits ) > 0.0)
                      {
                        if(NormalizeDouble( Bid - TrailingStop*Point - 
                           virtStopLoss, Digits) > 0.0 || virtStopLoss < Point)
                        {
                          virtStopLoss = Bid - TrailingStop*Point;
                        }
                      }
                  }
              }
          }
        //---- if SELL position is open
        if(virtType == OP_SELL)
          {
            //---- if MACD crossed 0-line upwards,
            if(NormalizeDouble(MACD_1 - CloseLuft*Point*0.1, 
               Digits + 1 ) >= 0.0)
              {
                //---- close the position
                virtOrderClose(Ask);
              }
            //---- if the signal did not change, accompany the position by 
            //     TrailingStop
            else
              {
                if ( TrailingStop > 0 )
                  {
                    if(NormalizeDouble( virtOpenPrice - Ask, 
                       Digits ) > 0.0 )
                      {
                        if(NormalizeDouble( virtStopLoss - ( Ask + 
                           TrailingStop*Point ), Digits ) > 0.0 ||
                           virtStopLoss <= Point )
                          {
                            virtStopLoss = Ask + TrailingStop*Point;
                          }
                      }
                  }
              }
          }
      }
    // skipped...
    return(0);
  } 
 
 
//---- virtual position closing function
void virtOrderClose(double closeprice)
  {
    //---- Save the parameters of the closed position in the array
    ArrayResize(virtClosedOrders, virtClosedOrdersCount + 1);
 
    virtClosedOrders[virtClosedOrdersCount][0] = virtType;
    virtClosedOrders[virtClosedOrdersCount][1] = virtOpenPrice;
    virtClosedOrders[virtClosedOrdersCount][2] = virtStopLoss;
    virtClosedOrders[virtClosedOrdersCount][3] = virtTakeProfit;
    virtClosedOrders[virtClosedOrdersCount][4] = closeprice;
 
    virtClosedOrdersCount ++;
 
    //---- clear variables
    virtTicket = 0;
    virtType = 0;
    virtOpenPrice = 0.0;
    virtStopLoss = 0.0;
    virtTakeProfit = 0.0;
  }
可以看到,修改变为简单的将新值分配给 virtStopLoss 变量。 平仓非常困难——所有平仓订单的特征都保存在一个数组内。 随后整个虚拟记录将保存在内。 我们将从中提取平仓头寸的信息,用于进行新开仓的决策。

现在我们需要根据 StopLoss 和 TakeProfit 处理平仓头寸。 为此,添加多个字符串至已创建的控制块。
if(virtType == OP_BUY)
  {
    //---- check, whether SL was not activated 
    if(virtStopLoss > 0.0 && NormalizeDouble(virtStopLoss - Bid, 
       Digits ) >= 0.0)
      {
        virtOrderClose(virtStopLoss);
      }
    //---- check, whether TPL was not activated
    if(virtTakeProfit > 0.0 && NormalizeDouble( Bid - virtTakeProfit, 
       Digits ) >= 0.0)
      {
        virtOrderClose(virtTakeProfit);
      }
  }
现在,虚拟记录已准备好,可以添加过滤标准。

什么是“好”以及什么是“坏”?


在实施一定的条件后,我们需要禁止开仓。 但要选择什么条件呢? 连续多次亏损交易、StopLoss 激活或数次最后交易平均利润下降。 很难给出确定的回答——每种形式都有其优点和缺点。

为了检验每种条件的有效性,我们对三种条件进行编码并对其记录进行测试:
extern int TradeFiltrVariant = 0;
 
//---- Function of checking the necessity of the real trading
bool virtCheckCondition()
  {
    int pos, check_pos = 2;
    double last_profit = 0.0, pre_last_profit = 0.0;
    
    //---- depending on the value of TradeFiltrVariant:
    switch(TradeFiltrVariant)
      {
        //---- 1: prohibit real trading, if 2 last deals are losing
        case 1:
          {
            //---- if the virtual history contains enough orders,
            if(virtClosedOrdersCount >= check_pos)
              {
                for(pos = 1; pos check_pos; pos ++)
                  {
                    //---- if the deal is profitable, return true
                    if((virtClosedOrders[virtClosedOrdersCount-pos][0] == 0 && 
                        virtClosedOrders[virtClosedOrdersCount-pos][4] - 
                        virtClosedOrders[virtClosedOrdersCount-pos][1] >= 0.0) ||
                        (virtClosedOrders[virtClosedOrdersCount-pos][0] == 1 && 
                        virtClosedOrders[virtClosedOrdersCount-pos][1] - 
                        virtClosedOrders[virtClosedOrdersCount-pos][4] >= 0.0))
                      {
                        return(true);
                      }
                    }
              }
            return(false);
          }
        //---- 2: prohibit real trading if the last position was closed 
        //        by StopLoss
        case 2:
          {
            //---- if the virtual history contains enough orders,
            if(virtClosedOrdersCount > 0)
              {
                //---- if the closing price of the last order is equal to StopLoss,
                if(virtClosedOrders[virtClosedOrdersCount-1][2] - 
                   virtClosedOrders[virtClosedOrdersCount-1][4] < Point &&
                   virtClosedOrders[virtClosedOrdersCount-1][4] - 
                   virtClosedOrders[virtClosedOrdersCount-1][2] < Point)
                  {
                    return(false);
                  }
              }
            return(true);
          }
        //---- 3: prohibit real trading, if the profit of the last position  
        //----    is lower than that of the last but one position (or loss is higher)
        case 3:
          {
            if(virtClosedOrdersCount >= 2)
              {
                if(virtClosedOrders[virtClosedOrdersCount-1][0] == 0)
                  {
                    last_profit =  virtClosedOrders[virtClosedOrdersCount-1][4] - 
                                   virtClosedOrders[virtClosedOrdersCount-1][1];
                  }
                else
                  {
                    last_profit =  virtClosedOrders[virtClosedOrdersCount-1][1] - 
                                   virtClosedOrders[virtClosedOrdersCount-1][4];
                  }
                if(virtClosedOrders[virtClosedOrdersCount-2][0] == 0)
                  {
                    pre_last_profit = virtClosedOrders[virtClosedOrdersCount-2][4] - 
                                      virtClosedOrders[virtClosedOrdersCount-2][1];
                  }
                else
                  {
                    pre_last_profit = virtClosedOrders[virtClosedOrdersCount-2][1] - 
                                      virtClosedOrders[virtClosedOrdersCount-2][4];
                  }
 
                if(pre_last_profit - last_profit > 0.0)
                  {
                    return(false);
                  }
              }
            return(true);
          }
        //---- by default the filter is off, i.e. positions will be always opened in reality
        default: return(true);
      }
    return(true);
  }
 
void OpenBuy()
  {
    int _GetLastError = 0;
    double _OpenPriceLevel, _StopLossLevel, _TakeProfitLevel;
    _OpenPriceLevel = NormalizeDouble(Ask, Digits);
 
    if(StopLoss > 0)
      { 
        _StopLossLevel = NormalizeDouble(_OpenPriceLevel - StopLoss*Point, Digits); 
      }
    else
      { 
        _StopLossLevel = 0.0; 
      }
 
    if(TakeProfit > 0)
      { 
        _TakeProfitLevel = NormalizeDouble(_OpenPriceLevel + TakeProfit*Point, Digits); 
      }
    else
      { 
        _TakeProfitLevel = 0.0; 
      }
 
    //---- open a virtual position
    virtOrderSend(OP_BUY, _OpenPriceLevel, _StopLossLevel, _TakeProfitLevel);
 
    //---- if virtual positions filter prohibits trading, exit
    if(virtCheckCondition() == false) 
      { 
        return(0); 
      }
 
    if(OrderSend( Symbol(), OP_BUY, 0.1, _OpenPriceLevel, 3, _StopLossLevel, 
          _TakeProfitLevel, "CrossMACD", _MagicNumber, 0, Green) < 0 )
      {
        _GetLastError = GetLastError();
        Alert("Error OrderSend № ", _GetLastError);
        return(-1);
      }
  }
可以看到,现在有了外部变量 TradeFiltrVariant。 它负责选择过滤标准:
extern int TradeFiltrVariant = 0;
//---- 0: filter is off, i.e. position is always opened in reality
//---- 1: prohibit real trading, if 2 last positions are losing
//---- 2: prohibit real trading, if the last position closed by StopLoss
//---- 3: prohibit real trading, if the profit of the last position is lower, 
//----    than that of the last but one psition (or loss is higher)

现在用不同的过滤器测试 Expert Advisor 并对比结果。

游戏是否得不偿失?


为了测试,我选择了以下参数:
交易品种: GBPUSD
时段: H4, 01.01.2005 - 01.01.2006
建模模式:所有的价格变动(建模质量 90%,报价来源为 HistoryCenter)

EA 参数:
StopLoss: 50
TakeProfit: 0(禁用)
TrailingStop: 0(禁用)
FastEMAPeriod: 12
SlowEMAPeriod: 26
OpenLuft: 10
CloseLuft: 0

下表显示了所用过滤器结果的依赖性:

TradeFiltrVariant总利润/损失
总交易次数
获利交易次数
亏损交易次数
0 1678.75
41 9 (22%)
32 (78%)
1 105.65 20
2 (10%)
18 (90%)
2 -550.20 11 0 (0%)
11 (100%)
3 1225.13 28 7 (25%)
21 (75%)









可以看到,关于过滤器有用性的理论未被证实。 此外,使用过滤器的交易结果比不使用过滤器的交易结果要低。 唯一的例外是第三种形式——交易利润率较高(25 % 比 22%),但三种形式中的总利润都相对较低。

那么问题出在哪里呢? 可能是过滤标准错误。 让我们将三个过滤器更改至对立情形,即在以下情况关闭真实交易:
  • 最后两次交易获利;
  • 最后头寸获利(我们没有类比 StopLoss,因为已禁用 TakeProfit);
  • 最后头寸的获利高于倒数第二头寸。
为了不削减 Expert Advisor,仅需再添加三个 TradeFiltrVariant 的值 - 4、5 和 6:
//---- 4: prohibit real trading, if two last trades are profitable
//---- 5: prohibit real trading, if the last position is profitable
//---- 6: prohibit real trading, if the profit of the last position is higher, 
//----    than that of the last but one position (or loss is lower)
 
    //---- 4: prohibit real trading, if two last trades are profitable
    case 4:
      {
        if(virtClosedOrdersCount >= check_pos)
          {
            for(pos = 1; pos check_pos; pos ++)
              {
                //---- if the trade is losing, return true
                if((virtClosedOrders[virtClosedOrdersCount-pos][0] == 0 && 
                   virtClosedOrders[virtClosedOrdersCount-pos][1] - 
                   virtClosedOrders[virtClosedOrdersCount-pos][4] > 0.0) ||
                   (virtClosedOrders[virtClosedOrdersCount-pos][0] == 1 && 
                   virtClosedOrders[virtClosedOrdersCount-pos][4] - 
                   virtClosedOrders[virtClosedOrdersCount-pos][1] > 0.0))
                  {
                    return(true);
                  }
              }
          }
        return(false);
      }
    //---- 5: prohibit real trading, if the last position is profitable
    case 5:
      {
        if(virtClosedOrdersCount >= 1)
          {
            if(virtClosedOrders[virtClosedOrdersCount-1][0] == 0)
              {
                last_profit =  virtClosedOrders[virtClosedOrdersCount-1][4] - 
                               virtClosedOrders[virtClosedOrdersCount-1][1];
              }
            else
              {
                last_profit =  virtClosedOrders[virtClosedOrdersCount-1][1] - 
                               virtClosedOrders[virtClosedOrdersCount-1][4];
              }
 
            if(last_profit > 0.0)
              {
                return(false);
              }
          }
        return(true);
      }
    //---- 6: prohibit real trading, if the profit of the last position is higher, 
    //----    than that of the last but one position (or loss is lower)
    case 6:
      {
        if(virtClosedOrdersCount >= 2)
          {
            if(virtClosedOrders[virtClosedOrdersCount-1][0] == 0) 
              {
                last_profit =  virtClosedOrders[virtClosedOrdersCount-1][4] - 
                               virtClosedOrders[virtClosedOrdersCount-1][1];
              }
            else
              {
                last_profit =  virtClosedOrders[virtClosedOrdersCount-1][1] - 
                               virtClosedOrders[virtClosedOrdersCount-1][4];
              }
            if(virtClosedOrders[virtClosedOrdersCount-2][0] == 0)
              {
                pre_last_profit = virtClosedOrders[virtClosedOrdersCount-2][4] - 
                                  virtClosedOrders[virtClosedOrdersCount-2][1];
              }
            else
              {
                pre_last_profit = virtClosedOrders[virtClosedOrdersCount-2][1] - 
                                  virtClosedOrders[virtClosedOrdersCount-2][4];
              }
 
            if(last_profit - pre_last_profit > 0.0)
              {
                return(false);
              }
          }
        return(true);
      }

现在来测试三个新变量并将其添加至我们的表格:

AdaptVariant总利润/损失
总交易次数
获利交易次数
亏损交易次数
0 1678.75
41 9 (22%)
32 (78%)
1 105.65 20 2 (10%)
18 (90%)
2 -550.20 11 0 (0%)
11 (100%)
3 1225.13 28 7 (25%)
21 (75%)
4
1779.24
39 9 (23%) 30 (77%)
5
2178.95
31
9 (29%)
22 (71%)
6
602.32
24
5 (21%)
19 (79%)












第六个变量过滤掉半数交易——既有获利交易也有亏损交易。 第四个过滤掉两次亏损交易,总利润增加了 100.49$。

最好的是在每次获利交易后禁止交易的变量——过滤掉 10 次亏损交易而没有过滤掉获利交易。

于是希望产生了——即使如此简单流行的策略也是可以改善的!

总结


我认为,这种过滤器对于现实系统改善是不够的——需要进行更深入的研究和做出新的结论。

对于每一种策略,所描述的过滤器可能更加复杂和迥异。 它们的有效性直接取决于获利和亏损头寸的互换。

本文仅阐述了过滤问题。 但我希望能够抛砖引玉,推动进一步的发展。

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

附加的文件 |
显示支撑/阻力位 显示支撑/阻力位

文章涉及在 MetaTrader 4 程序中探明和指示支撑/阻力位。 方便又通用的指标基于简单的算法。 本文同时探讨了一个有用的主题,即创建能够在一个工作空间显示不同时间范围结果的简单指标。

绘制支撑/阻力位的方法 绘制支撑/阻力位的方法

本文描述了用于检测支撑/阻力位的简单脚本的创建过程。 由于面向的对象为初学者,你可以找到过程中每个步骤的详细解释。 然而,尽管脚本非常简单,本文对于高级交易者和MetaTrader 4 平台用户也非常有帮助。 其中包含了数据导出至表格格式、表格导入 Microsoft Excel 和绘制便于更加详细分析图表的示例。

测试程序中的断点:这是可能的! 测试程序中的断点:这是可能的!

本文探讨了通过测试程序时的断点仿真,显示的调试信息。

在测试程序中对重新报价建模和 Expert Advisor 稳定性分析 在测试程序中对重新报价建模和 Expert Advisor 稳定性分析

重新报价是很多 Expert Advisor 的噩梦,尤其对于进入/退出交易条件非常敏感的 Expert Advisor。本文提供了一种检查 EA 对于重新报价稳定性的方法。