Gold Grid EA MT5: MQL5 Mechanics and Exposure Limits
Inspect a buy-only grid EA example with point-based spacing, increasing lots and basket-profit closure. It requires a hedging account and has no stop-loss or equity-loss cap. The source explains the mechanics; no verified gold backtest or safe account size is established here.
Strategy logic
Entry conditions
For the chart symbol, request an initial buy when no matching symbol-and-magic positions exist. Add a buy when the current ask is at least GridStep points below the lowest matching entry, up to MaxOrders separate positions. Requested lots follow InitialLots times LotMultiplier raised to the current position count, rounded down to the broker volume step. The example rejects netting accounts because position count would not count grid entries correctly. Use one instance per symbol and magic number.
Exit conditions
When matching floating position profit plus swap reaches ProfitTarget in account currency, latch basket closure and retry remaining positions on subsequent ticks. This is a requested threshold, not guaranteed realized net profit: commissions, price movement and partial/rejected fills matter. A new grid starts after the basket is empty. There is no stop-loss, equity stop or maximum loss; a position cap only limits additions. Closure state is not persisted across a restart.
MQL5 Expert Advisor Code
//+------------------------------------------------------------------+
//| Gold Grid EA - XAUUSD |
//| Pineify.app — WARNING: Grid EAs carry significant risk |
//+------------------------------------------------------------------+
#property copyright "Pineify.app"
#property version "1.00"
input int MagicNumber = 345678;
input double InitialLots = 0.01;
input double LotMultiplier= 1.5;
input int GridStep = 150; // Grid spacing in points
input int MaxOrders = 8;
input double ProfitTarget = 50.0; // Account-currency floating profit + swap threshold
bool closingBasket=false;
int OnInit() {
if(MagicNumber<0 || InitialLots<=0 || LotMultiplier<1 || GridStep<=0 || MaxOrders<1 || ProfitTarget<=0)
return INIT_PARAMETERS_INCORRECT;
if(AccountInfoInteger(ACCOUNT_MARGIN_MODE)!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) {
Print("This position-count grid requires a hedging account.");
return INIT_FAILED;
}
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason) {}
void OnTick() {
int openOrders = CountPositions(MagicNumber);
double floatingProfit = GetFloatingProfit(MagicNumber);
if(openOrders==0) closingBasket=false;
if(openOrders>0 && floatingProfit>=ProfitTarget) closingBasket=true;
// Continue closing after partial/rejected requests even if profit changes.
if (closingBasket) {
CloseAllPositions(MagicNumber);
return;
}
// Open initial order
if (openOrders == 0) {
OpenTrade(ORDER_TYPE_BUY, InitialLots, MagicNumber);
return;
}
// Add grid orders if price drops GridStep from lowest order
if (openOrders < MaxOrders) {
double lowestPrice = GetLowestOrderPrice(MagicNumber);
double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if (currentAsk>0 && lowestPrice!=DBL_MAX && lowestPrice - currentAsk >= GridStep * _Point) {
double nextLots = InitialLots * MathPow(LotMultiplier, openOrders);
OpenTrade(ORDER_TYPE_BUY, nextLots, MagicNumber);
}
}
}
double GetLowestOrderPrice(int magic) {
double lowest = DBL_MAX;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (PositionSelectByTicket(ticket) &&
(int)PositionGetInteger(POSITION_MAGIC) == magic &&
PositionGetString(POSITION_SYMBOL)==_Symbol) {
double price = PositionGetDouble(POSITION_PRICE_OPEN);
if (price < lowest) lowest = price;
}
}
return lowest;
}
double GetFloatingProfit(int magic) {
double total = 0;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (PositionSelectByTicket(ticket) &&
(int)PositionGetInteger(POSITION_MAGIC) == magic &&
PositionGetString(POSITION_SYMBOL)==_Symbol)
total += PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP);
}
return total;
}
void CloseAllPositions(int magic) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (!PositionSelectByTicket(ticket)) continue;
if ((int)PositionGetInteger(POSITION_MAGIC) != magic ||
PositionGetString(POSITION_SYMBOL)!=_Symbol) continue;
MqlTradeRequest req = {}; MqlTradeResult res = {};
req.action = TRADE_ACTION_DEAL;
req.position = ticket;
req.symbol = _Symbol;
req.volume = PositionGetDouble(POSITION_VOLUME);
req.type = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
req.price = (req.type == ORDER_TYPE_SELL)
? SymbolInfoDouble(_Symbol, SYMBOL_BID)
: SymbolInfoDouble(_Symbol, SYMBOL_ASK);
req.type_filling = ORDER_FILLING_IOC;
if(!OrderSend(req,res) || (res.retcode!=TRADE_RETCODE_DONE && res.retcode!=TRADE_RETCODE_DONE_PARTIAL))
PrintFormat("Grid close failed: %u %s",res.retcode,res.comment);
}
}
int CountPositions(int magic) {
int count = 0;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (PositionSelectByTicket(ticket) &&
(int)PositionGetInteger(POSITION_MAGIC) == magic &&
PositionGetString(POSITION_SYMBOL)==_Symbol) count++;
}
return count;
}
void OpenTrade(ENUM_ORDER_TYPE type, double lots, int magic) {
double step=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
double minLot=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
double maxLot=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
if(step<=0 || minLot<=0 || maxLot<minLot || !MathIsValidNumber(lots)) {
Print("Invalid symbol volume properties or requested lots"); return;
}
lots=NormalizeDouble(MathFloor((lots+1e-10)/step)*step,8);
if(lots<minLot || lots>maxLot) {
Print("Grid order rejected by example volume limits: ",lots); return;
}
MqlTradeRequest req = {}; MqlTradeResult res = {};
req.action = TRADE_ACTION_DEAL; req.symbol = _Symbol; req.volume = lots;
req.type = type;
req.price = (type == ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK)
: SymbolInfoDouble(_Symbol, SYMBOL_BID);
req.magic = magic; req.comment = "Pineify Grid";
req.type_filling = ORDER_FILLING_IOC;
if(!OrderSend(req,res) || (res.retcode!=TRADE_RETCODE_DONE && res.retcode!=TRADE_RETCODE_DONE_PARTIAL))
PrintFormat("Grid entry failed: %u %s",res.retcode,res.comment);
}Copy this code into MetaEditor, save it in the MQL5/Experts folder, and compile with F7.
Generate a custom XAUUSD grid EA
Describe the entry, exit, position, order, and risk rules you want. Pineify generates editable MQL5 source code that you can inspect and compile in MetaEditor.
Pine Script vs MQL5: Same Strategy, Different Platforms
| Aspect | Pine Script (TradingView) | MQL5 (MetaTrader 5) |
|---|---|---|
| Execution | Series evaluated on chart updates | Event handlers with explicit buffers |
| Deployment | Runs in TradingView with alerts | Runs in the MT5 terminal or on a VPS |
| Broker access | Strategy orders are simulated; live automation needs a separate integration | Direct broker connectivity |
| Backtesting | Strategy Tester for strategy scripts | Strategy Tester for Expert Advisors |
| Code complexity | Simpler, functional syntax | C++-like, more powerful |
The calculation can be implemented on either platform, but the runtime model and buffer APIs differ.
Frequently Asked Questions
Related MQL5 pages
Technical references
Risk and testing note
These educational examples require independent compilation and testing. A compile pass or an illustrative setting does not establish execution correctness or profitability. Any reported performance must be supported by reproducible data and test settings; past results do not predict future returns. Trading involves significant risk of loss. This content is not financial advice.