Files
mt5bridge-ccxt/mql5/Mt5BridgeHelper.mq5

231 lines
8.6 KiB
Plaintext

//+------------------------------------------------------------------+
//| 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 <Trade\Trade.mqh>
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();
}
//+------------------------------------------------------------------+