MT4のEAにニュースフィルターを追加する方法
米雇用統計(NFP)や政策金利発表のさなかにもエントリーを続けるEA(エキスパートアドバイザー)は、もう戦略どおりに動いているとはいえません。広がったスプレッドの中で、コイン投げをしているのと同じです。そこで役に立つのがニュースフィルターです。相場を動かす指標の発表前後の数分間だけ新規エントリーを止め、それ以外の時間は戦略に任せます。
MetaTrader 5には経済指標カレンダーが内蔵されていますが、MetaTrader 4にはありません。そのためEAは、ニュースをどこかから取ってくる必要があります。このガイドで使うのは、無料のShowMyTrades経済指標カレンダーAPIです。口座登録もAPIキーも不要で、ターミナルで許可するURLは1つだけ。どのEAにも貼り付けられるインクルードファイルを1つ用意します。
🧭 作るものの全体像
15分ごと
今後2日分のニュースをダウンロード
(CSV、リクエスト1回)
│
▼
ティックごと
現在時刻の[-30分, +30分]の範囲に発表があるか?
ある → 新規エントリーしない
ない → 戦略を実行
| 決めること | コードの初期値 | 理由 |
|---|---|---|
| 対象の通貨 | EUR,USD | 取引している通貨ペアを構成する2通貨。空欄にすると主要9通貨すべてが対象になります |
| 対象の重要度 | high | 重要度「中」も対象にして、より慎重に止めたいならhigh,medium |
| 発表前の停止 | 30分 | 発表前はスプレッドが広がり、流動性も薄くなります |
| 発表後の停止 | 30分 | 最初の値動きも、その後の反転も、この時間帯に起こります |
| ダウンロード間隔 | 15分 | カレンダーが変わるのは1日に数回程度です。しかもAPI側で15分間キャッシュしています |
🔑 ステップ1:MetaTraderでURLを許可する
インターネットからデータを取得するMQL4の関数WebRequest()は、あらかじめ許可したURLにしかアクセスできません。ターミナルでツール → オプション → エキスパートアドバイザ(Tools → Options → Expert Advisors)を開き、WebRequestを許可するURLリスト(Allow WebRequest for listed URL)にチェックを入れて、次のURLを追加します。
https://showmytrades.com
この設定を忘れると、ダウンロードは-1を返し、エラー4060になります。そのときはインクルードファイルが「エキスパート」タブ(Experts)に注意を表示するので、フィルターがなぜ反応しないのかと悩まずに済みます。
📄 ステップ2:インクルードファイルを保存する
次のコードをSMT_NewsFilter.mqhという名前で、ターミナルのMQL4\Includeフォルダー(ファイル → データフォルダを開く、File → Open Data Folder)に保存します。APIリファレンスに載せているものと同じファイルで、MQL5でもそのままコンパイルできます。
//+------------------------------------------------------------------+
//| SMT_NewsFilter.mqh - MT4・MT5用ニュースフィルター
//| データ:https://showmytrades.com/api/public/calendar/v1(無料)
//| ドキュメント:https://showmytrades.com/docs/api/economic-calendar
//| 設定:ツール > オプション > エキスパートアドバイザ >
//| WebRequestを許可するURLリストに https://showmytrades.com を追加
//+------------------------------------------------------------------+
#property strict
input string SMT_Currencies = "EUR,USD"; // ニュース:通貨(カンマ区切り、空欄 = すべて)
input string SMT_Impact = "high"; // ニュース:重要度(high | high,medium | all)
input int SMT_MinutesBefore = 30; // ニュース:発表前の停止時間(分)
input int SMT_MinutesAfter = 30; // ニュース:発表後の停止時間(分)
input int SMT_RefreshMinutes = 15; // ニュース:ダウンロード間隔(分、最小15)
datetime smt_eventTime[];
string smt_eventTitle[];
datetime smt_nextDownload = 0;
long smt_serverTime = 0; // 前回ダウンロード時のサーバーのUTC時刻(Unix秒)
uint smt_syncTick = 0; // その時点のGetTickCount()
bool smt_clockSynced = false;
//--- 本当のUTC時刻:サーバーの時刻に、ダウンロードからの経過ミリ秒を足したもの。
//--- 経過時間はGetTickCount()で数えるので、パソコンのタイムゾーン、夏時間、
//--- 時計の補正の影響を受けない(カウンターが一周してもuintの引き算で正しく求まる)。
//--- 最初のダウンロードまではTimeGMT()で代用する。TimeCurrent()は使わない:
//--- ブローカーのGMTオフセットは分からず、夏時間で変わるため。
datetime SMT_Now()
{
if(!smt_clockSynced) return TimeGMT();
uint elapsedMs = GetTickCount() - smt_syncTick;
return (datetime)(smt_serverTime + elapsedMs / 1000);
}
//--- ブローカーの時計とUTCの差(秒、7200 = GMT+2)。これ単体でも便利:
//--- ほとんどのEAはこの値を知らない。TimeCurrent()は最後のティックの時刻なので、
//--- 週末ではなく、市場が開いているあいだに読むこと。
int SMT_BrokerGmtOffsetSeconds()
{
return (int)(TimeCurrent() - SMT_Now());
}
//--- n番目のカンマより後ろすべて(CSVのタイトル。タイトル自体にカンマを含むことがある)
string SMT_AfterComma(const string line, const int n)
{
int pos = -1;
for(int i = 0; i < n; i++)
{
pos = StringFind(line, ",", pos + 1);
if(pos < 0) return "";
}
string rest = StringSubstr(line, pos + 1);
StringReplace(rest, "\"\"", "\"");
if(StringLen(rest) >= 2 && StringGetCharacter(rest, 0) == '"')
rest = StringSubstr(rest, 1, StringLen(rest) - 2);
return rest;
}
//--- 今後2日分をダウンロード(範囲には直近6時間も含まれる)
bool SMT_Download()
{
string url = "https://showmytrades.com/api/public/calendar/v1?format=csv&days=2&impact=" + SMT_Impact;
if(StringLen(SMT_Currencies) > 0) url += "¤cy=" + SMT_Currencies;
char post[], result[];
string headers;
ResetLastError();
int status = WebRequest("GET", url, "", NULL, 10000, post, 0, result, headers);
if(status != 200)
{
Print("[SMT] calendar download failed: HTTP ", status, ", error ", GetLastError(),
". Is https://showmytrades.com in Tools > Options > Expert Advisors > Allow WebRequest?");
smt_nextDownload = TimeCurrent() + 60; // 1分後に再試行
return false;
}
string body = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8);
string lines[];
int n = StringSplit(body, '\n', lines);
ArrayResize(smt_eventTime, 0);
ArrayResize(smt_eventTitle, 0);
for(int i = 1; i < n; i++) // 0行目はヘッダー
{
// 最終行 "# server_time=1760702400 server_time_iso=...":サーバーのUTC時刻
if(StringFind(lines[i], "# server_time=") == 0)
{
long serverTime = StringToInteger(StringSubstr(lines[i], 14, 10));
if(serverTime > 0)
{
smt_serverTime = serverTime;
smt_syncTick = GetTickCount();
smt_clockSynced = true;
}
continue;
}
string f[];
if(StringSplit(lines[i], ',', f) < 8) continue;
int k = ArraySize(smt_eventTime);
ArrayResize(smt_eventTime, k + 1);
ArrayResize(smt_eventTitle, k + 1);
smt_eventTime[k] = (datetime)StringToInteger(f[0]); // Unix秒、UTC
smt_eventTitle[k] = f[2] + " " + f[3] + ": " + SMT_AfterComma(lines[i], 7);
}
smt_nextDownload = TimeCurrent() + MathMax(SMT_RefreshMinutes, 15) * 60;
Print("[SMT] calendar: ", ArraySize(smt_eventTime), " events loaded. UTC now ",
TimeToString(SMT_Now(), TIME_DATE|TIME_MINUTES), ", broker clock is GMT",
(SMT_BrokerGmtOffsetSeconds() >= 0 ? "+" : ""), DoubleToString(SMT_BrokerGmtOffsetSeconds() / 3600.0, 1));
return true;
}
//--- 停止時間帯に発表があるあいだtrue。reasonにはどの発表かが入る
bool SMT_IsNewsTime(string &reason)
{
reason = "";
if(MQLInfoInteger(MQL_TESTER)) return false; // テスターではネットワークが使えない
if(TimeCurrent() >= smt_nextDownload) SMT_Download();
datetime now = SMT_Now();
for(int i = 0; i < ArraySize(smt_eventTime); i++)
{
if(now >= smt_eventTime[i] - SMT_MinutesBefore * 60 && now <= smt_eventTime[i] + SMT_MinutesAfter * 60)
{
reason = smt_eventTitle[i];
return true;
}
}
return false;
}
処理の流れは次のとおりです。入力パラメーターからリクエストを組み立て、CSVをダウンロードし、イベントごとに時刻とラベルを1つずつ保持します。そのうえで、EAが知りたいただ1つの問い、いま停止時間帯に発表が入っているかに答えます。
🔌 ステップ3:エントリーの前に毎回呼び出す
EAでこのファイルをインクルードし、ポジションを持つ前に確認します。
#include <SMT_NewsFilter.mqh>
void OnTick()
{
string news;
if(SMT_IsNewsTime(news))
{
Comment("News filter active: ", news);
return; // ニュースの時間帯は新規エントリーしない
}
Comment("");
// ... エントリーのロジック ...
}
5つの入力パラメーター(SMT_Currencies、SMT_Impact、分単位の3つの値)は、ほかの設定と同じくEAのパラメーター画面に表示されます。チャートごとに別の値を設定できます。
再コンパイルしてEAをチャートにセットしたら、「エキスパート」タブを見てください。最初の行に[SMT] calendar: N events loadedと出ていれば動いています。指標の少ない日はNが0になることもありますが、それは正常で、エラーではありません。
⏱️ 時刻、時計、ストラテジーテスター
フィルターが正しい分に作動するかどうかは、次の3点で決まります。
| APIはUTCで返し、現在時刻も教えてくれる | tsはUTCのUnixタイム(秒)です。ダウンロードのたびに末尾にサーバー自身の時刻(# server_time=…)が付き、SMT_Now()がそれをパソコンのティックカウンターで進めていきます。このカウンターはタイムゾーンにも夏時間にも影響されません。ブローカーのタイムゾーン、どこかの国の夏時間、VPSのタイムゾーン設定が正しいかどうか、そのどれにも左右されない仕組みです |
TimeCurrent()は使わない | ブローカー時間はUTCからずれていて、そのずれはEAには分からず、しかも年に2回変わります。SMT_BrokerGmtOffsetSeconds()がこのずれを測ってくれるので、それ単体でも役に立ちますが、フィルター自体はこの値に頼っていません |
| ストラテジーテスターではインターネットが使えない | テスターではWebRequest()が無効になるため、バックテストでは関数が「ニュースなし」を返します。過去のデータでニュースフィルターを試すには過去のイベントを収めたローカルファイルが必要で、それはまた別の作業になります |
🎛️ 調整のしかた
- 保有ポジション:このコードが止めるのは新規エントリーだけです。発表前に決済するか、損切りの幅を狭めるか広げるかは、戦略で決めることです。そのタイミングは同じ
SMT_IsNewsTime()で分かります。 - 通貨ペアの片方だけ:ドルのニュースだけを気にするなら、
SMT_CurrenciesをUSDにします。 - 重要度「中」も含める:
SMT_Impact = "high,medium"にすると、対象のイベントはおよそ3倍になります。スキャルパーには向いていますが、スイングトレードのシステムには多すぎることがよくあります。 - 祝日:
impact=allにすると、市場の祝日が終日のイベントとして含まれます。ほとんどのフィルターには不要なので、初期設定では除外しています。
🔍 データを確かめる
同じカレンダーは経済指標カレンダーのページでも見られます。EAが「USD high: Nonfarm Payrolls」で停止したと表示したら、そのページで同じイベントと予想を確認でき、発表後は結果も確認できます。
❓ よくある質問
ShowMyTradesのアカウントは必要ですか?
不要です。経済指標カレンダーAPIは無料で公開されていて、キーもいりません。認証済みの公開ページが欲しいならShowMyTradesで口座を記録するとよいですが、ニュースフィルター自体はそれに依存しません。
通信量はどのくらいですか?
チャート1枚につき15分に1回のリクエストで、1回あたり数KBです。1つのターミナルでEAを10個動かしても、1時間に40リクエストです。上限はIPアドレスごとに毎分600リクエストです。
Forex Factoryを直接読めばよいのでは?
それもできますし、実際に多くのEAがそうしています。ただ、Forex Factoryの週次ファイルはIPアドレスごとに5分間で2回までしかダウンロードできないため、多数のターミナルを動かすVPSでは破綻します。actual(結果)の値もありません。ShowMyTradesのエンドポイントは通貨と重要度をサーバー側で絞り込み、返すデータは数KBだけで、定期的な取得(ポーリング)を前提に作っています。
VPSの時計やタイムゾーンがずれています。それでもフィルターは正しい分に作動しますか?
はい。サーバーはダウンロードのたびにUTCの現在時刻をEAに伝え、EAはその時刻を自分で進め続けます。VPSのタイムゾーンが違っていても、時計が数分ずれていても影響はありません。VPSで夏時間の切り替えがあっても同じです。EAは経過時間を、時計ではなくティックカウンターで数えているからです。
ダウンロードに失敗したら?
フィルターは最後に読み込んだカレンダーを使い続け、1分後に再試行します。一度も読み込めていない場合(起動したばかりで、インターネットにつながらないときなど)は「ニュースなし」を返すため、EAは取引します。それが困るなら、ArraySize(smt_eventTime) == 0をチェックして動作を決めてください。
同じファイルはMT5でも使えますか?
はい、そのまま使えます。MT5には独自の経済指標カレンダーもあるので、使いやすいほうを選んでください。
🔗 関連ガイド
- 経済指標カレンダーAPIのリファレンス:すべてのパラメーター、JSON形式、cTrader向けのWebSocket
- MT5のニュースフィルター:内蔵カレンダーとAPI、どちらを使う?
- cTraderのcBotにニュースフィルターを追加する:HTTPが使えない環境で同じ考え方を実現
- 販売ページでEAのフォワード実績をリアルタイムに見せる方法:EAが結果を出しはじめたら、それを見せる
ニュースフィルターに必要なのは、1ページ分のコードとURLが1つだけです。EAがすでにShowMyTradesで記録されているなら、ニュース前後で見送った取引の分だけ、ドローダウンが浅く済んでいることが記録に表れます。