日内交易中的时间转换原则

kamal | 25 二月, 2016

简介

观察值的统计均一性在分析之前价格走势时始终扮演重要角色。当出现均一性时,才可能深入研究过程特性,以揭示用于打造交易系统的规律性。但汇率过程是不均一的,即不同交易时段的活动有不均一性,这是众所周知的事实,将稍后予以证实:美国、欧洲、亚洲以及互相切换。

我敢说没有几个新系统开发者以及并非所有“经验丰富”的开发者会想到:即使最简单的移动平均线类型且受时间限制的指标在一天的不同阶段,实际上是不同的单位。毫无疑问,存在以价格而非时间构建的系统。典型的示例是基于砖形图和 K 线图方法的系统,但他们只是少数。但我再次强调,它们中大多数跟时间密切相关,通常是间接通过指标关联。

以上所述显然仅仅指日内交易系统。对于更大的时间范围,即使有周期性,也并不明显。在日内交易中,必然经常出现系统显示不同时间的不同获利能力。现在仔细考虑产生这些影响的因素以及克服它们的方法。



理论

从随机过程统计学的观点来看,第一次接近的价格变动过程通常视为一种扩散,即一种物质或能量从高密度区域传播到低密度区域。众所周知,时间转换将连续鞅引入相对容易分布的布朗运动。

不管价格变动过程是扩散还是鞅,抛开这个问题,让我们记住:没有什么会阻挡从时间转换过程以相同方式切换到到以更简单方式分布的过程(统计上及时而均一的过程)。为此,使用柱而非时间分割再自然不过,但仅有固定数量的价格变动,即一个柱包含不是 60 分钟而是(比如)1000 个价格变动。

理论上将这种时间称为操作时间,对于时间转换过程是很自然的。在本例中,过程具有统计上稳定的平均偏差,从而可以更清楚的检测到市场氛围的形成并有效的跟随机波动分离。同时注意:使用普通指标不仅是可能的,而且更加均一。不幸的是,MetaTrader 4 尚未包含这一选项。所以,我们将采用另一个方法 - 用时间裕度重排系统。以下是修正指标的示例。但首先来分析目前所讨论的非均一性。

统计数据

国外货币对的图表清晰显示了市场内活动在一天的某个阶段出现下降。例如:





此外,在常见的货币对上也观察到相同的效果。这引起了对交易量和波动行为的更加详细的检验。很明显,为了更好的理解日内波动性,交易量(即柱内价格变动)非常重要。但交易量本身就是一个随机值,所以我们需要参考历史平均值。如果统计“原材料”以“错误的”方式表现,这种参考可能显得不怎么合法。

为了检验这些假设,让我们编写一个简单的指标 - ExpectedVolume,用来发现每小时价格变动的历史平均数量,histSteps 向前追溯,每一步长跨度数天。这些参数的典型值分别为 100 和 1。测试在时间范围 H1 下进行,在其他日内时间范围下参数应该更改。指标代码如下:

//+------------------------------------------------------------------+
//|                                             Expected Volumes.mq4 |
//|                                     Copyright © 2007, Amir Aliev |
//|                                       http://finmat.blogspot.com/ |
//+------------------------------------------------------------------+
#property  copyright "Copyright © 2007, Amir Aliev"
#property  link      "http://finmat.blogspot.com/"
//---- 
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_color1 Blue
//---- input parameters
extern int hist_steps = 100;      // Number of observations
extern int span = 1;              // Days to step back each time 
//---- buffers
double ExtMapBuffer1[];
int sum;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
   string short_name;
//---- indicators
   SetIndexStyle(0, DRAW_HISTOGRAM);
   SetIndexBuffer(0,ExtMapBuffer1);
//----
   short_name = "Expected volumes(" + hist_steps + ")";
   IndicatorShortName(short_name);
   SetIndexLabel(0, short_name);
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
  {
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
   int counted_bars = IndicatorCounted();
   int rest = Bars - counted_bars;
   int j, k, u;
//----     
   while(rest >= 0)
     {
      if(Bars - rest < span * 23 * hist_steps) 
        {
         ExtMapBuffer1[rest] = 0;     
         rest--; 
         continue;                                   
        }
      sum = 0;
      j = 0;
      k = 0;
      u = 0;
      while(j < hist_steps && k < Bars) 
        {
         if(TimeHour(Time[rest+k]) == TimeHour(Time[rest]))
           {
            u++;
            if(u == span)
              {
               u = 0;
               j++;
               sum += Volume[rest + k]; 
              }
            k += 23;
           }
         k++;
        }
      ExtMapBuffer1[rest] = sum / hist_steps;     
      rest--;                                    
     }
//----
   return(0);
  }
