Connected terminals

Demo terminals shown; real terminals report in once the EA polls with your key.

API keys

No keys yet. Generate one to authenticate your EA.

Webhook tester · GET /api/signals/latest

https://your-app.lovable.app/api/public/signals/latest
// response appears here

DumbyBeatle_EA.mq5

  1. Copy the file into MQL5/Experts and compile in MetaEditor.
  2. Whitelist https://your-app.lovable.app under Tools → Options → Expert Advisors → WebRequest.
  3. Attach to any chart, paste your API key, enable Algo Trading.
//+------------------------------------------------------------------+
//|                                             DumbyBeatle_EA.mq5    |
//|            DumbyBeatle Quant Engine - MetaTrader 5 Cloud Bridge   |
//+------------------------------------------------------------------+
#property copyright "DumbyBeatle"
#property version   "1.00"
#property strict
#include <Trade\Trade.mqh>

input string InpApiUrl        = "https://your-app.lovable.app/api/public/signals/latest";   // Signal endpoint (add to Tools>Options>Expert Advisors>WebRequest)
input string InpApiKey        = "dbk_live_xxx";      // Your API key
input int    InpPollSeconds   = 5;                   // Poll interval
input int    InpMaxSpreadPts  = 30;                  // Spread guard (points)
input bool   InpUseKillzones  = true;                // Trade only London / NY sessions
input int    InpLondonStart   = 7;                   // London open (server hour)
input int    InpLondonEnd     = 11;
input int    InpNYStart       = 12;                  // New York open (server hour)
input int    InpNYEnd         = 16;
input double InpMaxLots       = 5.0;                 // Hard lot cap
input long   InpMagic         = 770077;

CTrade   trade;
string   g_lastId = "";

int OnInit()
{
   trade.SetExpertMagicNumber(InpMagic);
   EventSetTimer(InpPollSeconds);
   Print("DumbyBeatle EA initialised. Polling ", InpApiUrl);
   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason) { EventKillTimer(); }

void OnTimer()
{
   string body;
   if(!FetchSignal(body)) return;
   string cmd = JsonGet(body, "command");
   if(cmd == "CLOSE_ALL" || cmd == "FLATTEN") { EmergencyClose(); return; }
   if(cmd != "OPEN") return;

   string id = JsonGet(body, "id");
   if(id == g_lastId) return;

   string sym = JsonGet(body, "symbol");
   string act = JsonGet(body, "action");
   double sl  = StringToDouble(JsonGet(body, "sl"));
   double tp  = StringToDouble(JsonGet(body, "tp"));
   double lot = MathMin(StringToDouble(JsonGet(body, "lots")), InpMaxLots);

   if(!SymbolSelect(sym, true)) { Print("Symbol not available: ", sym); return; }
   if(!SpreadOk(sym))   { Print("Spread guard blocked ", sym); return; }
   if(InpUseKillzones && !InKillzone()) { Print("Outside killzone, skip ", sym); return; }

   bool ok = (act == "BUY") ? trade.Buy(lot, sym, 0, sl, tp, "DB:" + id)
                            : trade.Sell(lot, sym, 0, sl, tp, "DB:" + id);
   if(ok) { g_lastId = id; Print("Executed ", act, " ", sym, " lots=", lot); }
   else    Print("Order failed: ", trade.ResultRetcodeDescription());
}

bool FetchSignal(string &out)
{
   char   post[], result[];
   string headers = "X-API-Key: " + InpApiKey + "\r\nContent-Type: application/json\r\n";
   string resHeaders;
   ResetLastError();
   int code = WebRequest("GET", InpApiUrl, headers, 5000, post, result, resHeaders);
   if(code == -1) { Print("WebRequest error ", GetLastError(), " - whitelist the URL"); return false; }
   if(code != 200) { Print("HTTP ", code); return false; }
   out = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8);
   return true;
}

bool SpreadOk(string sym)
{
   long spread = SymbolInfoInteger(sym, SYMBOL_SPREAD);
   return spread <= InpMaxSpreadPts;
}

bool InKillzone()
{
   MqlDateTime t; TimeToStruct(TimeCurrent(), t);
   return (t.hour >= InpLondonStart && t.hour < InpLondonEnd) ||
          (t.hour >= InpNYStart && t.hour < InpNYEnd);
}

void EmergencyClose()
{
   Print("!!! EMERGENCY CLOSE_ALL received !!!");
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0) continue;
      if(PositionGetInteger(POSITION_MAGIC) != InpMagic) continue;
      trade.PositionClose(ticket);
   }
   for(int j = OrdersTotal() - 1; j >= 0; j--)
   {
      ulong t = OrderGetTicket(j);
      if(t != 0 && OrderGetInteger(ORDER_MAGIC) == InpMagic) trade.OrderDelete(t);
   }
}

// Minimal flat JSON reader: "key":"value" or "key":number
string JsonGet(string json, string key)
{
   string pat = "\"" + key + "\":";
   int p = StringFind(json, pat);
   if(p < 0) return "";
   p += StringLen(pat);
   bool quoted = (StringGetCharacter(json, p) == '"');
   if(quoted) p++;
   int e = p;
   int n = StringLen(json);
   while(e < n)
   {
      ushort c = StringGetCharacter(json, e);
      if(quoted && c == '"') break;
      if(!quoted && (c == ',' || c == '}')) break;
      e++;
   }
   return StringSubstr(json, p, e - p);
}
//+------------------------------------------------------------------+