Cómo agregar un filtro de noticias a tu Expert Advisor de MT4
Un Expert Advisor que sigue operando en plena publicación de las nóminas no agrícolas (NFP) o de una decisión de tasas de interés ya no aplica su estrategia: está lanzando una moneda al aire, y con el spread bien abierto. Un filtro de noticias lo evita. Suspende las entradas nuevas durante unos minutos antes y después de las publicaciones que mueven el mercado, y el resto del tiempo deja que la estrategia haga su trabajo.
MetaTrader 5 trae un calendario integrado. MetaTrader 4, no, así que el EA tiene que conseguir las noticias por su cuenta. Esta guía usa la API gratuita del calendario económico de ShowMyTrades: sin cuenta ni clave de API, con una sola URL que autorizar en el terminal y un archivo include que puedes pegar en cualquier EA.
🧭 Qué vas a construir
cada 15 minutos en cada tick
┌────────────────────┐ ┌──────────────────────────────────┐
│ descarga noticias │ ──► │ ¿hay una publicación dentro de │
│ de los próximos │ │ [-30 min, +30 min] desde ahora? │
│ 2 días (CSV, una │ │ sí → ninguna entrada nueva │
│ sola solicitud) │ │ no → se ejecuta la estrategia │
└────────────────────┘ └──────────────────────────────────┘
| Decisión | Valor predeterminado en el código | Por qué |
|---|---|---|
| Qué divisas | EUR,USD | Las dos divisas del par con el que operas. Si lo dejas vacío, entran las nueve principales |
| Qué impacto | high | high,medium para un filtro más prudente |
| Pausa antes | 30 minutos | Antes del dato los spreads se amplían y la liquidez se reduce |
| Pausa después | 30 minutos | Aquí se concentran la primera reacción y el giro que suele venir después |
| Intervalo de descarga | 15 minutos | El calendario cambia pocas veces al día y, de todos modos, la API guarda los datos en caché durante 15 minutos |
🔑 Paso 1: autoriza la dirección en MetaTrader
WebRequest(), la función de MQL4 que descarga datos de internet, solo se comunica con las direcciones que hayas aprobado. En el terminal abre Herramientas → Opciones → Asesores Expertos (Tools → Options → Expert Advisors), marca Permitir WebRequest para las siguientes URL (Allow WebRequest for listed URL) y agrega:
https://showmytrades.com
Si te saltas este paso, la descarga devuelve -1 con el error 4060. Cuando eso ocurre, el archivo include deja un aviso en la pestaña Expertos (Experts), así que no te quedarás preguntándote por qué el filtro no dice nada.
📄 Paso 2: guarda el archivo include
Guarda el código siguiente como SMT_NewsFilter.mqh en la carpeta MQL4\Include de tu terminal; la encuentras en Archivo → Abrir carpeta de datos (File → Open Data Folder). Es el mismo archivo que aparece en la referencia de la API, y además compila tal cual en MQL5.
//+------------------------------------------------------------------+
//| SMT_NewsFilter.mqh - filtro de noticias para MT4 y MT5 |
//| Datos: https://showmytrades.com/api/public/calendar/v1 (gratis) |
//| Docs: https://showmytrades.com/docs/api/economic-calendar |
//| Configuración: Herramientas > Opciones > Asesores Expertos > |
//| Permitir WebRequest (Allow WebRequest) para |
//| https://showmytrades.com |
//+------------------------------------------------------------------+
#property strict
input string SMT_Currencies = "EUR,USD"; // Noticias: divisas (separadas por comas, vacío = todas)
input string SMT_Impact = "high"; // Noticias: impacto (high | high,medium | all)
input int SMT_MinutesBefore = 30; // Noticias: pausa antes de una publicación (minutos)
input int SMT_MinutesAfter = 30; // Noticias: pausa después de una publicación (minutos)
input int SMT_RefreshMinutes = 15; // Noticias: intervalo de descarga (minutos, mín. 15)
datetime smt_eventTime[];
string smt_eventTitle[];
datetime smt_nextDownload = 0;
long smt_serverTime = 0; // hora UTC del servidor (segundos Unix) en la última descarga
uint smt_syncTick = 0; // GetTickCount() en ese mismo momento
bool smt_clockSynced = false;
//--- la hora UTC real: el reloj del servidor más los milisegundos transcurridos desde la
//--- descarga, contados con GetTickCount(), que no sabe de zonas horarias, horario de
//--- verano ni ajustes del reloj del equipo (la resta en uint sobrevive a su desbordamiento).
//--- Antes de la primera descarga se usa TimeGMT() como respaldo. Nunca TimeCurrent(): el
//--- desfase GMT del bróker es desconocido y salta con el horario de verano.
datetime SMT_Now()
{
if(!smt_clockSynced) return TimeGMT();
uint elapsedMs = GetTickCount() - smt_syncTick;
return (datetime)(smt_serverTime + elapsedMs / 1000);
}
//--- cuánto se aparta el reloj del bróker de UTC, en segundos (7200 = GMT+2). Útil por sí
//--- solo: la mayoría de los EA nunca lo conoce. TimeCurrent() es la hora del último tick,
//--- así que léelo con el mercado abierto, no durante el fin de semana.
int SMT_BrokerGmtOffsetSeconds()
{
return (int)(TimeCurrent() - SMT_Now());
}
//--- todo lo que hay después de la n-ésima coma (el título del CSV, que puede contener comas)
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;
}
//--- descarga los próximos 2 días (la ventana también cubre las últimas 6 horas)
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; // reintenta en un minuto
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++) // la línea 0 es el encabezado
{
// última línea "# server_time=1760702400 server_time_iso=...": el reloj UTC del servidor
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]); // segundos 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 mientras haya una publicación dentro de la ventana de pausa; reason indica cuál
bool SMT_IsNewsTime(string &reason)
{
reason = "";
if(MQLInfoInteger(MQL_TESTER)) return false; // sin red en el probador de estrategias
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;
}
Lo que hace, por orden: construye la solicitud a partir de los parámetros de entrada, descarga el CSV, guarda una marca de tiempo y una etiqueta por evento, y responde a la única pregunta que le importa al EA: ¿hay ahora mismo una publicación dentro de la ventana?
🔌 Paso 3: llámala antes de cada entrada
En tu EA, incluye el archivo y pregunta antes de abrir una posición:
#include <SMT_NewsFilter.mqh>
void OnTick()
{
string news;
if(SMT_IsNewsTime(news))
{
Comment("News filter active: ", news);
return; // sin entradas nuevas durante la ventana de noticias
}
Comment("");
// ... tu lógica de entrada ...
}
Los cinco parámetros de entrada (SMT_Currencies, SMT_Impact y los tres valores en minutos) aparecen en la ventana de parámetros del EA como cualquier otra opción, así que cada gráfico puede tener los suyos.
Vuelve a compilar, coloca el EA en el gráfico y mira la pestaña Expertos: la primera línea debería ser [SMT] calendar: N events loaded. En un día tranquilo N puede ser cero, y eso es lo correcto, no un error.
⏱️ Horas, relojes y el probador de estrategias
Hay tres detalles que deciden si el filtro salta en el minuto correcto.
| La API habla en UTC y te dice qué hora es | ts es tiempo Unix en segundos, en UTC. Cada descarga termina con el reloj del propio servidor (# server_time=…), y SMT_Now() lo mantiene en marcha con el contador de ticks del equipo, que no sabe de zonas horarias ni de horario de verano. Nada depende de la zona horaria del bróker, del horario de verano de ningún país ni de que el VPS tenga bien configurada la zona |
Nunca TimeCurrent() | La hora del bróker va desfasada respecto a UTC en una cantidad que el EA no puede conocer y que cambia dos veces al año. SMT_BrokerGmtOffsetSeconds() te mide ese desfase, algo útil por sí solo; el filtro nunca depende de él |
| El probador de estrategias (Strategy Tester) no tiene internet | Ahí WebRequest() está desactivada, así que en los backtests la función responde «no hay noticias». Para probar un filtro de noticias sobre datos históricos hace falta un archivo local con los eventos pasados, y eso ya es otro proyecto |
🎛️ Cómo ajustarlo
- Posiciones abiertas. El código solo bloquea las entradas nuevas. Cerrar, ajustar los stops o ampliarlos antes de una publicación es una decisión de estrategia; el mismo
SMT_IsNewsTime()te dice cuándo hacerlo. - Solo una divisa del par. Pon
SMT_CurrenciesenUSDsi solo te interesan las noticias del dólar. - También impacto medio. Con
SMT_Impact = "high,medium"los eventos se multiplican más o menos por tres. Va bien para los scalpers y suele ser demasiado para los sistemas de swing. - Días festivos.
impact=allincluye los días festivos de los mercados como eventos de día completo. Casi ningún filtro los quiere, y con el valor predeterminado quedan fuera.
🔍 Cómo comprobar los datos
El mismo calendario también es una página que puedes consultar: calendario económico. Cuando el EA indique que se detuvo por «USD high: Nonfarm Payrolls», ahí verás el evento con su previsión y, después de la publicación, el dato publicado (columna «Actual»).
❓ Preguntas frecuentes
¿Hace falta una cuenta de ShowMyTrades? No. La API del calendario es gratuita y abierta, sin clave. Si quieres la página pública verificada, haz seguimiento de tu cuenta en ShowMyTrades, pero el filtro de noticias no depende de ello.
¿Cuánto tráfico genera? Una solicitud cada 15 minutos por gráfico, de unos pocos kilobytes cada una. Diez EA en un mismo terminal suman 40 solicitudes por hora. El límite es de 600 por minuto por dirección IP.
¿Por qué no leer directamente Forex Factory?
Se puede, y muchos EA lo hacen. Su archivo semanal admite dos descargas cada cinco minutos por dirección IP, algo que no aguanta un VPS con muchos terminales, y no trae los valores actual. El endpoint de ShowMyTrades filtra por divisa e impacto en el servidor, devuelve unos pocos kilobytes y está pensado para consultarse a intervalos regulares.
El reloj o la zona horaria de mi VPS están mal. ¿El filtro salta igualmente en el minuto correcto? Sí. Con cada descarga, el servidor le dice al EA qué hora es en UTC, y el EA mantiene ese reloj en marcha por su cuenta. Que el VPS tenga la zona equivocada o vaya unos minutos desfasado no cambia nada. Ni siquiera el cambio al horario de verano en el VPS afecta: el EA mide el tiempo transcurrido con el contador de ticks, no con el reloj.
¿Qué pasa si la descarga falla?
El filtro conserva el último calendario que cargó y vuelve a intentarlo al cabo de un minuto. Si nunca llegó a cargar ninguno (arranque en limpio, sin internet), responde «no hay noticias» y el EA opera. Si no es lo que quieres, comprueba ArraySize(smt_eventTime) == 0 y decide qué hacer.
¿El mismo archivo funciona en MT5? Sí, sin cambios. MT5 además tiene su propio calendario; usa el que mejor te venga.
🔗 Artículos relacionados
- Referencia de la API del calendario económico: todos los parámetros, el formato JSON y el WebSocket para cTrader
- Filtro de noticias en MT5: ¿calendario integrado o API?
- Cómo agregar un filtro de noticias a un cBot de cTrader: la misma idea donde no hay HTTP disponible
- Resultados en real de tu EA en la página de ventas: cuando el EA opere bien, muéstralo
El filtro cabe en una página de código y una URL. Si ya haces seguimiento de tu EA en ShowMyTrades, las operaciones que evita en torno a las noticias aparecerán como el drawdown que no tuvo.
Cómo mostrar varias cuentas de trading en una sola página
Varias cuentas, varios EA o varios brókeres en una misma página web: una tabla comparativa en vivo con una fila por cuenta, gráficos uno al lado del otro o una sola curva combinada. Con el código.
Filtro de noticias en MT5: ¿calendario integrado o API?
MetaTrader 5 incluye un calendario económico que los Expert Advisors pueden leer sin descargar nada. Cuándo basta, cuándo no y cómo usar en su lugar una API de calendario gratuita, con código MQL5 para las dos opciones.