//+------------------------------------------------------------------+

为了检验统计均一性,观察结果应该独立,例如,按工作日分割。为此,分配跨度=5。获得以下结果:





相邻的波峰几乎一致。这意味着按每小时价格变动赋值的波动性在统计上是均一的。这种波动性的结构从下图清晰可见(左侧 - EURUSD,右侧 - USDJPY):



它们清楚的显示了交易时段内活动的三个峰值:亚洲、欧洲和美国。必须注意,分割成这些交易时段并非常见 - 有时会选择其他时段。我们可以注意到一些细节,例如美国时段的活动特征(在两个图表上均有反复出现)。


指标更改

在更改指标时,理解如何精确的将时间纳入非常重要。对于类似移动平均线的简单指标,这非常容易,然而更改振荡指标等则非常困难。

最终,有必要引入一个“可操作”的时间范围。现在来尝试更改一些简单的指标。其中最简单的是修正的交易量 - 实际交易量除以预期交易量。偏离该指标值 1 的两侧的偏差反映了市场上活动的增加/减少。代码非常简单。它包含在本文随附文件中。

下一个示例是平均值。实际上,我们只需要通过柱内价格变动次数衡量柱的特征,平均值就是基于其所创建(例如开盘价)。最后的数字并不完全等于所有价格变动上价格值的总和。为了更加精确的评估,我们需要采用柱的加权平均值而非‘开盘价’。指标是强行建立的,这就是为什么其计算需要大量和实际上不必要的计算成本。这就是为什么要再添加一个参数 - 指标将绘制的过去的柱数默认等于 500。此外,平均值的周期并未按柱设置,而是按价格变动的次数。代码如下:

//+------------------------------------------------------------------+
//|                                                Corrected SMA.mq4 |
//|                                      Copyright © 2007, Amir Aliev |
//|                                    http://finmat.blogspot.com/   |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, Amir Aliev"
#property link      "http://finmat.blogspot.com/"
//----
#property indicator_chart_window
#property indicator_color1 Red
//---- input parameters
extern int MA_Ticks = 10000;
extern int MA_Shift = 0;
extern int MA_Start = 500;
//---- indicator buffers
double ExtMapBuffer[];
double ExpVolBuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//----
   SetIndexStyle(0, DRAW_LINE);
   SetIndexShift(0, MA_Shift);
   IndicatorBuffers(2);
//---- indicator buffers mapping
   SetIndexBuffer(0, ExtMapBuffer);
   SetIndexBuffer(1, ExpVolBuffer);
   SetIndexDrawBegin(0, 0);  
//---- initialization done
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
   int counted_bars = IndicatorCounted();
   int rest  = Bars - counted_bars;
   int restt = Bars - counted_bars;
   double sum;                               
   int ts;                                   
   int evol;                                 
   int volsum;
   int j;
//----
   while(restt >= 0)
     {
       volsum = 0;
       for(int k = 0; k < 30; k++) 
           volsum += iVolume(NULL, 0, restt + k*24); 
       ExpVolBuffer[restt] = volsum / 30;
       restt--;
     }
//----
   while(ExpVolBuffer[rest] == 0 && rest >= 0) 
       rest--;
   rest -= MA_Ticks / 200;
   if(rest > MA_Start) 
       rest = MA_Start;  
//----
   while(rest >= 0)
     {
       sum = 0;
       ts = 0;
       j = rest;
       while(ts < MA_Ticks)
         {
           evol = ExpVolBuffer[j];
           Print("Evol = ", evol);
           if(ts + evol < MA_Ticks)
             {
               sum += evol * Open[j];
               ts += evol;
             }
           else
             {
               sum += (MA_Ticks - ts) * Open[j];
               ts = MA_Ticks;
             }
           j++;
         }
       ExtMapBuffer[rest] = sum / MA_Ticks;
       rest--;
     }   
//----
   return(0);
  }
//+------------------------------------------------------------------+

