外汇EA编写教程:依据价格相关性的统计数据过滤信号

如何开始

撰写本文的想法是在我阅读了 Larry Williams 所定的《Long-Term Secrets to Short-Term Trading》(短线交易秘诀)之后出现的,在该书中,投资界的世界记录保持者(在1987 年,他的资本增加了 11,000%)通过“… 大学教授和其他学院派——他们拥有丰富的理论,但缺少市场知识…”完全打破了关于过去的价格行为与将来的趋势之间没有任何相关性的神话。

如果您掷硬币 100 次,则它将 50 次正面向上,50 次反面向上。对于下一次掷硬币,正面向上的概率为 50%,反面向上的概率也为 50%。每次掷硬币时概率都不会改变,因为此游戏是随机的并且没有记忆。假定市场像掷硬币一样以无序方式运行。

因此,当新的柱出现时,价格有相同的机会上扬或下降,以前的柱对当前的柱甚至没有最轻微的影响。太浪漫了!创建一个交易系统,设置比止损大的获利(即将 math. expectation 设置到一个正值),诀窍就是如此。太令人兴奋了。然而,问题在于我们关于市场行为的假定并不是肯定成立的。坦白地说,它是荒谬的!我将证明这一点。

让我们使用 MQL5 向导创建一个 EA 交易模板,并在适合执行任务的条件下通过输入简单的字母数字来呈现它。我们将对一 EA 交易进行编码,以模拟在一根、两根和三根柱收盘之后的买入。模拟意味着程序将简单地记住所分析柱的参数。在本例中,发送订单(一种更加普遍的方式)将不起作用,因为点差和库存费能够对收到的信息的可靠性提出疑问。

代码如下:

//+------------------------------------------------------------------+
//|                                                     explorer.mq5 |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//---Variables---
double profit_percent,open_cur,close_cur;
double profit_trades=0,loss_trades=0,day_cur,hour_cur,min_cur,count;
double open[],close[];
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
/* Calculate percent of closures with increase from the total number */
   profit_percent=NormalizeDouble(profit_trades*100/(profit_trades+loss_trades),2);
   Print("Percent of closures with increase ",profit_percent,"%");   // Enter data to the Journal
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---find out the time---
   MqlDateTime time;                        // Create a structure to store time
   TimeToStruct(TimeCurrent(),time);         // Structuring the data
   day_cur=time.day_of_week;              // Receive the value of the current day
   hour_cur=time.hour;                    // Receive the current hour
   min_cur=time.min;                      // Receive the current minute
//---Find out the prices---
   CopyOpen(NULL,0,0,4,open);ArraySetAsSeries(open,true);
   CopyClose(NULL,0,0,4,close);ArraySetAsSeries(close,true);

   if(close[1]<open[1]/*&&close[2]<open[2]&&close[3]<open[3]*/ && count==0) // If it closed with a loss
     {
      open_cur=open[0];                   // Remember open price of the current bar
      count=1;
     }
   if(open_cur!=open[0] && count==1)      // The current bar has closed
     {
      close_cur=close[1];                 // Remember the close price of the formed bar
      count=0;
      if(close_cur>=open_cur)profit_trades+=1;  // If the close price is higher than open,
      else loss_trades+=1;                      // +1 to closures with profit, otherwise +1 to closures with loss
     }
  }
//+------------------------------------------------------------------+

测试将在 EUR/USD 上 2000 年 1 月 1 日至 2010 年 12 月 31 日的区间内进行:

图 1. 收盘价上涨的百分比

图 1. 收盘价上涨的百分比

(第一根柱显示整个时间内的数据,第二根柱显示只一天收盘价下跌的数据,第三根柱显示连续两天收盘价下跌的数据,第四根柱显示连续三天收盘价下跌的数据)

这是我要谈论的!以前的柱对当前的柱有相当显著的影响,因为价格始终寻求将损失赢回来。

前进的另一步

太好了!一旦我们确定价格行为不是偶然的,则我们应尽可能快地运用这一令人惊异的事实。当然,对于一个独立的交易系统而言这并不足够,但它是一个优秀的工具,能够将您从单调乏味且通常错误的信号中解放出来。让我们实施它。

那么,我们需要:

  1. 一个自动交易系统,至少显示去年的正结果。
  2. 某些确定在价格行为中存在相关性的有趣例子。

我发现 L. Williams 写的书中有很多有用的想法。我将向您分享其中的一个。

