MT5でローソク足確定までの残り時間表示ツールを作成する【MQLプログラミングの基礎】
1.ファイルの新規作成
この記事では、チャートのローソク足が確定するまでの残り時間を表示するツールを作成します。
※この記事はMQL5の内容です。MQL4に対応した類似記事は以下を参照してください。
「MQLプログラミング言語のOnTimer関数を利用するための準備」
MetaEditorの「新規作成」アイコンをクリックしてMQLウィザードを開始します。

「カスタムインディケータ」を選択して「次へ」をクリック。

ファイル名は「RemainingTime.mq5」とします。
表示する時間用の下記パラメーターを追加し、「次へ」をクリック。
| 名前 | タイプ | 初期値 |
|---|---|---|
| POSX | int | 5 |
| POSY | int | 20 |
| SIZE | int | 16 |
| CLR | color | White |
| FONT | string | Arial Bold |

イベントハンドラは一番上のOnCalculate(…,open,high,low,close)とOnTimerを選択して「次へ」をクリック。
残り時間の計算は、OnTimer関数内で実施します。

「カスタムインディケータの描画プロパティ」では、特に何も追加せず「完了」をクリックすれば、ファイルのベースが作成されます。
今回のファイルでは、インジケーターバッファを利用しないので、「#property indicator_chart_window」の行の下へ、下記を追加します。
#property indicator_buffers 0
#property indicator_plots 0
残り時間を表示する基準位置(チャートの四隅から選択)を決めるためのパラメーターCORNERを「input int POSX=5;」の上に追記します。
input ENUM_BASE_CORNER CORNER = CORNER_RIGHT_UPPER; // 基準位置
インジケーター設定でパラメーター(インプットタブ)を開いたときに、変数名が日本語表示されるよう、各パラメーターの後ろに「 // 」と名称を追記します。
input int POSX = 5; // 位置X
input int POSY = 20; // 位置Y
input int SIZE = 16; // 文字サイズ
input color CLR = clrWhite; // 色
input string FONT = "Arial Bold"; // フォント
2.残り時間表示用ラベルを作成
ラベルオブジェクトを利用してチャート上へ残り時間を表示します。
MetaEditorメニューの「ヘルプ」から「MQL5リファレンス」を開いて、「目次」タブを選択し、「標準的な定数、列挙と構造体」→「オブジェクト定数」→「オブジェクト型」にある「OBJ_LABEL」を選択してください。