编写完这个简单指标后,更改更加复杂的指标不再是问题。例如在 MACD 代码应使用调整过的而不是简单的移动平均线。相应的代码也在附件中给出。

应该注意的是,为了快速计算已调整平均值,每小时的价格变动经验平均值应计算一次,且不应在联机时,以避免重复计算。这里对此省略,但如果我们对历史数据上进行全面的测试/优化,生产效率会明显提高。另外,也有人喜欢另一种建立交易量平均值的调整方式,我们将分别进行讨论。

对之前周期的交易量取平均值似乎没有意义:在平均值计算中将可用交易量作为系数就足够了。下面是这种代码的示例。仍然需要注意的是,基于技术根据这种平均值是用于较小时间范围(例如 M1-M5)中最佳平均值。

//+------------------------------------------------------------------+
//|                                             Corrected SMA II.mq4 |
//|                                     Copyright © 2007, Amir Aliev |
//|                                       http://finmat.blogspot.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, Amir Aliev"
#property link      "http://finmat.blogspot.com/"
 
#property indicator_chart_window
#property indicator_color1 Red
//---- input parameters
extern int MA_Ticks = 1000;
//---- indicator buffers
double sum = 0;                               
int ticks = 0;
bool collected = false;
bool started = false;
int fbar = 0;
double ExtMapBuffer[];
int oldRange = 0;
int lbarVol = 0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//----
   SetIndexStyle(0, DRAW_LINE);
//---- indicator buffers mapping
   SetIndexBuffer(0, ExtMapBuffer);
//---- initialization done
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
   int rest = Bars - IndicatorCounted();
   if(! rest) 
       return (0);
   Print("Ticks = ", ticks);
   Print("Rest = ", rest);
   Print("fbar = ", fbar);  
   rest--;
   fbar += rest;
   while(!collected && (rest >= 0))
     {
      if(ticks + Volume[rest] < MA_Ticks)
        {
         ticks += Volume[rest];
         sum += Volume[rest] * Open[rest];
         if(!started)
           {
            fbar = rest;
            started = true;
           }
         rest--;
         continue;
        } 
      collected = true;
     }
   if(! collected) 
       return (0);
 
   ticks += (Volume[rest] - lbarVol);
   sum += (Volume[rest] - lbarVol) * Open[rest];
   lbarVol = Volume[rest];
   while(ticks > MA_Ticks)
     {
       Print("fbar-- because bar ticks reaches 1000");
       ticks -= Volume[fbar];
       sum -= Volume[fbar] * Open[fbar];
       fbar--;
     }
   ExtMapBuffer[rest] = sum / ticks;
   rest--;
   while(rest >= 0)
     {
      ticks += Volume[rest];
      sum += Volume[rest] * Open[rest];
      lbarVol = Volume[rest];
      while(ticks > MA_Ticks)
        {
         Print("fbar-- because of new bar ");
         ticks -= Volume[fbar];
         sum -= Volume[fbar] * Open[fbar];
         fbar--;
        }
      ExtMapBuffer[rest] = sum / ticks;
      rest--;
     } 
//----
   return(0);
  }
//+------------------------------------------------------------------+

但是,作者认为,尽管使用该指标在一些情况下有帮助,总体而言它的含义跟本文中描述的有所不同。将市场上“竞争”激烈的价格值纳入考虑的想法,非常做作,因为小范围的偏差可能由技术原因而非市场原因造成。此外,将波动性变化纳入考虑似乎不合理(这正是我们所讨论的)。

每个现有的指标都可以修正以进行具体的任务,例如改变平均值以计算柱内数值总和或更改平均值计算的参数。记住,我们已经通过“开盘价”进行计算,视觉上会产生一种延迟感 - 时间转换和去波动性以及本文未讨论的概念(比如周期性波动),可以进行深入解读。




总结

应该注意,尽管价格平均值非常不稳定且难以预测,波动性(即增量的二阶矩)从统计学的观点看更加“宜人”,有很多著名的特征,比如群集特性、股票市场上的杠杆效应和其他 。

这就是操作时间概念本身非常有用且从技术分析观点看来非常自然。当然,一些关于经济状况的重要新闻会在某个时间发布,严重打破均一性并苛刻“对待”,这种情况也会发生。但多数情况下使用时间转换可以获得更加稳定的结果,增加交易策略的获利能力。