Fix download links
This commit is contained in:
@@ -0,0 +1,332 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user