2016年3月21日月曜日

[MT4インジケータ]マーケットプロファイルを眺めてみる。マーケットプロファイル その1

マーケットプロファイル(Market Profile)とは、相場の滞在した箇所をカウントして横軸のチャートにしたものです。

有名な指標ですのでMT4にも、たくさんインジケータが存在します。

マーケットプロファイルそのものを利用したい場合は、そちらを活用されたほうが良いかと思います。
マーケットプロファイルといえば、過去記事のMT4インジケータ 各国の取引時間を表示する。で以前も紹介した、Peaky FX さんの、日本、EU、アメリカ時間を矩形を表示するインジケータに時間ごとのプロファイルを表示する機能もありますね。

無料で便利なインジケータでお勧めです。

三つ巴のストップ狩り合戦に乗っかって、大きな動きを取る!
アジア・欧州・NYの時間帯をチャートに表示、Sessions EXアジア・欧州・NYの時間帯をチャートに表示、Sessions EX | fx-on.com

おなじくPeaky FXさんがDL Marketで1日単位のマーケットプロファイルも出していますね。
http://www.dlmarket.jp/products/detail.php?product_id=316616

・・・・FX-ONさんにはマーケットプロファイル誰も出していないのですね・・どなたかは出していてもよさそうなものなのですが・・・。

まぁさておき、マーケットプロファイルを利用したい場合は、そちらをダウンロードされることをお勧めします。

■1日単位のマーケットプロファイル

本来のマーケットプロファイルは値幅をマーキングしますが、ここは簡易版として1分足の終値を指定のPips幅に分解してカウントしたいと思います。トレンド状態にあるときはプロファイルのピークが複数発生します。レンジ状態のときはピークが一つだけ発生します。過去の状態がどちらなのか?というのを判定するためのインジケータです。

過去何日間かのピーク位置をブレイクラインに置くという考え方をする人もいるようです。

■EURUSD 3/4のマーケットプロファイル

マーケットプロファイルの問題は、取引が薄い時間帯に山ができてしまうことです。たとえばEURUSDは、ロンドンフィキシング終了後、EU時間開始までは動かないことが多々あります。ここに8時間分の山ができてしまいます。たとえば、3/4ですが、日本時間ではほとんど動かず、プロファイルに大きな山ができています。この山にどの程度意味があるのか、悩ましいところです。

考え方がいくつかあるとは思いますが、EURUSDのような日本時間が動かない前提の通貨は
日本時間午前 A
日本時間午後 B
EU時間 30分毎 C〜T(ぐらい?)
LF以降 U
とかのプロファイルの間隔に変化をつけるといった時間幅の恣意的変更が必要に思われます。

マーケットプロファイルが作成された時代と違って現在ではもっと多くのデータを利用可能です。ということで、明日は個数の代わりにTickをカウントするとどうなるかを見てみたいと思います。

久しぶりにMQL的なところも書きたいと思います。
いつもの矩形描画をしているところですが、塗りつぶし矩形ではなくて枠線矩形を描画しています。

これは、OBJPROP_BACKというプロパティをfalseに指定することで枠線描画になります。FILLプロパティないなぁと思って探していたのですが、BACKでした。背景にするというプロパティかと思っていましたが、背景を描画するかどうか?というフラグでした。
ObjectSetInteger( chartId, name, OBJPROP_BACK, false );

あと、横軸の解像度が時間軸に影響を受けてしまいます。15分足ぐらいで表示するのがよさそうです。

■マーケットTickプロファイルシリーズ
・2/3と3/18のプロファイル形状が似ている。Tickプロファイル覚書
http://mt4program.blogspot.jp/2016/03/mt423318tick.html

・マーケットプロファイルを眺めてみる。マーケットプロファイル その1
http://mt4program.blogspot.jp/2016/03/mt4_21.html

・Tick値でプロファイルを作ってみる。マーケットプロファイル その2
http://mt4program.blogspot.jp/2016/03/mt4tick.html

・マーケットプロファイルを色分けして表示する。マーケットプロファイル その3
http://mt4program.blogspot.jp/2016/03/mt4_24.html

MT4開発日記で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ FX システムトレード派へ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。

プログラムはこちらから


//------------------------------------------------------------------
// マーケットプロファイル
#property copyright "Copyright 2016,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict
#property indicator_chart_window

