//+------------------------------------------------------------------+ //| EABiBot_Copier.mq4 | //| MT4 -> Binary brokers trade copier (license + bridge auth) | //+------------------------------------------------------------------+ #property strict #property version "6.00" #property description "MUBOT Copier — MT4 trade copier with license + bridge auth." //=================== Inputs ======================================== input string __sec0__ = "===== LICENSE + ACCOUNT ====="; input string LicenseKey = ""; // paste license from Admin tool input string BridgeUser = ""; // username from Admin tool input string BridgePass = ""; // password from Admin tool input string __sec1__ = "===== COPIER ====="; input bool EnableCopier = true; input string CopierURL = "https://mubot.live/api/signal.php"; input string LicenseURL = "https://mubot.live/api/license.php"; input string ApiKey = "eabot_test_2026"; input int ExpirySeconds = 60; input double Amount = 1.0; input int MagicFilter = 0; input bool OnlyMarketOrders = true; input string __sec2__ = "===== General ====="; input int PollMs = 1000; input int LicenseRecheckMin = 30; input bool DebugMode = true; //=================== Globals ======================================= int g_seenTickets[]; datetime g_startTime; string g_panelPrefix = "EABiBot_"; datetime g_lastPanelTs = 0; int g_sentCount = 0; int g_failCount = 0; string g_lastStatus = ""; bool g_licenseOk = false; string g_licenseInfo = "checking..."; datetime g_licenseLast = 0; //+------------------------------------------------------------------+ int OnInit() { g_startTime = TimeCurrent(); ArrayResize(g_seenTickets, 0); EventSetMillisecondTimer(PollMs); if (StringLen(LicenseKey) == 0 && StringLen(BridgeUser) == 0) { g_licenseOk = false; g_licenseInfo = "no license / username — paste from Admin tool"; Print("[MUBOT] ", g_licenseInfo); } else { CheckLicense(); } PrintFormat("[MUBOT] v6 init. Copier=%s License=%s", EnableCopier ? "ON" : "OFF", g_licenseOk ? "OK" : "INVALID"); ShowPanel(); return INIT_SUCCEEDED; } void OnDeinit(const int reason) { EventKillTimer(); ClearPanel(); } void OnTick() { if (EnableCopier && g_licenseOk) ScanOrders(); } void OnTimer() { if (TimeCurrent() - g_licenseLast >= LicenseRecheckMin * 60) CheckLicense(); if (EnableCopier && g_licenseOk) ScanOrders(); if (TimeCurrent() - g_lastPanelTs >= 1) ShowPanel(); } //=================================================================== // LICENSE //=================================================================== void CheckLicense() { g_licenseLast = TimeCurrent(); if (StringLen(LicenseKey) == 0) { g_licenseOk = false; g_licenseInfo = "no license key"; return; } string url = LicenseURL + "?license=" + LicenseKey; char post[]; char result[]; string headers = ""; string rh; ResetLastError(); int code = WebRequest("GET", url, headers, 5000, post, result, rh); if (code == -1) { g_licenseOk = false; g_licenseInfo = StringFormat("WebRequest err=%d (allow %s in MT4)", GetLastError(), LicenseURL); Print("[MUBOT] license check ", g_licenseInfo); return; } if (code < 200 || code >= 300) { g_licenseOk = false; g_licenseInfo = StringFormat("license HTTP %d", code); return; } string body = CharArrayToString(result, 0, ArraySize(result), CP_UTF8); bool ok = (StringFind(body, "\"ok\":true") >= 0); g_licenseOk = ok; if (!ok) { int p = StringFind(body, "\"error\":\""); string err = "invalid"; if (p >= 0) { p += 9; int q = StringFind(body, "\"", p); if (q > p) err = StringSubstr(body, p, q - p); } g_licenseInfo = err; } else { int dDays = ExtractInt(body, "\"daysLeft\":", -999); int dHours = ExtractInt(body, "\"hoursLeft\":", -999); string left = "OK"; if (dDays == -1) { left = "lifetime"; } else if (dDays >= 1) { left = IntegerToString(dDays) + " days left"; } else if (dHours >= 0) { left = IntegerToString(dHours) + " hours left"; } g_licenseInfo = left; } if (DebugMode) PrintFormat("[MUBOT] license: %s (%s)", g_licenseOk ? "OK" : "FAIL", g_licenseInfo); } int ExtractInt(string body, string key, int defVal) { int p = StringFind(body, key); if (p < 0) return defVal; p += StringLen(key); int q = StringFind(body, ",", p); if (q < 0) q = StringFind(body, "}", p); if (q <= p) return defVal; return (int)StringToInteger(StringSubstr(body, p, q - p)); } //=================================================================== // COPIER //=================================================================== void ScanOrders() { int total = OrdersTotal(); for (int i = 0; i < total; i++) { if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue; int ticket = OrderTicket(); int type = OrderType(); if (OnlyMarketOrders && type != OP_BUY && type != OP_SELL) continue; if (MagicFilter != 0 && OrderMagicNumber() != MagicFilter) continue; if (OrderOpenTime() < g_startTime) continue; if (CopierAlreadySent(ticket)) continue; string sym = OrderSymbol(); string dir = (type == OP_BUY) ? "CALL" : "PUT"; if (SendCopierSignal(ticket, sym, dir)) { CopierRememberTicket(ticket); g_sentCount++; } else { g_failCount++; } } } bool CopierAlreadySent(int t) { for (int i = 0; i < ArraySize(g_seenTickets); i++) if (g_seenTickets[i] == t) return true; return false; } void CopierRememberTicket(int t) { int n = ArraySize(g_seenTickets); ArrayResize(g_seenTickets, n + 1); g_seenTickets[n] = t; } bool SendCopierSignal(int ticket, string symbol, string dir) { // Bridge accepts EITHER license JWT OR username/password — we send both // so it works regardless of which path is configured. string payload = StringFormat( "{\"api_key\":\"%s\",\"license\":\"%s\",\"username\":\"%s\",\"password\":\"%s\"," "\"ticket\":%d,\"symbol\":\"%s\",\"direction\":\"%s\"," "\"amount\":%.2f,\"expiry\":%d,\"timestamp\":%d}", ApiKey, LicenseKey, BridgeUser, BridgePass, ticket, NormalizeSymbol(symbol), dir, Amount, ExpirySeconds, (int)TimeCurrent()); char post[]; StringToCharArray(payload, post, 0, StringLen(payload)); string headers = "Content-Type: application/json\r\n"; char result[]; string rh; ResetLastError(); int code = WebRequest("POST", CopierURL, headers, 5000, post, result, rh); if (code == -1) { g_lastStatus = StringFormat("WebRequest err=%d (allow %s)", GetLastError(), CopierURL); PrintFormat("[Copier] %s", g_lastStatus); return false; } if (code == 403) { g_lastStatus = "license/auth rejected by bridge"; g_licenseOk = false; g_licenseInfo = "rejected by bridge"; return false; } if (DebugMode) PrintFormat("[Copier] tkt=%d %s %s -> HTTP %d", ticket, symbol, dir, code); bool ok = (code >= 200 && code < 300); g_lastStatus = StringFormat("tkt=%d %s %s HTTP %d", ticket, symbol, dir, code); return ok; } string NormalizeSymbol(string s) { string up = s; StringToUpper(up); if (StringLen(up) > 6) { string head = StringSubstr(up, 0, 6); bool ok = true; for (int i = 0; i < 6; i++) { ushort c = StringGetCharacter(head, i); if (c < 'A' || c > 'Z') { ok = false; break; } } if (ok) return head; } return up; } //=================================================================== // STATUS PANEL //=================================================================== void ShowPanel() { g_lastPanelTs = TimeCurrent(); ClearPanel(); string lines[]; ArrayResize(lines, 0); AddLine(lines, StringFormat("MUBOT Copier v6 %s", EnableCopier ? "ON" : "OFF")); AddLine(lines, StringFormat("License: %s — %s", g_licenseOk ? "OK" : "INVALID", g_licenseInfo)); AddLine(lines, StringFormat("User: %s", StringLen(BridgeUser) > 0 ? BridgeUser : "(not set)")); AddLine(lines, StringFormat("Bridge: %s", CopierURL)); AddLine(lines, StringFormat("Sent: %d Failed: %d", g_sentCount, g_failCount)); if (StringLen(g_lastStatus) > 0) AddLine(lines, "Last: " + g_lastStatus); AddLine(lines, StringFormat("Amount: %.2f Expiry: %ds", Amount, ExpirySeconds)); for (int i = 0; i < ArraySize(lines); i++) { string nm = g_panelPrefix + IntegerToString(i); ObjectCreate(0, nm, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, nm, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, nm, OBJPROP_XDISTANCE, 10); ObjectSetInteger(0, nm, OBJPROP_YDISTANCE, 20 + i * 16); color clr = clrWhite; if (i == 0) clr = clrYellow; if (i == 1) clr = g_licenseOk ? clrLime : clrTomato; ObjectSetInteger(0, nm, OBJPROP_COLOR, clr); ObjectSetInteger(0, nm, OBJPROP_FONTSIZE, 10); ObjectSetString (0, nm, OBJPROP_FONT, "Consolas"); ObjectSetString (0, nm, OBJPROP_TEXT, lines[i]); } ChartRedraw(0); } void AddLine(string &arr[], string s) { int n = ArraySize(arr); ArrayResize(arr, n + 1); arr[n] = s; } void ClearPanel() { int total = ObjectsTotal(0, -1, -1); for (int i = total - 1; i >= 0; i--) { string nm = ObjectName(0, i); if (StringFind(nm, g_panelPrefix) == 0) ObjectDelete(0, nm); } } //+------------------------------------------------------------------+