Files
ironsan2kk-pixel 6450d1efc6 Fix download links
2026-06-08 10:36:15 +02:00

333 lines
15 KiB
Plaintext

//+------------------------------------------------------------------+
//| GridMaster Pro.mq5 |
//| Copyright 2024, Sajid. |
//| https://www.mql5.com/en/users/sajidmahamud835 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Sajid."
#property link "https://www.mql5.com/en/users/sajidmahamud835"
#property version "2.00"
#property strict
#property description "GridMaster Pro v2 — Bi-directional ATR grid with proper MM, min-stop awareness, and drawdown protection."
#include <Trade\Trade.mqh>
//--- Enums
enum ENUM_GRID_MODE {
GRID_NEUTRAL = 0, // Neutral: BUY below + SELL above
GRID_BULLISH = 1, // Bullish: BUY only
GRID_BEARISH = 2, // Bearish: SELL only
};
enum ENUM_LOT_MODE {
LOT_FIXED = 0, // Fixed lot size
LOT_DYNAMIC = 1, // Risk-based (% of balance per order)
};
//--- Input Parameters — Grid
input ENUM_GRID_MODE GridMode = GRID_NEUTRAL; // Grid Mode
input int MaxOrders = 5; // Max orders per side
input int ATRPeriod = 14; // ATR period
input double ATRMultiplier = 1.5; // ATR multiplier for grid distance
//--- Input Parameters — Orders
input ENUM_LOT_MODE LotMode = LOT_FIXED; // Lot sizing mode
input double LotSize = 0.1; // Fixed lot size
input double RiskPercent = 1.0; // Risk % per order (dynamic mode)
input bool UseTakeProfit = true; // Enable Take Profit
input double DefaultTP = 200.0; // Min TP in points (auto-adjusted for broker)
input bool UseStopLoss = true; // Enable Stop Loss
input double DefaultSL = 1000.0; // Min SL in points (auto-adjusted for broker)
input bool UseTrailingStop = true; // Enable Trailing Stop
input double TrailingPoints = 100.0; // Trailing stop in points
input double TrailingStep = 20.0; // Trailing step in points
//--- Input Parameters — Risk Management
input double MaxDrawdownPct = 5.0; // Max drawdown % before pausing
input bool CloseOnDrawdown = true; // Close all orders on drawdown breach
//--- Input Parameters — Magic & Debug
input int MagicBase = 47291; // Base magic number
input bool DebugMode = false; // Enable debug logging
//--- Global Variables
CTrade trade;
int magicNumber;
double gridDistance;
double accountEquityStart;
bool gridPaused = false;
string logFile;
//+------------------------------------------------------------------+
//| Expert initialization |
//+------------------------------------------------------------------+
int OnInit() {
// Generate collision-safe magic number: base + symbol hash + timeframe
magicNumber = MagicBase + (int)(StringLen(_Symbol) * 1000) + (int)Period();
trade.SetExpertMagicNumber(magicNumber);
trade.SetDeviationInPoints(50);
trade.SetTypeFilling(ORDER_FILLING_IOC);
accountEquityStart = AccountInfoDouble(ACCOUNT_EQUITY);
logFile = "GridMasterPro_" + _Symbol + "_" + IntegerToString(Period()) + ".log";
WriteLog("GridMaster Pro v2.00 initialized | Magic: " + IntegerToString(magicNumber) +
" | Symbol: " + _Symbol + " | Grid mode: " + EnumToString(GridMode));
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialization |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
WriteLog("EA deinitialized. Reason: " + IntegerToString(reason) + " | Open positions left: " + IntegerToString(CountOurPositions(ORDER_TYPE_BUY) + CountOurPositions(ORDER_TYPE_SELL)));
}
//+------------------------------------------------------------------+
//| Expert tick |
//+------------------------------------------------------------------+
void OnTick() {
// Drawdown check
if (CloseOnDrawdown && CheckDrawdown()) {
if (!gridPaused) {
WriteLog("DRAWDOWN LIMIT REACHED — closing all positions and pausing grid");
CloseAllPositions();
gridPaused = true;
}
return;
}
// Resume grid if paused and equity has recovered
if (gridPaused) {
double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
if (currentEquity >= accountEquityStart * (1.0 - MaxDrawdownPct / 200.0)) {
gridPaused = false;
accountEquityStart = currentEquity;
WriteLog("Grid resumed after equity recovery");
} else {
return;
}
}
// Recalculate grid distance every tick
gridDistance = CalculateGridDistance();
if (gridDistance <= 0) return;
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
int buyCount = CountOurPositions(ORDER_TYPE_BUY);
int sellCount = CountOurPositions(ORDER_TYPE_SELL);
// Manage trailing stops
if (UseTrailingStop) ManageTrailingStops();
// Place BUY grid orders
if (GridMode != GRID_BEARISH && buyCount < MaxOrders) {
double buyPrice = ask - gridDistance * (buyCount + 1) * _Point;
PlaceGridOrder(ORDER_TYPE_BUY, buyPrice);
}
// Place SELL grid orders
if (GridMode != GRID_BULLISH && sellCount < MaxOrders) {
double sellPrice = bid + gridDistance * (sellCount + 1) * _Point;
PlaceGridOrder(ORDER_TYPE_SELL, sellPrice);
}
}
//+------------------------------------------------------------------+
//| Place a grid order with proper SL/TP |
//+------------------------------------------------------------------+
void PlaceGridOrder(ENUM_ORDER_TYPE type, double price) {
// Check if order already exists near this price level
if (OrderExistsNearPrice(type, price, gridDistance * 0.5 * _Point)) return;
// Broker minimum stop distance
long stopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
double minStop = MathMax((double)stopLevel, 10.0) * _Point * 1.2; // 20% buffer over broker min
double sl = 0, tp = 0;
double lot = CalculateLot();
if (type == ORDER_TYPE_BUY) {
if (UseTakeProfit) tp = price + MathMax(DefaultTP * _Point, minStop * 1.5);
if (UseStopLoss) sl = price - MathMax(DefaultSL * _Point, minStop * MaxOrders);
} else {
if (UseTakeProfit) tp = price - MathMax(DefaultTP * _Point, minStop * 1.5);
if (UseStopLoss) sl = price + MathMax(DefaultSL * _Point, minStop * MaxOrders);
}
// Normalize prices
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
price = NormalizeDouble(price, digits);
sl = sl > 0 ? NormalizeDouble(sl, digits) : 0;
tp = tp > 0 ? NormalizeDouble(tp, digits) : 0;
bool sent;
if (type == ORDER_TYPE_BUY) {
sent = trade.Buy(lot, _Symbol, 0, sl, tp, "Grid BUY");
} else {
sent = trade.Sell(lot, _Symbol, 0, sl, tp, "Grid SELL");
}
if (sent) {
WriteLog("Placed " + (type == ORDER_TYPE_BUY ? "BUY" : "SELL") +
" | Lot: " + DoubleToString(lot, 2) +
" | Price: ~" + DoubleToString(price, digits) +
" | SL: " + DoubleToString(sl, digits) +
" | TP: " + DoubleToString(tp, digits));
} else {
WriteLog("FAILED to place " + (type == ORDER_TYPE_BUY ? "BUY" : "SELL") +
" | Error: " + IntegerToString(GetLastError()) +
" | Price: " + DoubleToString(price, digits) +
" | MinStop: " + DoubleToString(minStop / _Point, 0) + " pts");
}
}
//+------------------------------------------------------------------+
//| Manage trailing stops for all our positions |
//+------------------------------------------------------------------+
void ManageTrailingStops() {
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
long stopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
double minStop = MathMax((double)stopLevel, 10.0) * _Point * 1.2;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
if (!PositionSelectByTicket(PositionGetTicket(i))) continue;
if (PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
if (PositionGetInteger(POSITION_MAGIC) != magicNumber) continue;
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentSL = PositionGetDouble(POSITION_SL);
ulong ticket = PositionGetInteger(POSITION_TICKET);
double newSL = 0;
if (posType == POSITION_TYPE_BUY) {
double profit = bid - openPrice;
if (profit >= TrailingPoints * _Point) {
newSL = NormalizeDouble(bid - TrailingPoints * _Point, digits);
if (newSL > currentSL + TrailingStep * _Point && newSL > openPrice - minStop) {
trade.PositionModify(ticket, newSL, PositionGetDouble(POSITION_TP));
}
}
} else {
double profit = openPrice - ask;
if (profit >= TrailingPoints * _Point) {
newSL = NormalizeDouble(ask + TrailingPoints * _Point, digits);
if ((currentSL == 0 || newSL < currentSL - TrailingStep * _Point) && newSL < openPrice + minStop) {
trade.PositionModify(ticket, newSL, PositionGetDouble(POSITION_TP));
}
}
}
}
}
//+------------------------------------------------------------------+
//| Calculate ATR-based grid distance in points |
//+------------------------------------------------------------------+
double CalculateGridDistance() {
double atr = iATR(_Symbol, 0, ATRPeriod);
if (atr <= 0) return 0;
return (atr * ATRMultiplier) / _Point; // Return in points
}
//+------------------------------------------------------------------+
//| Calculate lot size based on mode |
//+------------------------------------------------------------------+
double CalculateLot() {
if (LotMode == LOT_FIXED) return LotSize;
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
if (tickValue <= 0 || tickSize <= 0 || DefaultSL <= 0) return LotSize;
double riskAmount = balance * RiskPercent / 100.0;
double slValue = DefaultSL * _Point / tickSize * tickValue;
double lot = NormalizeDouble(riskAmount / slValue, 2);
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
lot = MathMax(minLot, MathMin(maxLot, MathRound(lot / lotStep) * lotStep));
return lot;
}
//+------------------------------------------------------------------+
//| Count our open positions by type |
//+------------------------------------------------------------------+
int CountOurPositions(ENUM_ORDER_TYPE type) {
int count = 0;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
if (!PositionSelectByTicket(PositionGetTicket(i))) continue;
if (PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
if (PositionGetInteger(POSITION_MAGIC) != magicNumber) continue;
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if ((type == ORDER_TYPE_BUY && posType == POSITION_TYPE_BUY) ||
(type == ORDER_TYPE_SELL && posType == POSITION_TYPE_SELL)) {
count++;
}
}
return count;
}
//+------------------------------------------------------------------+
//| Check if order already exists near a price level |
//+------------------------------------------------------------------+
bool OrderExistsNearPrice(ENUM_ORDER_TYPE type, double price, double tolerance) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
if (!PositionSelectByTicket(PositionGetTicket(i))) continue;
if (PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
if (PositionGetInteger(POSITION_MAGIC) != magicNumber) continue;
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if ((type == ORDER_TYPE_BUY && posType == POSITION_TYPE_BUY) ||
(type == ORDER_TYPE_SELL && posType == POSITION_TYPE_SELL)) {
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
if (MathAbs(openPrice - price) <= tolerance) return true;
}
}
return false;
}
//+------------------------------------------------------------------+
//| Check if drawdown limit breached |
//+------------------------------------------------------------------+
bool CheckDrawdown() {
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double maxLoss = accountEquityStart * MaxDrawdownPct / 100.0;
return (accountEquityStart - equity) >= maxLoss;
}
//+------------------------------------------------------------------+
//| Close all our positions |
//+------------------------------------------------------------------+
void CloseAllPositions() {
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) != magicNumber) continue;
trade.PositionClose(ticket);
}
}
//+------------------------------------------------------------------+
//| Append log to file (fixes overwrite bug) |
//+------------------------------------------------------------------+
void WriteLog(string message) {
if (!DebugMode && StringFind(message, "FAILED") < 0 && StringFind(message, "DRAWDOWN") < 0) return;
int handle = FileOpen(logFile, FILE_READ | FILE_WRITE | FILE_TXT | FILE_COMMON);
if (handle != INVALID_HANDLE) {
FileSeek(handle, 0, SEEK_END); // Append — seek to end
string ts = TimeToString(TimeCurrent(), TIME_DATE | TIME_MINUTES | TIME_SECONDS);
FileWriteString(handle, ts + " | " + message + "\n");
FileClose(handle);
}
}