//+------------------------------------------------------------------+
//|                                                    MA_Cross_Scalp |
//|                                                Starter template   |
//+------------------------------------------------------------------+
#property copyright "YourName"
#property version   "1.10"
#property strict

// Input parameters
input int     FastEMAPeriod = 5;
input int     SlowEMAPeriod = 20;
input double  RiskPercent   = 1.0;      // Risk per trade in % of account balance
input int     StopLoss      = 50;       // in points (e.g., 50 points = 5 pips if 1 pip = 10 points)
input int     TakeProfit    = 100;      // in points
input int     Slippage      = 3;
input int     MinHourTrade  = 8;        // Start trading from 08:00 server time
input int     MaxHourTrade  = 22;       // Stop trading at 22:00 server time
input double  MaxSpread     = 15.0;     // Maximum allowed spread in points (1.5 pips)

datetime      lastTradeTime = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   // Check trading hours
   datetime timeCurrent = TimeCurrent();
   MqlDateTime tm;
   TimeToStruct(timeCurrent,tm);
   if(tm.hour < MinHourTrade || tm.hour >= MaxHourTrade)
      return;

   // Check spread
   double spread_points = (Ask - Bid)/_Point;
   if(spread_points > MaxSpread)
      return;

   // Calculate EMAs on current timeframe
   double fastEMA = iMA(NULL,0,FastEMAPeriod,0,MODE_EMA,PRICE_CLOSE,0);
   double slowEMA = iMA(NULL,0,SlowEMAPeriod,0,MODE_EMA,PRICE_CLOSE,0);
   double fastEMA_prev = iMA(NULL,0,FastEMAPeriod,0,MODE_EMA,PRICE_CLOSE,1);
   double slowEMA_prev = iMA(NULL,0,SlowEMAPeriod,0,MODE_EMA,PRICE_CLOSE,1);

   // Check for existing open positions on symbol and filter
   int totalPositions = PositionsTotal();
   bool longPositionExists = false;
   bool shortPositionExists = false;
   for(int i=0; i<totalPositions; i++)
     {
      ulong ticket = PositionGetTicket(i);
      if(PositionSelectByTicket(ticket))
        {
         if(PositionGetString(POSITION_SYMBOL) == Symbol())
           {
            ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
            if(pos_type == POSITION_TYPE_BUY)
               longPositionExists = true;
            else if(pos_type == POSITION_TYPE_SELL)
               shortPositionExists = true;
           }
        }
     }

   // Entry: check crossover
   // Buy signal: fast EMA crosses above slow EMA
   if(fastEMA_prev < slowEMA_prev && fastEMA > slowEMA && !longPositionExists)
     {
      // Close short positions if any
      if(shortPositionExists)
         ClosePositions(POSITION_TYPE_SELL);

      // Open buy position
      OpenPosition(ORDER_TYPE_BUY);
     }

   // Sell signal: fast EMA crosses below slow EMA
   if(fastEMA_prev > slowEMA_prev && fastEMA < slowEMA && !shortPositionExists)
     {
      // Close long positions if any
      if(longPositionExists)
         ClosePositions(POSITION_TYPE_BUY);

      // Open sell position
      OpenPosition(ORDER_TYPE_SELL);
     }
  }