input int StartHour = 0; //開始時間

input color RectangleColor = clrSkyBlue;  // 矩形枠線色
input color ProfileColor = clrDarkGray;   // プロファイルの色

input int DrawProfileCount = 10;       //最初に描画するプロファイル数
input int ProfileResolutionPips = 5;   // プロファイルの解像度(PIPS)

input bool AutoWidth = true;           // 横幅を最大値が矩形の2/3になるよう自動調整する。
input int ProfitWidth = 1000;  // AutoWidth=false時のプロフィットベースの矩形横幅

input ENUM_TIMEFRAMES CountPeriod = PERIOD_M1;  // カウントする時間足

#define OBJECT_NAME "MKPF"

//------------------------------------------------------------------
//初期化
int OnInit()
{
   string short_name = "MarketProfile";
   IndicatorShortName(short_name);
   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);
      
      // 先頭文字列がOBJECT_NAMEと一致する場合、削除する。
      if( StringFind(name, 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( Period() > PERIOD_H4 ) return rates_total;
   
      //Pips計算 小数点桁数が3or5の場合、Point()*10=1pips
   int digit = (int)MarketInfo(Symbol(), MODE_DIGITS);
   double point = (double)MarketInfo(Symbol(), MODE_POINT);
   double onePips = point * (digit == 3 || digit == 5 ? 10 : 1);
   
   int drawCount = 0 ;
   for( int i = 0; i < rates_total && !IsStopped(); i++ )
   {
      MqlDateTime current;
      TimeToStruct(time[i], current);
      
      if( current.hour == StartHour )
      {
         current.sec = 0;
         current.min = 0;

         datetime baseTime = StructToTime(current);
         i = iBarShift(Symbol(), Period(), baseTime);
         if( i < 0 ) break ;
      
         // 矩形を描画する。
         datetime endTime = baseTime + 24 * 60 * 60 - 1;
         int barShift = iBarShift(Symbol(), Period(), endTime);
         if( barShift < 0 ) barShift = 0;
         
         int highest = iHighest(Symbol(), Period(), MODE_HIGH, i - barShift + 1, barShift);
         double rangeHigh = iHigh( Symbol(), Period(), highest);
         int lowest = iLowest(Symbol(), Period(), MODE_LOW, i - barShift + 1, barShift);
         double rangeLow = iLow( Symbol(), Period(), lowest);
      
         CreateRectangleObject(baseTime, endTime, rangeHigh, rangeLow, RectangleColor);
         
         // プロファイルの描画        
         double resolution = ProfileResolutionPips * onePips;
         double center = (rangeHigh + rangeLow) / 2;

         int startShift = iBarShift(Symbol(), CountPeriod, baseTime);
         int endShift = iBarShift(Symbol(), CountPeriod, endTime);
         
         // 開始シフトが取れない場合は、プロファイル作成不能
         if( startShift < 0 ) break ;
         if( endShift < 0 ) endShift = 0;
         
         if( startShift != endShift )
         {
            // プロファイル数のカウント
            int resolutionCount = (int)( (rangeHigh - center) * 2 / resolution + 2);
            double profile[];
            ArrayResize(profile, resolutionCount);
            ArrayInitialize(profile, 0);

            for( int j = startShift; j >= endShift; j-- )
            {
               double profileClose = iClose(Symbol(), CountPeriod, j);
               
               int index = (int)MathFloor( (profileClose - rangeLow) / resolution); 
               if(index >= resolutionCount) index = resolutionCount - 1;
               
               profile[index]++;
            }
            
            DeleteProfileRectangleObject(baseTime);
            
            //矩形に対して2/3位置まで描画する。
            int maxCount = (int)ProfitWidth;
            if( AutoWidth )
            {
               maxCount = (int)(profile[ArrayMaximum(profile)] * 3 / 2);
            }
            
            for( int j = 0; j < resolutionCount; j++ )
            {
               double bottom = center - resolution * resolutionCount / 2 + j * resolution; 
               double top = bottom + resolution; 
               
               datetime rightTime = baseTime + (int) (profile[j] * 86400 / maxCount);
               
               CreateProfileRectangleObject(j, baseTime, rightTime, top, bottom, ProfileColor);
            }
         } 
        
         drawCount++;
      }
      
      if( (drawCount > 0 && i >= rates_total - prev_calculated) || drawCount >= DrawProfileCount ) break ;
   }
   //元となる値を計算する。
   return(rates_total - 1);
}

//------------------------------------------------------------------
//プロファイル矩形を削除する
// 成功時 True
bool DeleteProfileRectangleObject
   ( datetime startTime    // 開始時間
   )
{
   //オブジェクトを作成する。
   long chartId = ChartID();

   string pfName = OBJECT_NAME  + "_PF_" + TimeToStr(startTime);

   int total = ObjectsTotal(chartId);
   //生成したオブジェクトを削除する。
   //0から削除するとインデックス位置がずれて
   //正しく削除できないため、後ろから削除するようにする。
   for( int i = total - 1; i >= 0 ; i--)
   {
      string name = ObjectName(chartId, i);
      
      // 先頭文字列がOBJECT_NAMEと一致する場合、削除する。
      if( StringFind(name, pfName) == 0 )
      {
         ObjectDelete(chartId, name);
      }
   }
   
   return true;
}

//------------------------------------------------------------------
//プロファイル矩形を生成する。
// 生成成功時 True
bool CreateProfileRectangleObject
   ( int i                 // インデックス
   , datetime startTime    // 開始時間
   , datetime endTime      // 終了時間
   , double top            // 上限価格
   , double bottom         // 下限価格
   , color backColor       // 背景色
   )
{
   //オブジェクトを作成する。
   long chartId = ChartID();

   string name = OBJECT_NAME  + "_PF_" + TimeToStr(startTime) + "_" + IntegerToString(i);

   int index = ObjectFind( chartId, name );
   if( index < 0 )
   {
      if ( ObjectCreate( chartId, name, OBJ_RECTANGLE, 0, startTime, top, endTime, bottom ) == false )
      {
         return false;
      }
      ObjectSetInteger( chartId, name, OBJPROP_COLOR, backColor );
      ObjectSetInteger(chartId, name, OBJPROP_HIDDEN, true);
      ObjectSetInteger(chartId, name, OBJPROP_SELECTABLE, false);
   }
   else
   {
      ObjectSetInteger( chartId, name, OBJPROP_TIME1, startTime);
      ObjectSetInteger( chartId, name, OBJPROP_TIME2, endTime);
      ObjectSetDouble( chartId, name, OBJPROP_PRICE1, top);
      ObjectSetDouble( chartId, name, OBJPROP_PRICE2, bottom);
   }

   return true;
}

//------------------------------------------------------------------
//矩形を生成する。
// 生成成功時 True
bool CreateRectangleObject
   ( datetime startTime    // 開始時間
   , datetime endTime      // 終了時間
   , double top            // 上限価格
   , double bottom         // 下限価格
   , color backColor       // 背景色
   )
{
   //オブジェクトを作成する。
   long chartId = ChartID();

   string name = OBJECT_NAME + TimeToStr(startTime);

   int index = ObjectFind( chartId, name );
   if( index < 0 )
   {
      if ( ObjectCreate( chartId, name, OBJ_RECTANGLE, 0, startTime, top, endTime, bottom ) == false )
      {
         return false;
      }
      ObjectSetInteger( chartId, name, OBJPROP_BACK, false );
      ObjectSetInteger( chartId, name, OBJPROP_COLOR, backColor );
      ObjectSetInteger(chartId, name, OBJPROP_HIDDEN, true);
      ObjectSetInteger(chartId, name, OBJPROP_SELECTABLE, false);
   }
   else
   {
      ObjectSetInteger( chartId, name, OBJPROP_TIME1, startTime);
      ObjectSetInteger( chartId, name, OBJPROP_TIME2, endTime);
      ObjectSetDouble( chartId, name, OBJPROP_PRICE1, top);
      ObjectSetDouble( chartId, name, OBJPROP_PRICE2, bottom);
   }

   return true;
}

//------------------------------------------------------------------
//矩形オブジェクトを削除する
bool DeleteRectangleObject(
   datetime time           //表示時間(横軸)
)
{
   //オブジェクトを作成する。
   long chartId = ChartID();
   string name = OBJECT_NAME + TimeToStr(time);
   return ObjectDelete(chartId, name);
}

//+------------------------------------------------------------------+