2015年4月10日金曜日

矢印の縦位置をチャートの高さを見て調整する。

※2015/11/2追記
// アンカー
   ObjectSetInteger(chartId, objectName, OBJPROP_ANCHOR, objectType == OBJ_ARROW_BUY ? ANCHOR_BOTTOM : ANCHOR_TOP);
って書けば、もっとスマートにできます・・。調査不足でした・・・・。



前回の記事でチャート上に矢印を表示できましたが、
買(↑)矢印は矢印の頭がクローズ時間にくっついていて、売矢印(↓)は矢印のお尻がくっついています。

なんとなくですが、クローズ時間を中心に


といった感じで、両方ともお尻基準になってほしいと思いましたので、オブジェクトをピクセルと価格のスケールから移動するコードを追加しました。
まず、チャートの高さ方向のピクセルと、価格の最大最小値を取得します。
矢印の高さは15pixcelですので、「15 * ( 価格の最大値 - 価格の最小値 ) / 高さ」分の価格だけずらして上げればよい事が分かります。

チャートの情報はChartGetXXXXX系の関数で取得します。ChartGetIntegerの場合は下記の形です。
ChartGetInteger
long ChartGetInteger(
long chart_id, // チャートID
int prop_id, // プロパティ種別
int sub_window=0 // サブウィンドウ番号 0の場合チャート
);
同様に戻り値の型違いで、ChartGetDouble、ChartGetStringが存在しています。

前回の記事からCreateArrawObject以外は変えていませんので、CreateArrawObjectだけ載せます。


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

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

if( objectType == OBJ_ARROW_BUY )
{
long hight = ChartGetInteger(chartId, CHART_HEIGHT_IN_PIXELS);

double min = ChartGetDouble(chartId, CHART_PRICE_MIN);
double max = ChartGetDouble(chartId, CHART_PRICE_MAX);

// 表示位置を矢印の大きさの分だけ上方向にずらす。
price = price + arrowSize * (max - min) / hight;
}

ObjectDelete(chartId, OBJECT_NAME);

if( !ObjectCreate(chartId, OBJECT_NAME, objectType, 0, time, price) )
{
return false;
}
ObjectSetInteger(chartId, OBJECT_NAME, OBJPROP_HIDDEN, true);
ObjectSetInteger(chartId, OBJECT_NAME, OBJPROP_COLOR, objectType == OBJ_ARROW_BUY ? C'200,200,255' : C'255,128,128');
ObjectSetInteger(chartId, OBJECT_NAME, OBJPROP_ARROWCODE, objectType == OBJ_ARROW_BUY ? 233 : 234);

return true;
}


2015年4月9日木曜日

[MT4プログラミング]MT4のチャート上に矢印を表示する。

※矢印の位置を調整する記事です。

矢印の縦位置をチャートの高さを見て調整する。
http://mt4program.blogspot.jp/2015/10/blog-post_17.html

本記事は、オブジェクトを使って矢印を出すやり方を記載しています。バッファーを使っても矢印が描画可能です。そちらの記事はこちらから

[MT4インジケータ]値動きが一定を超えたら矢印を出す
http://mt4program.blogspot.jp/2016/04/mt4_12.html


実際取引に使う上で、グラフ上に何かの記号が出せるとよりわかりやすい場合があります。
結局今買うべき?売るべき?様子見?などわかりやすい記号で出せると便利ですよね。きっと。

Arrow.PNG

今回はコードの前にまず使う関数を説明します。
MT4ではObjectCreate関数で、チャート上にオブジェクトを表示できます。

OjbectCreate
bool ObjectCreate(
long chart_id, // チャートID
string object_name, // オブジェクト名
ENUM_OBJECT object_type, // オブジェクト種別
int sub_window, // 0:価格チャート、1~子チャート
datetime time1, // 横軸(時間)の表示位置
double price1, // 縦軸(価格)の表示位置
...
datetime timeN=0, // オブジェクトの種類によって複数、
double priceN=0 // 縦軸横軸を指定します。指定不要の場合省略可能です。
);

