インジケータ作成の練習がてらなのですが、フラクタルに価格をつけてみました。
横になると重なってしまうことがあるため、回転させて縦に表示しています。
前回のチャートに合わせて移動しないラベルの場合は、XY座標をピクセル値として指定しましたが、価格と合わせて動くテキストの場合、時間と価格を指定します。こんな感じです。
if( !ObjectCreate(chartId, objectName, OBJ_TEXT, 0, time, price) )
{
return false;
}
色の設定等は、ObjectSetXXXXX系の関数で行います。
フラクタルを表す矢印と重なってしまうため、ラベルの表示位置を微調整する工夫をしてみました。
このように矢印と価格を同時に出そうとすると、小手先のテクニックが必要そうな雰囲気です。もしかしたらもう少しスマートな手法があるかもしれません。
ブログランキングにご協力よろしくお願いいたします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);
}
}
}
}