チャート操作関数を使って全チャートを一括操作する方法
「OnChartEvent」でひな形を作成
この記事では、チャート操作関数を使って全チャートを一括操作する方法を紹介します。まずはファイルのひな形を作成しましょう。ファイルの新規作成で「カスタムインディケータ」を選択し、ファイル名を「SyncDemo」とします。
「カスタムインディケータのイベントハンドラ」の画面で「OnChartEvent」にチェックを入れて進み、そのまま「完了」をクリックすれば、ひな形の完成です。
ローソク足のサイズを同期
最初に、ローソク足のサイズを同期させるロジックを組みましょう。チャートの拡大・縮小をしたときに処理を実行するようにしたいので、次のif文を記述し、元となるチャートのスケールを取得します。
if (id == CHARTEVENT_CHART_CHANGE){
int scale = (int)ChartGetInteger(0, CHART_SCALE);
そして、複数あるチャートのうちの最初のチャートIDを取得する「ChartFirst」関数を定義して、while文の中で繰り返し処理を実行させるようにします。
long chart_id = ChartFirst();
while (true) {
while文で繰り返す処理は、「該当するチャートのチャートIDが、操作したチャートのスケールと違った場合、そのチャートを表示中のスケールに合わせる」という内容です。元となるチャートへの操作を防ぐため、最初に「if (Chart_id != ChartID())」というif文を記述します。これを加えることで、チャートIDが一致した場合には処理をしません。また、指定したチャートを更新するための「ChartRedraw」関数も使用します。
if (chart_id != ChartID()) {
if (ChartGetInteger(chart_id, CHART_SCALE) != scale) {
ChartSetInteger(chart_id, CHART_SCALE, scale);
}
ChartRedraw(chart_id);
}
処理を実行した後、取得したチャートIDを「ChartNext」関数で次に送ります。もし次のチャートが見つからなかった場合、この関数は「-1」を返すので、そうなったときに繰り返し処理を抜けるよう「break;
」を加えます。
cfhart_id = ChartNext(chart_id);
if (chart_id < 0) break;
これをコンパイルして二つのチャートにセットし、最初のチャートのローソク足のサイズを変更すると、それと同期してもう一方のチャートのローソク足もサイズ変更されます。
チャートの時間位置を同期
続いて、チャートをクリックしたとき(マウスのボタンをクリックして指を離したとき)にチャートの時間位置を同期させます。
ローソク足のサイズを同期するコードを流用し、今度は「CHARTEVENT_CLICK」を使ってチャートをクリックしたときの処理を定義します。チャートの自動スクロールがオフの場合のみ同期させるようにしたいので、「CHART_AUTOSCROLL」を使って次のように記述します。
if (id == CHARTEVENT_CLICK) {
bool auto = (bool)ChartGetInteger(0, CHART_AUTOSCROLL);
続いて、時間の情報を取得するコードを追加します。「CHART_FIRST_VISIBLE_BAR」はチャート上に表示されている左端のバー番号、「CHART_WIDTH_IN_BARS」はチャート幅全体のバー本数を表します。もしbarR(右端のバー番号)がマイナスの場合は「0」とし、「datetime」関数で時間に変換します。
int barR = int(ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR) - ChartGetInteger(0, CHART_WIDTH_IN_BARS));
if (barR < 0) barR = 0;
datetime timeR = Time[barR];
また、while文で繰り返す処理も変更します。操作する先のチャートに対しては「ChartGetInteger」ではなく、「ChartSetInteger」を使います。そして変換した時間にチャートのバーを同期させます。
if (chart_id != ChartID() && !auto) {
ChartSetInteger(chart_id, CHART_AUTOSCROLL, false);
int bar = iBarShift(ChartSymbol(chart_id), ChartPeriod(chart_id), timeR);
ここで指定したチャートをの時間を移動させる「ChartNavigate」関数を使います。チャートの右側を合わせるので、チャートのポジションは「CHART_END」とします。このとき「-bar - 2」とすると、ちょうど合ういます。
ChartNavigate(chart_id, CHART_END, -bar - 2);
ChartRedraw(chart_id);
これをコンパイルすると、元となるチャートをスクロールしたときにもう一方のチャートも同期して動くことが分かります。
右端シフトによる時間位置のズレを解消
最後に、チャートの右端をシフトした際の位置のズレを解消しましょう。「int bar = iBarShift(ChartSymbol(chart_id), ChartPeriod(chart_id), timeR);」の下に次のコードを加えます。チャートのシフトサイズ(CHART_SHIFT_SIZE)は10~50%の設定が可能です。
シフトサイズとは、チャートの右端シフトをした際にどれくらいの余白を作るかの割合を示し、50%に設定するとチャートの中央までシフトします。このシフトのサイズと、チャートの全幅のローソク足の本数から、シフト量がローソク足何本に相当するかを計算します。全幅の本数にチャートのシフトサイズを掛けて、それを100で割ってint型(整数)に変換します。
int shift = 0;
if (ChartGetInteger(chart_id, CHART_SHIFT)) {
shift = int(ChartGetInteger(chart_id, CHART_WIDTH_IN_BARS) * ChartGetDouble(chart_id, CHART_SHIFT_SIZE) / 100);
}
また、「ChartNavigate(chart_id, CHART_END, -bar - 2);」の一文に「- shift」を追加します。
ChartNavigate(chart_id, CHART_END, -bar – 2 - shift);
これでコンパイルすれば、シフトしていても正しく同期されます。時間足の異なる4種類のチャートでシフトしてみると、各時間足のチャートがそれぞれ適切な時間で同期されていることが分かります。
ソースコード
今回、作成したソースコードは下記の通りです。
//+------------------------------------------------------------------+
//| SyncDemo.mq4 |
//| Copyright 2021, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
#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);
}
//+------------------------------------------------------------------+
//| ChartEvent function |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
if (id == CHARTEVENT_CHART_CHANGE) {
int scale = (int)ChartGetInteger(0, CHART_SCALE);
long chart_id = ChartFirst();
while (true) {
if (chart_id != ChartID()) {
if (ChartGetInteger(chart_id, CHART_SCALE) != scale) {
ChartSetInteger(chart_id, CHART_SCALE, scale);
}
ChartRedraw(chart_id);
}
chart_id = ChartNext(chart_id);
if (chart_id < 0) break;
}
}
if (id == CHARTEVENT_CLICK) {
bool auto = (bool)ChartGetInteger(0, CHART_AUTOSCROLL);
int barR = int(ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR) - ChartGetInteger(0, CHART_WIDTH_IN_BARS));
if (barR < 0) barR = 0;
datetime timeR = Time[barR];
long chart_id = ChartFirst();
while (true) {
if (chart_id != ChartID() && !auto) {
ChartSetInteger(chart_id, CHART_AUTOSCROLL, false);
int bar = iBarShift(ChartSymbol(chart_id), ChartPeriod(chart_id), timeR);
int shift = 0;
if (ChartGetInteger(chart_id, CHART_SHIFT)) {
shift = int(ChartGetInteger(chart_id, CHART_WIDTH_IN_BARS) * ChartGetDouble(chart_id, CHART_SHIFT_SIZE) / 100);
}
ChartNavigate(chart_id, CHART_END, -bar - 2 - shift);
ChartRedraw(chart_id);
}
chart_id = ChartNext(chart_id);
if (chart_id < 0) break;
}
}
}
//+------------------------------------------------------------------+
本記事の監修者・HT FX
2013年にFXを開始し、その後専業トレーダーへ。2014年からMT4/MT5のカスタムインジケーターの開発に取り組む。ブログでは100本を超えるインジケーターを無料公開。投資スタイルは自作の秒足インジケーターを利用したスキャルピング。
EA(自動売買)を学びたい方へオススメコンテンツ
OANDAではEA(自動売買)を稼働するプラットフォームMT4/MT5の基本的な使い方について、画像や動画付きで詳しく解説しています。MT4/MT5のインストールからEAの設定方法までを詳しく解説しているので、初心者の方でもスムーズにEA運用を始めることが可能です。またOANDAの口座をお持ちであれば、独自開発したオリジナルインジケーターを無料で利用することもできます。EA運用をお考えであれば、ぜひ口座開設をご検討ください。
本ホームページに掲載されている事項は、投資判断の参考となる情報の提供を目的としたものであり、投資の勧誘を目的としたものではありません。投資方針、投資タイミング等は、ご自身の責任において判断してください。本サービスの情報に基づいて行った取引のいかなる損失についても、当社は一切の責を負いかねますのでご了承ください。また、当社は、当該情報の正確性および完全性を保証または約束するものでなく、今後、予告なしに内容を変更または廃止する場合があります。なお、当該情報の欠落・誤謬等につきましてもその責を負いかねますのでご了承ください。