2015年6月30日火曜日

MT4インジケータ 移動平均の遅れをなくす? ゼロラグEMA

zema.PNG

移動平均の最大の問題は、移動平均が表している値は遅延しているということです。見た目単純にわかりやすく説明するのに、1,2,3と線形で増えていく値の5単純移動平均をExcelでグラフにしてみました。
移動平均の遅れ.PNG
元の値に対して遅れて移動平均が遅れて追従していく様子が分かるかと思います。

このように移動平均を元に判別すると、相場に参加する判断が遅くなる問題があり移動平均がシグナルを出す頃には、すでに手遅れになっているという事も多々発生します。

この移動平均の問題に対して、推定値を加えることで遅延をけし、なおかつ平滑化効果を得ようというのがゼロラグEMAです。
ページトップのチャートは、黄色が7EMA、赤が7ZEMAとなっています。

ロケット工学投資法を参考に、推定値として3バー先のモメンタムを加えています。

//------------------------------------------------------------------
// ZEMA
#property copyright "Copyright 2015,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict
#property indicator_chart_window

//バッファーを指定する。
#property indicator_buffers 1

//プロット数を指定する。
#property indicator_plots   1

#property indicator_label1  "ZEMA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrIndianRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

//インジケーター バッファ
double wsoBuffer[];

// 入力パラメータ 期間
extern int Period = 7;

// 推定値期間
extern int MomentamPeriod = 3;

// ゲイン要素
extern double Gain = 0.5;

// 対象価格
extern ENUM_APPLIED_PRICE MaPrice = PRICE_MEDIAN;

// バッファー
double zemaBuffer[];

// 平滑平均定数
double alfa = 0 ;

//------------------------------------------------------------------
//初期化
int OnInit()
{
//インジケーターバッファを初期化する。
SetIndexBuffer(0,zemaBuffer);
SetIndexDrawBegin(0, Period);

string short_name = "ZEMA( " + IntegerToString(Period)+" , "+  IntegerToString(MomentamPeriod) + " )";
IndicatorShortName(short_name);

// 2 / (Period + 1)だと0になる。int演算で切り捨てが発生する。
alfa =  2.0 / (Period + 1);

return(INIT_SUCCEEDED);
}


//------------------------------------------------------------------
//計算イベント
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[])            //スプレット
{
//元となる値を計算する。
for( int i = (rates_total - prev_calculated - 1); i >= 0 && !IsStopped(); i-- )
{
if( i > (rates_total - Period) ) continue;

// 最初の一個は単純平均
if( i == (rates_total - Period) )
{
double sum = 0;
for( int j = i; j < rates_total; j++ )
{
sum += GetPrice(open[j], close[j], high[j], low[j], MaPrice);
}
zemaBuffer[i] = sum / Period;
continue;
}

//ZEMA = alfa * ( price[0] + Gain * (price[0] - price[MomentamPeriod]) + (1-alfa) * zema[1]

double price0 = GetPrice(open[i], close[i], high[i], low[i], MaPrice);
double priceM = GetPrice(open[i + MomentamPeriod], close[i + MomentamPeriod], high[i + MomentamPeriod], low[i + MomentamPeriod], MaPrice);

zemaBuffer[i] = alfa * (price0 + Gain * (price0 - priceM) ) + (1 - alfa) * zemaBuffer[i + 1];
}

return(rates_total -1);
}

//------------------------------------------------------------------
// 価格を計算する。
// return 対象価格
double GetPrice(
double open,   // オープン値
double close,  // クローズ値
double high,   // 高値
double low,    // 安値
ENUM_APPLIED_PRICE maPrice    //取得価格
)
{
double price = 0;

switch( maPrice )
{
case PRICE_CLOSE:
price = close;
break;
case PRICE_OPEN:
price = open;
break;
case PRICE_HIGH:
price = high;
break;
case PRICE_LOW:
price = low;
break;
case PRICE_MEDIAN:
price = (high + low) / 2;
break;
case PRICE_TYPICAL:
price = (high + low + close) / 3;
break;
case PRICE_WEIGHTED:
price = (high + low + close + close) / 4;
break;
}
return price;
}