Berと価格データ P203
MQL4では、バーデータにClose[]やTime[]などの事前定義された配列と、現在の価格にアクセスするために事前定義された変数BidとAskを使用します。MQL5にはこれらの事前定義された配列と変数がありませんが、このデータにアクセスするための簡単な方法を作成できます。MQL5で足と価格のデータにアクセスする様々な方法を調べ、価格の操作を簡単にするクラスと関数を作成します。
現在の価格へのアクセス P203
この本の前半で、SymbolInfoDouble()関数を使用して現在の買値と売値を取得しました。Symbol…()関数では、情報を取得するシンボルと、ENUM_SYMBOL_INFO…列挙の1つからの値を指定する必要があります。SymbolInfoDouble()を使用して現在の買値と売値を取得する方法は次の通りです。
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
アスク変数とビッド変数は、現在のチャートシンボルの現在のビッド価格とアスク価格を保持します。
しかし、正しいENUM_SYMBOL_INFO…識別子とともに、どのSymbolInfo…()関数を使用するかを覚えておくのは面倒です。現在の買値と売値を取得する2つのクイック関数を作成します。
double Ask(string pSymbol = NULL)
{
if(pSymbol == NULL) pSymbol = _Symbol;
return(SymbolInfoDouble(pSymbol, SYMBOL_ASK));
}
double Bid(string pSymbol = NULL)
{
if(pSymbol == NULL) pSymbol = _Symbol;
return(SymbolInfoDouble(pSymbol, SYMBOL_BID));
}
どちらの関数にも、デフォルト値がNULLの1つのパラメータpSymbolが含まれています。定数NULLは空の文字列を参照します。pSymbolパラメータに銘柄が指定されていない場合、関数は現在のチャートシンボル(Symbol)を使用します。指定されたシンボルがSymbolInfoDouble()関数に渡され、結果がプログラムに返されます。
前のコード例を新しい関数で書き直してみましょう。
double ask = Ask();
double bid = Bid();
どちらの関数も、現在のチャートシンボルの現在の買値または売値を返します。
MqlTick構造体 P204
現在の価格を取得する別の方法は、SymbolInfoTick()関数とMqlTick構造体型のオブジェクトを使用することです。MqlTickは、現在のビッド、アスク、時間、ボリューム(出来高)および最終取引価格を保持する変数を含む構造体です。
struct MqlTick
{
datetime time; //価格が最後に更新された時刻
double bid; //現在のビッド価格
double ask; //現在のアスク価格
double last; //最後の取引の価格
ulong volume; //現在の最終価格の出来高
}
上記の情報はMQL5リファレンスからのものです。MqlTick構造体を使用して現在の価格を取得するには、まずMqlTickを型として使用してオブジェクトを作成します。次に、オブジェクトをSymbolInfoTick()関数に渡します。MQL5リファレンスからのSymbolInfoTick()の関数定義は次のとおりです。
bool SymbolInfoTick(string symbol, MqlTick& tick);
Tickパラメータは、参照によって渡されるMqlTickオブジェクトです。オブジェクトのメンバー変数には、最新の価格データが入力されます。以下は、SymbolInfoTick()とMqlTickを使用して現在の価格を取得する方法の例です。
MqlTick price;
SymbolInfoTick(_Symbol, price);
double ask = price.ask;
double bid = price.bid;
datetime time = price.time;
タイプとしてMqlTickを使用して、priceという名前のオブジェクトを宣言します。価格オブジェクトをSymbolInfoTick()関数に渡し、最初のパラメータに現在のチャートシンボルを指定します。この関数は、メンバー変数に現在の価格と時間の情報が格納された価格オブジェクトを返します。以下の例は、価格オブジェクトのメンバー変数を使用して取得された、現在の売値と買値、および最新のサーバー時刻を示しています。
SymbolInfoTick()を使用して現在の価格情報を取得するかどうかは、あなた次第です。ただし、この章の前半で定義したBid()およびAsk()関数のほうがはるかに簡単です。
Berデータ P205
章の冒頭で述べたように、MQL4にはチャートの各バーの始値、終値、高値、安値、時間、出来高のデータを保持する定義済みの配列があります。MQL5では、これらの配列を自分で作成して埋める必要があります。バーデータを取得するために使用できるCopy…()関数がいくつかあります。CopyRates()から始めましょう。
CopyRates()とMqlRates構造体 P205
MqlRatesは、各バーの価格、時間、出来高、およびスプレッド情報を保持する変数を含む構造体です。配列型として使用されます。MQL5リファレンスからのMqlRatesの構造定義は次のとおりです。
struct MqlRates
{
datetime time; //期間開始時間
double open; //始値
double high; //期間の最高値
double low; //期間の最低値
double close; //終値
long tick_volume; //ティックボリューム
int spread; //拡大
long real_volume; //取引量
}
MqlRates構造体を使用するには、MqlRates型の配列オブジェクトを宣言し、そのオブジェクトをCopyRates()関数に渡して、配列にデータを入力します。CopyRates()関数にはいくつかのバリエーションがありますが、コピーするデータの開始位置とバーを指定する必要がある最初のものだけに注目します。
int CopyRates(
string symbol_name, //シンボル名
ENUM_TIMEFRAMES timeframe, //期間
int start_pos, //開始位置
int count, //コピーするデータ数
MqlRates ratea_array[] //コピーするターゲット配列
);
symbol_nameパラメータは使用するチャートシンボル、timeframeは使用するチャート期間、start_posは開始バーのインデックス(0が最新のバー)、countは配列にコピーするバーの数、retes_array[]はMqlRatesオブジェクトです。
MqlRatesとCopyRates()を使用してバーデータを配列オブジェクトにコピーする方法を示しましょう。通常、最新のバーのデータのみが必要です。3つが適切な数です。
MqlRates bar[];
ArraySetAsSeries(bar, true);
CopyRates(_Symbol, _Period, 0, 3, bar);
double open = bar[1].open;
double close = bar[1].close;
まず、型としてMqlRatesを使用して、配列オブジェクトbar[]を宣言します。次に、ArraySetAsSeries()関数を使用して、価格シリーズのbar[]配列にインデックスを付けます。系列配列の場合、最新の要素のインデックスは0です。配列のインデックスを増やすと、時間が戻ります。非直列配列はこれの反対になります。価格または指標データを配列にコピーする前に、常にArraySetAsSeries()関数を使用して配列をシリーズとして設定することが重要です。
次に、CopyRates()関数を呼び出します。現在のチャート記号(Symbol)と期間(Period)を使用します。インデックス0、または最新のバーからコピーを開始します。3バー分のデータをコピーします。最後のパラメータはバーデータを保持するbar[]配列の名前です。
バーデータにアクセスするには、括弧内の適切なインデックスでbar[]配列を参照します。次に、ドット(.)演算子を使用して、配列オブジェクトのメンバー変数にアクセスします。たとえば、最後のバーの終値を取得するには、bar[1].closeを使用します。
これを単純化するために、バーのデータを取得するためのクラスを作成することができます。クラスにCbarsという名前を付けます。このクラスと、この章で作成するその他の関数は、\MQL5\Include\Mql5Book\フォルダにあるPrice.mqhという名前の新しいインクルードファイルに入ります。Cbarsのクラス宣言は次のとおりです。
class Cbars
{
public:
MqlRates bar[];
Cbars(void);
void Update(string pSymbol, ENUM_TIMEFRAMES pPeriod);
};
CBarsクラスのメンバーはすべて公開されます。Bar[]配列は価格データを保持します。Cbars(void)関数はクラスコンストラクターであり、オブジェクトが作成されるたびに自動的に実行される関数です。Cbarsクラスのコンストラクタは次のとおりです。
CBars : : Cbars(void)
{
ArraySetAsSeries(bar, true);
}
CBarsクラスコンストラクターは、bar[]配列を系列配列として設定します。これは、クラスに基づいてオブジェクトを作成するたびに自動的に行われます。Update()CopyRates()関数を呼び出し、bar[]配列にデータを入力します。Cbar : : Update()の関数宣言は次のとおりです。
#define MAX_BARS 100
void CBars : : Update(string pSymbol, ENUM_TIMEFRAMES pPeriod)
{
CopyRates(pSymbol, pPeriod, 0, MAX_BARS, bar);
}
プログラムのOnTick()イベントハンドラで少なくとも1回、Update()関数を呼び出して、bar[]配列に最新のデータを入力します。MAX_BARS定数は、Update()関数を呼び出すたびにコピーするデータに相当するバーの数を定義します。これを100バーに設定しました。
次に、価格を取得するための使いやすい関数をいくつか追加する必要があります。以下は、指定されたバーの終値を取得する関数です。
double CBar : : Close(int pShift = 0)
{
return(bar[pShift].close);
}
この関数は、指定されたインデックス値のbar[].closeの値を取得するだけです。pShiftパラメータのデフォルト値は0であるため、関数にパラメータが渡されない場合、現在のバーの価格が取得されます。
高値、安値、オープン、時間、出来高の情報を取得する関数がいくつかあります。必要なすべての関数を含む完全なCBars関数宣言を次に示します。
class Cbars
{
public:
MqlRates bar[];
CBars(void)
void Update(string pSymbol, ENUM_TIMEFRAMES pPeriod);
double Close(int pShift);
double High(int pShift);
double Low(int pShift);
double Open(int pShift);
datetime Time(int pShift);
long TickVolume(int pShift);
long Volume(int pShift);
}
関数名はMqlRates構造体の各変数に対応していることに注意してください。EAでCBarsクラスを使用する方法を示しましょう。まず、CBarsクラスに基づいてオブジェクトを作成する必要があります。次に、価格データにアクセスする前にUpdate()関数を呼び出す必要があります。最後に、価格取得関数またはbar[]配列を使用して価格を取得します。
//インクルードディレクティブ
#include <Mql5Book\Price.mqh>
CBars Price;
//OnTick()イベントハンドラ
Price.Update(_Symbol, _Period);
double close = Price.Close();
double alsoClose = Price.bar[0].close; // close変数と同じ値
Price.mqhファイルを含め、CB;arsクラスに基づいてPriceという名前のオブジェクトを作成します。OnTick()イベントハンドラでは、価格データにアクセスする前に、Price.Update()関数を呼び出して、現在のチャートシンボルと期間のバーデータを取得します。
次に、価格データにアクセスする2つの方法を示します。最も簡単な方法は、CBarsクラスで定義した価格取得関数を使用することです。close変数には現在のバーの終値が含まれ、Price.Close()関数を使用して取得されます。alsoClose変数には、現在のバーの終値も含まれており、closeメンバー変数でbar[]配列を使用して取得されます。
ファイル\MQL5\Experts\Mql5Book\Simple ExpertAdvisor with Function.mq5はCbars関数を使用して終値にアクセスするように更新されました。
//インクルードディレクティブ
#include <Mql5Book\Price.mqh>
CBars Price;
//OnTick()イベントハンドラ
void OnTick()
{
//移動平均
double ma[];
ArraySetAsSeries(ma, true);
int maHandle = iMA(_Symbol, 0, MAPeriod, MODE_LWMA, 0, PRICE_CLOSE);
CopyBuffer(maHandle, 0, 0, 1, ma);
//価格を更新
Price.Update(_Symbol, _Period);
//現在位置情報
long positionType = PositionType();
//買い成り行き注文を開く
if(Price.Close() > ma[0] && glBuyPlaced == false && positionType != POSITION_TYPE_BUY)
{
//…
}
その他のCopy…()関数 P209
単一のデータ系列を配列にコピーする必要がある場合は、代わりに次の関数のいずれかを使用できます。
- double CopyClose() - 終値のみをコピーします。
- double CopyOpen() - 始値のみをコピーします。
- double CopyHigh() - 高値のみをコピーします。
- double CopyLow() - 安値のみをコピーします。
- datetime CopyTime() - 各バーのタイムスタンプをコピーします。
- long CopyTickVolume() - 各バーのティックボリュームをコピーします。
- long CopyRealVolume() - 各バーの取引量をコピーします。
- int CopySpread() - 各バーのスプレッド値をコピーします。
これらはCopyRates()関数と同様に機能します。まず、適切な型の配列を宣言します。各関数に必要な型は、関数名の前に記載されています。次に、ArraySetAsSeries()を使用して系列配列として設定します。次に、適切なCopy…()関数を使用してデータを配列にコピーします。
CopyClose()関数を使用してデモを行います。CopyClose()の関数定義は次のとおりです。
int CopyClose(
string symbol_name, //シンボル名
ENUM_TIMEFRAMES timeframe, //限目
int start_pos, //開始位置
int count, //コピーするデータ数
double close_array[] //コピーするターゲット配列
);
パラメータはCopyRates()とほぼ同じです。この関数には、開始日と終了日も指定できるいくつかのバリエーションがあります。これらは、MQL5リファレンスの時系列とインジケータアクセスで確認できます。CopyClose()関数の使用例を次に示します。
double close[];
ArraySetAsSeries(close, true);
CopyClose(_Symbol, _Period, 0, 3, close);
double currentClose = close[0];
終値を保持するclose[]という名前のdouble型の配列を宣言します。次に、ArraySetAsSeries()を使用して系列配列として設定します。最後に、CopyClose()関数は3つのバーに相当するデータをclose[]配列のゼロインデックスを参照します。
他のCopy…()関数は、配列の型が異なることを除いて、同じように機能します。詳細については、MQL5リファレンスを参照してください。単一の価格シリーズのみが必要な場合は、これらの関数が適切に機能する可能性があります。この本の残りの部分では、前のセクションで作成したCBarsクラスを使用します。
最高価格と最低価格 P210
価格系列の最高値または最低値を決定する必要する必要がある場合があります。たとえば、最後のXバーの最安値に買い注文のストップロスを配置することができます。MQL4では、関数iHighest()とiLowest()がこれらの役割を果たしました。MQL5では、もう少し作業を行う必要があります。
価格データがすでに配列にコピーされていると仮定すると、ArrayMaximum()およびArrayMinimum()関数を使用して、価格シリーズの最高値または最低値を見つけることができます。最後の10バーの最低値を見つける方法は次のとおりです。
double low[];
ArraySetAsSeries(low, true);
CopyLow(_Symbol, _Period, 0, 100, low);
int minIdx = ArrayMinimum(low, 0, 10);
double lowest = low[minIdx];
まず、価格データを保持するlow[]配列をシリーズとして設定し、CopyLow()関数を使用して、現在のチャートシンボルと期間の100本の安値データをlow[]配列にコピーします。
ArrayMinimum()関数は、検索する配列を最初のパラメータとして受け取ります。2番目のパラメータは開始位置で、3番目は検索するバーの数です。現在の足から始めて、10本の足をさかのぼって最安値を見つけます。ArrayMinimum()関数は、安値が最も低いバーのインデックス値を返します。この値をminIdx変数に保存します。low[minIdx]を呼び出すと、ArrayMinimum()関数によって特定された最安値が返されます。
任意の1次元数値配列でArrayMinimum()またはArrayMaximum()を使用できます。もっとも一般的には、最後のXバーの最高値と最低値を検索します。指定されたバー範囲の最高値または最低値を返すいくつかの単純な関数を作成できます。
double HighestHigh(string pSymbol, ENUM_TIMEFRAMES pPeriod, int pBars, int pStart = 0)
{
double high[];
ArraySetAsSeries(high, true);
int copied = CopyHigh(pSymbol, pPeriod, pStart, pBars, high);
if(copied == -1) return(copied);
int maxIdx = ArrayMaximum(high);
double highest = high[maxIdx];
return(highest);
}
この関数は、指定されたバー範囲の最高値を返します。pSymbolパラメータは使用するシンボルでpPeriodは使用するチャート期間です。pBarsパラメータは検索するバーの数で、pStartは開始位置です。pStartのデフォルト値は0で、現在のバーです。
high[]配列を宣言してシリーズとして設定し、CopyHigh()関数を使用して高値を配列にコピーします。pStartパラメータはデータのコピーを開始する場所を決定し、pBarsパラメータは配列内のデータ量を決定します。CopyHigh()関数の後の行に注意してください。コピーが何らかの理由で失敗した場合、エラーを示すために呼び出しプログラムに-1を返します。
ArrayMaximum()関数を呼び出すときは、検索する必要があるすべてのデータが既に含まれているため、配列名を指定するだけで済みます。最高値を含む配列のインデックスは、maxIdx変数に保存されます。次に、high[maxIdx]を使用して最高値を取得し、それを呼び出しプログラムに返します。
上記のHighestHigh()関数と同じように動作するLowestLow()関数もあります。両方の関数は、\MQL5\Include\Mql5Book\Price.mqhインクルードファイルで表示できます。
価格ベースのトレーディングシグナル P212
バーの終値、高値、安値を注文開始条件の一部として頻繁に使用したり、ストップロス、テイクプロフィット、未決注文価格を計算したりします。通常、最新の2つのバーの価格を使用します。
たとえば、指標線に対して終値をチェックする必要があるトレーディングシステムがある場合、最近閉じたバーの価格を取得し、同じバーの指標値と比較します。この本の前半で作成した単純なEAは、現在のバーの終値を現在の移動平均値と比較しました。
if(close[0] > ma[0] && glBuyPlaced == false
&& (positionType != POSITION_TYPE_BUY || openPosition == false))
{
//買いポジションを開く
}
しかし、注文を開く前に、バーが閉じるまで待ちたいと思うかもしれません。その場合、前のバーの移動平均と終値を確認する必要があります。現在のバーの配列インデックスは0であるため、前のバーのインデックスは1になります。
if(close[1] > ma[1] && glBuyPlaced == false
&& (positionType != POSITION_TYPE_BUY || openPosition == false))
{
//買いポジションを開く
}
終値が最後のバー内で移動平均線を超えることを確認することで、注文開始条件をさらに明確に定義できます。これを行う1つの方法は、前の足の終値が移動平均値を下回っていたかどうかを確認することです。
if(close[1] > ma[1] && close[2] <= ma[2]
&& (positionType != POSITION_TYPE_BUY || openPosition == false))
{
//買いポジションを開く
}
買いポジションは、終値が最新バーの移動平均よりも大きく、終値が前のバーの移動平均よりも小さい場合にのみオープンするため、glBuyPlaced変数の値を確認する必要がなくなりました。これは、強気または弱気の取引シグナルを常に示す取引条件がある場合に最適です。
ローソク足のパターン P213
価格データのもう1つの用途は、キャンドルパターンの検出です。多くのトレーダーは、ドジ、巻き込み、ハラミ、ハンマー/流れ星などの日本のローソク足パターンを使用します。価格データを使用して、チャート上のこれらのパターンを識別します。
ドジ =価格のトップかボトムが同じ値になったことを示す。
巻き込み=前のローソクが新しいローソクに完全に巻き込まれていることを示す。
ハラミ =前のローソクが新しいローソクの一部をカバーすることを示す。
ハンマー=価格が急激に下落した後に急激に上昇したことを示す。
流れ星 =価格が急激に上昇した後に急激に下落したことを示す。
図16.1-強気の巻き込みローソク
たとえば、巻き込みパターンを考えてみましょう。巻き込みパターンは、ローソクの本体が前のローソクの本体よりも長く、方向が逆になる反転パターンです。たとえば、下降トレンドの底にローソクが巻き込んでいるのを見ることができます。前のローソクは弱気ですが、最新のローソクは強気になります。最新のローソクの始値は、前のローソクの終値とほぼ同じになり、最新のローソクの終値は、前のローソクの始値より上になります。
ローソクパターンを取引するときは、予想されるトレンドの方向にある確認ローソクを探します。強気の巻き込み反転パターンの場合、強気の巻き込みローソクに続く強気のローソクを探しています。
これを取引条件として表現してみましょう。close[]とopen[]の2つの配列に価格データが格納されていると仮定すると、EAで強気の巻き込みパターンを特定する方法は次のとおりです。
if(close[3] < open[3] && close[2] > open[3] && close[1] > open[1])
{
Print(“強気の巻き込みパターンが検出されました。”);
}
3つ前の弱気なローソクから開始します。close[3] < open[3]という条件は、ローソクが弱気であることを意味します。つまり、終値が始値よりも低かったということです。次に巻き込みローソクを確認します。ローソク足の始値は、通常、前のローソク足の終値と同じ(または非常に近い)ため、巻き込みローソク足の始値をチェックする必要はありません。チェックする重要なことは、終値が前のバーの始値よりも大きいか、またはclose[2] > open[3]であるかどうかです。もしそうなら、私たちは前のローソクが新しいローソクに完全に巻き込まれている巻き込みローソクを持っています。
最後に、close[1] > open[1]の場合、最新のローソクは強気です。3つのローソク足すべてが条件に一致する場合、強気の巻き込みパターンがチャートで検出されたことを示すメッセージがログに出力されます。もちろん、買いポジションを開くこともできます。
バーの始値と終値を比較することで、ろうそくが強気か弱気かを判断できます。連続するろうそくの始値、高値、安値、終値を調べることで、ろうそくパターンを検出することもできます。