第十三节 指标编写

我们的交易信号都是来自对K线的分析。有些人把这些分析过程量化了,于是就成了指标。就用途来说,指标一般分为趋势类指标、震荡类指标以及成交量指标。所谓趋势类指标,顾名思义就是用于判断趋势的一类指标,比如均线、布林带等;震荡类指标就是用于做震荡市场,高空低多的一类指标,比如RSI、KDJ等 ;成交量一类的指标用于多空方向的强弱,比如金钱流向、成交量等。虽然分了这几类,实际上有些指标是可以相互用的,比如布林带也可以用于做震荡,MACD也可以用于做趋势。

MT4里面有很多指标已经集成进去了,我们可以直接用,但是我们开发交易策略的时候总是要编写一些里面没有的指标,比如做对冲的时候写一个双货币乖离度指标。

指标编写函数

我们打开编辑器,新建一个自定义指标,如图:

然后我们选择独立窗口指标,如果不选独立窗口的话指标就会绘制在主图上。

新建的指标如下图,第一个红框中表示的是指标采用的是独立窗口,如果我们在前面新建的过程中选择了独立窗口指标那么就会有这句代码,如果要把指标改成主窗口指标,可以把这句改成#property indicator_chart_window。第二个红框是初始化函数,只在指标加载的时候运行一次,这个和EA的初始化是一样的。第三个红框就是指标计算的函数了。

可以看到指标计算函数里面有十个参数,这十个参数不需要我们输入,系统自动帮我们引用好了,我们可以直接使用。下面说一下这几个参数的意义:

第一个参数是图表里面K线的总数;

第二个参数是已经计算完了的K线的个数,刚加载的时候是0;

第三到第十个参数分别是时间数组、开盘价数组、最高价数组、最低价数组、收盘价数组、 tick成交量数组、成交量数组以及点差数组,以上的数组我们都可以直接使用。

均线指标编写

讲了这么多,估计大家还是有点迷糊,不知道怎么使用,那么接下来我们编写一个简单移动均线(SMA)试一下。

首先对均线的一些参数进行设置,代码如下:

#property strict
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
double MA[];
input int peri=10;
int OnInit()
  {
      string name="SMA("+(string)peri+")";
      SetIndexBuffer(0,MA,INDICATOR_DATA);
      SetIndexStyle(0,DRAW_LINE,STYLE_SOLID,1,clrBlue);
      IndicatorShortName(name);
      return(INIT_SUCCEEDED);
  }

对于画线,需要1个缓冲区以及1个数据区,所以我们把indicator_buffers和indicator_plots都设置成了1,对于画不同的对象(比如说话柱状图),各种对象需要的缓冲区和数据区个数都不一样,参考下表:

可以看到,如果我要画一根K线的话那就需要4个缓冲区,实际上缓冲区的数量取决于使用到的数据的类别数,K线有开、高、低、收四个数据,所以需要4个缓冲区。数据区的话就比较容易了,有多少个对象就需要有多少个数据区。

那么接下来我们声明了一个名字叫MA的数组,以及创建了一个可以接收外部输入的变量,名字叫peri,这个变量用来存放输入的均线的计算周期数,默认为10.

接下来就是初始化函数OnInit()函数了,在这个函数当中我们首先声明了一个name变量用来储存均线的名称,然后我们用SetIndexBuffer函数将数组MA[]和编号为0的指标数据绑定了,也就是说MA[]这个数组里面的值,以后就用来绘制编号为0的指标了,MA[0]是多少,那么当前这个指标的数值就是多少,MA[1]是多少那么上一根K线的这个指标数值就是多少。关于指标的编号,第一个指标线的编号为0,往后顺延,当然我们可以将任意的数组跟里面的任意编号的指标线绑定,前提是不能超过缓冲区的数量。

然后我们设置了编号为0的指标的样式,指标用线条绘制,线条样式为实线,线宽为1,颜色为蓝色,完成以上步骤后结束初始化过程。

接下来我们就要用OnCalculate函数来计算指标的值了,这个过程我们只要把MA[]这个数组填充好了就可以画出所有K线的指标线。所以其实最关键的就是如何填充MA[]数组罢了。

这里我们计算每个K线SMA的值,然后赋值给对应的MA数组里面的组员即可,代码如下:

………
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   int i;
   int j;
   double maadd=0;
   if(rates_total<peri)
   {
      return(0);
   }
   ArrayResize(MA,rates_total);
   if(prev_calculated==0)
   {
      for(i=0;i<=rates_total-peri;i++)
      {  
         maadd=0;
         for(j=i;j<=i+peri-1;j++)
         {
            maadd=maadd+close[j];
         }
         MA[i]=maadd/peri;
      }
   }
   else
   {
      maadd=0;
      for(j=0;j<=peri-1;j++)
      {
         maadd=maadd+close[j];
      }
      MA[i]=maadd/peri;
   }
   return(rates_total);
  }

以上代码中我们首先定义了用于循环的变量i和j,然后定义了一个用于累加的中间变量maadd。如果当前图表总的K线根数rates_total小于我们输入的周期peri的话,那么我们知道这个移动平均线是没办法计算的,所以我们直接返回0,结束;否则的话就继续指标的运算。

如果已经计算过的K线根数为0,也就是说刚加载指标的时候,指标一次都没有计算过,那么我们就要对所有的K线对应的指标进行计算。过程很简单,我们从当前K线(编号为0)到编号为rates_total-peri做一个循环来逐个计算出每根K线的SMA值,这里为什么是rates_total-peri呢,因为在这个编号后的K线由于数量小于周期,那么就无法计算了。

对于编号为i的k线,它的SMA的值应该是包括这根K线在内的前peri根K线收盘价的平均值,那么我们再做一个循环把这些K线的收盘价相加赋值给maadd,再将maadd除以peri就可以得出第i根K线的SMA值了。

我们加载一下指标,如下图所示,可以看到图表中多了一条蓝色的均线,我们的代码运行正确。要注意的是我们的指标只有一个输入的参数那就是周期,其实均线的指标远远不止这么简单,其采用的价格以及计算方式不同,均线也是不一样的,比如EMA和SMA就完全不一样,这里仅仅作为一个示例。

其他对象类型

我们可以将指标用不同的对象来表示,实际上就是在对应的时间和价格上画一个对象。比如说我们把线条改成×号,代码如下:

#property strict
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
double MA[];
input int peri=10;
int OnInit()
  {
      string name="SMA("+(string)peri+")";
      SetIndexBuffer(0,MA,INDICATOR_DATA);
      SetIndexStyle(0,3,1,clrRed);
      IndicatorShortName(name);
      return(INIT_SUCCEEDED);
  }
……

运行之后的结果如下图:

可以看到我们通过SetIndexStyle函数把线条改成了×号。所以我们看到的光滑的均线,实际上是由一个一个点连接起来的,它不是连续的。

这节课我们打下了一个指标编写的基础,那么下节课我们试着去编写一个对冲的指标来看一下鼎鼎大名的三角对冲策略是不是真的可以做到,下节课见。