OBJ_LABEL内にある「//| ラベルを作成する」以下にあるLabelCreate関数をコピーして、今回のプログラムの最下部へ貼り付けます。
――――― コピーして貼り付けするコード(ここから) ―――――
//+------------------------------------------------------------------+
//| ラベルを作成する |
//+------------------------------------------------------------------+
bool LabelCreate(const long chart_ID=0, // チャート識別子
const string name="Label", // ラベル名
const int sub_window=0, // サブウィンドウ番号
const int x=0, // X 座標
const int y=0, // Y 座標
const ENUM_BASE_CORNER corner=CORNER_LEFT_UPPER, // アンカーに使用されるチャートのコーナー
const string text="Label", // テキスト
const string font="Arial", // フォント
const int font_size=10, // フォントサイズ
const color clr=clrRed, // 色
const double angle=0.0, // テキストの傾斜
const ENUM_ANCHOR_POINT anchor=ANCHOR_LEFT_UPPER, // アンカーの種類
const bool back=false, // 背景で表示する
const bool selection=false, // 強調表示して移動
const bool hidden=true, // オブジェクトリストに隠す
const long z_order=0) // マウスクリックの優先順位
{
//--- エラー値をリセットする
ResetLastError();
//--- ラベルを作成する
if(!ObjectCreate(chart_ID,name,OBJ_LABEL,sub_window,0,0))
{
Print(__FUNCTION__,
": failed to create text label! Error code = ",GetLastError());
return(false);
}
//--- ラベル座標を設定する
ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x);
ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y);
//--- ポイント座標が相対的に定義されているチャートのコーナーを設定
ObjectSetInteger(chart_ID,name,OBJPROP_CORNER,corner);
//--- テキストを設定する
ObjectSetString(chart_ID,name,OBJPROP_TEXT,text);
//--- テキストフォントを設定する
ObjectSetString(chart_ID,name,OBJPROP_FONT,font);
//--- フォントサイズを設定する
ObjectSetInteger(chart_ID,name,OBJPROP_FONTSIZE,font_size);
//--- テキストの傾斜を設定する
ObjectSetDouble(chart_ID,name,OBJPROP_ANGLE,angle);
//--- アンカーの種類を設定
ObjectSetInteger(chart_ID,name,OBJPROP_ANCHOR,anchor);
//--- 色を設定
ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr);
//--- 前景(false)または背景(true)に表示
ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back);
//--- マウスでラベルを移動させるモードを有効(true)か無効(false)にする
ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection);
ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection);
//--- オブジェクトリストのグラフィックオブジェクトを非表示(true)か表示(false)にする
ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden);
//--- チャートのマウスクリックのイベントを受信するための優先順位を設定する
ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order);
//--- 実行成功
return(true);
}
//+------------------------------------------------------------------+
――――― コピーして貼り付けするコード(ここまで) ―――――
チャートの基準位置(四隅)を決めるためのパラメーターCORNERに合わせて、文字の基準位置を変更するため、OnInit関数内に下記を追記します。
ENUM_ANCHOR_POINT anchor = ANCHOR_LEFT_UPPER;
switch (CORNER) {
case CORNER_LEFT_LOWER :
anchor = ANCHOR_LEFT_LOWER;
break;
case CORNER_RIGHT_UPPER :
anchor = ANCHOR_RIGHT_UPPER;
break;
case CORNER_RIGHT_LOWER :
anchor = ANCHOR_RIGHT_LOWER;
break;
}
OnInit関数内に下記を追記し、残り時間表示用の文字列を表示します。
LabelCreate(0, "RemainingTime", 0, POSX, POSY, CORNER, "0", FONT, SIZE, CLR, 0, anchor, false, false);
本ツールをチャートから削除したときなどに、残り時間表示を削除するため、下記のOnDeinit関数をOnInit関数の下などに追記します。
void OnDeinit(const int reason)
{
ObjectDelete(0, "RemainingTime");
}
上記までの状態でプログラムをコンパイルし、チャートにセットすると、チャートの右上隅に「0」が表示されます。

3.残り時間表示を更新する
OnTimer関数を利用して残り時間表示の処理をするため、OnInit関数内に下記を追記します。
EventSetMillisecondTimer(100);
上記の指定で、0.1秒(100ミリ秒)間隔で、OnTimer関数が繰り返し実行されるようになります。
残り時間(秒数)用の変数rTimeをOnTimer関数内に定義します。
int rTime = 0;
チャートの時間足に合わせて、それぞれ残り時間を下記のように計算します。
if (_Period <= PERIOD_H1) { // 1時間足以下・・・➀
rTime = PeriodSeconds() - (int)TimeLocal() % PeriodSeconds();
} else if (_Period <= PERIOD_W1) { ➂// 2時間足~週足・・・➁
rTime = int(iTime(NULL, 0, 0) + PeriodSeconds() - TimeCurrent());
} else if (_Period == PERIOD_MN1) { // 月足・・・➂
MqlDateTime timeCurrent;
TimeCurrent(timeCurrent);
datetime nextMonth = iTime(NULL, PERIOD_D1, 0); // 今日の日足の開始時間
for (int i = 0; i < 31; i++) {
nextMonth += PeriodSeconds(PERIOD_D1); // 1日分の秒数を加算
MqlDateTime timeNext;
TimeToStruct(nextMonth, timeNext);
if (timeCurrent.mon != timeNext.mon) break; // 月切り替わりで終了
}
if (nextMonth > TimeCurrent()) rTime = int(nextMonth - TimeCurrent());
}
①1時間足以下の計算
滑らかな時間の切り替わり表示を優先し、PC時計の時間(TimeLocal())を利用して計算しています。
1本の足の秒数(PeriodSeconds())から、現在時間をその秒数で割った余りを引くことで残り時間を計算しています。②2時間足~週足の計算
時差の影響を避けるため、PC時間(TimeLocal())ではなく、サーバー時間(TimeCurrent())を利用して計算しています。
足の開始時間(iTime(NULL, 0, 0))に1本の足の秒数を足して、足の切り替わり時間(確定時間)を算出し、そこから現在時間(TimeCurrent())を引くことで残り時間を計算しています(時差を計算した上で、PC時間から時差を引いて対応することも可能です)。③月足の計算
for文にて、今日の日足の開始時間に1日分の秒数を繰り返し足していき、月が切り替わる時間を検索しています。
検索で取得した月切り替わり時間から現在時間を引くことで、残り時間を計算しています。
計算した残り時間が1本の足の秒数と一致する場合は「0」と表示するため、下記を追記します。
if (rTime == PeriodSeconds() && _Period != PERIOD_MN1) rTime = 0;
表示する「時間」「分」「秒」の値を計算するため、変数「hour」「min」「sec」を下記のように定義します。
int hour = rTime / 3600;
int min = (rTime % 3600) / 60;
int sec = rTime % 60;
残り時間表示用の文字列変数textを定義するため、下記を追記します。
string text;
残り時間が1時間(3600秒)以上あるとき、textに時間の部分を入力するため、下記を追記します。
if (rTime >= 3600) text += (string)hour + ":";
残り時間が1分(60秒)以上あるとき、textに分の部分を入力するため、下記を追記します。
残り時間が1時間(3600秒)以上ある場合は、9分以下の表示でも2桁表示するようにしています。
if (rTime >= 60) text += (rTime >= 3600 ? IntegerToString(min, 2, '0') : (string)min) + ":";
textに秒の部分を入力するため、下記を追記します。
残り時間が1分(60秒)以上ある場合は、9秒以下の表示でも2桁表示するようにしています。
text += rTime >= 60 ? IntegerToString(sec, 2, '0') : (string)sec;
計算した残り時間をチャート上のラベルへ反映するため、下記を追記します。
ObjectSetString(0, "RemainingTime", OBJPROP_TEXT, text);
ChartRedraw();
上記までの作業で今回のツールは完成です。
コンパイルすると、ローソク足確定までの残り時間がチャート上に表示されます。