作ったオブジェクトは、インジケーター終了後削除する必要があります。
ObjectDelete
bool ObjectDelete(
long chart_id, // チャートID
string object_name // オブジェクト名
);

チャートのプロパティを設定する場合はObjectSetXXXXXを使用します。XXXXXは型が入ります。たとえばIntegerだと次のようになります。
ObjectSetInteger
bool ObjectSetInteger(
long chart_id, // チャートID
string object_name, // オブジェクト名
int index, // プロパティ種別
long value // 値
);
XXXXXの部分と、値引数の型が異なるものが、ObjectSetDouble、ObjectSetInteger、ObjectSetStringの3種類用意されています。
旧MQL4で使用されていたObjectSetも引き続き利用できるようですが将来のことも考えて新しい関数で作成します。

各関数でのchart_id引数は、ChartID()関数で取得した値を設定するのが間違いなさそうです。

上記関数を使って、チャートウィンドウに今買うべきか売るべきかを示すサインを出すプログラムを書きたいと思います。
今回はオブジェクトを表示するサンプルですので、サインそのものはいい加減な値です。

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



ソースコードはこちらです。

メディアンフィルタを適用した移動平均インジケータ

前回の例では、クローズ値のメディアンフィルタ値と移動平均を表示させましたが、基本クローズ値と移動平均を比較するような物なのであまり意味がありません。

そこで、期間の短い移動平均の角度が暴れるのを防ぐ為、元となるデータにメディアンフィルタを適用した後、移動平均を求める短期向け移動平均インジケータを作成してみたいと思います。

先に結果から。
medianMA.PNG
赤線が期間5のクローズ値移動平均、水色線がメディアンフィルタ長3、期間5のクローズ値移動平均です。9:20頃チャートが暴れていますが、単純な移動平均の場合チャートに引っ張られて暴れていますが、メディアンフィルタを適用した移動平均だと暴れず描画されています。メディアンフィルタのノイズ除去効果が表れていることが分かります。
短期の移動平均で角度を見るような場合、効果があるように思われます。

今回は真面目に自分でも使うように移動平均に必要なパラメータ等もきっちり設定しました。

なお前回の記事で作成した、GetMedianValue関数はNavigatorウィンドウ内のIncludeというフォルダにCustomというフォルダを作成した後、MedianFilter.mqhというファイルに中身を丸ごとコピーしました。
getMedianValue.PNG

さて、コードの中身です。
//+------------------------------------------------------------------+
//メディアンフィルタを適用した移動平均
//+------------------------------------------------------------------+
#property copyright "Copyright 2015,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict
#property indicator_chart_window

#include <Custom/MedianFilter.mqh>

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

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

//移動平均
#property indicator_label1  "Avg"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrAqua
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

#property indicator_type2   DRAW_NONE
#property indicator_type3   DRAW_NONE


//入力パラメータ メディアンフィルター長
input int   FilterLength = 3;

//入力パラメータ 期間
input int   AveragingPeriod = 5;

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

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

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

//インジケーター バッファ
double         avgBuffer[];
double         baseValues[];
double         medianBuffer[];

