English Русский Español Deutsch 日本語 Português
按记录过滤

按记录过滤

MetaTrader 4交易系统 | 19 二月 2016, 11:18
1 254 0
Andrey Khatimlianskii
Andrey Khatimlianskii

简介


有不同的过滤器:指标值、市场波动性、时间和工作日。 它们全部可以用来过滤亏损交易。 将过滤器添加至 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 Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/1441

附加的文件 |
单纯使用 MQL5 语言处理 ZIP 档案 单纯使用 MQL5 语言处理 ZIP 档案
MQL5 语言在不断进化, 持续地增加了用于处理数据的新特性. 正因为这些创新, 现在我们可以在不引入第三方DLL库的情况下, 只使用通常的MQL5工具就可以操作ZIP档案了. 本文专注于具体的实现, 并且提供了CZip类作为例子, 它是一个用于读取, 创建和修改ZIP档案的通用工具.
显示支撑/阻力位 显示支撑/阻力位
文章涉及在 MetaTrader 4 程序中探明和指示支撑/阻力位。 方便又通用的指标基于简单的算法。 本文同时探讨了一个有用的主题,即创建能够在一个工作空间显示不同时间范围结果的简单指标。
机器学习模型的变量评估和选择 机器学习模型的变量评估和选择
本文重点介绍机器学习模型中输入变量(预测因子)的选择,预处理以及评估的相关细节。同时将探讨新的方法和预测因子深度分析及其对模型过度拟合可能的影响。模型的总体效果很大程度上取决于这一阶段的结果。我们将分析两个包,它们分别提供预测因子选择的新老方法。
绘制支撑/阻力位的方法 绘制支撑/阻力位的方法
本文描述了用于检测支撑/阻力位的简单脚本的创建过程。 由于面向的对象为初学者,你可以找到过程中每个步骤的详细解释。 然而,尽管脚本非常简单,本文对于高级交易者和MetaTrader 4 平台用户也非常有帮助。 其中包含了数据导出至表格格式、表格导入 Microsoft Excel 和绘制便于更加详细分析图表的示例。