TDW(交易日)策略。它允许我们查看会发生什么事情,如果在一个星期的某些交易日中我们只买入,并且在其他交易日仅建立短仓的话。之后,我们可以假定某一天内的价格很大可能比其他交易日内的价格上涨得多。这种现象的原因是什么?地理政治情形、宏观经济统计?或者如 A. Elder 在其著作中所写的,星期一和星期二是停止操作日,星期四和星期五是专业人士的时间?让我们试着来理解。

首先,我们在一周的每个交易日仅买入,然后是仅卖出。然后在研究结束时我们将匹配最佳结果,并且这将成为我们的交易系统的过滤器。此外,我对这一点有些话要说。它纯粹是一个经典!

系统以两个 MAMACDake 为基础。信号:                                                           

  1. 如果快速移动平均线从下往上穿越慢速移动平均线,并且 MACD 直方图低于零线,则买入
  2. 如果快速移动平均线从上往下穿越慢速移动平均线,并且 MACD 高于零线,则卖出

使用从一个点的跟踪止损平仓。手数固定为 – 0.1。

为方便起见,已将 EA 交易类放在一个单独的头文件中:

//+------------------------------------------------------------------+
//|                                                       moving.mqh |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
//+------------------------------------------------------------------+
//| Класс my_expert                                                  |
//+------------------------------------------------------------------+
class my_expert
  {                                                  // Creating a class
   // Closed class members
private:
   int               ma_red_per,ma_yel_per;          // Periods of MAs
   int               ma_red_han,ma_yel_han,macd_han; // Handles
   double            sl,ts;                          // Stop orders
   double            lots;                           // Lot
   double            MA_RED[],MA_YEL[],MACD[];       // Arrays for the indicator values
   MqlTradeRequest   request;                         // Structure of a trade request
   MqlTradeResult    result;                          // Structure of a server response
                                                    // Open class members   
public:
   void              ma_expert();                                   // Constructor
   void get_lot(double lot){lots=lot;}                               // Receiving a lot  
   void get_periods(int red,int yel){ma_red_per=red;ma_yel_per=yel;} // Receiving the periods of MAs
   void get_stops(double SL,double TS){sl=SL;ts=TS;}                  // Receiving the values of stops
   void              init();                                         // Receiving the indicator values
   bool              check_for_buy();                                // Checking for buy
   bool              check_for_sell();                               // Checking for sell
   void              open_buy();                                     // Open buy
   void              open_sell();                                    // Open sell
   void              position_modify();                              // Position modification
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
/* Function definition */
//---Constructor---
void my_expert::ma_expert(void)
  {
//--- Reset the values of variables
   ZeroMemory(ma_red_han);
   ZeroMemory(ma_yel_han);
   ZeroMemory(macd_han);
  }
//---The function for receiving the indicator values---
void  my_expert::init(void)
  {
   ma_red_han=iMA(_Symbol,_Period,ma_red_per,0,MODE_EMA,PRICE_CLOSE); // Handle of the slow MA
   ma_yel_han=iMA(_Symbol,_Period,ma_yel_per,0,MODE_EMA,PRICE_CLOSE); // Handle of the fast MA
   macd_han=iMACD(_Symbol,_Period,12,26,9,PRICE_CLOSE);               // Handle of MACDaka
//---Copy data into arrays and set indexing like in a time-series---
   CopyBuffer(ma_red_han,0,0,4,MA_RED);
   CopyBuffer(ma_yel_han,0,0,4,MA_YEL);
   CopyBuffer(macd_han,0,0,2,MACD);
   ArraySetAsSeries(MA_RED,true);
   ArraySetAsSeries(MA_YEL,true);
   ArraySetAsSeries(MACD,true);
  }
//---Function to check conditions to open buy---   
bool my_expert::check_for_buy(void)
  {
   init();  //Receive values of indicator buffers
/* If the fast MA has crossed the slow MA from bottom up between 2nd and 3rd bars, 
   and there was no crossing back. MACD-hist is below zero */
   if(MA_RED[3]>MA_YEL[3] && MA_RED[1]<MA_YEL[1] && MA_RED[0]<MA_YEL[0] && MACD[1]<0)
     {
      return(true);
     }
   return(false);
  }
//----Function to check conditions to open sell---
bool my_expert::check_for_sell(void)
  {
   init();  //Receive values of indicator buffers
/* If the fast MA has crossed the slow MA from up downwards between 2nd and 3rd bars,
  and there was no crossing back. MACD-hist is above zero */
   if(MA_RED[3]<MA_YEL[3] && MA_RED[1]>MA_YEL[1] && MA_RED[0]>MA_YEL[0] && MACD[1]>0)
     {
      return(true);
     }
   return(false);
  }
//---Open buy---
/* Form a standard trade request to buy */
void my_expert::open_buy(void)
  {
   request.action=TRADE_ACTION_DEAL;
   request.symbol=_Symbol;
   request.volume=lots;
   request.price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
   request.sl=request.price-sl*_Point;
   request.tp=0;
   request.deviation=10;
   request.type=ORDER_TYPE_BUY;
   request.type_filling=ORDER_FILLING_FOK;
   OrderSend(request,result);
   return;
  }
//---Open sell---
/* Form a standard trade request to sell */
void my_expert::open_sell(void)
  {
   request.action=TRADE_ACTION_DEAL;
   request.symbol=_Symbol;
   request.volume=lots;
   request.price=SymbolInfoDouble(Symbol(),SYMBOL_BID);
   request.sl=request.price+sl*_Point;
   request.tp=0;
   request.deviation=10;
   request.type=ORDER_TYPE_SELL;
   request.type_filling=ORDER_FILLING_FOK;
   OrderSend(request,result);
   return;
  }
//---Position modification---
void my_expert::position_modify(void)
  {
   if(PositionGetSymbol(0)==_Symbol)
     {     //If a position is for our symbol
      request.action=TRADE_ACTION_SLTP;
      request.symbol=_Symbol;
      request.deviation=10;
      //---If a buy position---
      if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
        {
/* if distance from price to stop loss is more than trailing stop
   and the new stop loss is not less than the previous one */
         if(SymbolInfoDouble(Symbol(),SYMBOL_BID)-PositionGetDouble(POSITION_SL)>_Point*ts)
           {
            if(PositionGetDouble(POSITION_SL)<SymbolInfoDouble(Symbol(),SYMBOL_BID)-_Point*ts)
              {
               request.sl=SymbolInfoDouble(Symbol(),SYMBOL_BID)-_Point*ts;
               request.tp=PositionGetDouble(POSITION_TP);
               OrderSend(request,result);
              }
           }
        }
      //---If it is a sell position---                
      else if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
        {
/*  if distance from price to stop loss is more than the trailing stop value
   and the new stop loss is not above the previous one. Or the stop loss from the moment of opening is equal to zero */
         if((PositionGetDouble(POSITION_SL)-SymbolInfoDouble(Symbol(),SYMBOL_ASK))>(_Point*ts))
           {
            if((PositionGetDouble(POSITION_SL)>(SymbolInfoDouble(Symbol(),SYMBOL_ASK)+_Point*ts)) || 
               (PositionGetDouble(POSITION_SL)==0))
              {
               request.sl=SymbolInfoDouble(Symbol(),SYMBOL_ASK)+_Point*ts;
               request.tp=PositionGetDouble(POSITION_TP);
               OrderSend(request,result);
              }
           }
        }
     }
  }
//+------------------------------------------------------------------

《利用 MQL5 面向对象编程法编写 EA 交易》一文的作者致敬。没有它我将什么事也做不了!我向不是非常精通这个讨厌的、但是功能异常强大的面向对象编程的每一个人推荐阅读该文。

将含有类的文件添加到 EA 交易的主代码中,创建一个对象并初始化函数:

//+------------------------------------------------------------------+
//|                                                       Moving.mq5 |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//---Include a file with the class---
#include <moving.mqh>
//---External Variables---
input int MA_RED_PERIOD=7; // The period of a slow MA
input int MA_YEL_PERIOD=2; // The period of a fast MA
input int STOP_LOSS=800;   // Stop loss
input int TRAL_STOP=800;   // Trailing stop
input double LOTS=0.1;     // Lot
//---Create an object---
my_expert expert;
//---Initialize the MqlDataTime structure---
MqlDateTime time;
int day_of_week;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---Initialize the EA
   expert.get_periods(MA_RED_PERIOD,MA_YEL_PERIOD);   // Set the MA periods
   expert.get_lot(LOTS);                              // Set the lot
   expert.get_stops(STOP_LOSS,TRAL_STOP);             // Set stop orders  
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   TimeToStruct(TimeCurrent(),time);
   day_of_week=time.day_of_week;
   if(PositionsTotal()<1)
     {
      if(day_of_week==5 && expert.check_for_buy()==true){expert.open_buy();}
      else if(day_of_week==1 && expert.check_for_sell()==true){expert.open_sell();}
     }
   else expert.position_modify();
  }