//------------------------------------------------------------------
//初期化
int OnInit()
{
// メディアンフィルタ長は奇数の必要がある。
if( FilterLength % 2 == 0 || FilterLength < 3 )
{
return(INIT_PARAMETERS_INCORRECT);
}

//インジケーターバッファを初期化する。
SetIndexBuffer(0,avgBuffer);
SetIndexBuffer(1,baseValues);
SetIndexBuffer(2,medianBuffer);

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[])            //スプレット
{
//(1)元となる値を計算する。
for( int i = 0; i < (rates_total - prev_calculated); i++ )
{
switch( MaPrice )
{
case PRICE_CLOSE:
baseValues[i] = close[i];
break;
case PRICE_OPEN:
baseValues[i] = open[i];
break;
case PRICE_HIGH:
baseValues[i] = high[i];
break;
case PRICE_LOW:
baseValues[i] = low[i];
break;
case PRICE_MEDIAN:
baseValues[i] = (high[i] + low[i]) / 2;
break;
case PRICE_TYPICAL:
baseValues[i] = (high[i] + low[i] + close[i]) / 3;
break;
case PRICE_WEIGHTED:
baseValues[i] = (high[i] + low[i] + close[i] + close[i]) / 4;
break;
}
}

//要素の0が一番新しいデータ
//(2)要素数-処理済み要素数-1~0のデータを更新する。
for( int i = (rates_total - prev_calculated - 1); i >= 0; i-- )
{
//(3)メディアンフィルタを適用する。
medianBuffer[i] = GetMedianValue(baseValues, rates_total, FilterLength, i);
//(4)移動平均を計算する。
avgBuffer[i] = iMAOnArray(medianBuffer, rates_total, AveragingPeriod, MaShift, MaMethod, i);
}

//中央値インデックスを計算する。
int halfIndex = (FilterLength - (FilterLength % 2)) / 2;

//メディアンフィルタの影響範囲はすべて更新する。
return(rates_total - 1 - halfIndex);
}


まず、今回はバッファを3つ用意しました。
移動平均を計算する元の値として7個値を定義できるため、その計算結果を格納しておくのばbaseValuesです。baseValuesに対して、メディアンフィルタを適用した値を保持しておくのがmedianValuesです。最後にmedianValuesの移動平均をとったものがavgValuesとなります。

コードが長くなってきた為(1)等、コード内のコメントの番号部分について解説するようにします。

��1)では、baseValuesに対して、パラメータで指定された値を保存します。メディアンフィルタは前後の値を参照する為、個別にループを作成して内容をすべて埋めています。
��2)ループを回していますが、前回までと違い今回は古い値からループを回しています。これは、medianBufferの内容が古いものから正しく埋まっていないと移動平均が求められない為です。
��3)メディアンフィルタを適用しています。この部分は前回とほぼ同じですが、対象がbaseValuesとなっています。
��4)移動平均を計算しています。前回はiMAという関数を使いましたが、これは指定された通貨ペアに対して移動平均をとります。今回は、iMAOnArrayというプログラム内の配列に対して移動平均を算出する関数を使用します。
iMAOnArray
使い方はiMAとよく似ていますが、第1引数に移動平均を求める元となる配列、第2引数に配列の大きさを指定します。
double iMAOnArray(
double array[], // 移動平均を求める元になる配列
int total, // 配列の大きさ
int ma_period, // 期間
int ma_shift, // 移動平均を右方向にシフトする数
int ma_method, // averaging method移動平均計算方法
int shift // 移動平均算出位置(最新位置からのシフト量)
);

外部ファイル化したMedianFilter.mqhの内容も掲載しておきます。
//+------------------------------------------------------------------+
//|                                                 MedianFilter.mqh |
//|                                         Copyright 2015,  Daisuke |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property strict

//------------------------------------------------------------------
//メディアンフィルター適用後の値を取得する。
double GetMedianValue(
const double &targetValues[],   //メディアンフィルタを計算する元配列
int valuesCount,                //targetValuesの要素数
int filterLength,               //メディアンフィルタ長
int targetIndex)                //メディアンフィルタ値取得の対象インデックス
{
//ソート用領域を確保
double workValues[];
ArrayResize(workValues, filterLength);

//中央値インデックスを計算する。
int halfIndex = (filterLength - (filterLength % 2)) / 2;

int setIndex = 0 ;
// targetIndexを中央値として前後filterLength/2(小数点切り捨て)分だけ取得する。
for( int i = targetIndex - halfIndex; i <= targetIndex + halfIndex; i++ )
{
// データ端対策
int valueIndex = i ;
if( i < 0 ) valueIndex = 0;
if( i >= valuesCount ) valueIndex = valuesCount - 1;

workValues[setIndex] = targetValues[valueIndex];
setIndex++;
}

//データをソートして、ソート結果の中央値を取得する。
ArraySort(workValues);
double returnValue = workValues[halfIndex];

//領域解放・・・不要? なんとなく職業プログラマ的に確保したものは解放したくなる。
ArrayFree(workValues);

return returnValue;
}


