2015年10月6日火曜日

[MT4インジケータ&プログラミング]平均足をマルチタイムフレームで。

※このプログラムは未来の情報を見ている不具合があります。

heikinasi.PNG

数あるインジケータの中でも平均足というのは面白いです。

高値安値の平均値を結んでいったときに上昇しているか下降しているかを表しています。

ろうそく足のように見えますが、実質は指数移動平均の仲間のようです。
平均足open = (前回平均足Close + 前回平均足Open) / 2
に対して下記の値がどの位置にいるかを示しています。
平均足close = (open + high + low + close) / 4
※(open + high + low + close) / 4 は以下OHLC/4と省略します。

平均足openは下記のように分解できますので、結局のところ前回OHLC/4に対するα=0.5 EMAとなります。
平均足open = (前回平均足Close + 前回平均足Open) / 2
      = (前回OHLC/4 + 前回平均足Open) / 2
      = (1/2) * 前回OHLC/4 + (1/2) * 前回平均足Open
      = 0.5 * 前回OHLC/4 + (1 - 0.5) * 前回平均足Open

平均足は、前回OHLC/4のEMAに対して今回のOHLC/4が高いか低いかでトレンドを判断する指標となります。

ということは、四本値の平均をとって平滑化効果を出し、それをさらにEMA遅延させています。
平均足もEMAの遅延効果を使ったインジケータといえるかと思います。

さておき、平滑化効果を四本値の平均で出しているので、時間足の選択が重要になってきます。
そこで、マルチタイムフレームに対応した平均足の表示にチャレンジしてみたいと思います。

試しに作ってみたのですが、現状とても遅いため、MT4のツール→オプション→チャートのチャートバー数を5000本程度にしてお試しください。設定後はMT4の再起動が必要です。

両ひげが歩かないかで色が変わります。サブウィンドウの下から月足→1分足までと描画しますが、現在チャートに表示している時間までの表示となります。

今回各時間の平均足を求めるのにMT4に標準でついてくるHeikin Ashi.mq4をiCustomを利用して呼び出しています。これが原因でとても遅いですが、まぁとりあえずの横着版ってことで^^;;;
ここを改善したコードも書けますが、ちゃちゃっと作るレベルではないので、いったん、ここまでにしたいと思います。

また、平均足はEMAを利用した値比較ですが、比較対象とするMAをいろいろ切り替えて平均足っぽい描画ができるのではないかなぁ?とか考えちゃいますよね?、また時間があるときにいろいろ試してみたいと思います。


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

//------------------------------------------------------------------
// マルチタイムフレーム平均足
#property copyright "Copyright 2015,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict
#property indicator_separate_window
#property indicator_maximum   90
#property indicator_minimum   0

//オブジェクト名
#define HEIKIN_OBJECT_NAME "HEIKIN"

//チャート名
#define HEIKIN_SHORT_NAME "HeikinMT"

//タイムフレーム数
#define TIMEFRAME_COUNT 9

// 上ひげ 上げ平均足色
extern color UpUpcolor = C'0,0,255';
// 両ひげ 上げ平均足色
extern color UpDualColor = C'128,128,255';
// 両ひげ 下げ平均足色
extern color DownDualColor = C'255,128,128';
// 下ひげ 下げ平均足色
extern color DownDownColor = C'255,0,0';

// 最小表示タイムフレーム
extern ENUM_TIMEFRAMES MinTimeframe = PERIOD_M5;

// 最大表示タイムフレーム
extern ENUM_TIMEFRAMES MaxTimeframe = PERIOD_MN1;

// タイムフレーム一覧
ENUM_TIMEFRAMES timeframes[];

string shortName;

//------------------------------------------------------------------
//初期化
int OnInit()
{
ArrayResize(timeframes, TIMEFRAME_COUNT);
timeframes[0] = PERIOD_M1;
timeframes[1] = PERIOD_M5;
timeframes[2] = PERIOD_M15;
timeframes[3] = PERIOD_M30;
timeframes[4] = PERIOD_H1;
timeframes[5] = PERIOD_H4;
timeframes[6] = PERIOD_D1;
timeframes[7] = PERIOD_W1;
timeframes[8] = PERIOD_MN1;

// 短縮名を設定
shortName = "HeikinMT" + TimeToStr(TimeCurrent());

IndicatorShortName(shortName);

return(INIT_SUCCEEDED);
}

