Files

388 lines
16 KiB
Plaintext

//+------------------------------------------------------------------+
//| MadTurtle.mq5 |
//| Improved ML EA for XAUUSD H1 |
//+------------------------------------------------------------------+
#property copyright "Mad Turtle v2.0"
#property link ""
#property version "2.00"
#property strict
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\AccountInfo.mqh>
#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);
}
//+------------------------------------------------------------------+