2015年4月8日水曜日

移動平均とメディアンフィルタ比較 インジケーターの作成

前回まで、簡単にプログラムのテンプレートから、チャート描画ができました。

今度は組み込みの指標と自作の指標を比較するチャートを作ってみたいと思います。
サンプルとして移動平均とメディアンフィルターを選択します。

メディアンフィルターは画像処理の世界ではよく知られるアルゴリズムです。取得したい値の前後の値を含めてソートして、その中央値を採用するというアルゴリズムになります。画像処理では粒状ノイズの除去に使用されます。

これをFXの価格チャートに適用したらどうなるか?という実験です。
先に実行結果を表示します。
median.PNG
6:30のバーで、クローズ値ではない値が取られていることが確認できます。このようにメディアンは大きな変換の際ノイズをとれる動作をします。メディアンフィルタの最大の難点は最新の値にはフィルタが適用されていない事で、現在の状態を解析するのには向いていないようです。

移動平均と比較するとトレンド変化に敏感に反応しています(※)ので、短期のトレンド変換判定には使えるかもしれません。
��※)基本クローズ値を結んだだけですので、当たり前なのですが、だましがたくさん発生します。


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

//+------------------------------------------------------------------+
//メディアンフィルタ+移動平均
//+------------------------------------------------------------------+
#property copyright "Copyright 2015,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict
#property indicator_chart_window

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

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

//メディアンライン
#property indicator_label1  "Median"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

//移動平均
#property indicator_label2  "Avg"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrAqua
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2

//入力パラメータ メディアンフィルター長
input int      FilterLength=3;
//入力パラメータ 移動平均を表示するかどうか
input bool     IsShowAvg=true;

//インジケーター バッファ
double         medianBuffer[];
double         avgBuffer[];

//------------------------------------------------------------------
//初期化
int OnInit()
{
// メディアンフィルタ長は奇数の必要がある。
if( FilterLength % 2 == 0 )
{
return(INIT_PARAMETERS_INCORRECT);
}

//インジケーターバッファを初期化する。
SetIndexBuffer(0,medianBuffer);
SetIndexBuffer(1,avgBuffer);

// IsShowAvg == falseの時は、移動平均をグラフ上に表示しない。
if( !IsShowAvg )
{
SetIndexStyle(1, DRAW_NONE);
}

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[])            //スプレット
{
//要素の0が一番新しいデータ
//0~要素数-処理済み要素数-1のデータを更新する。
for( int i = 0; i < (rates_total - prev_calculated); i++ )
{
avgBuffer[i] = iMA(Symbol(), 0, FilterLength, 0, MODE_SMA, PRICE_CLOSE, i);
medianBuffer[i] = GetMedianValue(close, rates_total, FilterLength, i);
}

//中央値インデックスを計算する。
int halfIndex = (FilterLength - (FilterLength % 2)) / 2;
//メディアンフィルタの影響範囲はすべて更新する。
return(rates_total - 1 - halfIndex);
}

//------------------------------------------------------------------
//メディアンフィルター適用後の値を取得する。
double GetMedianValue(const double &targetValues[],   //メディアンフィルタを計算する元配列
int valuesCount,          //targetValuesの要素数
int filterLength,         //メディアンフィルタ長
int targetIndex)          //メディアンフィルタ値取得の対象インデックス
{
//ソート用領域を確保
double workValues[];
ArrayResize(workValues, filterLength);

//中央値インデックスを計算する。
int halfIndex = (filterLength - (filterLength % 2)) / 2;

int setIndex = 0 ;
// targetIndexを中央値として前後filterLength/2(小数点切り捨て)分だけ取得する。
for( int i = targetIndex - halfIndex; i <= targetIndex + halfIndex; i++ )
{
// データ端対策
int valueIndex = i ;
if( i < 0 ) valueIndex = 0;
if( i >= valuesCount ) valueIndex = valuesCount - 1;

workValues[setIndex] = targetValues[valueIndex];
setIndex++;
}

//データをソートして、ソート結果の中央値を取得する。
ArraySort(workValues);
double returnValue = workValues[halfIndex];

//領域解放・・・不要? なんとなく職業プログラマ的に確保したものは解放したくなる。
ArrayFree(workValues);

return returnValue;
}

