MetaTrader 5 Publisher EA
Feature Coming Soon
Publisher Sync (auto-sync via EA) is currently under maintenance. Use Statement Import instead.
MT5 Installation Steps
- 1.Download
YourApp_PublisherEA.ex5 - 2.Open MT5 → File → Open Data Folder
- 3.Place file in:
MQL5/Experts/ - 4.Restart MT5
- 5.Go to Tools → Options → Expert Advisors
- 6.Check "Allow WebRequest for listed URL"
- 7.Add your domain:
https://myfxjourney.com - 8.Open a chart and drag the EA onto it
- 9.Configure Inputs tab (see configuration below)
- 10.Ensure "Algo Trading" is enabled (top-right icon)
Input Configuration (On Chart)
PublisherUrl
Your app's publisher endpoint URL (without token)
Example: https://yourapp.com/api/publisher/mt5
PublisherToken
Your authentication token (copy from Connect Account wizard)
Example: pub_1234567890_abc123xyz
SyncIntervalSeconds
How often to sync (default: 900 = 15 minutes)
BackfillDays
How many days back to sync (default: 7)
YourApp_PublisherEA.mq5
MetaTrader 5 Source Code (compile to .ex5)
#property strict
#property version "1.00"
#property description "MyFXJourney Publisher EA (read-only) - posts account snapshots to your endpoint."
input string PublisherUrl = "https://mt5key.ttcapitaltradingfx-514.workers.dev"; // Cloudflare proxy URL
input string PublisherToken = "pub_XXXXXXXXXXXXXXXXXXXX";
input string ApiKey = ""; // Optional (only if your endpoint requires it)
input int SendIntervalSeconds = 60;
input int LookbackDays = 7; // history window for deals_count
input bool SendPingOnly = false; // true = only {"ping":true}
input bool DebugPrint = true;
datetime g_lastSent = 0;
// --- JSON escape helper (minimal but safe for our fields) ---
string JsonEscape(const string sIn)
{
string s = sIn;
s = StringReplace(s, "\\", "\\\\");
s = StringReplace(s, "\"", "\\\"");
s = StringReplace(s, "\r", "\\r");
s = StringReplace(s, "\n", "\\n");
return s;
}
string IsoTime(datetime t)
{
// ISO-like without timezone: YYYY.MM.DD HH:MI:SS -> YYYY-MM-DDTHH:MI:SS
string a = TimeToString(t, TIME_DATE|TIME_SECONDS);
a = StringReplace(a, ".", "-");
a = StringReplace(a, " ", "T");
return a;
}
string BuildPayload(datetime fromTime, datetime toTime)
{
// Account metadata
string server = AccountInfoString(ACCOUNT_SERVER);
long login = (long)AccountInfoInteger(ACCOUNT_LOGIN);
string company = AccountInfoString(ACCOUNT_COMPANY);
string name = AccountInfoString(ACCOUNT_NAME);
string currency = AccountInfoString(ACCOUNT_CURRENCY);
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double margin = AccountInfoDouble(ACCOUNT_MARGIN);
double free = AccountInfoDouble(ACCOUNT_FREEMARGIN);
int dealsCount = 0;
if(HistorySelect(fromTime, toTime))
dealsCount = (int)HistoryDealsTotal();
// Build JSON safely with StringFormat to avoid syntax errors + warnings
string json = StringFormat(
"{"
"\"from\":\"%s\","
"\"to\":\"%s\","
"\"account\":{"
"\"login\":%I64d,"
"\"server\":\"%s\","
"\"company\":\"%s\","
"\"name\":\"%s\","
"\"currency\":\"%s\""
"},"
"\"equity\":{"
"\"balance\":%s,"
"\"equity\":%s,"
"\"margin\":%s,"
"\"free_margin\":%s"
"},"
"\"deals_count\":%d"
"}",
IsoTime(fromTime),
IsoTime(toTime),
login,
JsonEscape(server),
JsonEscape(company),
JsonEscape(name),
JsonEscape(currency),
DoubleToString(balance, 2),
DoubleToString(equity, 2),
DoubleToString(margin, 2),
DoubleToString(free, 2),
dealsCount
);
return json;
}
bool PostJson(const string url, const string body, string &responseBody, int &httpCode)
{
responseBody = "";
httpCode = -1;
// IMPORTANT: Headers MUST be a single string with \r\n line breaks
string headers = "Content-Type: application/json\r\n";
headers += "X-Publisher-Token: " + PublisherToken + "\r\n";
if(StringLen(ApiKey) > 0)
headers += "api_key: " + ApiKey + "\r\n";
char data[];
StringToCharArray(body, data, 0, WHOLE_ARRAY, CP_UTF8);
char result[];
string result_headers = "";
ResetLastError();
// WebRequest signatures vary; this one is the most common/compatible in MT5:
// int WebRequest(const string method,const string url,const string headers,int timeout,
// const char &data[],char &result[],string &result_headers);
httpCode = WebRequest("POST", url, headers, 15000, data, result, result_headers);
if(httpCode == -1)
{
int err = GetLastError();
responseBody = StringFormat("WebRequest failed. GetLastError=%d. Make sure URL is whitelisted in Tools->Options->Expert Advisors.", err);
return false;
}
responseBody = CharArrayToString(result, 0, -1, CP_UTF8);
if(DebugPrint)
{
Print("POST ", url);
Print("HTTP ", httpCode);
if(StringLen(result_headers) > 0) Print("Resp headers: ", result_headers);
if(StringLen(responseBody) > 0) Print("Resp body: ", responseBody);
}
return (httpCode >= 200 && httpCode < 300);
}
void SendNow()
{
if(StringLen(PublisherUrl) < 10 || StringFind(PublisherUrl, "http") != 0)
{
Print("PublisherUrl looks invalid. Use the Base44 FUNCTION URL (API Information -> Function URL).");
return;
}
if(StringLen(PublisherToken) < 5)
{
Print("PublisherToken missing/too short.");
return;
}
string body;
if(SendPingOnly)
{
body = "{\"ping\":true}";
}
else
{
datetime toTime = TimeCurrent();
datetime fromTime = toTime - (datetime)(LookbackDays * 86400);
body = BuildPayload(fromTime, toTime);
}
string resp;
int code = -1;
bool ok = PostJson(PublisherUrl, body, resp, code);
if(!ok)
Print("Send FAILED. httpCode=", code, " resp=", resp);
else
Print("Send OK. httpCode=", code);
}
int OnInit()
{
EventSetTimer(MathMax(5, SendIntervalSeconds));
Print("MyFXJourneyPublisher started. Interval=", SendIntervalSeconds, "s");
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
EventKillTimer();
}
void OnTimer()
{
// simple throttle (in case timer overlaps)
datetime now = TimeCurrent();
if(g_lastSent != 0 && (now - g_lastSent) < (datetime)SendIntervalSeconds)
return;
g_lastSent = now;
SendNow();
}Features
- Read-only: no trade permissions needed
- Auto-syncs closed deals every 15 minutes
- Captures profit, commission, swap, and equity
- 7-day backfill for reconciliation
- Uses Global Variables for persistent cursor
- Debug logging in MT5 Journal
