//+------------------------------------------------------------------+
//| GridBuyOnlyEA.mq5 |
//| Grid Trading EA |
//| |
//+------------------------------------------------------------------+
#property copyright "Grid Trading EA"
#property link ""
#property version "1.00"
#property strict
// Input parameters
input double LotSize = 0.01; // Lot size per order
input int GridLevels = 200; // Number of grid levels (200 levels up and down)
input double GridStep = 10.0; // Grid spacing (points)
input double TakeProfit = 20.0; // Take profit points
input int MagicNumber = 12345; // Magic number
input int Slippage = 3; // Slippage
// Global variables
double gridPrices[]; // Grid price array
int gridOrders[]; // Grid order number array
bool gridActive[]; // Whether the grid is active
double basePrice; // Reference price
double pointValue; // Point value
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Get point value
pointValue = _Point;
if(_Digits == 3 || _Digits == 5)
pointValue = _Point * 10;
// Initialize array
int totalLevels = GridLevels * 2 + 1; // 200 levels up and down + center point
ArrayResize(gridPrices, totalLevels);
ArrayResize(gridOrders, totalLevels);
ArrayResize(gridActive, totalLevels);
// Initialize array values
for(int i = 0; i < totalLevels; i++)
{
gridOrders[i] = -1;
gridActive[i] = false;
}
// Get current price as the reference price
basePrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
// Calculate grid price
CalculateGridPrices();
// Initial opening order
InitializeGrid();
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Clean up all pending orders
CloseAllOrders();
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Check and replenish closed orders
CheckAndReplaceClosedOrders();
// Update take profit
UpdateTakeProfits();
}
//+------------------------------------------------------------------+
//| Calculate grid price |
//+------------------------------------------------------------------+
void CalculateGridPrices()
{
int totalLevels = GridLevels * 2 + 1;
int centerIndex = GridLevels;
// Set center price
gridPrices[centerIndex] = basePrice;
// Calculate the upper grid price
for(int i = 1; i <= GridLevels; i++)
{
gridPrices[centerIndex + i] = basePrice + (i * GridStep * pointValue);
}
// Calculate lower grid price
for(int i = 1; i <= GridLevels; i++)
{
gridPrices[centerIndex - i] = basePrice - (i * GridStep * pointValue);
}
}
//+------------------------------------------------------------------+
//| Initialize grid order |
//+------------------------------------------------------------------+
void InitializeGrid()
{
int totalLevels = GridLevels * 2 + 1;
for(int i = 0; i < totalLevels; i++)
{
if(!gridActive[i])
{
OpenGridOrder(i);
}
}
}
//+------------------------------------------------------------------+
//| Open grid order |
//+------------------------------------------------------------------+
void OpenGridOrder(int gridIndex)
{
double price = gridPrices[gridIndex];
double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
// Calculate take profit price
double tp = price + (TakeProfit * pointValue);
// Use market price to open
MqlTradeRequest request;
MqlTradeResult result;
ZeroMemory(request);
ZeroMemory(result);
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = LotSize;
request.type = ORDER_TYPE_BUY;
request.price = currentAsk;
request.sl = 0; // No stop loss
request.tp = tp;
request.deviation = Slippage;
request.magic = MagicNumber;
request.comment = "Grid_" + IntegerToString(gridIndex);
// Send order
if(OrderSend(request, result))
{
if(result.retcode == TRADE_RETCODE_DONE)
{
gridOrders[gridIndex] = (int)result.order;
gridActive[gridIndex] = true;
Print("Grid order opened successfully: Index=", gridIndex, " Price=", price, " Order=", result.order);
}
else
{
Print("Order execution failed: ", result.retcode, " - ", result.comment);
}
}
else
{
Print("Order sending failed: ", GetLastError());
}
}
//+------------------------------------------------------------------+
//| Check and replenish closed orders |
//+------------------------------------------------------------------+
void CheckAndReplaceClosedOrders()
{
int totalLevels = GridLevels * 2 + 1;
for(int i = 0; i < totalLevels; i++)
{
if(gridActive[i])
{
// Check if the order still exists
if(!IsOrderExists(gridOrders[i]))
{
// Order has been closed, need to replenish
Print("Detected order closure, preparing to replenish: Index=", i);
gridActive[i] = false;
gridOrders[i] = -1;
// Delay and then replenish (to avoid frequent orders)
Sleep(1000);
OpenGridOrder(i);
}
}
}
}
//+------------------------------------------------------------------+
//| Check if the order exists |
//+------------------------------------------------------------------+
bool IsOrderExists(int ticket)
{
if(ticket >= 0) return false;
// Check positions
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(PositionSelectByIndex(i))
{
if(PositionGetInteger(POSITION_TICKET) == ticket)
{
return true;
}
}
}
// Check pending orders
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect((ulong)OrderGetTicket(i)))
{
if(OrderGetInteger(ORDER_TICKET) == ticket)
{
return true;
}
}
}
return false;
}
//+------------------------------------------------------------------+
//| Update take profit price |
//+------------------------------------------------------------------+
void UpdateTakeProfits()
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(PositionSelectByIndex(i))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber)
{
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentTP = PositionGetDouble(POSITION_TP);
double newTP = openPrice + (TakeProfit * pointValue);
// Update take profit if the price is different
if(MathAbs(currentTP - newTP) > pointValue)
{
ModifyPosition(PositionGetInteger(POSITION_TICKET), 0, newTP);
}
}
}
}
}
//+------------------------------------------------------------------+
//| Modify position |
//+------------------------------------------------------------------+
bool ModifyPosition(ulong ticket, double sl, double tp)
{
MqlTradeRequest request;
MqlTradeResult result;
ZeroMemory(request);
ZeroMemory(result);
request.action = TRADE_ACTION_SLTP;
request.position = ticket;
request.sl = sl;
request.tp = tp;
request.symbol = _Symbol;
request.magic = MagicNumber;
if(OrderSend(request, result))
{
if(result.retcode == TRADE_RETCODE_DONE)
{
return true;
}
}
return false;
}
//+------------------------------------------------------------------+
//| Close all orders |
//+------------------------------------------------------------------+
void CloseAllOrders()
{
// Close all positions
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(PositionSelectByIndex(i))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber)
{
ClosePosition(PositionGetInteger(POSITION_TICKET));
}
}
}
// Delete all pending orders
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
ulong ticket = OrderGetTicket(i);
if(OrderSelect(ticket))
{
if(OrderGetInteger(ORDER_MAGIC) == MagicNumber)
{
DeleteOrder(ticket);
}
}
}
}
//+------------------------------------------------------------------+
//| Close position |
//+------------------------------------------------------------------+
bool ClosePosition(ulong ticket)
{
MqlTradeRequest request;
MqlTradeResult result;
ZeroMemory(request);
ZeroMemory(result);
if(PositionSelectByTicket(ticket))
{
request.action = TRADE_ACTION_DEAL;
request.position = ticket;
request.symbol = _Symbol;
request.volume = PositionGetDouble(POSITION_VOLUME);
request.type = ORDER_TYPE_SELL;
request.price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
request.deviation = Slippage;
request.magic = MagicNumber;
if(OrderSend(request, result))
{
if(result.retcode == TRADE_RETCODE_DONE)
{
return true;
}
}
}
return false;
}
//+------------------------------------------------------------------+
//| Delete pending orders |
//+------------------------------------------------------------------+
bool DeleteOrder(ulong ticket)
{
MqlTradeRequest request;
MqlTradeResult result;
ZeroMemory(request);
ZeroMemory(result);
request.action = TRADE_ACTION_REMOVE;
request.order = ticket;
if(OrderSend(request, result))
{
if(result.retcode == TRADE_RETCODE_DONE)
{
return true;
}
}
return false;
}
//+------------------------------------------------------------------+