おお・・自分で言うのもなんですが、一挙にプログラムっぽくなりました(笑)

プログラム解説です。特徴的な部分だけ、ピックアップします。

//入力パラメータ メディアンフィルター長
input int      FilterLength=3;

//入力パラメータ メディアンフィルター長
input bool     IsShowAvg=true;

今回は入力パラメータを指定できるようにしました。
入力パラメータはグローバル変数として定義して、input句が頭につきます。

if( !IsShowAvg )
{
SetIndexStyle(1, DRAW_NONE);
}


SetIndexStyleはプロット情報を上書きするメソッドです。
パラメータにより移動平均を表示しないと選択された場合、SetStyleIndexにてDRAW_NONEを指定することによって表示させないことが可能です。
SetStyleIndex

void SetIndexStyle(
int index, // 対象となるINDEX(0スタート。indicator_type?の?の値から-1になるので注意)
int type, // indicator_type? に相当
int style=EMPTY, // indicator_style? に相当
int width=EMPTY, // indicator_width? に相当
color clr=clrNONE);// indicator_color? に相当
Style以降は省略可能です。


avgBuffer[i] = iMA(Symbol(), 0, FilterLength, 0, MODE_SMA, PRICE_CLOSE, i);

iMAは移動平均をもとめるための組み込み関数です。
iMA

double iMA(
string symbol, // 移動平均を計算する通貨ペア名
int timeframe, // timeframe フレーム時間 0を指定すると挿入されているチャートの値
int ma_period, // 期間
int ma_shift, // 移動平均を右方向にシフトする数
int ma_method, // averaging method移動平均計算方法
int applied_price, // applied price適用価格
int shift); // 移動平均算出位置(最新位置からのシフト量)

double GetMedianValue(const double &targetValues[],   //メディアンフィルタを計算する元配列
int valuesCount,          //targetValuesの要素数
int filterLength,         //メディアンフィルタ長
int targetIndex)          //メディアンフィルタ値取得の対象インデックス

メディアンフィルタ算出部は関数化してみました。
C言語と同じように関数として宣言して、それをプログラム内で呼び出し可能です。

宣言位置は、実際に使用するコードの後ろでも問題なく動作します。




時間足の中央値を描画するサンプル

単純な例として、時間足の中央値を線で結ぶプログラムを書きたいと思います。

まずプロパティにバッファーとプロット情報を追加します。
//バッファーを指定する。
#property indicator_buffers 1

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

//プロット情報を記述する。
#property indicator_label1  "Center"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2


indicator_buffersは内部で使用するバッファーの数になります。C言語でいうところの配列を1個使用するという宣言になります。
indicator_plotsは、indicator_buffersで指定したバッファのうち、いくつを描画に使用するか?という指定になります。indicator_buffersを2、indicator_plotsを1に指定した場合、一つ目のバッファを描画するという指定です。
2015/4/8 修正 MT4では、indicator_plots効かないようです。上記設定をしても、黒い線がチャート上に出てしまいます。

プロット情報は、バッファの内容をどのように描画するか?という宣言となります。
indicator_label? と?の部分には数字が入ります。プロットが複数ある場合、この中身が1,2,3と増えていきます。上から、ラベル名、インディケータ種別、色、描画スタイル、幅となります。タイプとスタイルは、いっぱいありますが、とりあえずは実線を描画するスタイルで進めたいと思います。
種類的には次の通りです。必要に応じて調べるため自分向けにメモも含めて^^;

タイプの一覧
スタイルの一覧