//------------------------------------------------------------------
//終了処理
void OnDeinit(const int reason)
{
//オブジェクトを作成する。
long chartId = ChartID();

int total = ObjectsTotal(chartId);
//生成したオブジェクトを削除する。
//0から削除するとインデックス位置がずれて
//正しく削除できないため、後ろから削除するようにする。
for( int i = total - 1; i >= 0 ; i--)
{
string name = ObjectName(chartId, i);

// 先頭文字列がHEIKIN_OBJECT_NAMEと一致する場合、削除する。
if( StringFind(name, HEIKIN_OBJECT_NAME) == 0 )
{
ObjectDelete(chartId, name);
}
}
}
//------------------------------------------------------------------
//計算イベント
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[])            //スプレット
{
if( GetCountTimeFrame() == 0 ) return rates_total ;

int subWindowIndex = WindowFind(shortName);
int currentPeriod = Period();

//処理済み時間
datetime calculatedTime[TIMEFRAME_COUNT];
ArrayFill(calculatedTime, 0, TIMEFRAME_COUNT, 0);

for( int i = rates_total - prev_calculated - 1 ; i >= 0; i-- )
{
for( int timeIndex = 0 ; timeIndex < TIMEFRAME_COUNT; timeIndex++)
{
// 対象のタイムフレームを取得する。
ENUM_TIMEFRAMES target = timeframes[timeIndex];
if( target < MinTimeframe || target > MaxTimeframe) continue;
if( target < currentPeriod) continue;
int shift = iBarShift(NULL, target, time[i], false);
if( shift < 0 ) continue;

datetime startTime = iTime(NULL, target, shift);

//iCustomの呼び出しが遅いので、なるべく呼び出し回数を減らすようにする。
if( i > 1 && startTime == calculatedTime[timeIndex] ) continue;
calculatedTime[timeIndex] = startTime;

datetime endTime;

if( shift > 0 )
{
endTime =  iTime(NULL, target, shift - 1);
}
else
{
endTime = startTime + PeriodSeconds(target);
}

// 標準の平均足を利用する。
double beforeOpen = iCustom(NULL, target, "Heiken Ashi", Red, White, Red, White, 2, shift + 1);
double beforeClose = iCustom(NULL, target, "Heiken Ashi", Red, White, Red, White, 2, shift + 1);

if( beforeOpen == 0 || beforeClose == 0 )
{
continue;
}

double haOpen = (beforeOpen + beforeClose) / 2;
double haHigh = iHigh(NULL, target, shift);
double haLow = iLow(NULL, target, shift);
double haClose = (iOpen(NULL, target, shift) + iClose(NULL, target, shift) + haHigh + haLow) / 4;

haHigh = MathMax(haHigh, MathMax(haOpen, haClose));
haLow = MathMin(haLow, MathMin(haOpen, haClose));

// 最新値が更新される場合は、オブジェクトを再描画する。
if( i <= 1 )
{
DeleteRectangleObject(startTime, target);
}

color backColor;
if( haOpen < haClose)
{
//下髭出ている場合UpDualColor、それ以外UpUpcolor
// haOpen < haClose において haClose < haHighが成り立つので上髭は見なくてもよい。
backColor = haLow < haOpen ? UpDualColor : UpUpcolor;
}
else
{
//上髭出ている場合UpDualColor、それ以外UpUpcolor
// haOpen > haClose において haClose > haLowが成り立つので下髭は見なくてもよい。
backColor =  haHigh > haOpen ? DownDualColor : DownDownColor;
}

CreateRectangleObject(startTime, endTime, target, backColor, subWindowIndex);
}

}

return(rates_total - 1);
}

//------------------------------------------------------------------
//売買シグナルを矩形で描画する。
bool CreateRectangleObject(
datetime startTime,  //表示時間(横軸)
datetime endTime,    //表示時間(横軸)
ENUM_TIMEFRAMES period,          //対象ピリオド
color backColor,     //背景色
int subWindowIndex   //サブウィンドウインデックス
)
{
//オブジェクトを作成する。
long chartId = ChartID();

string name = GetObjectName(startTime, period);
int index = GetTimeFrameIndex(period);
if( index < 0 ) return true;
if( ObjectFind(chartId, name) >= 0 ) return true;


//チャート縦幅いっぱいに表示する。
double min = ChartGetDouble(chartId, CHART_FIXED_MIN, subWindowIndex);
double max = ChartGetDouble(chartId, CHART_FIXED_MAX, subWindowIndex);
double oneBlockSize = (max - min) / TIMEFRAME_COUNT;

double top = oneBlockSize * (TIMEFRAME_COUNT - index);
double bottom = oneBlockSize * (TIMEFRAME_COUNT - index - 1);

if( !ObjectCreate(chartId, name, OBJ_RECTANGLE, subWindowIndex, startTime, top, endTime, bottom) )
{
return false;
}
ObjectSetInteger(chartId, name, OBJPROP_COLOR, backColor);
ObjectSetInteger(chartId, name, OBJPROP_READONLY, true);
ObjectSetInteger(chartId, name, OBJPROP_SELECTABLE, false);

return true;
}


//------------------------------------------------------------------
//オブジェクトを削除する。
bool DeleteRectangleObject(
datetime startTime,  //開始時間
ENUM_TIMEFRAMES period)          //対象ピリオド
{
//オブジェクトを作成する。
long chartId = ChartID();
string name = GetObjectName(startTime, period);

if( ObjectDelete(chartId, name) )
{
#ifdef DEBUG
Print("Ojbect Deleted " + name);
#endif
}

return true;
}

//------------------------------------------------------------------
//オブジェクト名を取得する。
string GetObjectName(
datetime startTime,  //開始時間
ENUM_TIMEFRAMES period)          //対象ピリオド
{
return StringFormat("%s_%s_%d",HEIKIN_OBJECT_NAME, TimeToStr(startTime), period);
}

//------------------------------------------------------------------
//対象となるタイムフレーム数を取得する。
int GetCountTimeFrame()
{
int count = 0 ;
int currentPeriod = Period();
for( int i = 0; i < TIMEFRAME_COUNT; i++)
{
if( timeframes[i] >= MinTimeframe
&& timeframes[i] >= currentPeriod
&& timeframes[i] <= MaxTimeframe)
{
count++;
}
}
return count;
}

//------------------------------------------------------------------
//対象となるタイムフレームのインデックスを取得する。
int GetTimeFrameIndex(ENUM_TIMEFRAMES timeframe)
{
int index = -1 ;
if( timeframe < MinTimeframe || timeframe > MaxTimeframe) return -1;

for( int i = 0; i < TIMEFRAME_COUNT; i++)
{
if( timeframe == timeframes[i] ) return i;
}
return index;
}