2015年11月2日月曜日

[MT4プログラミング] チャート上に価格を出す。フラクタルに価格をつけてみた。


インジケータ作成の練習がてらなのですが、フラクタルに価格をつけてみました。
横になると重なってしまうことがあるため、回転させて縦に表示しています。

前回のチャートに合わせて移動しないラベルの場合は、XY座標をピクセル値として指定しましたが、価格と合わせて動くテキストの場合、時間と価格を指定します。こんな感じです。

   if( !ObjectCreate(chartId, objectName, OBJ_TEXT, 0, time, price) )
   {
      return false;
   }


色の設定等は、ObjectSetXXXXX系の関数で行います。

フラクタルを表す矢印と重なってしまうため、ラベルの表示位置を微調整する工夫をしてみました。

縦位置のピクセルでのオフセットがないのがとっても大変です・・。BITMAPならあるのに・・・。
このように矢印と価格を同時に出そうとすると、小手先のテクニックが必要そうな雰囲気です。もしかしたらもう少しスマートな手法があるかもしれません。


ブログランキングにご協力よろしくお願いいたしますm(_ _ )m
にほんブログ村 為替ブログへ
にほんブログ村



プログラムはこちらから





//------------------------------------------------------------------
// フラクタル 価格表示付き
#property copyright "Copyright 2015,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict
#property indicator_chart_window

//オブジェクト名
#define OBJECT_NAME "OBJ_FRACTALS"

// 表示小数点
int marketDigit;

// チャート高さ
int chartHeight = 0;

// 価格幅
double priceHeigh = 0;

// ラベルオフセット
double labelOffset = 0;

//------------------------------------------------------------------
//初期化
int OnInit()
{
   marketDigit = (int)MarketInfo(NULL, MODE_DIGITS);

   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);
      }
   }
}


//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
{
   ReoffsetLabels();

   for( int i = rates_total - prev_calculated - 1; i >= 0; i--)
   {
      double fractalHigh = iFractals(NULL, PERIOD_CURRENT, MODE_UPPER, i);
      double fractalLow = iFractals(NULL, PERIOD_CURRENT, MODE_LOWER, i);
      
      if( fractalHigh > 0 )
      {
         CreateArrawObject(OBJ_ARROW_BUY, time[i], fractalHigh);
      }
      if( fractalLow > 0 )
      {
         CreateArrawObject(OBJ_ARROW_SELL, time[i], fractalLow);
      }
   }
   
   // 前後2本でフラクタルは決まるため、3本前まで確認する。  
   return(rates_total - 3);
}

//------------------------------------------------------------------
//矢印オブジェクトを生成する。
bool CreateArrawObject(
   ENUM_OBJECT objectType,  //オブジェクトの種類(OBJ_ARROW_BUY/OBJ_ARROW_SELL)
   datetime time,           //表示時間(横軸)
   double price )           //表示時間(縦軸)
{
   //オブジェクトを作成する。
   long chartId = ChartID();

   string objectName = StringFormat("%s_ARR_%s_%s", OBJECT_NAME, objectType == OBJ_ARROW_BUY ? "B" : "S", TimeToStr(time));
   
   if( !ObjectCreate(chartId, objectName, objectType, 0, time, price) )
   {
      return false;
   }

   // 読み取り専用
   ObjectSetInteger(0, objectName, OBJPROP_READONLY, true);
   // 選択不可
   ObjectSetInteger(0, objectName, OBJPROP_SELECTABLE, false);
   // 名前を隠す
   ObjectSetInteger(chartId, OBJECT_NAME, OBJPROP_HIDDEN, true);
   // アンカー
   ObjectSetInteger(chartId, objectName, OBJPROP_ANCHOR, objectType == OBJ_ARROW_BUY ? ANCHOR_BOTTOM : ANCHOR_TOP);
   // 色
   ObjectSetInteger(chartId, objectName, OBJPROP_COLOR, objectType == OBJ_ARROW_BUY ? C'200,200,255' : C'255,128,128');
   // ↑種別
   ObjectSetInteger(chartId, objectName, OBJPROP_ARROWCODE, objectType == OBJ_ARROW_BUY ? 233 : 234);
   
   //価格テキストを追加する。
   objectName = StringFormat("%s_TXT_%s_%s", OBJECT_NAME, objectType == OBJ_ARROW_BUY ? "B" : "S", TimeToStr(time));

   // 表示位置を矢印の大きさの分だけ上方向にずらす。
   price = price + labelOffset * (objectType == OBJ_ARROW_BUY ? 1 : -1);

   if( !ObjectCreate(chartId, objectName, OBJ_TEXT, 0, time, price) )
   {
      return false;
   }

   // 読み取り専用
   ObjectSetInteger(0, objectName, OBJPROP_READONLY, true);
   // 選択不可
   ObjectSetInteger(0, objectName, OBJPROP_SELECTABLE, false);
   // 名前を隠す
   ObjectSetInteger(chartId, OBJECT_NAME, OBJPROP_HIDDEN, true);
   // アンカー
   ObjectSetInteger(chartId, objectName, OBJPROP_ANCHOR, ANCHOR_BOTTOM);
   // 色
   ObjectSetInteger(chartId, objectName, OBJPROP_COLOR, objectType == OBJ_ARROW_BUY ? C'200,200,255' : C'255,128,128');
   // 角度 縦に回転させる。
   ObjectSetDouble(chartId, objectName, OBJPROP_ANGLE, objectType == OBJ_ARROW_BUY ? 90 : -90);

   // 表示文字列
   ObjectSetString(chartId, objectName, OBJPROP_TEXT, DoubleToString(price, marketDigit));

   return true;
}

//------------------------------------------------------------------
//テキストラベルの位置を調整する。
void ReoffsetLabels()
{
   long chartId = ChartID();

   long arrowSize = 20;
   int height = (int)ChartGetInteger(chartId, CHART_HEIGHT_IN_PIXELS);
   double min = ChartGetDouble(chartId, CHART_PRICE_MIN);
   double max = ChartGetDouble(chartId, CHART_PRICE_MAX);
   
   double diff = (max - min);
   
   if( height == 0 || diff == 0 ) return ;
   double rate = priceHeigh / diff;
   
   if( labelOffset == 0  || height != chartHeight || rate > 1.10 || rate < 0.9 )
   {
      double oldOffset = labelOffset;
      chartHeight = height;
      priceHeigh = diff;
      labelOffset = diff / height * arrowSize; 
      
      string labelObjectName = StringFormat("%s_TXT_", OBJECT_NAME);
      string labelObjectSellName = StringFormat("%s_TXT_S", OBJECT_NAME);
      
      int total = ObjectsTotal(chartId);

      for( int i = total - 1; i >= 0 ; i--)
      {
         string name = ObjectName(chartId, i);
         
         // 先頭文字列がOBJECT_NAMEと一致する場合
         if( StringFind(name, labelObjectName) == 0 )
         {
            // オフセット差し替え
            double price = ObjectGetDouble(chartId, name, OBJPROP_PRICE);
            int flag = 1;
            if( StringFind(name, labelObjectSellName) == 0)
            {
               flag = - 1;
            }
            
            price = price - oldOffset * flag + labelOffset * flag;
            ObjectSetDouble( chartId, name, OBJPROP_PRICE, price);
         }
      }
   }
}