//インジケーター バッファ
double         centerBuffer[];
//------------------------------------------------------------------
//初期化
int OnInit()
{
//インジケーターバッファを初期化する。
SetIndexBuffer(0,centerBuffer);

return(INIT_SUCCEEDED);
}

インジケーター用のバッファをグローバル変数として宣言して、OnInit内で初期化します。

OnInit関数内で、SetIndexBuffer関数を使用してバッファを初期化します。
SetIndexBuffer関数は、第1引数にバッファのインデックス、第2引数に変数として宣言した配列名を指定します。
インデックスは0からindicator_buffers-1の値を指定します。今回は0だけです。
省略可能な第3引数にdata_typeがあります。省略すると、INDICATOR_DATAという形になります。こちらも後から必要に応じて調べる為に自分向けにメモを残して先にすすみます。(^^;;
data_typeの種類

//------------------------------------------------------------------
//計算イベント
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[])            //スプレット
{
//要素の0が一番新しいデータ
//0~要素数-処理済み要素数-1のデータを更新する。
for( int i = 0; i < (rates_total - prev_calculated); i++ )
{
centerBuffer[i] = (low[i] + high[i]) / 2;
}
//処理済みの要素数を戻り値に設定する。
//最新値は常に更新したいため、-1した値を戻り値とする。
return(rates_total - 1);
}

さて、早速中心値をプロットするプログラムを書きたいと思います。
まず、OnCaluculateの引数に分析に必要な値が含まれています。この値を使用して分析を行います。
各引数の役割はソース内のコメント通りですが、volumeだけ執筆時点では役割が分からず。元ドキュメントを見ると実ボリュームという説明になっていますが、実行してみても値が入ってないためよくわかりません。とりあえず後回しにします。

for( int i = 0; i < (rates_total - prev_calculated); i++ )

まず、for文で更新必要な要素数分だけループします。MQLでは時系列配列というインデックス値のカウント方法が異なる配列となっており、OnCalculate関数にわたってくる値は0が最新(チャートでいう一番右端のデータ)の値となっているようです。
��プログラマ的には0が最古、要素数-1が最新とか勝手に考えてしまうので少し混乱します(^^;;; )
 そのため、未処理である最新のデータだけを更新したい場合、0からrates_total-prev_calculated-1のインデックスまで配列を更新します。
centerBuffer[i] = (low[i] + high[i]) / 2;

中心値ですので(安値+高値)/2の値をcenterBuffer[i]に指定します。
return(rates_total - 1);

戻り値は、OnCalculateで処理した要素数を指定します。この値は、次回OnCalculateが呼び出される際に、prev_calculatedへ設定されます。OnCalculateはチャートのtick値が更新されるたびに呼び出されますので、そのたびに最新バー値を更新するため、rates_total-1に指定します。

ということで実行!見事中央値が描画できました!
sample1running.PNG

コード全文
//+------------------------------------------------------------------+
//|                                                      Sample1.mq4 |
//|                                         Copyright 2015,  Daisuke |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict
#property indicator_chart_window

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

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

//プロット情報を記述する。
#property indicator_label1  "Center"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

//インジケーター バッファ
double         centerBuffer[];
//------------------------------------------------------------------
//初期化
int OnInit()
{
//インジケーターバッファを初期化する。
SetIndexBuffer(0,centerBuffer);

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[])            //スプレット
{
//要素の0が一番新しいデータ
//0~要素数-処理済み要素数-1のデータを更新する。
for( int i = 0; i < (rates_total - prev_calculated); i++ )
{
centerBuffer[i] = (low[i] + high[i]) / 2;
}
//処理済みの要素数を戻り値に設定する。
//最新値は常に更新したいため、-1した値を戻り値とする。
return(rates_total - 1);
}


2015年4月7日火曜日

インジケータウィザード 生成コードを見てみよう

ウィザードで生成されたコードを見ていきたいと思います。
//+------------------------------------------------------------------+
//|                                                      Sample1.mq4 |
//|                                         Copyright 2015,  Daisuke |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict
#property indicator_chart_window
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
//--- indicator buffers mapping

