655 lines
24 KiB
MQL5
655 lines
24 KiB
MQL5
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| BreakoutEA_UltimatePro.mq5|
|
||
|
|
//| ULTIMATE Breakout EA - AI Pattern + Dashboard |
|
||
|
|
//| + Session Filter + ATR Dynamic SL/TP + Full Risk Mgmt |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
#property copyright "BreakoutEA Ultimate Pro"
|
||
|
|
#property version "3.00"
|
||
|
|
#property description "Advanced Breakout EA with AI Pattern Recognition"
|
||
|
|
|
||
|
|
#include <Trade\Trade.mqh>
|
||
|
|
#include <Trade\PositionInfo.mqh>
|
||
|
|
|
||
|
|
//===================================================================
|
||
|
|
input group "═══════ STRATEGY: Breakout S/R ═══════"
|
||
|
|
input int InpLookbackBars = 20;
|
||
|
|
input int InpBreakoutBuffer = 5;
|
||
|
|
|
||
|
|
input group "═══════ AI PATTERN RECOGNITION ═══════"
|
||
|
|
input bool InpUseAI = true;
|
||
|
|
input bool InpUseEngulfing = true; // Engulfing candle pattern
|
||
|
|
input bool InpUsePinBar = true; // Pin Bar / Hammer
|
||
|
|
input bool InpUseInsideBar = true; // Inside Bar breakout
|
||
|
|
input bool InpUseMomentum = true; // Momentum burst detection
|
||
|
|
input int InpMinAIScore = 2; // Min pattern score to trade (1-4)
|
||
|
|
|
||
|
|
input group "═══════ SESSION FILTER ═══════"
|
||
|
|
input bool InpUseSession = true;
|
||
|
|
input bool InpTradeLondon = true; // London: 08:00-17:00 GMT
|
||
|
|
input bool InpTradeNewYork = true; // New York: 13:00-22:00 GMT
|
||
|
|
input bool InpTradeTokyo = false; // Tokyo: 00:00-09:00 GMT
|
||
|
|
input int InpGMTOffset = 7; // Your broker GMT offset (e.g. GMT+7)
|
||
|
|
|
||
|
|
input group "═══════ ATR DYNAMIC SL/TP ═══════"
|
||
|
|
input bool InpUseATRDynamic = true;
|
||
|
|
input int InpATRPeriod = 14;
|
||
|
|
input double InpATRSLMulti = 1.5; // SL = ATR × multiplier
|
||
|
|
input double InpATRTPMulti = 3.0; // TP = ATR × multiplier
|
||
|
|
input double InpATRTrailMulti = 1.0; // Trailing = ATR × multiplier
|
||
|
|
input double InpFallbackSLPips = 20.0; // Fallback SL if ATR fails
|
||
|
|
|
||
|
|
input group "═══════ INDICATOR FILTERS ═══════"
|
||
|
|
input bool InpUseRSI = true;
|
||
|
|
input int InpRSIPeriod = 14;
|
||
|
|
input bool InpUseMACD = true;
|
||
|
|
input int InpMACDFast = 12;
|
||
|
|
input int InpMACDSlow = 26;
|
||
|
|
input int InpMACDSignal = 9;
|
||
|
|
|
||
|
|
input group "═══════ MARTINGALE ═══════"
|
||
|
|
input bool InpUseMartingale = true;
|
||
|
|
input double InpMartMultiplier = 2.0;
|
||
|
|
input int InpMaxMartSteps = 3;
|
||
|
|
|
||
|
|
input group "═══════ RISK MANAGEMENT ═══════"
|
||
|
|
input double InpRiskPercent = 1.0;
|
||
|
|
input bool InpUseTrailing = true;
|
||
|
|
input double InpBreakevenPips = 10.0;
|
||
|
|
input double InpMaxDailyLossPct = 5.0;
|
||
|
|
|
||
|
|
input group "═══════ DASHBOARD ═══════"
|
||
|
|
input bool InpShowDashboard = true;
|
||
|
|
input color InpDashBG = C'20,24,35';
|
||
|
|
input color InpDashText = clrWhite;
|
||
|
|
input color InpDashProfit = clrLimeGreen;
|
||
|
|
input color InpDashLoss = clrTomato;
|
||
|
|
input color InpDashAccent = C'100,160,255';
|
||
|
|
|
||
|
|
input group "═══════ TRADE SETTINGS ═══════"
|
||
|
|
input int InpMagicNumber = 777999;
|
||
|
|
input int InpMaxSpread = 30;
|
||
|
|
input string InpComment = "ULT_PRO";
|
||
|
|
|
||
|
|
//===================================================================
|
||
|
|
CTrade trade;
|
||
|
|
|
||
|
|
int h_RSI, h_MACD, h_ATR;
|
||
|
|
double g_pipValue;
|
||
|
|
int g_digits;
|
||
|
|
int g_martStep = 0;
|
||
|
|
double g_lastLot = 0;
|
||
|
|
double g_dailyStartBal = 0;
|
||
|
|
datetime g_lastBar = 0;
|
||
|
|
|
||
|
|
// Stats
|
||
|
|
int g_totalTrades = 0;
|
||
|
|
int g_winTrades = 0;
|
||
|
|
int g_lossTrades = 0;
|
||
|
|
double g_totalProfit = 0;
|
||
|
|
double g_maxDD = 0;
|
||
|
|
double g_peakBalance = 0;
|
||
|
|
|
||
|
|
// Dashboard object names
|
||
|
|
string PREFIX = "DASH_";
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
int OnInit()
|
||
|
|
{
|
||
|
|
trade.SetMagicNumber(InpMagicNumber);
|
||
|
|
trade.SetDeviationInPoints(10);
|
||
|
|
g_digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||
|
|
g_pipValue = (g_digits == 3 || g_digits == 5) ? 10.0 : 1.0;
|
||
|
|
|
||
|
|
h_RSI = iRSI(_Symbol, PERIOD_CURRENT, InpRSIPeriod, PRICE_CLOSE);
|
||
|
|
h_MACD = iMACD(_Symbol, PERIOD_CURRENT, InpMACDFast, InpMACDSlow, InpMACDSignal, PRICE_CLOSE);
|
||
|
|
h_ATR = iATR(_Symbol, PERIOD_CURRENT, InpATRPeriod);
|
||
|
|
|
||
|
|
if(h_RSI==INVALID_HANDLE || h_MACD==INVALID_HANDLE || h_ATR==INVALID_HANDLE)
|
||
|
|
{ Print("Indicator init failed!"); return INIT_FAILED; }
|
||
|
|
|
||
|
|
g_dailyStartBal = AccountInfoDouble(ACCOUNT_BALANCE);
|
||
|
|
g_peakBalance = g_dailyStartBal;
|
||
|
|
|
||
|
|
LoadHistoryStats();
|
||
|
|
|
||
|
|
if(InpShowDashboard) CreateDashboard();
|
||
|
|
EventSetTimer(1);
|
||
|
|
return INIT_SUCCEEDED;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void OnDeinit(const int reason)
|
||
|
|
{
|
||
|
|
IndicatorRelease(h_RSI);
|
||
|
|
IndicatorRelease(h_MACD);
|
||
|
|
IndicatorRelease(h_ATR);
|
||
|
|
EventKillTimer();
|
||
|
|
if(InpShowDashboard) DeleteDashboard();
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void OnTick()
|
||
|
|
{
|
||
|
|
if(IsDailyLossBreached()) { CloseAllPositions(); return; }
|
||
|
|
if(InpUseTrailing) ManageTrailingATR();
|
||
|
|
ManageBreakeven();
|
||
|
|
|
||
|
|
datetime curBar = iTime(_Symbol, PERIOD_CURRENT, 0);
|
||
|
|
if(curBar == g_lastBar) return;
|
||
|
|
g_lastBar = curBar;
|
||
|
|
|
||
|
|
if((int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) > InpMaxSpread) return;
|
||
|
|
if(InpUseSession && !IsSessionActive()) return;
|
||
|
|
if(HasOpenPosition()) return;
|
||
|
|
|
||
|
|
double resistance = GetResistance();
|
||
|
|
double support = GetSupport();
|
||
|
|
if(resistance == 0 || support == 0) return;
|
||
|
|
|
||
|
|
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||
|
|
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||
|
|
double buffer = InpBreakoutBuffer * _Point;
|
||
|
|
|
||
|
|
bool breakUp = (ask > resistance + buffer);
|
||
|
|
bool breakDown = (bid < support - buffer);
|
||
|
|
if(!breakUp && !breakDown) return;
|
||
|
|
|
||
|
|
// AI Pattern Score
|
||
|
|
int aiScore = 0;
|
||
|
|
if(InpUseAI)
|
||
|
|
{
|
||
|
|
aiScore = GetAIScore(breakUp);
|
||
|
|
if(aiScore < InpMinAIScore) return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(breakUp && !CheckFilters(true)) return;
|
||
|
|
if(breakDown && !CheckFilters(false)) return;
|
||
|
|
|
||
|
|
double atr = GetATR();
|
||
|
|
double lot = CalculateLot(atr);
|
||
|
|
if(lot <= 0) return;
|
||
|
|
|
||
|
|
if(breakUp)
|
||
|
|
{
|
||
|
|
double sl, tp;
|
||
|
|
CalcSLTP(true, ask, atr, sl, tp);
|
||
|
|
if(trade.Buy(lot, _Symbol, ask, sl, tp, InpComment+"_B"))
|
||
|
|
{ g_lastLot = lot; g_totalTrades++; Print("BUY | Lot:", lot, " AI:", aiScore, " ATR:", DoubleToString(atr/_Point/g_pipValue,1), "p"); }
|
||
|
|
}
|
||
|
|
else if(breakDown)
|
||
|
|
{
|
||
|
|
double sl, tp;
|
||
|
|
CalcSLTP(false, bid, atr, sl, tp);
|
||
|
|
if(trade.Sell(lot, _Symbol, bid, sl, tp, InpComment+"_S"))
|
||
|
|
{ g_lastLot = lot; g_totalTrades++; Print("SELL | Lot:", lot, " AI:", aiScore, " ATR:", DoubleToString(atr/_Point/g_pipValue,1), "p"); }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void OnTimer() { if(InpShowDashboard) UpdateDashboard(); }
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void OnTradeTransaction(const MqlTradeTransaction &trans,
|
||
|
|
const MqlTradeRequest &req, const MqlTradeResult &res)
|
||
|
|
{
|
||
|
|
if(trans.type == TRADE_TRANSACTION_DEAL_ADD)
|
||
|
|
{
|
||
|
|
if(HistoryDealSelect(trans.deal))
|
||
|
|
{
|
||
|
|
double profit = HistoryDealGetDouble(trans.deal, DEAL_PROFIT);
|
||
|
|
if(profit == 0) return;
|
||
|
|
g_totalProfit += profit;
|
||
|
|
if(profit > 0) g_winTrades++;
|
||
|
|
else g_lossTrades++;
|
||
|
|
|
||
|
|
double bal = AccountInfoDouble(ACCOUNT_BALANCE);
|
||
|
|
if(bal > g_peakBalance) g_peakBalance = bal;
|
||
|
|
double dd = (g_peakBalance - bal) / g_peakBalance * 100.0;
|
||
|
|
if(dd > g_maxDD) g_maxDD = dd;
|
||
|
|
|
||
|
|
if(InpUseMartingale)
|
||
|
|
g_martStep = (profit >= 0) ? 0 : MathMin(g_martStep+1, InpMaxMartSteps);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//===================================================================
|
||
|
|
//| AI PATTERN RECOGNITION
|
||
|
|
//===================================================================
|
||
|
|
int GetAIScore(bool isBuy)
|
||
|
|
{
|
||
|
|
int score = 0;
|
||
|
|
double o1,h1,l1,c1, o2,h2,l2,c2;
|
||
|
|
|
||
|
|
// Candle data: index 1 = last closed, 2 = previous
|
||
|
|
o1 = iOpen (_Symbol,PERIOD_CURRENT,1); h1 = iHigh(_Symbol,PERIOD_CURRENT,1);
|
||
|
|
l1 = iLow (_Symbol,PERIOD_CURRENT,1); c1 = iClose(_Symbol,PERIOD_CURRENT,1);
|
||
|
|
o2 = iOpen (_Symbol,PERIOD_CURRENT,2); h2 = iHigh(_Symbol,PERIOD_CURRENT,2);
|
||
|
|
l2 = iLow (_Symbol,PERIOD_CURRENT,2); c2 = iClose(_Symbol,PERIOD_CURRENT,2);
|
||
|
|
|
||
|
|
double body1 = MathAbs(c1 - o1);
|
||
|
|
double range1 = h1 - l1;
|
||
|
|
double body2 = MathAbs(c2 - o2);
|
||
|
|
|
||
|
|
// 1. Engulfing Pattern
|
||
|
|
if(InpUseEngulfing && body1 > 0 && body2 > 0)
|
||
|
|
{
|
||
|
|
if(isBuy && c1 > o1 && c2 < o2 && c1 > o2 && o1 < c2) score++;
|
||
|
|
if(!isBuy && c1 < o1 && c2 > o2 && c1 < o2 && o1 > c2) score++;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 2. Pin Bar / Hammer
|
||
|
|
if(InpUsePinBar && range1 > 0)
|
||
|
|
{
|
||
|
|
double upperWick = h1 - MathMax(o1, c1);
|
||
|
|
double lowerWick = MathMin(o1, c1) - l1;
|
||
|
|
double bodyRatio = body1 / range1;
|
||
|
|
if(bodyRatio < 0.35)
|
||
|
|
{
|
||
|
|
if(isBuy && lowerWick > upperWick * 2.0) score++;
|
||
|
|
if(!isBuy && upperWick > lowerWick * 2.0) score++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 3. Inside Bar Breakout
|
||
|
|
if(InpUseInsideBar)
|
||
|
|
{
|
||
|
|
if(h1 < h2 && l1 > l2) // inside bar confirmed
|
||
|
|
{
|
||
|
|
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||
|
|
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||
|
|
if(isBuy && ask > h2) score++;
|
||
|
|
if(!isBuy && bid < l2) score++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 4. Momentum Burst
|
||
|
|
if(InpUseMomentum && range1 > 0)
|
||
|
|
{
|
||
|
|
double atr[];
|
||
|
|
ArraySetAsSeries(atr, true);
|
||
|
|
if(CopyBuffer(h_ATR, 0, 1, 1, atr) == 1)
|
||
|
|
{
|
||
|
|
bool bigCandle = (range1 > atr[0] * 1.5);
|
||
|
|
bool bullMom = (c1 > o1 && c1 > h2);
|
||
|
|
bool bearMom = (c1 < o1 && c1 < l2);
|
||
|
|
if(isBuy && bigCandle && bullMom) score++;
|
||
|
|
if(!isBuy && bigCandle && bearMom) score++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return score;
|
||
|
|
}
|
||
|
|
|
||
|
|
//===================================================================
|
||
|
|
//| SESSION FILTER
|
||
|
|
//===================================================================
|
||
|
|
bool IsSessionActive()
|
||
|
|
{
|
||
|
|
MqlDateTime dt;
|
||
|
|
TimeToStruct(TimeCurrent(), dt);
|
||
|
|
int brokerHour = dt.hour;
|
||
|
|
int gmtHour = (brokerHour - InpGMTOffset + 24) % 24;
|
||
|
|
|
||
|
|
bool london = InpTradeLondon && (gmtHour >= 8 && gmtHour < 17);
|
||
|
|
bool newyork = InpTradeNewYork && (gmtHour >= 13 && gmtHour < 22);
|
||
|
|
bool tokyo = InpTradeTokyo && (gmtHour >= 0 && gmtHour < 9);
|
||
|
|
|
||
|
|
return (london || newyork || tokyo);
|
||
|
|
}
|
||
|
|
|
||
|
|
//===================================================================
|
||
|
|
//| ATR DYNAMIC SL/TP
|
||
|
|
//===================================================================
|
||
|
|
void CalcSLTP(bool isBuy, double price, double atr, double &sl, double &tp)
|
||
|
|
{
|
||
|
|
double slDist, tpDist;
|
||
|
|
if(InpUseATRDynamic && atr > 0)
|
||
|
|
{
|
||
|
|
slDist = atr * InpATRSLMulti;
|
||
|
|
tpDist = atr * InpATRTPMulti;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
slDist = InpFallbackSLPips * g_pipValue * _Point;
|
||
|
|
tpDist = slDist * 2.0;
|
||
|
|
}
|
||
|
|
if(isBuy)
|
||
|
|
{
|
||
|
|
sl = NormalizeDouble(price - slDist, g_digits);
|
||
|
|
tp = NormalizeDouble(price + tpDist, g_digits);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
sl = NormalizeDouble(price + slDist, g_digits);
|
||
|
|
tp = NormalizeDouble(price - tpDist, g_digits);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
double GetATR()
|
||
|
|
{
|
||
|
|
double atr[];
|
||
|
|
ArraySetAsSeries(atr, true);
|
||
|
|
if(CopyBuffer(h_ATR, 0, 1, 1, atr) < 1) return 0;
|
||
|
|
return atr[0];
|
||
|
|
}
|
||
|
|
|
||
|
|
//===================================================================
|
||
|
|
//| TRAILING STOP (ATR-based)
|
||
|
|
//===================================================================
|
||
|
|
void ManageTrailingATR()
|
||
|
|
{
|
||
|
|
double atr = GetATR();
|
||
|
|
double trailDist = (atr > 0) ? atr * InpATRTrailMulti : InpBreakevenPips * g_pipValue * _Point;
|
||
|
|
|
||
|
|
for(int i = PositionsTotal()-1; i >= 0; i--)
|
||
|
|
{
|
||
|
|
ulong ticket = PositionGetTicket(i);
|
||
|
|
if(!PositionSelectByTicket(ticket)) continue;
|
||
|
|
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
|
||
|
|
if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) continue;
|
||
|
|
|
||
|
|
long type = PositionGetInteger(POSITION_TYPE);
|
||
|
|
double sl = PositionGetDouble(POSITION_SL);
|
||
|
|
double tp = PositionGetDouble(POSITION_TP);
|
||
|
|
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||
|
|
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||
|
|
|
||
|
|
if(type == POSITION_TYPE_BUY)
|
||
|
|
{
|
||
|
|
double newSL = NormalizeDouble(bid - trailDist, g_digits);
|
||
|
|
if(newSL > sl) trade.PositionModify(ticket, newSL, tp);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
double newSL = NormalizeDouble(ask + trailDist, g_digits);
|
||
|
|
if(sl == 0 || newSL < sl) trade.PositionModify(ticket, newSL, tp);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//===================================================================
|
||
|
|
//| DASHBOARD
|
||
|
|
//===================================================================
|
||
|
|
void CreateDashboard()
|
||
|
|
{
|
||
|
|
int x = 15, y = 40, w = 260, h = 320;
|
||
|
|
|
||
|
|
// Background panel
|
||
|
|
CreateRect(PREFIX+"BG", x, y, w, h, InpDashBG, 180);
|
||
|
|
|
||
|
|
// Header
|
||
|
|
CreateLabel(PREFIX+"TITLE", "⚡ BREAKOUT EA ULTIMATE", x+10, y+10, InpDashAccent, 10, true);
|
||
|
|
CreateLabel(PREFIX+"SYM", _Symbol+" | "+EnumToString(Period()), x+10, y+28, clrSilver, 8);
|
||
|
|
|
||
|
|
// Separator line
|
||
|
|
CreateRect(PREFIX+"LINE1", x, y+44, w, 1, InpDashAccent, 255);
|
||
|
|
|
||
|
|
// Stats rows
|
||
|
|
int ry = y+52;
|
||
|
|
CreateLabel(PREFIX+"L_BAL", "Balance", x+10, ry, clrSilver, 8); ry += 18;
|
||
|
|
CreateLabel(PREFIX+"L_EQ", "Equity", x+10, ry, clrSilver, 8); ry += 18;
|
||
|
|
CreateLabel(PREFIX+"L_PNL", "Open P/L", x+10, ry, clrSilver, 8); ry += 18;
|
||
|
|
CreateLabel(PREFIX+"L_TOT", "Total Profit", x+10, ry, clrSilver, 8); ry += 18;
|
||
|
|
|
||
|
|
CreateRect(PREFIX+"LINE2", x, ry+4, w, 1, C'50,55,70', 255); ry += 14;
|
||
|
|
|
||
|
|
CreateLabel(PREFIX+"L_WIN", "Win", x+10, ry, clrSilver, 8); ry += 18;
|
||
|
|
CreateLabel(PREFIX+"L_LOSE", "Loss", x+10, ry, clrSilver, 8); ry += 18;
|
||
|
|
CreateLabel(PREFIX+"L_WR", "Win Rate", x+10, ry, clrSilver, 8); ry += 18;
|
||
|
|
CreateLabel(PREFIX+"L_DD", "Max Drawdown", x+10, ry, clrSilver, 8); ry += 18;
|
||
|
|
|
||
|
|
CreateRect(PREFIX+"LINE3", x, ry+4, w, 1, C'50,55,70', 255); ry += 14;
|
||
|
|
|
||
|
|
CreateLabel(PREFIX+"L_SESS", "Session", x+10, ry, clrSilver, 8); ry += 18;
|
||
|
|
CreateLabel(PREFIX+"L_MART", "Mart Step", x+10, ry, clrSilver, 8); ry += 18;
|
||
|
|
CreateLabel(PREFIX+"L_AISIG", "AI Score", x+10, ry, clrSilver, 8);
|
||
|
|
|
||
|
|
// Value placeholders (right-aligned)
|
||
|
|
ry = y+52;
|
||
|
|
string vals[] = {"V_BAL","V_EQ","V_PNL","V_TOT","V_WIN","V_LOSE","V_WR","V_DD","V_SESS","V_MART","V_AISIG"};
|
||
|
|
for(int i = 0; i < ArraySize(vals); i++)
|
||
|
|
{
|
||
|
|
CreateLabel(PREFIX+vals[i], "---", x+w-15, ry, InpDashText, 8);
|
||
|
|
ry += (i == 3 || i == 7) ? 32 : 18;
|
||
|
|
}
|
||
|
|
|
||
|
|
ChartRedraw();
|
||
|
|
}
|
||
|
|
|
||
|
|
void UpdateDashboard()
|
||
|
|
{
|
||
|
|
double bal = AccountInfoDouble(ACCOUNT_BALANCE);
|
||
|
|
double eq = AccountInfoDouble(ACCOUNT_EQUITY);
|
||
|
|
double openPL = eq - bal;
|
||
|
|
double wr = (g_totalTrades > 0) ? (double)g_winTrades/g_totalTrades*100.0 : 0;
|
||
|
|
bool active = IsSessionActive();
|
||
|
|
int aiNow = GetAIScore(true) + GetAIScore(false);
|
||
|
|
|
||
|
|
color pnlColor = (openPL >= 0) ? InpDashProfit : InpDashLoss;
|
||
|
|
color totColor = (g_totalProfit >= 0) ? InpDashProfit : InpDashLoss;
|
||
|
|
color sessColor = active ? InpDashProfit : clrOrange;
|
||
|
|
color martColor = (g_martStep == 0) ? InpDashProfit : (g_martStep < InpMaxMartSteps ? clrOrange : InpDashLoss);
|
||
|
|
|
||
|
|
SetLabelText(PREFIX+"V_BAL", "$"+DoubleToString(bal, 2), InpDashText);
|
||
|
|
SetLabelText(PREFIX+"V_EQ", "$"+DoubleToString(eq, 2), InpDashText);
|
||
|
|
SetLabelText(PREFIX+"V_PNL", (openPL>=0?"+":"")+DoubleToString(openPL,2), pnlColor);
|
||
|
|
SetLabelText(PREFIX+"V_TOT", (g_totalProfit>=0?"+":"")+DoubleToString(g_totalProfit,2), totColor);
|
||
|
|
SetLabelText(PREFIX+"V_WIN", IntegerToString(g_winTrades), InpDashProfit);
|
||
|
|
SetLabelText(PREFIX+"V_LOSE", IntegerToString(g_lossTrades), InpDashLoss);
|
||
|
|
SetLabelText(PREFIX+"V_WR", DoubleToString(wr, 1)+"%", (wr>=50?InpDashProfit:InpDashLoss));
|
||
|
|
SetLabelText(PREFIX+"V_DD", DoubleToString(g_maxDD, 2)+"%", (g_maxDD<10?InpDashProfit:(g_maxDD<20?clrOrange:InpDashLoss)));
|
||
|
|
SetLabelText(PREFIX+"V_SESS", active ? "ACTIVE ✓" : "CLOSED ✗", sessColor);
|
||
|
|
SetLabelText(PREFIX+"V_MART", IntegerToString(g_martStep)+"/"+IntegerToString(InpMaxMartSteps), martColor);
|
||
|
|
SetLabelText(PREFIX+"V_AISIG", IntegerToString(aiNow)+"/4", (aiNow>=InpMinAIScore?InpDashProfit:clrOrange));
|
||
|
|
|
||
|
|
ChartRedraw();
|
||
|
|
}
|
||
|
|
|
||
|
|
void DeleteDashboard()
|
||
|
|
{
|
||
|
|
ObjectsDeleteAll(0, PREFIX);
|
||
|
|
ChartRedraw();
|
||
|
|
}
|
||
|
|
|
||
|
|
void CreateRect(string name, int x, int y, int w, int h, color clr, uchar alpha)
|
||
|
|
{
|
||
|
|
ObjectCreate(0, name, OBJ_RECTANGLE_LABEL, 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_BGCOLOR, clr);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_BORDER_TYPE,BORDER_FLAT);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_BACK, false);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
|
||
|
|
}
|
||
|
|
|
||
|
|
void CreateLabel(string name, string text, int x, int y, color clr, int fontSize, bool bold=false)
|
||
|
|
{
|
||
|
|
ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER);
|
||
|
|
ObjectSetString (0, name, OBJPROP_TEXT, text);
|
||
|
|
ObjectSetString (0, name, OBJPROP_FONT, bold ? "Arial Bold" : "Arial");
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontSize);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_BACK, false);
|
||
|
|
}
|
||
|
|
|
||
|
|
void SetLabelText(string name, string text, color clr)
|
||
|
|
{
|
||
|
|
if(ObjectFind(0, name) >= 0)
|
||
|
|
{
|
||
|
|
ObjectSetString (0, name, OBJPROP_TEXT, text);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
|
||
|
|
// Right-align value labels
|
||
|
|
int x = ObjectGetInteger(0, name, OBJPROP_XDISTANCE);
|
||
|
|
ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_RIGHT_UPPER);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//===================================================================
|
||
|
|
//| FILTERS
|
||
|
|
//===================================================================
|
||
|
|
bool CheckFilters(bool isBuy)
|
||
|
|
{
|
||
|
|
if(InpUseRSI && !RSIFilter(isBuy)) return false;
|
||
|
|
if(InpUseMACD && !MACDFilter(isBuy)) return false;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool RSIFilter(bool isBuy)
|
||
|
|
{
|
||
|
|
double rsi[];
|
||
|
|
ArraySetAsSeries(rsi, true);
|
||
|
|
if(CopyBuffer(h_RSI, 0, 1, 1, rsi) < 1) return false;
|
||
|
|
return isBuy ? (rsi[0] > 50) : (rsi[0] < 50);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool MACDFilter(bool isBuy)
|
||
|
|
{
|
||
|
|
double main[], sig[];
|
||
|
|
ArraySetAsSeries(main, true); ArraySetAsSeries(sig, true);
|
||
|
|
if(CopyBuffer(h_MACD, 0, 1, 1, main) < 1) return false;
|
||
|
|
if(CopyBuffer(h_MACD, 1, 1, 1, sig) < 1) return false;
|
||
|
|
return isBuy ? (main[0] > sig[0]) : (main[0] < sig[0]);
|
||
|
|
}
|
||
|
|
|
||
|
|
//===================================================================
|
||
|
|
//| LOT SIZE + MARTINGALE
|
||
|
|
//===================================================================
|
||
|
|
double CalculateLot(double atr)
|
||
|
|
{
|
||
|
|
double bal = AccountInfoDouble(ACCOUNT_BALANCE);
|
||
|
|
double riskAmt = bal * InpRiskPercent / 100.0;
|
||
|
|
double slDist = (InpUseATRDynamic && atr > 0) ? atr * InpATRSLMulti : InpFallbackSLPips * g_pipValue * _Point;
|
||
|
|
double tickVal = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
|
||
|
|
double tickSz = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
|
||
|
|
if(tickSz <= 0 || slDist <= 0) return 0;
|
||
|
|
|
||
|
|
double lot = riskAmt / (slDist / _Point * (tickVal / tickSz));
|
||
|
|
|
||
|
|
if(InpUseMartingale && g_martStep > 0 && g_lastLot > 0)
|
||
|
|
lot = g_lastLot * MathPow(InpMartMultiplier, g_martStep);
|
||
|
|
|
||
|
|
return NormLot(lot);
|
||
|
|
}
|
||
|
|
|
||
|
|
double NormLot(double lot)
|
||
|
|
{
|
||
|
|
double mn = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
|
||
|
|
double mx = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
|
||
|
|
double st = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
|
||
|
|
lot = MathFloor(lot/st)*st;
|
||
|
|
return NormalizeDouble(MathMax(mn, MathMin(mx, lot)), 2);
|
||
|
|
}
|
||
|
|
|
||
|
|
//===================================================================
|
||
|
|
//| BREAKEVEN
|
||
|
|
//===================================================================
|
||
|
|
void ManageBreakeven()
|
||
|
|
{
|
||
|
|
double bePts = InpBreakevenPips * g_pipValue * _Point;
|
||
|
|
for(int i = PositionsTotal()-1; i >= 0; i--)
|
||
|
|
{
|
||
|
|
ulong ticket = PositionGetTicket(i);
|
||
|
|
if(!PositionSelectByTicket(ticket)) continue;
|
||
|
|
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
|
||
|
|
if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) continue;
|
||
|
|
|
||
|
|
long type = PositionGetInteger(POSITION_TYPE);
|
||
|
|
double open = PositionGetDouble(POSITION_PRICE_OPEN);
|
||
|
|
double sl = PositionGetDouble(POSITION_SL);
|
||
|
|
double tp = PositionGetDouble(POSITION_TP);
|
||
|
|
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||
|
|
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||
|
|
|
||
|
|
if(type == POSITION_TYPE_BUY && bid >= open+bePts && sl < open)
|
||
|
|
trade.PositionModify(ticket, NormalizeDouble(open+_Point, g_digits), tp);
|
||
|
|
else if(type == POSITION_TYPE_SELL && ask <= open-bePts && (sl > open || sl == 0))
|
||
|
|
trade.PositionModify(ticket, NormalizeDouble(open-_Point, g_digits), tp);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//===================================================================
|
||
|
|
//| S/R LEVELS
|
||
|
|
//===================================================================
|
||
|
|
double GetResistance()
|
||
|
|
{
|
||
|
|
double h[];
|
||
|
|
ArraySetAsSeries(h, true);
|
||
|
|
if(CopyHigh(_Symbol, PERIOD_CURRENT, 1, InpLookbackBars, h) < InpLookbackBars) return 0;
|
||
|
|
return h[ArrayMaximum(h, 0, InpLookbackBars)];
|
||
|
|
}
|
||
|
|
|
||
|
|
double GetSupport()
|
||
|
|
{
|
||
|
|
double l[];
|
||
|
|
ArraySetAsSeries(l, true);
|
||
|
|
if(CopyLow(_Symbol, PERIOD_CURRENT, 1, InpLookbackBars, l) < InpLookbackBars) return 0;
|
||
|
|
return l[ArrayMinimum(l, 0, InpLookbackBars)];
|
||
|
|
}
|
||
|
|
|
||
|
|
//===================================================================
|
||
|
|
//| POSITION UTILS
|
||
|
|
//===================================================================
|
||
|
|
bool HasOpenPosition()
|
||
|
|
{
|
||
|
|
for(int i = PositionsTotal()-1; i >= 0; i--)
|
||
|
|
{
|
||
|
|
ulong t = PositionGetTicket(i);
|
||
|
|
if(PositionSelectByTicket(t))
|
||
|
|
if(PositionGetString(POSITION_SYMBOL)==_Symbol &&
|
||
|
|
PositionGetInteger(POSITION_MAGIC)==InpMagicNumber) return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
void CloseAllPositions()
|
||
|
|
{
|
||
|
|
for(int i = PositionsTotal()-1; i >= 0; i--)
|
||
|
|
{
|
||
|
|
ulong t = PositionGetTicket(i);
|
||
|
|
if(PositionSelectByTicket(t))
|
||
|
|
if(PositionGetString(POSITION_SYMBOL)==_Symbol &&
|
||
|
|
PositionGetInteger(POSITION_MAGIC)==InpMagicNumber)
|
||
|
|
trade.PositionClose(t);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//===================================================================
|
||
|
|
//| DAILY LOSS PROTECTION
|
||
|
|
//===================================================================
|
||
|
|
bool IsDailyLossBreached()
|
||
|
|
{
|
||
|
|
MqlDateTime dt; TimeToStruct(TimeCurrent(), dt);
|
||
|
|
static int lastDay = -1;
|
||
|
|
if(dt.day != lastDay) { lastDay = dt.day; g_dailyStartBal = AccountInfoDouble(ACCOUNT_BALANCE); }
|
||
|
|
double loss = (g_dailyStartBal - AccountInfoDouble(ACCOUNT_BALANCE)) / g_dailyStartBal * 100.0;
|
||
|
|
if(loss >= InpMaxDailyLossPct) { Print("Daily loss limit hit! EA halted."); return true; }
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
//===================================================================
|
||
|
|
//| LOAD HISTORY STATS (from closed deals)
|
||
|
|
//===================================================================
|
||
|
|
void LoadHistoryStats()
|
||
|
|
{
|
||
|
|
HistorySelect(0, TimeCurrent());
|
||
|
|
for(int i = 0; i < HistoryDealsTotal(); i++)
|
||
|
|
{
|
||
|
|
ulong deal = HistoryDealGetTicket(i);
|
||
|
|
if(HistoryDealGetInteger(deal, DEAL_MAGIC) != InpMagicNumber) continue;
|
||
|
|
double profit = HistoryDealGetDouble(deal, DEAL_PROFIT);
|
||
|
|
if(profit == 0) continue;
|
||
|
|
g_totalProfit += profit;
|
||
|
|
g_totalTrades++;
|
||
|
|
if(profit > 0) g_winTrades++; else g_lossTrades++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|