//+------------------------------------------------------------------+ //| MadTurtle.mq5 | //| Improved ML EA for XAUUSD H1 | //+------------------------------------------------------------------+ #property copyright "Mad Turtle v2.0" #property link "" #property version "2.00" #property strict #include #include #include #include "MadTurtle_API.mqh" #include "MadTurtle_MTF.mqh" #include "MadTurtle_UI.mqh" //+------------------------------------------------------------------+ //| Inputs - Server | //+------------------------------------------------------------------+ input string InpServerHost = "127.0.0.1"; input int InpServerPort = 8000; input string InpApiToken = ""; input bool InpUseSSL = false; input int InpRequestTimeout = 8000; input int InpRetryCount = 2; //+------------------------------------------------------------------+ //| Inputs - Trading | //+------------------------------------------------------------------+ input double InpLotSize = 0.01; input int InpMagicNumber = 20250613; input int InpSlippage = 10; input int InpStopLossPts = 0; input int InpTakeProfitPts = 0; input int InpMaxPositions = 1; input int InpMinConfidence = 30; //+------------------------------------------------------------------+ //| Inputs - Multi-Timeframe | //+------------------------------------------------------------------+ input bool InpEnableMTF = true; input bool InpRequireH4Match = true; input bool InpRequireD1Match = false; //+------------------------------------------------------------------+ //| Inputs - UI | //+------------------------------------------------------------------+ input bool InpShowUI = true; //+------------------------------------------------------------------+ //| Globals | //+------------------------------------------------------------------+ MadTurtleServerCfg g_server; CTrade g_trade; CPositionInfo g_posInfo; CAccountInfo g_account; MadTurtleMTFState g_mtf; MadTurtleAPIResponse g_lastAPIResp; double g_equityCurve[]; double g_totalProfit; double g_maxDrawdown; double g_lastTradeProfit; string g_lastSignal; double g_buyConf; double g_sellConf; int g_positionsOpen; int g_cpuCores; double g_ramUsed; //+------------------------------------------------------------------+ //| Expert initialization | //+------------------------------------------------------------------+ int OnInit() { g_server = MadTurtle_InitDefaultServer(); g_server.host = InpServerHost; g_server.port = InpServerPort; g_server.api_token = InpApiToken; g_server.timeout_ms = InpRequestTimeout; g_server.retry_count = InpRetryCount; g_server.use_ssl = InpUseSSL; g_trade.SetExpertMagicNumber(InpMagicNumber); g_trade.SetDeviationInPoints(InpSlippage); g_trade.SetAsyncMode(false); ArrayResize(g_equityCurve, 500); ArrayInitialize(g_equityCurve, 0); g_totalProfit = 0; g_maxDrawdown = 0; g_lastTradeProfit = 0; g_lastSignal = "NONE"; g_buyConf = 0; g_sellConf = 0; g_positionsOpen = 0; ZeroMemory(g_mtf); g_mtf.h4_bias = -1; g_mtf.d1_bias = -1; if(InpShowUI) { MadTurtle_DrawPanel(UI_STATUS_X, UI_STATUS_Y, UI_STATUS_W, UI_STATUS_H); MadTurtle_DrawPanel(UI_METRICS_X, UI_METRICS_Y, UI_METRICS_W, UI_METRICS_H); MadTurtle_DrawPanel(UI_OSC_X, UI_OSC_Y, UI_OSC_W, UI_OSC_H); } Print("Mad Turtle v2.0 initialized. Server: ", g_server.host, ":", g_server.port); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Main tick loop | //+------------------------------------------------------------------+ void OnTick() { UpdateEquityCurve(); UpdateSystemInfo(); MadTurtle_UpdateMTFState(g_mtf); g_positionsOpen = CountOpenPositions(); double features[]; ExtractFeatures(features); if(ArraySize(features) == 14) { g_lastAPIResp = MadTurtle_GetSignal(g_server, features); if(g_lastAPIResp.success) { ParseSignalResponse(g_lastAPIResp.body); } } if(g_positionsOpen < InpMaxPositions) { if(StringCompare(g_lastSignal, "BUY") == 0 && g_buyConf >= InpMinConfidence) { if(InpEnableMTF && InpRequireH4Match) { if(!MadTurtle_MTF_ConfirmBuy(g_mtf)) return; } OpenPosition(ORDER_TYPE_BUY); } else if(StringCompare(g_lastSignal, "SELL") == 0 && g_sellConf >= InpMinConfidence) { if(InpEnableMTF && InpRequireH4Match) { if(!MadTurtle_MTF_ConfirmSell(g_mtf)) return; } OpenPosition(ORDER_TYPE_SELL); } } ManageOpenPositions(); if(InpShowUI) { DrawStatusPanel(); DrawMetricsPanel(); MadTurtle_DrawOscillator(g_buyConf, g_sellConf, UI_OSC_X + 5, UI_OSC_Y + 15, UI_OSC_W - 10, UI_OSC_H - 30); DrawSignalArrows(); } } //+------------------------------------------------------------------+ //| Extract 14 features from current H1 bars | //+------------------------------------------------------------------+ void ExtractFeatures(double &feats[]) { ArrayResize(feats, 14); double open1 = iOpen(_Symbol, PERIOD_H1, 1); double close1 = iClose(_Symbol, PERIOD_H1, 1); double close3 = iClose(_Symbol, PERIOD_H1, 3); double close6 = iClose(_Symbol, PERIOD_H1, 6); double close = iClose(_Symbol, PERIOD_H1, 0); double high1 = iHigh(_Symbol, PERIOD_H1, 1); double low1 = iLow(_Symbol, PERIOD_H1, 1); feats[0] = MathLog(close1 / open1); feats[1] = MathLog(close1 / close3); feats[2] = MathLog(close1 / close6); feats[3] = MadTurtle_GetMA(PERIOD_H1, 10, 1) / close; feats[4] = MadTurtle_GetMA(PERIOD_H1, 20, 1) / close; feats[5] = MadTurtle_GetMA(PERIOD_H1, 50, 1) / close; feats[6] = (MadTurtle_GetMA(PERIOD_H1, 12, 1) - MadTurtle_GetMA(PERIOD_H1, 26, 1)) / close; feats[7] = feats[6]; feats[8] = MadTurtle_GetRSI(PERIOD_H1, 14, 1) / 100.0; feats[9] = MadTurtle_GetATR(PERIOD_H1, 14, 1) / (close + 1e-9); feats[10] = feats[9]; long vol1 = iVolume(_Symbol, PERIOD_H1, 1); long vol2 = iVolume(_Symbol, PERIOD_H1, 2); feats[11] = vol2 > 0 ? (double)vol1 / (double)vol2 : 1.0; feats[12] = (high1 - low1) / (close + 1e-9); feats[13] = (close - MadTurtle_GetMA(PERIOD_H1, 20, 1)) / (close + 1e-9); } //+------------------------------------------------------------------+ //| Parse JSON response from inference server | //+------------------------------------------------------------------+ void ParseSignalResponse(string json) { g_lastSignal = "NONE"; g_buyConf = 0; g_sellConf = 0; string sig = StringSubstr(json, StringFind(json, "\"signal\":\"") + 10); sig = StringSubstr(sig, 0, StringFind(sig, "\"")); if(StringLen(sig) > 0) g_lastSignal = sig; string conf = StringSubstr(json, StringFind(json, "\"confidence\":") + 13); conf = StringSubstr(conf, 0, StringFind(conf, ",")); if(StringLen(conf) > 0) { double c = StringToDouble(conf); if(g_lastSignal == "BUY") g_buyConf = c * 100.0; if(g_lastSignal == "SELL") g_sellConf = c * 100.0; } string buy = StringSubstr(json, StringFind(json, "\"buy_prob\":") + 11); buy = StringSubstr(buy, 0, StringFind(buy, ",")); if(StringLen(buy) > 0) g_buyConf = StringToDouble(buy) * 100.0; string sell = StringSubstr(json, StringFind(json, "\"sell_prob\":") + 12); sell = StringSubstr(sell, 0, StringFind(sell, ",")); if(StringLen(sell) > 0) g_sellConf = StringToDouble(sell) * 100.0; } //+------------------------------------------------------------------+ //| Open trade position | //+------------------------------------------------------------------+ void OpenPosition(ENUM_ORDER_TYPE type) { double price = (type == ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID); double sl = 0, tp = 0; double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); if(InpStopLossPts > 0) { sl = (type == ORDER_TYPE_BUY) ? price - InpStopLossPts * point : price + InpStopLossPts * point; } if(InpTakeProfitPts > 0) { tp = (type == ORDER_TYPE_BUY) ? price + InpTakeProfitPts * point : price - InpTakeProfitPts * point; } if(type == ORDER_TYPE_BUY) { g_trade.Buy(InpLotSize, _Symbol, price, sl, tp, "MadTurtle"); } else { g_trade.Sell(InpLotSize, _Symbol, price, sl, tp, "MadTurtle"); } if(g_trade.ResultRetcode() == TRADE_RETCODE_DONE) { Print("Opened ", EnumToString(type), " @ ", price); } else { Print("Open failed: ", g_trade.ResultRetcodeDescription()); } } //+------------------------------------------------------------------+ //| Manage SL/TP on open positions | //+------------------------------------------------------------------+ void ManageOpenPositions() { for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(PositionSelectByTicket(ticket)) { if(PositionGetInteger(POSITION_MAGIC) == InpMagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol) { } } } } //+------------------------------------------------------------------+ //| Count open positions for this EA | //+------------------------------------------------------------------+ int CountOpenPositions() { int count = 0; for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(PositionSelectByTicket(ticket)) { if(PositionGetInteger(POSITION_MAGIC) == InpMagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol) { count++; } } } return count; } //+------------------------------------------------------------------+ //| Update equity curve array | //+------------------------------------------------------------------+ void UpdateEquityCurve() { double equity = AccountInfoDouble(ACCOUNT_EQUITY); static int idx = 0; if(idx >= ArraySize(g_equityCurve)) { ArrayCopy(g_equityCurve, g_equityCurve, 0, 1, ArraySize(g_equityCurve) - 1); g_equityCurve[ArraySize(g_equityCurve) - 1] = equity; } else { g_equityCurve[idx++] = equity; } g_totalProfit = equity - AccountInfoDouble(ACCOUNT_BALANCE); if(g_totalProfit < 0 && MathAbs(g_totalProfit) > g_maxDrawdown) { g_maxDrawdown = MathAbs(g_totalProfit); } } //+------------------------------------------------------------------+ //| Collect system info | //+------------------------------------------------------------------+ void UpdateSystemInfo() { g_ramUsed = (double)TerminalInfoInteger(TERMINAL_MEMORY_USED) / (1024.0 * 1024.0); g_cpuCores = (int)TerminalInfoInteger(TERMINAL_CPU_CORES); g_positionsOpen = CountOpenPositions(); } //+------------------------------------------------------------------+ //| UI: Status Dashboard | //+------------------------------------------------------------------+ void DrawStatusPanel() { int yOff = 18; MadTurtle_DrawLabel("ui_status_ready", "READY: GOOD", UI_STATUS_X + 8, UI_STATUS_Y + 8, 9, UI_COLOR_PROFIT); MadTurtle_DrawLabel("ui_status_conn", "CONNECTING: 0 ms", UI_STATUS_X + 8, UI_STATUS_Y + yOff, 8); yOff += 16; MadTurtle_DrawLabel("ui_status_cpu", "CPU Core: " + IntegerToString(g_cpuCores), UI_STATUS_X + 8, UI_STATUS_Y + yOff, 8); yOff += 16; MadTurtle_DrawLabel("ui_status_spread", "SPREAD: " + IntegerToString((int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD)), UI_STATUS_X + 8, UI_STATUS_Y + yOff, 8); yOff += 16; MadTurtle_DrawLabel("ui_status_build", "BUILD: " + IntegerToString((int)TerminalInfoInteger(TERMINAL_BUILD)), UI_STATUS_X + 8, UI_STATUS_Y + yOff, 8); yOff += 16; MadTurtle_DrawLabel("ui_status_leverage", "LEVERAGE: 1:" + IntegerToString((int)AccountInfoInteger(ACCOUNT_LEVERAGE)), UI_STATUS_X + 8, UI_STATUS_Y + yOff, 8); yOff += 16; MadTurtle_DrawLabel("ui_status_netting", "NETTING: NO", UI_STATUS_X + 8, UI_STATUS_Y + yOff, 8); yOff += 16; MadTurtle_DrawLabel("ui_status_ram", "RAM Used: " + DoubleToString(g_ramUsed, 1) + " GB", UI_STATUS_X + 8, UI_STATUS_Y + yOff, 8); yOff += 16; MadTurtle_DrawLabel("ui_status_stopout", "STOPOUT: 80%", UI_STATUS_X + 8, UI_STATUS_Y + yOff, 8); yOff += 16; MadTurtle_DrawLabel("ui_status_dll", "DLLs OFF: OK", UI_STATUS_X + 8, UI_STATUS_Y + yOff, 8); } //+------------------------------------------------------------------+ //| UI: Metrics / P&L box | //+------------------------------------------------------------------+ void DrawMetricsPanel() { MadTurtle_DrawLabel("ui_metrics_last", "LAST: " + DoubleToString(g_lastTradeProfit, 2), UI_METRICS_X + 8, UI_METRICS_Y + 8, 9, UI_COLOR_PROFIT); MadTurtle_DrawLabel("ui_metrics_total", "TOTAL: " + DoubleToString(g_totalProfit, 2), UI_METRICS_X + 8, UI_METRICS_Y + 30, 9, UI_COLOR_PROFIT); MadTurtle_DrawLabel("ui_metrics_dd", "DRAWDOWN: " + DoubleToString(-g_maxDrawdown, 2), UI_METRICS_X + 8, UI_METRICS_Y + 52, 9, UI_COLOR_LOSS); MadTurtle_DrawEquityCurve(g_equityCurve, UI_METRICS_X + 8, UI_METRICS_Y + 74, UI_METRICS_W - 16, 40, UI_COLOR_PROFIT); } //+------------------------------------------------------------------+ //| Chart signal arrows | //+------------------------------------------------------------------+ void DrawSignalArrows() { static datetime lastArrowTime = 0; if(lastArrowTime == iTime(_Symbol, PERIOD_H1, 0)) return; lastArrowTime = iTime(_Symbol, PERIOD_H1, 0); if(StringCompare(g_lastSignal, "BUY") == 0) { CreateArrow("sig_buy_" + IntegerToString((int)lastArrowTime), UI_COLOR_BUY, lastArrowTime, iLow(_Symbol, PERIOD_H1, 0) - 10 * _Point); } else if(StringCompare(g_lastSignal, "SELL") == 0) { CreateArrow("sig_sell_" + IntegerToString((int)lastArrowTime), UI_COLOR_SELL, lastArrowTime, iHigh(_Symbol, PERIOD_H1, 0) + 10 * _Point); } } void CreateArrow(string name, color clr, datetime time, double price) { if(ObjectFind(0, name) >= 0) ObjectDelete(0, name); ObjectCreate(0, name, OBJ_ARROW, 0, time, price); ObjectSetInteger(0, name, OBJPROP_COLOR, clr); ObjectSetInteger(0, name, OBJPROP_WIDTH, 2); ObjectSetInteger(0, name, OBJPROP_ARROWCODE, (clr == UI_COLOR_BUY) ? 233 : 234); ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, name, OBJPROP_HIDDEN, true); } //+------------------------------------------------------------------+ //| Deinit | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { MadTurtle_CleanupUI(); Print("Mad Turtle stopped. Reason: ", reason); } //+------------------------------------------------------------------+