//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| 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[])
{
//---

//--- return value of prev_calculated for next call
return(rates_total);
}
//+------------------------------------------------------------------+


C言語がベースとなっている為、//で始まる行はコメント行となります。
MQLをやるならC言語の習得が必須と思われるので、もし今からプログラムを始める方は、MQLとは別にC言語の本も見ておいた方が良いかもしれません。
私も学生時代、明解C言語 入門編などで勉強しました。会社に入ってくる新人にも今でもここから勉強させてます。今は「新」がついたのですよねー。

#property copyright "Copyright 2015,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"

最初の3行はプログラムに関する情報です。
上から、著作権、リンク、バージョン情報となっています。特にプログラムの動作には影響を与えない為、情報が間違っていた時などに修正します。

#property strict

MT4 Build600から大きく変わったMQLコンパイラにて、古い書き方と互換性を保ちつつMQL5のいいとこどりをするためのオプションです。
新旧MQL4コンパイラの違いによると、いろいろ違いがあるようですが、基本つけておいて問題なさそうです。
プログラマ視点からすると、変数スコープがブロック単位になったり、よりちゃんとしたプログラム言語に近づいた感じです。

int OnInit()
{
return(INIT_SUCCEEDED);
}

OnInitは初期化関数となります。旧MQLではint init()ですが、今はint OnInit()に変更になっています。
OnInitの戻り値はINIT_SUCCEEDEDとなっていますが、これ以外にも次の3つが指定できます。
INIT_FAILED:初期化失敗
INIT_PARAMETERS_INCORRECT:パラメータ異常
INIT_AGENT_NOT_SUITABLE:動作環境が適していない。メモリが足りない。OpenCLが無い等。

上記はいずれを指定しても初期化失敗となります。エラー状況に応じて戻り値を変えてくださいとのことのようです。戻り値にINIT_SUCCEEDEDを指定した場合のみ初期化成功となります。
正確には0を指定した場合のみ初期化成功。0以外の場合初期化失敗です。

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[])
{
return(rates_total);
}

OnCalculateはチャートに計算が必要な更新が入った場合呼び出されます。旧MQLではint start()です。
インジケーターでは、ここに具体的な処理を記述することになります。

次回は具体的に、このサンプルにチャート上で描画するコードを埋め込みたいと思います。


2015年4月6日月曜日

新規インジケーターの追加

さっそく、インジケータの作成を行ってみます。
newIndicator0.PNG

メタエディタの左側にあるNavigatorウィンドウから、インジケーターを追加したいフォルダを選択して右クリックメニューを出します。
NewFileという項目を選択すると追加ウィザードが開始されます。

newIndicator1.PNG
追加するファイルを選択します。
今回はカスタムインジケーターを作成する為、「Custom Indicator」を選択します。

newIndicator2.PNG
新しく作るインジケーターの名前や著作権表示、リンク、パラメータを指定します。
ここでは、インジケーターの名前を「Sample1」にします。その他は適当に入力します。
インジケーターの名前は今から作るインジケーターが分かりやすいような名前を入力します。

後からソースコード上でも変更可能です。
パラメーターもなしで登録します。

newIndicator3.PNG
空の関数としてコード上に展開されるイベントを指定します。
OnCalculate:価格更新時に呼び出される関数 (MQL4ではstart()でした)
OnTimer:一定間隔で呼び出される関数。EventSetTimer関数で呼び出し間隔を指定します。
OnChartEvent:チャート上のマウス動作などのイベントで呼び出される関数。

こちらも後からコード上で記述することもできますので、とりあえずデフォルトのOnCalculateのみ指定します。
MQL5 イベント

newIndicator4.png
最後は、チャートに関する項目です。

「Indicator in separate window」にチェックを入れると、チャートに下部に分離して表示するインジケーターになります。MinimumやMaximumは分離して表示するインジケーターの最大最小値です。

Plotsはチャート上に描画する項目を指定します。
ここも後で指定可能ですので省略します。

ここまででウィザード完了です。Sample1.mq4というファイルが作成されます。