//+------------------------------------------------------------------+

好了!我想指出一些特点。为了在软件层面识别星期几,我使用了 MqlDateTime 结构。首先,我将当前服务器时间转换为一个结构化格式。我们得到当前日期的索引(1-星期一,…,5-星期五),并将其与我们已经设定的值进行比较。

请试一下!为了不让乏味的研究和额外的数字给您添加负担,我在一个表格中提供了所有结果。

如下所示:

表 1. 星期一至星期五的买入汇总

表 1. 星期一至星期五的买入汇总

表 2. 星期一至星期五的卖出汇总

表 2. 星期一至星期五的卖出汇总

最佳结果以绿色突出显示,最差结果以橙色突出显示。

我提出了一些限制,即在上述操作之后,系统必须确保盈利且结合低的相对亏损、不错的盈利交易百分比(在这里,交易次数越少越好)以及相对较高的每次交易盈利。

显然,最有效的系统是在星期五买入并在星期一卖出。结合这些条件:

if(PositionsTotal()<1){
      if(day_of_week==5&&expert.check_for_buy()==true){expert.open_buy();}
      else if(day_of_week==1&&expert.check_for_sell()==true){expert.open_sell();}}
   else expert.position_modify();

现在,EA 交易在两个方向上建仓,但是在严格定义的日期进行。为了清楚说明,我绘制了不使用过滤器及使用过滤器获得的图表:

图 2. 不使用过滤器的 EA 测试结果 (EURUSD, H1, 01.01.2010-31.12.2010,)

图 2. 不使用过滤器的 EA 测试结果 (EURUSD, H1, 01.01.2010-31.12.2010,)

图 3. 使用过滤器的 EA 测试结果 (EURUSD, H1, 01.01.2010-31.12.2010,)

图 3. 使用过滤器的 EA 测试结果 (EURUSD, H1, 01.01.2010-31.12.2010)

您觉得这个结果如何?通过使用过滤器,交易系统变得更加稳定。在修改之前,EA 交易主要增加了测试区间的前半部分的余额,在“升级”之后,它增加了整个区间的余额。

我们比较报告:

表 3. 使用过滤器之前和之后的测试结果

表 3. 使用过滤器之前和之后的测试结果

唯一不能忽略的痛苦因素是净盈利几乎下降了 1000 美元 (26%)。但是交易次数减少了几乎 3.5 倍,即首先显著减少了做出亏损交易的可能性,其次显著减少了点差费用(218*2-62*2=312 美元,仅针对欧元/美元)。盈利百分比增加到 57%,这已经非常显著了。每笔交易的盈利增加了 14%,达到 113 美元。如 L. Williams 所说的:“这是值得交易的金额!”

总结

价格不是随机运行的 – 这是事实。此事实能够且应该被运用。我仅仅给出一个例子,它仅仅是能够提高您的交易系统性能的无数情形和技术中极其微小的一部分。然而,这种多样性隐藏着一个缺点。并不是能够整合每一种过滤器,因此必须仔细选择,彻底思考所有可能的场景。

不要忘记无论过滤器有多完美,它也会扼杀盈利的交易,即也会减少您的盈利…祝您好运!

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

附加的文件 |

explorer.mq5
(2.84 KB)
moving.mq5
(2.28 KB)
moving.mqh
(6.98 KB)

 

 


MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!

 

免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。

風險提示

MyFxtops邁投所列信息僅供參考,不構成投資建議,也不代表任何形式的推薦或者誘導行為。MyFxtops邁投非外匯經紀商,不接觸妳的任何資金。 MYFXTOPS不保證客戶盈利,不承擔任何責任。從事外彙和差價合約等金融產品的槓桿交易具有高風險,損失有可能超過本金,請量力而行,入市前需充分了解潛在的風險。過去的交易成績並不代表以後的交易成績。依據各地區法律法規,MyFxtops邁投不向中國大陸、美國、加拿大、朝鮮居民提供服務。

邁投公眾號

聯繫我們

客服QQ:981617007
Email: service@myfxtop.com

MyFxtops 邁投