2015年4月8日水曜日

移動平均とメディアンフィルタ比較 インジケーターの作成

前回まで、簡単にプログラムのテンプレートから、チャート描画ができました。

今度は組み込みの指標と自作の指標を比較するチャートを作ってみたいと思います。
サンプルとして移動平均とメディアンフィルターを選択します。

メディアンフィルターは画像処理の世界ではよく知られるアルゴリズムです。取得したい値の前後の値を含めてソートして、その中央値を採用するというアルゴリズムになります。画像処理では粒状ノイズの除去に使用されます。

これをFXの価格チャートに適用したらどうなるか?という実験です。
先に実行結果を表示します。
median.PNG
6:30のバーで、クローズ値ではない値が取られていることが確認できます。このようにメディアンは大きな変換の際ノイズをとれる動作をします。メディアンフィルタの最大の難点は最新の値にはフィルタが適用されていない事で、現在の状態を解析するのには向いていないようです。

移動平均と比較するとトレンド変化に敏感に反応しています(※)ので、短期のトレンド変換判定には使えるかもしれません。
��※)基本クローズ値を結んだだけですので、当たり前なのですが、だましがたくさん発生します。


FX-ONブログランキングにご協力よろしくお願いいたしますm(_ _ )m

//+------------------------------------------------------------------+
//メディアンフィルタ+移動平均
//+------------------------------------------------------------------+
#property copyright "Copyright 2015,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict
#property indicator_chart_window

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

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

//メディアンライン
#property indicator_label1  "Median"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

//移動平均
#property indicator_label2  "Avg"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrAqua
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2

//入力パラメータ メディアンフィルター長
input int      FilterLength=3;
//入力パラメータ 移動平均を表示するかどうか
input bool     IsShowAvg=true;

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

//------------------------------------------------------------------
//初期化
int OnInit()
{
// メディアンフィルタ長は奇数の必要がある。
if( FilterLength % 2 == 0 )
{
return(INIT_PARAMETERS_INCORRECT);
}

//インジケーターバッファを初期化する。
SetIndexBuffer(0,medianBuffer);
SetIndexBuffer(1,avgBuffer);

// IsShowAvg == falseの時は、移動平均をグラフ上に表示しない。
if( !IsShowAvg )
{
SetIndexStyle(1, DRAW_NONE);
}

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[])            //スプレット
{
//要素の0が一番新しいデータ
//0~要素数-処理済み要素数-1のデータを更新する。
for( int i = 0; i < (rates_total - prev_calculated); i++ )
{
avgBuffer[i] = iMA(Symbol(), 0, FilterLength, 0, MODE_SMA, PRICE_CLOSE, i);
medianBuffer[i] = GetMedianValue(close, rates_total, FilterLength, i);
}

//中央値インデックスを計算する。
int halfIndex = (FilterLength - (FilterLength % 2)) / 2;
//メディアンフィルタの影響範囲はすべて更新する。
return(rates_total - 1 - halfIndex);
}

//------------------------------------------------------------------
//メディアンフィルター適用後の値を取得する。
double GetMedianValue(const double &targetValues[],   //メディアンフィルタを計算する元配列
int valuesCount,          //targetValuesの要素数
int filterLength,         //メディアンフィルタ長
int targetIndex)          //メディアンフィルタ値取得の対象インデックス
{
//ソート用領域を確保
double workValues[];
ArrayResize(workValues, filterLength);

//中央値インデックスを計算する。
int halfIndex = (filterLength - (filterLength % 2)) / 2;

int setIndex = 0 ;
// targetIndexを中央値として前後filterLength/2(小数点切り捨て)分だけ取得する。
for( int i = targetIndex - halfIndex; i <= targetIndex + halfIndex; i++ )
{
// データ端対策
int valueIndex = i ;
if( i < 0 ) valueIndex = 0;
if( i >= valuesCount ) valueIndex = valuesCount - 1;

workValues[setIndex] = targetValues[valueIndex];
setIndex++;
}

//データをソートして、ソート結果の中央値を取得する。
ArraySort(workValues);
double returnValue = workValues[halfIndex];

//領域解放・・・不要? なんとなく職業プログラマ的に確保したものは解放したくなる。
ArrayFree(workValues);

return returnValue;
}

おお・・自分で言うのもなんですが、一挙にプログラムっぽくなりました(笑)

プログラム解説です。特徴的な部分だけ、ピックアップします。

//入力パラメータ メディアンフィルター長
input int      FilterLength=3;

//入力パラメータ メディアンフィルター長
input bool     IsShowAvg=true;

今回は入力パラメータを指定できるようにしました。
入力パラメータはグローバル変数として定義して、input句が頭につきます。

if( !IsShowAvg )
{
SetIndexStyle(1, DRAW_NONE);
}


SetIndexStyleはプロット情報を上書きするメソッドです。
パラメータにより移動平均を表示しないと選択された場合、SetStyleIndexにてDRAW_NONEを指定することによって表示させないことが可能です。
SetStyleIndex

void SetIndexStyle(
int index, // 対象となるINDEX(0スタート。indicator_type?の?の値から-1になるので注意)
int type, // indicator_type? に相当
int style=EMPTY, // indicator_style? に相当
int width=EMPTY, // indicator_width? に相当
color clr=clrNONE);// indicator_color? に相当
Style以降は省略可能です。


avgBuffer[i] = iMA(Symbol(), 0, FilterLength, 0, MODE_SMA, PRICE_CLOSE, i);

iMAは移動平均をもとめるための組み込み関数です。
iMA

double iMA(
string symbol, // 移動平均を計算する通貨ペア名
int timeframe, // timeframe フレーム時間 0を指定すると挿入されているチャートの値
int ma_period, // 期間
int ma_shift, // 移動平均を右方向にシフトする数
int ma_method, // averaging method移動平均計算方法
int applied_price, // applied price適用価格
int shift); // 移動平均算出位置(最新位置からのシフト量)

double GetMedianValue(const double &targetValues[],   //メディアンフィルタを計算する元配列
int valuesCount,          //targetValuesの要素数
int filterLength,         //メディアンフィルタ長
int targetIndex)          //メディアンフィルタ値取得の対象インデックス

メディアンフィルタ算出部は関数化してみました。
C言語と同じように関数として宣言して、それをプログラム内で呼び出し可能です。

宣言位置は、実際に使用するコードの後ろでも問題なく動作します。