Economic calendar API
GET https://showmytrades.com/api/public/calendar/v1
Scheduled macroeconomic releases, central-bank decisions and speeches, and market holidays for USD, EUR, GBP, JPY, AUD, NZD, CAD, CHF and CNY, with forecast, previous and actual values. The same data as the economic calendar page, in a format made for code: Unix timestamps, lowercase impact levels, JSON or CSV.
It is free and needs no API key. The typical use is a news filter: a trading robot that pauses before and after high-impact news. This page has the endpoint, then working code for MetaTrader 4, MetaTrader 5 and cTrader that you can paste as it is.
If-None-Match costs
nothing while the data is unchanged. The limit is 600 requests per minute per IP address, so
dozens of terminals on one VPS can start at the same time without being refused.Query parameters
USD, EUR, GBP, JPY, AUD, NZD, CAD, CHF, CNY. Example:
currency=EUR,USD. Omit it for all nine.high, medium, low, holiday, or all. Defaults to
high. impact=high,medium is the usual choice for a cautious filter.1 to 30. Defaults to 1. The window always starts 6 hours
in the past, so a filter that pauses after a release still sees the release.json (default) or csv. CSV is easier to parse in MQL.Anything outside those lists is a 400 with a message that says what is allowed, so a
misconfigured robot notices on its first run instead of trading through the news.
Example
Today's high-impact news for the euro and the dollar:
curl "https://showmytrades.com/api/public/calendar/v1?currency=EUR,USD&impact=high&days=1"
{
"generated_at": "2026-10-17T07:12:40Z",
"from": "2026-10-17T01:12:40Z",
"to": "2026-10-18T07:12:40Z",
"count": 2,
"events": [
{
"ts": 1760702400,
"time": "2026-10-17T12:00:00Z",
"currency": "EUR",
"impact": "high",
"title": "ECB President Lagarde Speech",
"forecast": "",
"previous": "",
"actual": ""
},
{
"ts": 1760704200,
"time": "2026-10-17T12:30:00Z",
"currency": "USD",
"impact": "high",
"title": "Building Permits MoM",
"forecast": "1.33M",
"previous": "1.31M",
"actual": ""
}
],
"attribution": { "required": false, "text": "Economic calendar by ShowMyTrades", "url": "https://showmytrades.com/tools/economic-calendar" },
"docs": "https://showmytrades.com/docs/api/economic-calendar"
}
datetime as it is;
compare it with TimeGMT(), never with the broker's TimeCurrent().high, medium, low or holiday. A holiday is a closed market, not a release: its time
is the start of the day."0.3%", "$70.5B", "1.33M". Empty when not available.
actual is empty until the release and is filled in minutes after it.The same request with format=csv:
ts,time,currency,impact,forecast,previous,actual,title
1760702400,2026-10-17T12:00:00Z,EUR,high,,,,ECB President Lagarde Speech
1760704200,2026-10-17T12:30:00Z,USD,high,1.33M,1.31M,,Building Permits MoM
The title is the last column because it is the only one that can contain a comma (it is then wrapped in double quotes, RFC 4180). Read the first seven columns with a split on commas and take everything after the seventh comma as the title.
MetaTrader 4 and 5: a news filter in one include file
The same file compiles in MQL4 and MQL5. Save it as SMT_NewsFilter.mqh in MQL4\Include
(or MQL5\Include), then include it in your Expert Advisor.
Before it can download anything, allow the address in the terminal: Tools → Options →
Expert Advisors → tick Allow WebRequest for listed URL and add https://showmytrades.com.
Without that line WebRequest() returns -1 and error 4060; the file prints a reminder.
//+------------------------------------------------------------------+
//| SMT_NewsFilter.mqh - news filter for MT4 and MT5 |
//| Data: https://showmytrades.com/api/public/calendar/v1 (free) |
//| Docs: https://showmytrades.com/docs/api/economic-calendar |
//| Setup: Tools > Options > Expert Advisors > Allow WebRequest for |
//| https://showmytrades.com |
//+------------------------------------------------------------------+
#property strict
input string SMT_Currencies = "EUR,USD"; // News: currencies (comma-separated, empty = all)
input string SMT_Impact = "high"; // News: impact (high | high,medium | all)
input int SMT_MinutesBefore = 30; // News: pause before a release (minutes)
input int SMT_MinutesAfter = 30; // News: pause after a release (minutes)
input int SMT_RefreshMinutes = 15; // News: download interval (minutes, min 15)
datetime smt_eventTime[];
string smt_eventTitle[];
datetime smt_nextDownload = 0;
//--- everything after the n-th comma (the CSV title, which may itself contain commas)
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;
}
//--- downloads the next 2 days (the window also covers the last 6 hours)
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; // retry in a minute
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++) // line 0 is the header
{
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 seconds, 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");
return true;
}
//--- true while a release is within the pause window; reason tells which one
bool SMT_IsNewsTime(string &reason)
{
reason = "";
if(MQLInfoInteger(MQL_TESTER)) return false; // no network in the tester
if(TimeCurrent() >= smt_nextDownload) SMT_Download();
datetime now = TimeGMT();
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;
}
Using it in an Expert Advisor:
#include <SMT_NewsFilter.mqh>
void OnTick()
{
string news;
if(SMT_IsNewsTime(news))
{
Comment("News filter active: ", news);
return; // no new entries during the news window
}
Comment("");
// ... your entry logic ...
}
A few things worth knowing:
- Times.
tsis UTC. The file compares it withTimeGMT(), which is the computer's clock in UTC: keep the VPS clock synchronised. Broker time (TimeCurrent()) is only used to space the downloads. - The tester.
WebRequest()does not work in the strategy tester, so the filter returns "no news" there. Backtesting a news filter needs historical data stored locally; that is a different job. - Several charts. Each chart downloads on its own. Ten EAs on one terminal are ten requests every 15 minutes, well inside the limits.
- MT5 has a built-in calendar. The
Calendar*functions of MQL5 work without any download on most brokers, and that is a fine choice. Use this API when the broker does not provide the built-in calendar, when you want one file for MT4 and MT5, or when you want the same data on cTrader.
cTrader: a news filter for a cBot, cloud included
cTrader Cloud does not let a cBot make HTTP requests: Http.Get() fails with status 503
there. The only network it allows is a WebSocket on port 25345. So the calendar is also
served on that port:
ws://showmytrades.com:25345/
Open the socket, send one line of text with the same parameters as the HTTP query, and read one text frame back with the same JSON or CSV body:
→ currency=EUR,USD&impact=high&days=2&format=csv
← ts,time,currency,impact,forecast,previous,actual,title
1760704200,2026-10-17T12:30:00Z,USD,high,1.33M,1.31M,,Building Permits MoM
...
ping answers pong, an invalid request answers a line starting with Error:, and the
connection closes after a minute of silence. The same port also answers a plain HTTP GET
with the same path, which is convenient for a quick test from a terminal:
curl "http://showmytrades.com:25345/api/public/calendar/v1?currency=USD&impact=high"
The cBot below works unchanged on the desktop and in the cloud, with AccessRights.None, so it
can be published. It downloads the calendar at start and every 15 minutes, and
IsNewsTime() tells you whether to stay out.
// News filter for cTrader (desktop and cloud), data by ShowMyTrades, free, no key.
// Docs: https://showmytrades.com/docs/api/economic-calendar
using System;
using System.Collections.Generic;
using cAlgo.API;
namespace cAlgo.Robots
{
[Robot(AccessRights = AccessRights.None, TimeZone = TimeZones.UTC)]
public class SmtNewsFilterBot : Robot
{
[Parameter("News: currencies (comma-separated, empty = all)", DefaultValue = "EUR,USD")]
public string NewsCurrencies { get; set; }
[Parameter("News: impact (high | high,medium | all)", DefaultValue = "high")]
public string NewsImpact { get; set; }
[Parameter("News: minutes before", DefaultValue = 30, MinValue = 0)]
public int MinutesBefore { get; set; }
[Parameter("News: minutes after", DefaultValue = 30, MinValue = 0)]
public int MinutesAfter { get; set; }
private const string CalendarWs = "ws://showmytrades.com:25345/";
private const string CalendarHttp = "https://showmytrades.com/api/public/calendar/v1?";
private readonly List<KeyValuePair<DateTime, string>> _events = new List<KeyValuePair<DateTime, string>>();
private WebSocketClient _ws;
private bool _received;
protected override void OnStart()
{
DownloadCalendar();
Timer.Start(TimeSpan.FromMinutes(15));
}
protected override void OnTimer()
{
DownloadCalendar();
}
protected override void OnTick()
{
string reason;
if (IsNewsTime(out reason))
{
// No new entries during the news window. Manage open positions as you prefer.
return;
}
// ... your entry logic ...
}
protected override void OnStop()
{
CloseSocket();
}
// True while a release is inside the pause window; reason says which one.
public bool IsNewsTime(out string reason)
{
DateTime now = Server.TimeInUtc;
foreach (KeyValuePair<DateTime, string> e in _events)
{
if (now >= e.Key.AddMinutes(-MinutesBefore) && now <= e.Key.AddMinutes(MinutesAfter))
{
reason = e.Value;
return true;
}
}
reason = null;
return false;
}
private string Query()
{
string q = "format=csv&days=2&impact=" + NewsImpact;
if (!string.IsNullOrWhiteSpace(NewsCurrencies)) q += "¤cy=" + NewsCurrencies.Replace(" ", "");
return q;
}
private void DownloadCalendar()
{
if (RunningMode != RunningMode.RealTime) return; // no network in the backtester
// cTrader Cloud blocks HTTP from cBots and allows only a WebSocket on port 25345,
// so the socket is the primary path everywhere; HTTP is the fallback on the desktop.
try
{
CloseSocket();
_received = false;
_ws = new WebSocketClient(new WebSocketClientOptions { Timeout = TimeSpan.FromSeconds(15) });
_ws.Connected += OnSocketConnected;
_ws.TextReceived += OnSocketText;
_ws.Disconnected += OnSocketDisconnected;
_ws.Connect(new Uri(CalendarWs));
}
catch (Exception ex)
{
Print("[SMT] calendar socket failed: {0}", ex.Message);
DownloadOverHttp();
}
}
private void OnSocketConnected(WebSocketClientConnectedEventArgs args)
{
try { _ws.Send(Query()); }
catch (Exception ex) { Print("[SMT] calendar send failed: {0}", ex.Message); }
}
private void OnSocketText(WebSocketClientTextReceivedEventArgs args)
{
_received = true;
if (args.Text.StartsWith("Error:")) Print("[SMT] calendar: {0}", args.Text);
else ParseCsv(args.Text);
CloseSocket();
}
private void OnSocketDisconnected(WebSocketClientDisconnectEventArgs args)
{
if (!_received) DownloadOverHttp(); // desktop only: the cloud answers 503
}
private void DownloadOverHttp()
{
try
{
HttpResponse r = Http.Get(CalendarHttp + Query());
if (r.IsSuccessful) ParseCsv(r.Body);
else Print("[SMT] calendar HTTP failed: {0}", r.StatusCode);
}
catch (Exception ex) { Print("[SMT] calendar HTTP failed: {0}", ex.Message); }
}
private void CloseSocket()
{
WebSocketClient ws = _ws;
_ws = null;
if (ws == null) return;
try { ws.Connected -= OnSocketConnected; ws.TextReceived -= OnSocketText; ws.Disconnected -= OnSocketDisconnected; } catch (Exception) { }
try { if (ws.State == WebSocketClientState.Open) ws.Close(WebSocketClientCloseStatus.NormalClosure, "done"); } catch (Exception) { }
try { ws.Dispose(); } catch (Exception) { }
}
// CSV: ts,time,currency,impact,forecast,previous,actual,title (title last, may be quoted)
private void ParseCsv(string csv)
{
List<KeyValuePair<DateTime, string>> list = new List<KeyValuePair<DateTime, string>>();
foreach (string raw in csv.Split('\n'))
{
string line = raw.TrimEnd('\r');
if (line.Length == 0 || line.StartsWith("ts,")) continue;
string[] f = line.Split(',');
if (f.Length < 8) continue;
long ts;
if (!long.TryParse(f[0], out ts)) continue;
DateTime at = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(ts);
string title = AfterComma(line, 7).Replace("\"\"", "\"");
if (title.Length >= 2 && title[0] == '"') title = title.Substring(1, title.Length - 2);
list.Add(new KeyValuePair<DateTime, string>(at, f[2] + " " + f[3] + ": " + title));
}
_events.Clear();
_events.AddRange(list);
Print("[SMT] calendar: {0} events loaded", _events.Count);
}
private static string AfterComma(string line, int n)
{
int pos = -1;
for (int i = 0; i < n; i++)
{
pos = line.IndexOf(',', pos + 1);
if (pos < 0) return "";
}
return line.Substring(pos + 1);
}
}
}
Server.TimeInUtcis the comparison clock, so the broker's time zone never matters.- The backtester has no network: the filter is silent there, like in MetaTrader.
- The socket has no TLS (
ws://, notwss://): that is what cTrader Cloud connects with. The calendar is public and the request carries nothing about you or your account.
Python, JavaScript and everything else
Any HTTP client works. Python:
import requests
events = requests.get(
"https://showmytrades.com/api/public/calendar/v1",
params={"currency": "EUR,USD", "impact": "high,medium", "days": 7},
timeout=10,
).json()["events"]
for e in events:
print(e["time"], e["currency"], e["impact"], e["title"], e["forecast"], e["actual"])
JavaScript, in a browser too (CORS is open):
const res = await fetch('https://showmytrades.com/api/public/calendar/v1?currency=USD&impact=high&days=1')
const { events } = await res.json()
Polite polling with the ETag: keep the value from the last response and send it back; a 304
means nothing changed and has no body.
curl -i "https://showmytrades.com/api/public/calendar/v1?impact=high" \
-H 'If-None-Match: "a1b2c3d4e5f6a7b8c9d0"'
For AI assistants
The same data is a tool of the MCP server, get_economic_calendar, with the
same three parameters and no key. An assistant asked to "add a news filter to my EA" or
"what news is out today" can call it directly, and should point the user to the code on this
page for the robot itself.
Limits and terms
| Price | Free, no account, no key |
| Currencies | USD, EUR, GBP, JPY, AUD, NZD, CAD, CHF, CNY |
| Window | From 6 hours ago to 30 days ahead at most |
| Rate limit | 600 requests per minute per IP (HTTP), 60 messages per minute per IP (WebSocket) |
| Caching | 15 minutes, with ETag |
| Credit | Appreciated, not required: Economic calendar by ShowMyTrades, linked to the calendar page |
The calendar is assembled from public sources and served as-is, with no guarantee of completeness or timing; schedules change and some releases are moved or cancelled. It is information, not advice: whether to trade around a release is your decision.