//+------------------------------------------------------------------+
//| Open position helper function                                    |
//+------------------------------------------------------------------+
void OpenPosition(ENUM_ORDER_TYPE order_type)
  {
   double sl_points = StopLoss;
   double calculatedLot = CalculateLotSize(sl_points);

   // Validate lot size
   double minLot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN);
   double maxLot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MAX);
   double lotStep = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP);

   if(calculatedLot < minLot)
     {
      Print("Calculated lot size too small: ", DoubleToString(calculatedLot,2), ". Skipping trade.");
      return;  // Skip trade if lot size invalid
     }

   if(calculatedLot > maxLot)
     {
      calculatedLot = maxLot;
      Print("Calculated lot size exceeds max lot, set to max lot: ", maxLot);
     }

   // Round lot size down to nearest lot step
   calculatedLot = MathFloor(calculatedLot / lotStep) * lotStep;

   MqlTradeRequest request;
   MqlTradeResult result;
   ZeroMemory(request);
   ZeroMemory(result);

   request.action   = TRADE_ACTION_DEAL;
   request.symbol   = Symbol();
   request.volume   = calculatedLot;
   request.type     = order_type;
   request.price    = (order_type == ORDER_TYPE_BUY) ? SymbolInfoDouble(Symbol(), SYMBOL_ASK) : SymbolInfoDouble(Symbol(), SYMBOL_BID);
   request.deviation= Slippage;

   // Calculate stop loss and take profit prices
   if(order_type == ORDER_TYPE_BUY)
     {
      request.sl = request.price - StopLoss * _Point;
      request.tp = request.price + TakeProfit * _Point;
     }
   else if(order_type == ORDER_TYPE_SELL)
     {
      request.sl = request.price + StopLoss * _Point;
      request.tp = request.price - TakeProfit * _Point;
     }

   if(!OrderSend(request,result))
     {
      Print("OrderSend failed: ", GetLastError());
     }
   else
     {
      Print("Opened position: ", order_type == ORDER_TYPE_BUY ? "BUY" : "SELL", " ticket: ", result.order, " lots: ", DoubleToString(calculatedLot,2));
     }
  }

//+------------------------------------------------------------------+
//| Close all positions helper function for given type              |
//+------------------------------------------------------------------+
void ClosePositions(ENUM_POSITION_TYPE pos_type)
  {
   ulong ticket;
   double price;
   MqlTradeRequest request;
   MqlTradeResult result;
   for(int i=PositionsTotal()-1; i>=0; i--)
     {
      ticket = PositionGetTicket(i);
      if(PositionSelectByTicket(ticket))
        {
         if(PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_TYPE) == pos_type)
           {
            ZeroMemory(request);
            ZeroMemory(result);

            request.action = TRADE_ACTION_DEAL;
            request.position = ticket;
            if(pos_type == POSITION_TYPE_BUY)
               {
                request.type = ORDER_TYPE_SELL;
                price = SymbolInfoDouble(Symbol(), SYMBOL_BID);
               }
            else if(pos_type == POSITION_TYPE_SELL)
               {
                request.type = ORDER_TYPE_BUY;
                price = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
               }

            request.price = price;
            request.volume = PositionGetDouble(POSITION_VOLUME);
            request.deviation = Slippage;

            if(!OrderSend(request,result))
              {
               Print("Close failed: ", GetLastError());
              }
            else
              {
               Print("Closed position ticket ", ticket);
              }
           }
        }
     }
  }

//+------------------------------------------------------------------+
//| Calculate lot size based on risk % and stop loss distance       |
//+------------------------------------------------------------------+
double CalculateLotSize(double stopLossPoints)
  {
   double tickValue = 0.0;
   double tickSize = 0.0;
   double lotStep = 0.0;
   double minLot = 0.0;
   double maxLot = 0.0;

   if(!SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_VALUE, tickValue) ||
      !SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_SIZE, tickSize) ||
      !SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP, lotStep) ||
      !SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN, minLot) ||
      !SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MAX, maxLot))
     {
      Print("Failed to get symbol properties, using default lot size calculation");
      return(minLot); // Default to minimum to avoid errors
     }

   // Risk amount in account currency
   double riskAmount = AccountInfoDouble(ACCOUNT_BALANCE) * RiskPercent / 100.0;

   // Calculate stop loss in account currency per lot
   double stopLossPriceDistance = stopLossPoints * _Point;
   double stopLossMoney = (stopLossPriceDistance / tickSize) * tickValue;

   if(stopLossMoney <= 0)
     {
      Print("Invalid stop loss money calculation.");
      return(minLot);
     }

   double lotSize = riskAmount / stopLossMoney;

   // Round down to nearest lot step & keep within limits
   lotSize = MathFloor(lotSize / lotStep) * lotStep;

   if(lotSize < minLot)
      lotSize = minLot;

   if(lotSize > maxLot)
      lotSize = maxLot;

   return lotSize;
  }