BreakoutEA-UltimatePro/BreakoutEA_UltimatePro.mq5

655 lines
24 KiB
MQL5
Raw Permalink Normal View History

2026-05-18 02:42:21 +00:00
//+------------------------------------------------------------------+
//| 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++;
}
}
//+------------------------------------------------------------------+