4.ソースコード
今回、作成したソースコードは下記の通りです。
//+------------------------------------------------------------------+
//| RemainingTime.mq5 |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots 0
//--- input parameters
input ENUM_BASE_CORNER CORNER = CORNER_RIGHT_UPPER; // 基準位置
input int POSX = 5; // 位置X
input int POSY = 20; // 位置Y
input int SIZE = 16; // 文字サイズ
input color CLR = clrWhite; // 色
input string FONT = "Arial Bold"; // フォント
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- indicator buffers mapping
ENUM_ANCHOR_POINT anchor = ANCHOR_LEFT_UPPER;
switch (CORNER) {
case CORNER_LEFT_LOWER :
anchor = ANCHOR_LEFT_LOWER;
break;
case CORNER_RIGHT_UPPER :
anchor = ANCHOR_RIGHT_UPPER;
break;
case CORNER_RIGHT_LOWER :
anchor = ANCHOR_RIGHT_LOWER;
break;
}
LabelCreate(0, "RemainingTime", 0, POSX, POSY, CORNER, "0", FONT, SIZE, CLR, 0, anchor, false, false);
EventSetMillisecondTimer(100);
//---
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
ObjectDelete(0, "RemainingTime");
EventKillTimer();
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int32_t rates_total,
const int32_t 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 int32_t &spread[])
{
//---
//--- return value of prev_calculated for next call
return(rates_total);
}
//+------------------------------------------------------------------+
//| Timer function |
//+------------------------------------------------------------------+
void OnTimer()
{
//---
int rTime = 0;
if (_Period <= PERIOD_H1) { // 1時間足以下・・・➀
rTime = PeriodSeconds() - (int)TimeLocal() % PeriodSeconds();
} else if (_Period <= PERIOD_W1) { // 2時間足~週足・・・➁
rTime = int(iTime(NULL, 0, 0) + PeriodSeconds() - TimeCurrent());
} else if (_Period == PERIOD_MN1) { // 月足・・・➂
MqlDateTime timeCurrent;
TimeCurrent(timeCurrent);
datetime nextMonth = iTime(NULL, PERIOD_D1, 0); // 今日の日足の開始時間
for (int i = 0; i < 31; i++) {
nextMonth += PeriodSeconds(PERIOD_D1); // 1日分の秒数を加算
MqlDateTime timeNext;
TimeToStruct(nextMonth, timeNext);
if (timeCurrent.mon != timeNext.mon) break; // 月切り替わりで終了
}
if (nextMonth > TimeCurrent()) rTime = int(nextMonth - TimeCurrent());
}
if (rTime == PeriodSeconds() && _Period != PERIOD_MN1) rTime = 0;
int hour = rTime / 3600;
int min = (rTime % 3600) / 60;
int sec = rTime % 60;
string text;
if (rTime >= 3600) text += (string)hour + ":";
if (rTime >= 60) text += (rTime >= 3600 ? IntegerToString(min, 2, '0') : (string)min) + ":";
text += rTime >= 60 ? IntegerToString(sec, 2, '0') : (string)sec;
ObjectSetString(0, "RemainingTime", OBJPROP_TEXT, text);
ChartRedraw();
}
//+------------------------------------------------------------------+
//| ラベルを作成する |
//+------------------------------------------------------------------+
bool LabelCreate(const long chart_ID=0, // チャート識別子
const string name="Label", // ラベル名
const int sub_window=0, // サブウィンドウ番号
const int x=0, // X 座標
const int y=0, // Y 座標
const ENUM_BASE_CORNER corner=CORNER_LEFT_UPPER, // アンカーに使用されるチャートのコーナー
const string text="Label", // テキスト
const string font="Arial", // フォント
const int font_size=10, // フォントサイズ
const color clr=clrRed, // 色
const double angle=0.0, // テキストの傾斜
const ENUM_ANCHOR_POINT anchor=ANCHOR_LEFT_UPPER, // アンカーの種類
const bool back=false, // 背景で表示する
const bool selection=false, // 強調表示して移動
const bool hidden=true, // オブジェクトリストに隠す
const long z_order=0) // マウスクリックの優先順位
{
//--- エラー値をリセットする
ResetLastError();
//--- ラベルを作成する
if(!ObjectCreate(chart_ID,name,OBJ_LABEL,sub_window,0,0))
{
Print(__FUNCTION__,
": failed to create text label! Error code = ",GetLastError());
return(false);
}
//--- ラベル座標を設定する
ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x);
ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y);
//--- ポイント座標が相対的に定義されているチャートのコーナーを設定
ObjectSetInteger(chart_ID,name,OBJPROP_CORNER,corner);
//--- テキストを設定する
ObjectSetString(chart_ID,name,OBJPROP_TEXT,text);
//--- テキストフォントを設定する
ObjectSetString(chart_ID,name,OBJPROP_FONT,font);
//--- フォントサイズを設定する
ObjectSetInteger(chart_ID,name,OBJPROP_FONTSIZE,font_size);
//--- テキストの傾斜を設定する
ObjectSetDouble(chart_ID,name,OBJPROP_ANGLE,angle);
//--- アンカーの種類を設定
ObjectSetInteger(chart_ID,name,OBJPROP_ANCHOR,anchor);
//--- 色を設定
ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr);
//--- 前景(false)または背景(true)に表示
ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back);
//--- マウスでラベルを移動させるモードを有効(true)か無効(false)にする
ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection);
ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection);
//--- オブジェクトリストのグラフィックオブジェクトを非表示(true)か表示(false)にする
ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden);
//--- チャートのマウスクリックのイベントを受信するための優先順位を設定する
ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order);
//--- 実行成功
return(true);
}
//+------------------------------------------------------------------+
本記事の監修者・HT FX
2013年にFXを開始し、その後専業トレーダーへ。2014年からMT4/MT5のカスタムインジケーターの開発に取り組む。ブログでは100本を超えるインジケーターを無料公開。投資スタイルは自作の秒足インジケーターを利用したスキャルピング。
EA(自動売買)を学びたい方へオススメコンテンツ

OANDAではEA(自動売買)を稼働するプラットフォームMT4/MT5の基本的な使い方について、画像や動画付きで詳しく解説しています。MT4/MT5のインストールからEAの設定方法までを詳しく解説しているので、初心者の方でもスムーズにEA運用を始めることが可能です。またOANDAの口座をお持ちであれば、独自開発したオリジナルインジケーターを無料で利用することもできます。EA運用をお考えであれば、ぜひ口座開設をご検討ください。
本ホームページに掲載されている事項は、投資判断の参考となる情報の提供を目的としたものであり、投資の勧誘を目的としたものではありません。投資方針、投資タイミング等は、ご自身の責任において判断してください。本サービスの情報に基づいて行った取引のいかなる損失についても、当社は一切の責を負いかねますのでご了承ください。また、当社は、当該情報の正確性および完全性を保証または約束するものでなく、今後、予告なしに内容を変更または廃止する場合があります。なお、当該情報の欠落・誤謬等につきましてもその責を負いかねますのでご了承ください。