経済指標発表の直前・直後は、
スプレッドが急に広がったり、スリッページが大きくなったりします。
CalendarValueHistoryで発表予定を自動取得していましたが、
MQL4にはこの自動取得APIがありません。
この記事では、発表予定時刻を入力パラメータで指定し、
その前後だけ画面の背景パネルを警告色に変えるインジケーターの作り方を解説します。
・MT4に経済指標カレンダーを自動取得する関数が無いという前提
・発表予定時刻を手動で入力する設計の考え方
・StringSplitで複数の時刻をまとめて指定する方法
・OBJ_RECTANGLE_LABELで背景色を変えるパネルの作り方
このインジケーターで解決したい悩み
まず、何のために作るのかを確認します。
発表直後の急変動に損切りが巻き込まれる
重要な経済指標が発表された直後は、
通常時には来ないはずの価格まで一瞬で動くことがあります。
チャートに張り付いていないと発表時刻を忘れる
・経済指標の発表スケジュールは事前に公開されている
・ただしチャートを見るたびに外部サイトで確認するのは手間
・あらかじめ入力しておけば、見た瞬間に分かるようにできる
MQL5版との違い(MQL4には自動取得APIが無い)
既刊のMQL5版は`CalendarValueHistory()`で、
発表予定を自動取得していましたが、
MQL4にはこれに相当する関数が存在しません。
この記事はMQL4版として、手動入力という別の設計で同じ目的を実現します。
設計の考え方(手動スケジュール入力方式)
実際の設計方針を確認します。
発表予定時刻を入力パラメータで指定する
input string InpEventTimes = "08:30,21:30,23:00"; //発表予定時刻(HH:MM、カンマ区切り、サーバー時刻) input int InpWarnBeforeMin = 15; //発表前の警告時間(分) input int InpWarnAfterMin = 15; //発表後の警告時間(分)
発表予定時刻を”HH:MM”形式の文字列でまとめて入力する設計です。
StringSplitで複数の時刻を配列に分ける
string eventTimes[]; int count = StringSplit(InpEventTimes, ',', eventTimes);
既刊の文字列分割記事と同じ`StringSplit()`を使い、
カンマで区切られた時刻を1件ずつの配列に分けます。
“HH:MM”を1日のうちの分数に変換する
int ParseTimeToMinutes(string hhmm)
{
int hour = (int)StringToInteger(StringSubstr(hhmm, 0, 2));
int minute = (int)StringToInteger(StringSubstr(hhmm, 3, 2));
return(hour * 60 + minute);
}
“08:30″であれば8*60+30=510分、というように、
0時からの経過分数に変換して比較しやすくします。
発表時刻前後の判定ロジック
変換した分数を使って、警告すべき瞬間かどうかを判定します。
現在のサーバー時刻を分数に変換する
int nowMinutes = TimeHour(TimeCurrent()) * 60 + TimeMinute(TimeCurrent());
既刊server-time-jst-indicator-mt4.mdで扱った通り、
`TimeCurrent()`はブローカーのサーバー時刻を返すため、
入力する発表時刻もサーバー時刻基準で統一する必要があります。
日付をまたぐ場合を考慮した差分計算
bool IsNearEvent(int nowMin, int eventMin, int beforeMin, int afterMin)
{
int diff = nowMin - eventMin;
if(diff > 720)
diff -= 1440;
if(diff < -720)
diff += 1440;
return(diff >= -beforeMin && diff <= afterMin);
}
単純な引き算では、23時台の発表と0時台の現在時刻のように、
日付をまたぐ比較が壊れます。
720分(12時間)を基準に差分を正規化する
・1日は1440分
・差分が720分を超えたら1440分引く、-720分未満なら1440分足す
・これで『最も近い方向』の差分だけが残り、日付をまたぐ比較も正しく動く
OBJ_RECTANGLE_LABELで背景色を変えるパネルを作る
判定結果を、目に入りやすい形で表示します。
パネルの土台を作る
ObjectCreate(0, "EventWarnBox", OBJ_RECTANGLE_LABEL, 0, 0, 0); ObjectSetInteger(0, "EventWarnBox", OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, "EventWarnBox", OBJPROP_XDISTANCE, 10); ObjectSetInteger(0, "EventWarnBox", OBJPROP_YDISTANCE, 20); ObjectSetInteger(0, "EventWarnBox", OBJPROP_XSIZE, 220); ObjectSetInteger(0, "EventWarnBox", OBJPROP_YSIZE, 26);
既刊の背景色パネル記事と同じ、
スクリーン座標の背景パネルです。
警告中は赤、通常時はグレーに切り替える
if(isWarning) ObjectSetInteger(0, "EventWarnBox", OBJPROP_BGCOLOR, clrCrimson); else ObjectSetInteger(0, "EventWarnBox", OBJPROP_BGCOLOR, clrDimGray);
いずれかの発表時刻の前後、
`InpWarnBeforeMin`〜`InpWarnAfterMin`に入っていれば、
パネルの背景を赤に変えます。
パネルの文字で発表名の代わりに残り時間を表示する
ObjectSetText("EventWarnLabel", "指標発表まで " + IntegerToString(minUntil) + "分", 11, "Arial", clrWhite);
このインジケーター自体は発表の名称を持たないため、
「あと何分か」をパネルの文字で表示します。
実際に動く経済指標警告インジケーターのコード全体
ここまでの要素をまとめた、インジケーターの全体です。
//+------------------------------------------------------------------+
//| EconomicEventWarningIndicator.mq4 |
//| Copyright 2026, FX-EA System Project Creator |
//| https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, FX-EA System Project Creator"
#property link "https://creator.fx-ea-system-project.com/"
#property version "1.00"
#property strict
#property indicator_chart_window
#property indicator_plots 0
input string InpEventTimes = "08:30,21:30,23:00"; //発表予定時刻(HH:MM、カンマ区切り、サーバー時刻)
input int InpWarnBeforeMin = 15; //発表前の警告時間(分)
input int InpWarnAfterMin = 15; //発表後の警告時間(分)
string g_eventTimes[];
int g_eventCount = 0;
int ParseTimeToMinutes(string hhmm)
{
int hour = (int)StringToInteger(StringSubstr(hhmm, 0, 2));
int minute = (int)StringToInteger(StringSubstr(hhmm, 3, 2));
return(hour * 60 + minute);
}
bool IsNearEvent(int nowMin, int eventMin, int beforeMin, int afterMin, int &outDiff)
{
int diff = nowMin - eventMin;
if(diff > 720)
diff -= 1440;
if(diff < -720)
diff += 1440;
outDiff = diff;
return(diff >= -beforeMin && diff <= afterMin);
}
int OnInit()
{
g_eventCount = StringSplit(InpEventTimes, ',', g_eventTimes);
ObjectCreate(0, "EventWarnBox", OBJ_RECTANGLE_LABEL, 0, 0, 0);
ObjectSetInteger(0, "EventWarnBox", OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, "EventWarnBox", OBJPROP_XDISTANCE, 10);
ObjectSetInteger(0, "EventWarnBox", OBJPROP_YDISTANCE, 20);
ObjectSetInteger(0, "EventWarnBox", OBJPROP_XSIZE, 220);
ObjectSetInteger(0, "EventWarnBox", OBJPROP_YSIZE, 26);
ObjectSetInteger(0, "EventWarnBox", OBJPROP_BGCOLOR, clrDimGray);
ObjectSetInteger(0, "EventWarnBox", OBJPROP_BACK, false);
ObjectCreate(0, "EventWarnLabel", OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, "EventWarnLabel", OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, "EventWarnLabel", OBJPROP_XDISTANCE, 18);
ObjectSetInteger(0, "EventWarnLabel", OBJPROP_YDISTANCE, 26);
ObjectSetText("EventWarnLabel", "判定中...", 11, "Arial", clrWhite);
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
ObjectDelete(0, "EventWarnBox");
ObjectDelete(0, "EventWarnLabel");
}
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[])
{
int nowMinutes = TimeHour(TimeCurrent()) * 60 + TimeMinute(TimeCurrent());
bool isWarning = false;
int bestAbsDiff = 100000;
int bestDiff = 0;
for(int i = 0; i < g_eventCount; i++)
{
int eventMin = ParseTimeToMinutes(g_eventTimes[i]);
int diff;
if(IsNearEvent(nowMinutes, eventMin, InpWarnBeforeMin, InpWarnAfterMin, diff))
isWarning = true;
if(MathAbs(diff) < bestAbsDiff)
{
bestAbsDiff = (int)MathAbs(diff);
bestDiff = diff;
}
}
if(isWarning)
{
ObjectSetInteger(0, "EventWarnBox", OBJPROP_BGCOLOR, clrCrimson);
string phase = (bestDiff < 0) ? "発表まで " + IntegerToString(-bestDiff) + "分"
: "発表から " + IntegerToString(bestDiff) + "分経過";
ObjectSetText("EventWarnLabel", "警戒: " + phase, 11, "Arial", clrWhite);
}
else
{
ObjectSetInteger(0, "EventWarnBox", OBJPROP_BGCOLOR, clrDimGray);
ObjectSetText("EventWarnLabel", "次の発表まで " + IntegerToString(bestDiff * -1) + "分", 11, "Arial", clrWhite);
}
return(rates_total);
}
`InpEventTimes`に入力した時刻の前後、
`InpWarnBeforeMin`〜`InpWarnAfterMin`分の間だけ、
パネルが赤くなります。
MetaEditorでのコンパイル・実行手順
- MetaEditorで新規インジケーター「EconomicEventWarningIndicator」を作成し、上のコードを貼り付ける
- F7キー(またはコンパイルボタン)でコンパイルし、「0 error(s), 0 warning(s)」になることを確認する
- チャートへ適用し、`InpEventTimes`を直近数分後の時刻に変更して動作確認する
- 指定時刻の前後でパネルが赤く変わることを確認する
よくあるエラーと注意点
実際に運用する前に、確認しておきたい点です。
サーバー時刻と日本時間がずれる
・`InpEventTimes`はサーバー時刻基準で入力する
・既刊の日本時間計算記事の式で日本時間との差を事前に確認する
・サマータイムの有無でブローカーのサーバー時刻がずれる時期があるため、都度確認する
入力形式を間違えるとStringSplitが正しく分割できない
“08:30、21:30″のように全角カンマを使うと、
`StringSplit()`が区切り文字を認識できず1件として扱われます。
半角カンマ”,” で区切ってください。
日付をまたぐ時刻指定でのオフバイワン
“23:50″のような深夜の時刻を指定する場合、
警告時間を長く設定しすぎると日付をまたいだ計算の境界で、
意図しない時間帯まで警告が続くことがあります。
このインジケーターの使いどころと限界
得意な場面と、単独では対応しきれない場面を整理します。
手動更新が前提、重要度の自動判定はできない
このインジケーターは、
発表の重要度(高・中・低)を判定する機能を持っていません。
毎週・毎月、自分で確認して`InpEventTimes`を更新する運用が前提です。
MQL5版との役割分担
自動で経済指標カレンダーを取得したい場合は、
既刊のMQL5版の環境を検討してください。
よくある質問
経済指標発表の警告インジケーターについて、よく検索される疑問に回答します。
・毎週日曜〜月曜に、その週の重要指標の時刻をまとめて入力し直す運用がおすすめ
・警告時間(前後の分数)は、使っている通貨ペアのスプレッドの戻りが早いか遅いかで調整する
・EAと組み合わせる場合は判定関数だけを抜き出して流用できる
毎日自動で時刻を更新できますか?
このインジケーター単体では自動更新できません。
経済指標の日時は毎週・毎月変わるため、
入力パラメータを手動で書き換える運用を前提にしています。
発表直後は何分警告を続けるべきですか?
指標やブローカーのスプレッド制度によって異なります。
まずは`InpWarnAfterMin`を15分程度にして運用し、
スプレッドが戻る実際の時間を見ながら調整することを推奨します。
EAと組み合わせてエントリー禁止にできますか?
できます。
本記事の`IsNearEvent()`関数をそのままEAの`OnTick()`に移し、
戻り値がtrueの間は新規発注をスキップする条件にすれば、
既刊のEA記事と同じ、
『フィルター関数を発注条件に挟む』設計で応用できます。
【MQL4】経済指標発表の前後だけ背景色を変えて警告するインジケーターの作り方(手動スケジュール入力・OBJ_RECTANGLE_LABEL使用):まとめ
この記事では、MT4に経済指標カレンダーの自動取得APIが無いことを前提に、
発表予定時刻を手動で入力し、
その前後だけ背景パネルを警告色に変えるインジケーターの作り方を解説しました。
重要度の自動判定はできませんが、
毎週決まったタイミングで時刻を更新するだけで、
発表前後のリスクを目視で意識できるようになります。
コメント