//+------------------------------------------------------------------+
//|                                          CustomPriceGridEA.mq5   |
//|                    Grid Trading EA with OCO Logic                |
//|                 Converted from Custom Price Red Line Indicator   |
//+------------------------------------------------------------------+
#property copyright   "Grid Trading EA"
#property version     "1.00"
#property strict

#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\OrderInfo.mqh>

//─ INPUTS ────────────────────────────────────────────────────────────
input double  InpStartPrice   = 1000.0;     // Starting Price
input double  InpSpacing      = 25.0;       // Price Spacing
input int     InpNumLines     = 10;         // Number of Lines (up & down)
input double  InpLotSize      = 0.01;       // Lot Size
input int     InpTakeProfit   = 100;        // Take Profit (points)
input int     InpStopLoss     = 50;         // Stop Loss (points)
input int     InpMagicNumber  = 12345;      // Magic Number
input string  InpComment      = "GridEA";   // Order Comment

//─ GLOBAL VARIABLES ──────────────────────────────────────────────────
CTrade         trade;
CPositionInfo  position;
COrderInfo     order;

double         g_point;
ulong          g_buyTicket  = 0;
ulong          g_sellTicket = 0;
bool           g_initialized = false;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   // Setup trade parameters
   trade.SetExpertMagicNumber(InpMagicNumber);
   trade.SetDeviationInPoints(10);
   trade.SetTypeFilling(ORDER_FILLING_FOK);
   
   // Get point value
   g_point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   
   // Place initial orders
   PlaceInitialOrders();
   
   g_initialized = true;
   
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // Clean up any pending orders on removal
   DeleteAllPendingOrders();
}

//+------------------------------------------------------------------+
//| Find nearest grid levels above and below current price           |
//+------------------------------------------------------------------+
void GetNearestLevels(double &nearestAbove, double &nearestBelow)
{
   double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double startLevel = InpStartPrice - (InpNumLines * InpSpacing);
   
   nearestAbove = 0;
   nearestBelow = 0;
   
   // Find all levels and identify nearest above/below
   for(int i = 0; i <= (2 * InpNumLines); i++)
   {
      double level = startLevel + (i * InpSpacing);
      
      if(level > currentPrice)
      {
         if(nearestAbove == 0 || level < nearestAbove)
            nearestAbove = level;
      }
      else if(level < currentPrice)
      {
         if(nearestBelow == 0 || level > nearestBelow)
            nearestBelow = level;
      }
   }
}

//+------------------------------------------------------------------+
//| Place initial buy and sell limit orders                          |
//+------------------------------------------------------------------+
void PlaceInitialOrders()
{
   double nearestAbove, nearestBelow;
   GetNearestLevels(nearestAbove, nearestBelow);
   
   double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   
   // Delete any existing pending orders first
   DeleteAllPendingOrders();
   
   // Place SELL LIMIT at nearest level above (if valid)
   if(nearestAbove > 0 && nearestAbove > currentAsk)
   {
      double sl = nearestAbove + (InpStopLoss * g_point);
      double tp = nearestAbove - (InpTakeProfit * g_point);
      
      if(trade.SellLimit(InpLotSize, nearestAbove, _Symbol, sl, tp, 
                         ORDER_TIME_GTC, 0, InpComment))
      {
         g_sellTicket = trade.ResultOrder();
         Print("SELL LIMIT placed at ", nearestAbove, " Ticket: ", g_sellTicket);
      }
      else
      {
         Print("Failed to place SELL LIMIT. Error: ", GetLastError());
      }
   }
   
   // Place BUY LIMIT at nearest level below (if valid)
   if(nearestBelow > 0 && nearestBelow < currentBid)
   {
      double sl = nearestBelow - (InpStopLoss * g_point);
      double tp = nearestBelow + (InpTakeProfit * g_point);
      
      if(trade.BuyLimit(InpLotSize, nearestBelow, _Symbol, sl, tp, 
                        ORDER_TIME_GTC, 0, InpComment))
      {
         g_buyTicket = trade.ResultOrder();
         Print("BUY LIMIT placed at ", nearestBelow, " Ticket: ", g_buyTicket);
      }
      else
      {
         Print("Failed to place BUY LIMIT. Error: ", GetLastError());
      }
   }
}

//+------------------------------------------------------------------+
//| Delete all pending orders with our magic number                  |
//+------------------------------------------------------------------+
void DeleteAllPendingOrders()
{
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(order.SelectByIndex(i))
      {
         if(order.Symbol() == _Symbol && order.Magic() == InpMagicNumber)
         {
            trade.OrderDelete(order.Ticket());
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Check if a pending order was triggered                           |
//+------------------------------------------------------------------+
void CheckOrderStatus()
{
   bool buyExists = false;
   bool sellExists = false;
   bool hasPosition = false;
   
   // Check if we have any open positions
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(position.SelectByIndex(i))
      {
         if(position.Symbol() == _Symbol && position.Magic() == InpMagicNumber)
         {
            hasPosition = true;
            break;
         }
      }
   }
   
   // Check pending orders
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(order.SelectByIndex(i))
      {
         if(order.Symbol() == _Symbol && order.Magic() == InpMagicNumber)
         {
            if(order.Ticket() == g_buyTicket)
               buyExists = true;
            if(order.Ticket() == g_sellTicket)
               sellExists = true;
         }
      }
   }
   
   // If one order triggered (we have position but one order missing)
   if(hasPosition && (!buyExists || !sellExists))
   {
      // Delete the remaining pending order
      DeleteAllPendingOrders();
      g_buyTicket = 0;
      g_sellTicket = 0;
   }
   
   // If no position and no pending orders, place new ones
   if(!hasPosition && !buyExists && !sellExists && g_initialized)
   {
      PlaceInitialOrders();
   }
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   // Check order status on each tick
   CheckOrderStatus();
}

//+------------------------------------------------------------------+
//| Trade transaction event                                          |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction& trans,
                        const MqlTradeRequest& request,
                        const MqlTradeResult& result)
{
   // When a pending order becomes a position, delete the other
   if(trans.type == TRADE_TRANSACTION_DEAL_ADD)
   {
      if(trans.symbol == _Symbol)
      {
         // Give it a moment then check orders
         Sleep(100);
         CheckOrderStatus();
      }
   }
}
//+------------------------------------------------------------------+