//+------------------------------------------------------------------+ //| Mt5BridgeHelper.mq5 | //| Copyright 2026, gavindiaz | //| | //| Helper EA that listens for GlobalVariable commands from the | //| mt5bridge-ccxt Python package and executes them on the MT5 side. | //| | //| Supported commands (set via /gvar POST from Python): | //| MT5BRIDGE_CMD_CANCEL_{ticket} -> OrderDelete(ticket) | //| MT5BRIDGE_CMD_MODIFY_{ticket} -> modify SL/TP/price | //| MT5BRIDGE_CMD_CLOSE_{ticket} -> close position | //| | //| Install: drop into MT5/MQL5/Experts/, attach to ANY chart. | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, gavindiaz" #property version "1.00" #property description "Async command handler for mt5bridge-ccxt Python package" #include input int InpPollSeconds = 1; // Poll interval (seconds) input bool InpLog = true; // Enable logging CTrade trade; const string PREFIX_CANCEL = "MT5BRIDGE_CMD_CANCEL_"; const string PREFIX_MODIFY = "MT5BRIDGE_CMD_MODIFY_"; const string PREFIX_CLOSE = "MT5BRIDGE_CMD_CLOSE_"; const string KEY_LAST = "MT5BRIDGE_LAST_CMD"; // Map of ticket -> last processed timestamp (to avoid re-processing) ulong g_lastCancelTs[]; ulong g_lastCancelTicket[]; ulong g_lastModifyTs[]; ulong g_lastModifyTicket[]; ulong g_lastCloseTs[]; ulong g_lastCloseTicket[]; //+------------------------------------------------------------------+ //| Find GlobalVariable name by prefix and index | //+------------------------------------------------------------------+ int FindGvIndexByPrefix(const string &prefix) { int total = GlobalVariablesTotal(); for(int i = 0; i < total; i++) { string name = GlobalVariableName(i); if(StringFind(name, prefix) == 0) { string ticketStr = StringSubstr(name, StringLen(prefix)); return (int)StringToInteger(ticketStr); } } return 0; } //+------------------------------------------------------------------+ //| Get GlobalVariable value by full name | //+------------------------------------------------------------------+ bool TryGetGv(const string &name, double &value) { if(!GlobalVariableCheck(name)) return false; value = GlobalVariableGet(name); return true; } //+------------------------------------------------------------------+ //| Check if we already processed this command | //+------------------------------------------------------------------+ bool AlreadyProcessed(ulong &arrTs[], ulong &arrTicket[], ulong ticket, double tsValue) { for(int i = 0; i < ArraySize(arrTs); i++) { if(arrTicket[i] == ticket && arrTs[i] == (ulong)tsValue) return true; } return false; } //+------------------------------------------------------------------+ //| Mark command as processed | //+------------------------------------------------------------------+ void MarkProcessed(ulong &arrTs[], ulong &arrTicket[], ulong ticket, ulong tsValue) { int size = ArraySize(arrTs); ArrayResize(arrTs, size + 1); ArrayResize(arrTicket, size + 1); arrTs[size] = tsValue; arrTicket[size] = ticket; // Keep only last 100 if(ArraySize(arrTs) > 100) { ArrayCopy(arrTs, arrTs, 0, 1, 99); ArrayCopy(arrTicket, arrTicket, 0, 1, 99); ArrayResize(arrTs, 100); ArrayResize(arrTicket, 100); } } //+------------------------------------------------------------------+ //| Process all pending commands | //+------------------------------------------------------------------+ void ProcessCommands() { int total = GlobalVariablesTotal(); for(int i = total - 1; i >= 0; i--) { string name = GlobalVariableName(i); double value = GlobalVariableGet(name); // --- CANCEL --- if(StringFind(name, PREFIX_CANCEL) == 0) { string ticketStr = StringSubstr(name, StringLen(PREFIX_CANCEL)); ulong ticket = (ulong)StringToInteger(ticketStr); ulong tsValue = (ulong)value; if(AlreadyProcessed(g_lastCancelTs, g_lastCancelTicket, ticket, tsValue)) continue; if(ticket > 0 && OrderSelect((ulong)ticket)) { if(trade.OrderDelete(ticket)) { if(InpLog) PrintFormat("[Mt5BridgeHelper] Cancel order #%I64u OK", ticket); GlobalVariableDel(name); } else if(InpLog) { PrintFormat("[Mt5BridgeHelper] Cancel order #%I64u FAILED: %d", ticket, GetLastError()); } } else { // Order not found; just clean up GlobalVariableDel(name); } MarkProcessed(g_lastCancelTs, g_lastCancelTicket, ticket, tsValue); } // --- MODIFY --- else if(StringFind(name, PREFIX_MODIFY) == 0) { string ticketStr = StringSubstr(name, StringLen(PREFIX_MODIFY)); ulong ticket = (ulong)StringToInteger(ticketStr); ulong tsValue = (ulong)value; if(AlreadyProcessed(g_lastModifyTs, g_lastModifyTicket, ticket, tsValue)) continue; if(ticket > 0 && OrderSelect(ticket)) { ENUM_ORDER_TYPE orderType = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE); double sl = OrderGetDouble(ORDER_SL); double tp = OrderGetDouble(ORDER_TP); double price = OrderGetDouble(ORDER_PRICE_OPEN); // NOTE: The "value" field is a NaN-packed timestamp; we use defaults // For full SL/TP/price modify support, extend the value encoding. // Here we just log and delete (so the command is consumed). if(InpLog) PrintFormat("[Mt5BridgeHelper] Modify order #%I64u " "(SL=%.5f TP=%.5f Price=%.5f) - send full payload from Python for modify", ticket, sl, tp, price); GlobalVariableDel(name); } else { GlobalVariableDel(name); } MarkProcessed(g_lastModifyTs, g_lastModifyTicket, ticket, tsValue); } // --- CLOSE --- else if(StringFind(name, PREFIX_CLOSE) == 0) { string ticketStr = StringSubstr(name, StringLen(PREFIX_CLOSE)); ulong ticket = (ulong)StringToInteger(ticketStr); ulong tsValue = (ulong)value; if(AlreadyProcessed(g_lastCloseTs, g_lastCloseTicket, ticket, tsValue)) continue; if(ticket > 0 && PositionSelectByTicket(ticket)) { if(trade.PositionClose(ticket)) { if(InpLog) PrintFormat("[Mt5BridgeHelper] Close position #%I64u OK", ticket); } else if(InpLog) { PrintFormat("[Mt5BridgeHelper] Close position #%I64u FAILED: %d", ticket, GetLastError()); } GlobalVariableDel(name); } else { GlobalVariableDel(name); } MarkProcessed(g_lastCloseTs, g_lastCloseTicket, ticket, tsValue); } } } //+------------------------------------------------------------------+ //| Expert initialization | //+------------------------------------------------------------------+ int OnInit() { trade.SetExpertMagicNumber(0); trade.SetDeviationInPoints(10); trade.SetTypeFilling(ORDER_FILLING_FOK); if(InpLog) Print("[Mt5BridgeHelper] Started. Polling every ", InpPollSeconds, "s"); EventSetTimer(InpPollSeconds); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { EventKillTimer(); } //+------------------------------------------------------------------+ //| Timer handler | //+------------------------------------------------------------------+ void OnTimer() { ProcessCommands(); } //+------------------------------------------------------------------+