2015年4月30日木曜日

オシレータにも鈍感力を!期間が変化するRSI VLDMI

今日はVLDMI(Variable Length Dynamic Momentum Index)です。

今回は、はっきりとした論文等が見つからなかったため、調べた結果の私なりの解釈で実装したVLDMIです。
ざっくりいうと期間が動的に変化するRSI!ってことで、私の認識間違ってたらご指摘ください^^;;

さておき、Variable Lengthというのは、相場が凪いでいる時は鈍感に、動きだしたら敏感にするという考え方です。この考え方はいろんなオシレータの改良に使えると考えています。
今回はVLDMIを実装してみてRSIと比較してみました。

VLDMI.PNG
水色の線がVLDMI(14)、青色の線がRSI(14)になります。RSIに比べてVLDMIが動き出したときには敏感に反応していることが分かります。※()の中は期間を表しています。
VLDMIは期間を大体50%~200%の範囲で変動させます。つまりVLDMI(14)は 期間を7~24を標準偏差の変化率から動的に変更しているRSIという事になります。

VLDMIの見方ですがRSIと同様に70超えで買われすぎ30以下で売られすぎです。
チャートを眺めた感じ、このオシレータ5分足には結構使えるかな?といったところです。短期トレード向けか、押し目狙いのオシレータとなります。あと、ダイバージェンス現象がRSIと比べかなりはっきり出ます。そのあたりを参考にしている人も良いのではないでしょうか?

//------------------------------------------------------------------
// VLDMI Variable Length Dynamic Momentum Index
#property copyright "Copyright 2015,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict

#property indicator_separate_window
#property indicator_minimum   0
#property indicator_maximum   100
#property indicator_level1    70
#property indicator_level2    30
#property indicator_levelcolor clrSilver
#property indicator_levelstyle STYLE_DOT

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

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

// CDMAインデックス
#property indicator_label1  "VLDMI"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrAqua
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

#property indicator_type2   DRAW_NONE

// 入力パラメータ RSI算出基準期間
input int VariableLengthBase = 14;

// 入力パラメータ RSI算出価格
input ENUM_APPLIED_PRICE RsiPrice = PRICE_CLOSE;

// 入力パラメータ 標準偏差期間
input int StdPeriod = 5;

// 入力パラメータ 標準偏差平均期間
input int StdAvgPeriod = 10;

//入力パラメータ 表示移動
input int   StdShift = 0;

//入力パラメータ 移動平均種別
input ENUM_MA_METHOD StdMethod = MODE_EMA;

//入力パラメータ 適用価格
input ENUM_APPLIED_PRICE StdPrice = PRICE_CLOSE;

// バッファー
double vldmiBuffer[];
double stdDevBuffer[];

//------------------------------------------------------------------
//初期化
int OnInit()
{
SetIndexBuffer(0, vldmiBuffer);
SetIndexBuffer(1, stdDevBuffer);
SetIndexDrawBegin(0, (int)(VariableLengthBase * 2.5));
IndicatorShortName("VLDMI(" + IntegerToString(VariableLengthBase) + ")");

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; i-- )
{
// 標準偏差を求める。
stdDevBuffer[i] = i >= rates_total - StdPeriod - 1 ?
0 : iStdDev(NULL,PERIOD_CURRENT, StdPeriod, StdShift, StdMethod, StdPrice, i);

// 標準偏差のStdAvgPeriod間の平均を求める。
double stdDevAvg = iMAOnArray(stdDevBuffer, rates_total, StdAvgPeriod, 0, MODE_SMA, i);
if( stdDevAvg == 0 || stdDevBuffer[i] == 0)
{
vldmiBuffer[i] = 0 ;
continue;
}

// RSI対象期間を求める。対象期間 = 基準期間 / ( 標準偏差 / 標準偏差の平均 )
int variableLength = (int)(VariableLengthBase * stdDevAvg / stdDevBuffer[i] );
vldmiBuffer[i] = iRSI(NULL, PERIOD_CURRENT, variableLength, RsiPrice, i);
}

return (rates_total - 1);
}


 やっていることは割と単純で、標準関数で用意されているiStdDevを使用して標準偏差を求めて、iMAOnArrayの単純平均を利用して平均値を出しています。その後、現在の標準偏差/平均値を元に対象期間を変化させています。最後に用意されているiRSIを使用してRSI値を求めてます。

iStdDev
double iStdDev(
string symbol, // 対象通貨ペア
int timeframe, // 時間足
int ma_period, // 移動平均期間
int ma_shift, // 移動平均シフト量
int ma_method, // 移動平均種別
int applied_price, // 移動平均対象価格
int shift // シフト量
);
標準で用意されているiStdDevですが、なぜか移動平均のパラメータを要求されます。移動平均に対する標準偏差をとる関数のようです。自分で標本標準偏差を計算してVLDMIを表示させてみたのですが、あまりグラフに変化が無かったため、iStdDevを使っています。
厳密に標準偏差を取りたい場合や、High/Log/Closeの値も含めてばらつきを見たい場合などは自力で計算する必要があります。

後の二つは紹介だけ。
iMAOnArray
double iMAOnArray(
double array[], // 対象配列
int total, // 配列の要素数
int ma_period, // 移動平均期間
int ma_shift, // 移動平均シフト量
int ma_method, // 移動平均対象価格
int shift // シフト量
);

iRSI
double iRSI(
string symbol, // 対象通貨ペア
int timeframe, // 時間足
int period, // 期間
int applied_price, // 対象価格
int shift // シフト量
);

 余談ですが、対象期間を求めるような式のように割り算が続く場合は、なるべく掛け算になるように式を置き換えています。四則演算の速度は + = - > × >>> ÷ とダントツ割り算が遅くなります。繰り返し演算する場合はなるべく割り算を減らした方がパフォーマンスが良いです。まぁ今のPCは性能が高いので誤差の範囲!って言われてしまうとその通りなのですが・・癖です(笑)

 あと、このオシレータ、標準偏差の計算に時間がかかるためか、MT4のチャート要素数が多くなると、時間足の切り替え動作などが結構重くなります。標準偏差の計算時にルート演算が挟まっている為と思いますが、チャートのバー数などを極端に大きくしている場合などはご注意ください。

 さて、そろそろゴールデンウィークですね。
 そろそろ、手持ちのネタが尽きつつあるので、ゴールデンウィーク中に何か探したいと思います。