Initial commit: Mad Turtle v2.0 ML EA for XAUUSD H1 with Python inference server and MQL5 EA
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| MadTurtle_API.mqh - HTTP REST bridge to Python inference server |
|
||||
//+------------------------------------------------------------------+
|
||||
#ifndef MADTURTLE_API
|
||||
#define MADTURTLE_API
|
||||
|
||||
#import "wininet.dll"
|
||||
int InternetOpenW(string, int, string, string, int);
|
||||
int InternetOpenUrlW(int, string, string, int, int, int);
|
||||
int InternetReadFile(int, uchar &[], int, int &);
|
||||
int InternetCloseHandle(int);
|
||||
#import
|
||||
|
||||
// Timeout and buffer constants
|
||||
#define MT5_API_TIMEOUT_MS 8000
|
||||
#define MT5_API_BUFFER_SIZE 8192
|
||||
|
||||
// Server connection config
|
||||
struct MadTurtleServerCfg {
|
||||
string host;
|
||||
int port;
|
||||
string api_token;
|
||||
int timeout_ms;
|
||||
int retry_count;
|
||||
bool use_ssl;
|
||||
};
|
||||
|
||||
// API response container
|
||||
struct MadTurtleAPIResponse {
|
||||
int http_code;
|
||||
string body;
|
||||
bool success;
|
||||
string error_msg;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Initialize default server config |
|
||||
//+------------------------------------------------------------------+
|
||||
MadTurtleServerCfg MadTurtle_InitDefaultServer()
|
||||
{
|
||||
MadTurtleServerCfg cfg;
|
||||
cfg.host = "127.0.0.1";
|
||||
cfg.port = 8000;
|
||||
cfg.api_token = "";
|
||||
cfg.timeout_ms = MT5_API_TIMEOUT_MS;
|
||||
cfg.retry_count = 2;
|
||||
cfg.use_ssl = false;
|
||||
return cfg;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Build full URL from path |
|
||||
//+------------------------------------------------------------------+
|
||||
string MadTurtle_BuildURL(const MadTurtleServerCfg &cfg, string path)
|
||||
{
|
||||
string proto = cfg.use_ssl ? "https" : "http";
|
||||
return StringFormat("%s://%s:%d%s", proto, cfg.host, cfg.port, path);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| HTTP GET request |
|
||||
//+------------------------------------------------------------------+
|
||||
MadTurtleAPIResponse MadTurtle_HTTPGet(const MadTurtleServerCfg &cfg, string path)
|
||||
{
|
||||
MadTurtleAPIResponse resp;
|
||||
resp.http_code = 0;
|
||||
resp.success = false;
|
||||
resp.body = "";
|
||||
resp.error_msg = "";
|
||||
|
||||
int hInternet = InternetOpenW("MadTurtleEA/2.0", 1, NULL, NULL, 0);
|
||||
if(hInternet == 0) {
|
||||
resp.error_msg = "InternetOpenW failed";
|
||||
return resp;
|
||||
}
|
||||
|
||||
string url = MadTurtle_BuildURL(cfg, path);
|
||||
int hUrl = InternetOpenUrlW(hInternet, url, NULL, 0, 0x04000000, 0);
|
||||
if(hUrl == 0) {
|
||||
InternetCloseHandle(hInternet);
|
||||
resp.error_msg = "InternetOpenUrlW failed: " + IntegerToString(GetLastError());
|
||||
return resp;
|
||||
}
|
||||
|
||||
uchar buf[];
|
||||
ArrayResize(buf, MT5_API_BUFFER_SIZE);
|
||||
int bytesRead = 0;
|
||||
string body = "";
|
||||
|
||||
while(true) {
|
||||
int res = InternetReadFile(hUrl, buf, MT5_API_BUFFER_SIZE, bytesRead);
|
||||
if(res == 0 || bytesRead == 0) break;
|
||||
body += CharArrayToString(buf, 0, bytesRead, CP_UTF8);
|
||||
}
|
||||
|
||||
resp.body = body;
|
||||
resp.http_code = 200;
|
||||
resp.success = (StringLen(body) > 0);
|
||||
|
||||
InternetCloseHandle(hUrl);
|
||||
InternetCloseHandle(hInternet);
|
||||
return resp;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Call inference server with feature vector (GET) |
|
||||
//+------------------------------------------------------------------+
|
||||
MadTurtleAPIResponse MadTurtle_GetSignal(const MadTurtleServerCfg &cfg, double &features[])
|
||||
{
|
||||
MadTurtleAPIResponse resp;
|
||||
resp.http_code = 0;
|
||||
resp.success = false;
|
||||
resp.body = "";
|
||||
resp.error_msg = "";
|
||||
|
||||
int n = ArraySize(features);
|
||||
if(n == 0) {
|
||||
resp.error_msg = "Empty feature vector";
|
||||
return resp;
|
||||
}
|
||||
|
||||
string qs = "?";
|
||||
for(int i = 0; i < n; i++) {
|
||||
if(i > 0) qs += "&";
|
||||
qs += StringFormat("f%d=%.8f", i, features[i]);
|
||||
}
|
||||
|
||||
resp = MadTurtle_HTTPGet(cfg, "/v1/signal" + qs);
|
||||
|
||||
for(int attempt = 1; attempt < cfg.retry_count && !resp.success; attempt++) {
|
||||
Sleep(500);
|
||||
resp = MadTurtle_HTTPGet(cfg, "/v1/signal" + qs);
|
||||
}
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
#endif // MADTURTLE_API
|
||||
@@ -0,0 +1,134 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| MadTurtle_MTF.mqh - Multi-Timeframe confirmation filters |
|
||||
//+------------------------------------------------------------------+
|
||||
#ifndef MADTURTLE_MTF
|
||||
#define MADTURTLE_MTF
|
||||
|
||||
struct MadTurtleMTFState {
|
||||
double h4_sma_fast;
|
||||
double h4_sma_slow;
|
||||
double h4_rsi;
|
||||
double h4_macd;
|
||||
double h4_volume_ratio;
|
||||
ENUM_ORDER_TYPE h4_bias;
|
||||
|
||||
double d1_sma_fast;
|
||||
double d1_sma_slow;
|
||||
double d1_rsi;
|
||||
double d1_volume_ratio;
|
||||
ENUM_ORDER_TYPE d1_bias;
|
||||
|
||||
datetime h4_updated;
|
||||
datetime d1_updated;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Indicator value helpers (MQL5 handle + CopyBuffer) |
|
||||
//+------------------------------------------------------------------+
|
||||
double MadTurtle_GetMA(ENUM_TIMEFRAMES tf, int period, int shift)
|
||||
{
|
||||
int h = iMA(_Symbol, tf, period, 0, MODE_SMA, PRICE_CLOSE);
|
||||
if(h == INVALID_HANDLE) return 0;
|
||||
double buf[];
|
||||
if(CopyBuffer(h, 0, shift, 1, buf) != 1) { IndicatorRelease(h); return 0; }
|
||||
IndicatorRelease(h);
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
double MadTurtle_GetRSI(ENUM_TIMEFRAMES tf, int period, int shift)
|
||||
{
|
||||
int h = iRSI(_Symbol, tf, period, PRICE_CLOSE);
|
||||
if(h == INVALID_HANDLE) return 0;
|
||||
double buf[];
|
||||
if(CopyBuffer(h, 0, shift, 1, buf) != 1) { IndicatorRelease(h); return 0; }
|
||||
IndicatorRelease(h);
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
double MadTurtle_GetATR(ENUM_TIMEFRAMES tf, int period, int shift)
|
||||
{
|
||||
int h = iATR(_Symbol, tf, period);
|
||||
if(h == INVALID_HANDLE) return 0;
|
||||
double buf[];
|
||||
if(CopyBuffer(h, 0, shift, 1, buf) != 1) { IndicatorRelease(h); return 0; }
|
||||
IndicatorRelease(h);
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
double MadTurtle_GetMACD(ENUM_TIMEFRAMES tf, int fast, int slow, int signal, int shift)
|
||||
{
|
||||
int h = iMACD(_Symbol, tf, fast, slow, signal, PRICE_CLOSE);
|
||||
if(h == INVALID_HANDLE) return 0;
|
||||
double buf[];
|
||||
if(CopyBuffer(h, 0, shift, 1, buf) != 1) { IndicatorRelease(h); return 0; }
|
||||
IndicatorRelease(h);
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
double MadTurtle_GetVolume(ENUM_TIMEFRAMES tf, int shift)
|
||||
{
|
||||
long vol = iVolume(_Symbol, tf, shift);
|
||||
return (double)vol;
|
||||
}
|
||||
|
||||
ENUM_ORDER_TYPE MadTurtle_MTF_BiasFromSMA(double sma_fast, double sma_slow)
|
||||
{
|
||||
if(sma_fast > sma_slow && sma_fast > 0 && sma_slow > 0) return ORDER_TYPE_BUY;
|
||||
if(sma_fast < sma_slow && sma_fast > 0 && sma_slow > 0) return ORDER_TYPE_SELL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update multi-timeframe state |
|
||||
//+------------------------------------------------------------------+
|
||||
void MadTurtle_UpdateMTFState(MadTurtleMTFState &state)
|
||||
{
|
||||
datetime now = TimeCurrent();
|
||||
|
||||
if(now - state.h4_updated >= 60) {
|
||||
state.h4_sma_fast = MadTurtle_GetMA(PERIOD_H4, 10, 1);
|
||||
state.h4_sma_slow = MadTurtle_GetMA(PERIOD_H4, 50, 1);
|
||||
state.h4_rsi = MadTurtle_GetRSI(PERIOD_H4, 14, 1);
|
||||
state.h4_macd = MadTurtle_GetMACD(PERIOD_H4, 12, 26, 9, 1);
|
||||
state.h4_volume_ratio = MadTurtle_GetVolume(PERIOD_H4, 1);
|
||||
state.h4_bias = MadTurtle_MTF_BiasFromSMA(state.h4_sma_fast, state.h4_sma_slow);
|
||||
state.h4_updated = now;
|
||||
}
|
||||
|
||||
if(now - state.d1_updated >= 60) {
|
||||
state.d1_sma_fast = MadTurtle_GetMA(PERIOD_D1, 10, 1);
|
||||
state.d1_sma_slow = MadTurtle_GetMA(PERIOD_D1, 50, 1);
|
||||
state.d1_rsi = MadTurtle_GetRSI(PERIOD_D1, 14, 1);
|
||||
state.d1_volume_ratio = MadTurtle_GetVolume(PERIOD_D1, 1);
|
||||
state.d1_bias = MadTurtle_MTF_BiasFromSMA(state.d1_sma_fast, state.d1_sma_slow);
|
||||
state.d1_updated = now;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if MTF confirmation allows a BUY |
|
||||
//+------------------------------------------------------------------+
|
||||
bool MadTurtle_MTF_ConfirmBuy(const MadTurtleMTFState &state)
|
||||
{
|
||||
if(state.h4_bias != ORDER_TYPE_BUY) return false;
|
||||
if(state.d1_bias == ORDER_TYPE_SELL) return false;
|
||||
if(state.h4_rsi > 75.0) return false;
|
||||
if(state.d1_rsi > 75.0) return false;
|
||||
if(state.h4_macd < 0 && state.d1_sma_fast < state.d1_sma_slow) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if MTF confirmation allows a SELL |
|
||||
//+------------------------------------------------------------------+
|
||||
bool MadTurtle_MTF_ConfirmSell(const MadTurtleMTFState &state)
|
||||
{
|
||||
if(state.h4_bias != ORDER_TYPE_SELL) return false;
|
||||
if(state.d1_bias == ORDER_TYPE_BUY) return false;
|
||||
if(state.h4_rsi < 25.0) return false;
|
||||
if(state.d1_rsi < 25.0) return false;
|
||||
if(state.h4_macd > 0 && state.d1_sma_fast > state.d1_sma_slow) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // MADTURTLE_MTF
|
||||
@@ -0,0 +1,154 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| MadTurtle_UI.mqh - Status Dashboard, P&L, Signal Oscillator |
|
||||
//+------------------------------------------------------------------+
|
||||
#ifndef MADTURTLE_UI
|
||||
#define MADTURTLE_UI
|
||||
|
||||
// UI color theme
|
||||
#define UI_COLOR_BG clrBlack
|
||||
#define UI_COLOR_PANEL clrDarkSlateGray
|
||||
#define UI_COLOR_TEXT clrWhite
|
||||
#define UI_COLOR_PROFIT clrLime
|
||||
#define UI_COLOR_LOSS clrRed
|
||||
#define UI_COLOR_BUY clrDodgerBlue
|
||||
#define UI_COLOR_SELL clrOrangeRed
|
||||
#define UI_COLOR_HOLD clrGray
|
||||
#define UI_COLOR_ACCENT clrAqua
|
||||
|
||||
// Panel layout (x, y, w, h)
|
||||
#define UI_STATUS_X 10
|
||||
#define UI_STATUS_Y 10
|
||||
#define UI_STATUS_W 280
|
||||
#define UI_STATUS_H 180
|
||||
|
||||
#define UI_METRICS_X 10
|
||||
#define UI_METRICS_Y 200
|
||||
#define UI_METRICS_W 280
|
||||
#define UI_METRICS_H 120
|
||||
|
||||
#define UI_OSC_X 300
|
||||
#define UI_OSC_Y 10
|
||||
#define UI_OSC_W 220
|
||||
#define UI_OSC_H 140
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Draw rounded rectangle panel |
|
||||
//+------------------------------------------------------------------+
|
||||
void MadTurtle_DrawPanel(int x, int y, int w, int h, color bg = UI_COLOR_PANEL, color border = UI_COLOR_ACCENT)
|
||||
{
|
||||
string name = StringFormat("panel_bg_%d_%d", x, y);
|
||||
if(ObjectFind(0, name) < 0) {
|
||||
ObjectCreate(0, name, OBJ_RECTANGLE, 0, 0, 0);
|
||||
}
|
||||
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
|
||||
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
|
||||
ObjectSetInteger(0, name, OBJPROP_XSIZE, w);
|
||||
ObjectSetInteger(0, name, OBJPROP_YSIZE, h);
|
||||
ObjectSetInteger(0, name, OBJPROP_COLOR, border);
|
||||
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
|
||||
ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
|
||||
ObjectSetInteger(0, name, OBJPROP_FILL, true);
|
||||
ObjectSetInteger(0, name, OBJPROP_BGCOLOR, bg);
|
||||
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
|
||||
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Draw label text on panel |
|
||||
//+------------------------------------------------------------------+
|
||||
void MadTurtle_DrawLabel(string objName, string text, int x, int y, int fontSize = 9,
|
||||
color clr = UI_COLOR_TEXT, int anchor = ANCHOR_LEFT_UPPER)
|
||||
{
|
||||
if(ObjectFind(0, objName) < 0) {
|
||||
ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0);
|
||||
}
|
||||
ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, x);
|
||||
ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, y);
|
||||
ObjectSetString(0, objName, OBJPROP_TEXT, text);
|
||||
ObjectSetInteger(0, objName, OBJPROP_COLOR, clr);
|
||||
ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize);
|
||||
ObjectSetInteger(0, objName, OBJPROP_ANCHOR, anchor);
|
||||
ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
|
||||
ObjectSetInteger(0, objName, OBJPROP_HIDDEN, true);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Draw mini equity curve (simple text + bar) |
|
||||
//+------------------------------------------------------------------+
|
||||
void MadTurtle_DrawEquityCurve(double &equity[], int x, int y, int w, int h, color lineClr = UI_COLOR_PROFIT)
|
||||
{
|
||||
int n = ArraySize(equity);
|
||||
if(n < 2) return;
|
||||
|
||||
double minE = equity[ArrayMinimum(equity)];
|
||||
double maxE = equity[ArrayMaximum(equity)];
|
||||
if(maxE - minE < 1e-6) maxE = minE + 1.0;
|
||||
|
||||
// Draw as vertical bars in a rectangle
|
||||
int barW = (int)((double)w / n);
|
||||
if(barW < 1) barW = 1;
|
||||
|
||||
for(int i = 0; i < n; i++) {
|
||||
int barH = (int)((equity[i] - minE) / (maxE - minE) * h);
|
||||
if(barH < 1) barH = 1;
|
||||
string barName = StringFormat("eq_bar_%d", i);
|
||||
if(ObjectFind(0, barName) < 0) {
|
||||
ObjectCreate(0, barName, OBJ_RECTANGLE, 0, 0, 0);
|
||||
}
|
||||
ObjectSetInteger(0, barName, OBJPROP_XDISTANCE, x + i * barW);
|
||||
ObjectSetInteger(0, barName, OBJPROP_YDISTANCE, y + h - barH);
|
||||
ObjectSetInteger(0, barName, OBJPROP_XSIZE, barW);
|
||||
ObjectSetInteger(0, barName, OBJPROP_YSIZE, barH);
|
||||
ObjectSetInteger(0, barName, OBJPROP_COLOR, lineClr);
|
||||
ObjectSetInteger(0, barName, OBJPROP_FILL, true);
|
||||
ObjectSetInteger(0, barName, OBJPROP_SELECTABLE, false);
|
||||
ObjectSetInteger(0, barName, OBJPROP_HIDDEN, true);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Draw signal oscillator bars (buy/sell confidence) |
|
||||
//+------------------------------------------------------------------+
|
||||
void MadTurtle_DrawOscillator(double buyConf, double sellConf, int x, int y, int w, int h)
|
||||
{
|
||||
string objBuy = "osc_buy_bar";
|
||||
string objSell = "osc_sell_bar";
|
||||
|
||||
if(ObjectFind(0, objBuy) < 0) ObjectCreate(0, objBuy, OBJ_RECTANGLE, 0, 0, 0);
|
||||
if(ObjectFind(0, objSell) < 0) ObjectCreate(0, objSell, OBJ_RECTANGLE, 0, 0, 0);
|
||||
|
||||
int buyH = (int)(buyConf / 100.0 * h);
|
||||
int sellH = (int)(sellConf / 100.0 * h);
|
||||
|
||||
ObjectSetInteger(0, objBuy, OBJPROP_XDISTANCE, x);
|
||||
ObjectSetInteger(0, objBuy, OBJPROP_YDISTANCE, y + h - buyH);
|
||||
ObjectSetInteger(0, objBuy, OBJPROP_XSIZE, w / 2 - 2);
|
||||
ObjectSetInteger(0, objBuy, OBJPROP_YSIZE, buyH);
|
||||
ObjectSetInteger(0, objBuy, OBJPROP_COLOR, UI_COLOR_BUY);
|
||||
ObjectSetInteger(0, objBuy, OBJPROP_FILL, true);
|
||||
ObjectSetInteger(0, objBuy, OBJPROP_SELECTABLE, false);
|
||||
ObjectSetInteger(0, objBuy, OBJPROP_HIDDEN, true);
|
||||
|
||||
ObjectSetInteger(0, objSell, OBJPROP_XDISTANCE, x + w / 2 + 2);
|
||||
ObjectSetInteger(0, objSell, OBJPROP_YDISTANCE, y + h - sellH);
|
||||
ObjectSetInteger(0, objSell, OBJPROP_XSIZE, w / 2 - 2);
|
||||
ObjectSetInteger(0, objSell, OBJPROP_YSIZE, sellH);
|
||||
ObjectSetInteger(0, objSell, OBJPROP_COLOR, UI_COLOR_SELL);
|
||||
ObjectSetInteger(0, objSell, OBJPROP_FILL, true);
|
||||
ObjectSetInteger(0, objSell, OBJPROP_SELECTABLE, false);
|
||||
ObjectSetInteger(0, objSell, OBJPROP_HIDDEN, true);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Clean up all UI objects on deinit |
|
||||
//+------------------------------------------------------------------+
|
||||
void MadTurtle_CleanupUI()
|
||||
{
|
||||
ObjectsDeleteAll(0, "panel_bg_");
|
||||
ObjectsDeleteAll(0, "ui_label_");
|
||||
ObjectsDeleteAll(0, "eq_bar_");
|
||||
ObjectsDeleteAll(0, "osc_");
|
||||
ObjectsDeleteAll(0, "sig_arrow_");
|
||||
}
|
||||
|
||||
#endif // MADTURTLE_UI
|
||||
@@ -0,0 +1,387 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
Reference in New Issue
Block a user