467 lines
17 KiB
MQL5
467 lines
17 KiB
MQL5
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| AutoML Pipeline.mq5 |
|
||
|
|
//| Copyright 2025, MetaQuotes Ltd. |
|
||
|
|
//| https://www.mql5.com/en/users/johnhlomohang/ |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||
|
|
#property link "https://www.mql5.com/en/users/johnhlomohang/"
|
||
|
|
#property version "1.00"
|
||
|
|
|
||
|
|
#include <Trade\Trade.mqh>
|
||
|
|
#resource "\\Files\\AutoML\\ema_rsi_model.onnx" as uchar ExtModelBuffer[]
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Inputs |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
input group "=== Strategy (must match Python training config) ==="
|
||
|
|
input int InpEmaFast = 12; // Fast EMA period
|
||
|
|
input int InpEmaSlow = 26; // Slow EMA period
|
||
|
|
input int InpRsiPeriod = 14; // RSI period
|
||
|
|
input int InpAtrPeriod = 14; // ATR period
|
||
|
|
input int InpVolFast = 123; // StdDev short window
|
||
|
|
input int InpVolSlow = 864; // StdDev long window
|
||
|
|
input int InpMaxHoldBars = 254; // Time-stop (bars) — matches labeling
|
||
|
|
|
||
|
|
input group "=== AutoML Gate ==="
|
||
|
|
input bool InpUseModelGate = false; // false = raw EMA+RSI baseline (for A/B tests)
|
||
|
|
input double InpConfidence = 0.55; // Min P(profit) to take a signal (optimize 0.50–0.75)
|
||
|
|
|
||
|
|
input group "=== Risk & Trade Management ==="
|
||
|
|
input double InpLots = 0.36; // Fixed lot size
|
||
|
|
input bool InpUseSL = true; // Protective stop-loss
|
||
|
|
input double InpSLxATR = 12.4; // SL distance = ATR * this
|
||
|
|
input bool InpUseTrailing = true; // ATR trailing stop (locks in profit)
|
||
|
|
input double InpTrailxATR = 3.0; // Trail distance = ATR * this
|
||
|
|
input ulong InpMagic = 100010; // Magic number
|
||
|
|
input int InpSlippage = 7; // Max deviation (points)
|
||
|
|
|
||
|
|
input group "=== Display ==="
|
||
|
|
input bool InpShowDashboard = true; // On-chart status panel
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Globals |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
#define N_FEATURES 9
|
||
|
|
|
||
|
|
CTrade g_trade;
|
||
|
|
long g_onnx = INVALID_HANDLE; // ONNX session handle
|
||
|
|
int g_hEmaFast = INVALID_HANDLE;
|
||
|
|
int g_hEmaSlow = INVALID_HANDLE;
|
||
|
|
int g_hRsi = INVALID_HANDLE;
|
||
|
|
int g_hAtr = INVALID_HANDLE;
|
||
|
|
datetime g_lastBarTime = 0;
|
||
|
|
|
||
|
|
//--- Dashboard state
|
||
|
|
double g_lastConfidence = 0.0;
|
||
|
|
string g_lastSignal = "none";
|
||
|
|
string g_lastDecision = "-";
|
||
|
|
int g_signalsSeen = 0;
|
||
|
|
int g_signalsTaken = 0;
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Expert initialization |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
int OnInit()
|
||
|
|
{
|
||
|
|
//--- Indicator handles
|
||
|
|
g_hEmaFast = iMA(_Symbol, _Period, InpEmaFast, 0, MODE_EMA, PRICE_CLOSE);
|
||
|
|
g_hEmaSlow = iMA(_Symbol, _Period, InpEmaSlow, 0, MODE_EMA, PRICE_CLOSE);
|
||
|
|
g_hRsi = iRSI(_Symbol, _Period, InpRsiPeriod, PRICE_CLOSE);
|
||
|
|
g_hAtr = iATR(_Symbol, _Period, InpAtrPeriod);
|
||
|
|
|
||
|
|
if(g_hEmaFast==INVALID_HANDLE || g_hEmaSlow==INVALID_HANDLE ||
|
||
|
|
g_hRsi==INVALID_HANDLE || g_hAtr==INVALID_HANDLE)
|
||
|
|
{
|
||
|
|
Print("[INIT] Failed to create indicator handles");
|
||
|
|
return INIT_FAILED;
|
||
|
|
}
|
||
|
|
|
||
|
|
//--- ONNX session from the embedded resource
|
||
|
|
g_onnx = OnnxCreateFromBuffer(ExtModelBuffer, ONNX_DEFAULT);
|
||
|
|
if(g_onnx == INVALID_HANDLE)
|
||
|
|
{
|
||
|
|
PrintFormat("[INIT] OnnxCreateFromBuffer failed, error %d", GetLastError());
|
||
|
|
return INIT_FAILED;
|
||
|
|
}
|
||
|
|
|
||
|
|
//--- Pin the shapes. The model was exported with a dynamic batch
|
||
|
|
//--- dimension (None, 9); MT5 requires it fixed before running.
|
||
|
|
const long inShape[] = {1, N_FEATURES};
|
||
|
|
const long outLblShape[] = {1};
|
||
|
|
const long outProbShape[] = {1, 2};
|
||
|
|
|
||
|
|
if(!OnnxSetInputShape(g_onnx, 0, inShape))
|
||
|
|
{
|
||
|
|
PrintFormat("[INIT] OnnxSetInputShape failed, error %d", GetLastError());
|
||
|
|
return INIT_FAILED;
|
||
|
|
}
|
||
|
|
if(!OnnxSetOutputShape(g_onnx, 0, outLblShape) ||
|
||
|
|
!OnnxSetOutputShape(g_onnx, 1, outProbShape))
|
||
|
|
{
|
||
|
|
PrintFormat("[INIT] OnnxSetOutputShape failed, error %d", GetLastError());
|
||
|
|
return INIT_FAILED;
|
||
|
|
}
|
||
|
|
|
||
|
|
//--- Trade object
|
||
|
|
g_trade.SetExpertMagicNumber(InpMagic);
|
||
|
|
g_trade.SetDeviationInPoints(InpSlippage);
|
||
|
|
g_trade.SetTypeFillingBySymbol(_Symbol);
|
||
|
|
|
||
|
|
PrintFormat("[INIT] OK — model %d bytes embedded, gate=%s, threshold=%.2f",
|
||
|
|
ArraySize(ExtModelBuffer),
|
||
|
|
InpUseModelGate ? "ON" : "OFF (baseline)",
|
||
|
|
InpConfidence);
|
||
|
|
return INIT_SUCCEEDED;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Expert deinitialization |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void OnDeinit(const int reason)
|
||
|
|
{
|
||
|
|
if(g_onnx != INVALID_HANDLE)
|
||
|
|
OnnxRelease(g_onnx);
|
||
|
|
IndicatorRelease(g_hEmaFast);
|
||
|
|
IndicatorRelease(g_hEmaSlow);
|
||
|
|
IndicatorRelease(g_hRsi);
|
||
|
|
IndicatorRelease(g_hAtr);
|
||
|
|
Comment("");
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Expert tick |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void OnTick()
|
||
|
|
{
|
||
|
|
//--- Trailing runs on every tick so profit gets locked intrabar
|
||
|
|
if(InpUseTrailing)
|
||
|
|
ManageTrailingStop();
|
||
|
|
|
||
|
|
//--- Everything else is bar-close logic (mirrors the labeling)
|
||
|
|
if(!IsNewBar())
|
||
|
|
return;
|
||
|
|
|
||
|
|
ProcessClosedBar();
|
||
|
|
|
||
|
|
if(InpShowDashboard)
|
||
|
|
UpdateDashboard();
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| New-bar detector |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool IsNewBar()
|
||
|
|
{
|
||
|
|
datetime t = iTime(_Symbol, _Period, 0);
|
||
|
|
if(t == g_lastBarTime)
|
||
|
|
return false;
|
||
|
|
g_lastBarTime = t;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Core logic — runs once per bar, on the freshly CLOSED bar |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void ProcessClosedBar()
|
||
|
|
{
|
||
|
|
//--- 1) Read EMAs on bars 1 (closed) and 2 (prior)
|
||
|
|
double emaF[], emaS[];
|
||
|
|
if(CopyBuffer(g_hEmaFast, 0, 1, 2, emaF) < 2)
|
||
|
|
return; // [0]=bar1 [1]=bar2? No:
|
||
|
|
if(CopyBuffer(g_hEmaSlow, 0, 1, 2, emaS) < 2)
|
||
|
|
return;
|
||
|
|
//--- CopyBuffer fills as-series=false by default: emaF[0]=bar2, emaF[1]=bar1
|
||
|
|
double emaFast1 = emaF[1], emaFast2 = emaF[0];
|
||
|
|
double emaSlow1 = emaS[1], emaSlow2 = emaS[0];
|
||
|
|
|
||
|
|
//--- 2) Crossover on the closed bar (same rule as Python)
|
||
|
|
//--- Python: above[i] != above[i-1]
|
||
|
|
bool crossUp = (emaFast1 > emaSlow1) && (emaFast2 <= emaSlow2);
|
||
|
|
bool crossDn = (emaFast1 <= emaSlow1) && (emaFast2 > emaSlow2);
|
||
|
|
int direction = crossUp ? 1 : (crossDn ? -1 : 0);
|
||
|
|
|
||
|
|
//--- 3) Exit management first (mirrors the label simulation)
|
||
|
|
//--- Exit rule in training: opposite crossover OR time stop
|
||
|
|
if(PositionSelectByMagic())
|
||
|
|
{
|
||
|
|
long posType = PositionGetInteger(POSITION_TYPE);
|
||
|
|
bool opposite = (posType==POSITION_TYPE_BUY && crossDn) ||
|
||
|
|
(posType==POSITION_TYPE_SELL && crossUp);
|
||
|
|
|
||
|
|
datetime openTime = (datetime)PositionGetInteger(POSITION_TIME);
|
||
|
|
int barsHeld = iBarShift(_Symbol, _Period, openTime);
|
||
|
|
|
||
|
|
if(opposite)
|
||
|
|
{
|
||
|
|
g_trade.PositionClose(_Symbol);
|
||
|
|
PrintFormat("[EXIT] Opposite crossover after %d bars", barsHeld);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
if(barsHeld >= InpMaxHoldBars)
|
||
|
|
{
|
||
|
|
g_trade.PositionClose(_Symbol);
|
||
|
|
PrintFormat("[EXIT] Time stop hit (%d bars)", barsHeld);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//--- 4) Entry evaluation
|
||
|
|
if(direction == 0)
|
||
|
|
return; // no signal this bar
|
||
|
|
|
||
|
|
g_signalsSeen++;
|
||
|
|
g_lastSignal = (direction > 0) ? "BUY cross" : "SELL cross";
|
||
|
|
|
||
|
|
if(PositionSelectByMagic()) // still holding (same-direction cross)
|
||
|
|
{
|
||
|
|
g_lastDecision = "skipped (position open)";
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
//--- 5) Build the feature vector — THE CONTRACT
|
||
|
|
float features[N_FEATURES];
|
||
|
|
if(!ComputeFeatures(direction, features))
|
||
|
|
{
|
||
|
|
g_lastDecision = "skipped (feature error)";
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
//--- 6) Query the model
|
||
|
|
double pProfit = 1.0; // gate off => always pass
|
||
|
|
if(InpUseModelGate)
|
||
|
|
{
|
||
|
|
if(!RunModel(features, pProfit))
|
||
|
|
{
|
||
|
|
g_lastDecision = "skipped (inference error)";
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
g_lastConfidence = pProfit;
|
||
|
|
|
||
|
|
PrintFormat("[SIGNAL] %s | P(profit)=%.3f | threshold=%.2f",
|
||
|
|
g_lastSignal, pProfit, InpConfidence);
|
||
|
|
|
||
|
|
if(pProfit <= InpConfidence)
|
||
|
|
{
|
||
|
|
g_lastDecision = StringFormat("REJECTED (%.3f <= %.2f)", pProfit, InpConfidence);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
//--- 7) Execute
|
||
|
|
ExecuteEntry(direction);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Feature vector — order and math must match the Python notebook |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool ComputeFeatures(const int direction, float &f[])
|
||
|
|
{
|
||
|
|
//--- Prices of the closed bar
|
||
|
|
double close1 = iClose(_Symbol, _Period, 1);
|
||
|
|
double high1 = iHigh(_Symbol, _Period, 1);
|
||
|
|
double low1 = iLow(_Symbol, _Period, 1);
|
||
|
|
if(close1 <= 0.0)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
//--- Indicator values
|
||
|
|
double emaF[], emaS[], rsi[], atr[];
|
||
|
|
if(CopyBuffer(g_hEmaFast, 0, 1, 1, emaF) < 1)
|
||
|
|
return false;
|
||
|
|
if(CopyBuffer(g_hEmaSlow, 0, 1, 1, emaS) < 1)
|
||
|
|
return false;
|
||
|
|
if(CopyBuffer(g_hRsi, 0, 1, 2, rsi) < 2)
|
||
|
|
return false; // rsi[0]=bar2 rsi[1]=bar1
|
||
|
|
if(CopyBuffer(g_hAtr, 0, 1, 1, atr) < 1)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
//--- Sample standard deviations (pandas-compatible, ddof=1).
|
||
|
|
//--- iStdDev uses the POPULATION formula (ddof=0), which is
|
||
|
|
//--- systematically ~1–2%% smaller — a silent feature-drift bug.
|
||
|
|
double sdFast = StdDevSample(InpVolFast, 1);
|
||
|
|
double sdSlow = StdDevSample(InpVolSlow, 1);
|
||
|
|
if(sdFast <= 0.0 || sdSlow <= 0.0)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
double range1 = high1 - low1;
|
||
|
|
double rangePct = (range1 > 0.0) ? (close1 - low1) / range1 : 0.5;
|
||
|
|
|
||
|
|
f[0] = (float)(emaF[0] / close1 - 1.0); // ema_fast_rel
|
||
|
|
f[1] = (float)(emaS[0] / close1 - 1.0); // ema_slow_rel
|
||
|
|
f[2] = (float)((emaF[0] - emaS[0]) / close1); // ema_distance
|
||
|
|
f[3] = (float)(rsi[1]); // rsi (bar 1)
|
||
|
|
f[4] = (float)(rsi[1] - rsi[0]); // rsi_momentum
|
||
|
|
f[5] = (float)(atr[0] / close1); // atr_rel
|
||
|
|
f[6] = (float)(sdFast / sdSlow); // volatility_ratio
|
||
|
|
f[7] = (float)(rangePct); // close_range_pct
|
||
|
|
f[8] = (float)(direction); // signal_direction
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Sample std-dev |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
double StdDevSample(const int period, const int shift)
|
||
|
|
{
|
||
|
|
double closes[];
|
||
|
|
if(CopyClose(_Symbol, _Period, shift, period, closes) < period)
|
||
|
|
return 0.0;
|
||
|
|
|
||
|
|
double mean = 0.0;
|
||
|
|
for(int i = 0; i < period; i++)
|
||
|
|
mean += closes[i];
|
||
|
|
mean /= period;
|
||
|
|
|
||
|
|
double ss = 0.0;
|
||
|
|
for(int i = 0; i < period; i++)
|
||
|
|
{
|
||
|
|
double d = closes[i] - mean;
|
||
|
|
ss += d * d;
|
||
|
|
}
|
||
|
|
return MathSqrt(ss / (period - 1)); // ddof = 1
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| ONNX inference — returns P(trade closes in profit) |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool RunModel(const float &features[], double &pProfit)
|
||
|
|
{
|
||
|
|
long outLabel[1]; // output 0: predicted class (int64)
|
||
|
|
float outProbs[1][2]; // output 1: [P(loss), P(profit)]
|
||
|
|
|
||
|
|
if(!OnnxRun(g_onnx, ONNX_NO_CONVERSION, features, outLabel, outProbs))
|
||
|
|
{
|
||
|
|
PrintFormat("[ONNX] OnnxRun failed, error %d", GetLastError());
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
pProfit = (double)outProbs[0][1];
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Entry execution with optional ATR stop-loss |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void ExecuteEntry(const int direction)
|
||
|
|
{
|
||
|
|
double atr[];
|
||
|
|
if(CopyBuffer(g_hAtr, 0, 1, 1, atr) < 1)
|
||
|
|
return;
|
||
|
|
|
||
|
|
double sl = 0.0;
|
||
|
|
bool ok = false;
|
||
|
|
|
||
|
|
if(direction > 0)
|
||
|
|
{
|
||
|
|
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||
|
|
if(InpUseSL)
|
||
|
|
sl = NormalizeDouble(ask - InpSLxATR * atr[0], _Digits);
|
||
|
|
ok = g_trade.Buy(InpLots, _Symbol, 0.0, sl, 0.0, "AutoML gate");
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||
|
|
if(InpUseSL)
|
||
|
|
sl = NormalizeDouble(bid + InpSLxATR * atr[0], _Digits);
|
||
|
|
ok = g_trade.Sell(InpLots, _Symbol, 0.0, sl, 0.0, "AutoML gate");
|
||
|
|
}
|
||
|
|
|
||
|
|
if(ok)
|
||
|
|
{
|
||
|
|
g_signalsTaken++;
|
||
|
|
g_lastDecision = StringFormat("TAKEN (%.3f > %.2f)", g_lastConfidence, InpConfidence);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
g_lastDecision = StringFormat("order failed (%d)", (int)g_trade.ResultRetcode());
|
||
|
|
PrintFormat("[TRADE] Order failed: retcode=%d", (int)g_trade.ResultRetcode());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| ATR trailing stop — the "guarantee the profit" layer |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void ManageTrailingStop()
|
||
|
|
{
|
||
|
|
if(!PositionSelectByMagic())
|
||
|
|
return;
|
||
|
|
|
||
|
|
double atr[];
|
||
|
|
if(CopyBuffer(g_hAtr, 0, 1, 1, atr) < 1)
|
||
|
|
return;
|
||
|
|
double trail = InpTrailxATR * atr[0];
|
||
|
|
|
||
|
|
long type = PositionGetInteger(POSITION_TYPE);
|
||
|
|
double sl = PositionGetDouble(POSITION_SL);
|
||
|
|
double openPx = PositionGetDouble(POSITION_PRICE_OPEN);
|
||
|
|
|
||
|
|
if(type == POSITION_TYPE_BUY)
|
||
|
|
{
|
||
|
|
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||
|
|
double newSL = NormalizeDouble(bid - trail, _Digits);
|
||
|
|
//--- Only trail once in profit, only ever move the stop UP
|
||
|
|
if(newSL > openPx && (sl == 0.0 || newSL > sl))
|
||
|
|
g_trade.PositionModify(_Symbol, newSL, PositionGetDouble(POSITION_TP));
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||
|
|
double newSL = NormalizeDouble(ask + trail, _Digits);
|
||
|
|
//--- Only trail once in profit, only ever move the stop DOWN
|
||
|
|
if(newSL < openPx && (sl == 0.0 || newSL < sl))
|
||
|
|
g_trade.PositionModify(_Symbol, newSL, PositionGetDouble(POSITION_TP));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Select the EA's own position on this symbol |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool PositionSelectByMagic()
|
||
|
|
{
|
||
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||
|
|
{
|
||
|
|
ulong ticket = PositionGetTicket(i);
|
||
|
|
if(ticket == 0)
|
||
|
|
continue;
|
||
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
||
|
|
PositionGetInteger(POSITION_MAGIC) == (long)InpMagic)
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| On-chart dashboard |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void UpdateDashboard()
|
||
|
|
{
|
||
|
|
string gate = InpUseModelGate
|
||
|
|
? StringFormat("ON (threshold %.2f)", InpConfidence)
|
||
|
|
: "OFF — raw EMA+RSI baseline";
|
||
|
|
|
||
|
|
string pos = "flat";
|
||
|
|
if(PositionSelectByMagic())
|
||
|
|
{
|
||
|
|
long type = PositionGetInteger(POSITION_TYPE);
|
||
|
|
int held = iBarShift(_Symbol, _Period,
|
||
|
|
(datetime)PositionGetInteger(POSITION_TIME));
|
||
|
|
pos = StringFormat("%s | %d/%d bars | P/L %.2f",
|
||
|
|
type==POSITION_TYPE_BUY ? "LONG" : "SHORT",
|
||
|
|
held, InpMaxHoldBars,
|
||
|
|
PositionGetDouble(POSITION_PROFIT));
|
||
|
|
}
|
||
|
|
|
||
|
|
Comment(StringFormat(
|
||
|
|
"\n EMA+RSI AutoML EA (Part 10)"
|
||
|
|
"\n --------------------------------------"
|
||
|
|
"\n Model gate : %s"
|
||
|
|
"\n Last signal : %s"
|
||
|
|
"\n Last P(profit) : %.3f"
|
||
|
|
"\n Last decision : %s"
|
||
|
|
"\n Signals seen : %d taken: %d (%.0f%%)"
|
||
|
|
"\n Position : %s",
|
||
|
|
gate, g_lastSignal, g_lastConfidence, g_lastDecision,
|
||
|
|
g_signalsSeen, g_signalsTaken,
|
||
|
|
g_signalsSeen > 0 ? 100.0 * g_signalsTaken / g_signalsSeen : 0.0,
|
||
|
|
pos));
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|