🚀 AI One-Click Generation of mql4 Strategy Code

Mql4 Tutorial: Creating a Function to Partially Close Trades

mql4 | Published on: 2025/12/1
The following content is generated by EasyQuant .

Problem Description

How can I write a function to close half of my open trade?

Solution

To close half of an open trade in MQL4, you must perform a "Partial Close." This involves using the OrderClose() function but specifying a lot size smaller than the total volume of the order.

The most critical part of this logic is calculating the lot size correctly. You cannot simply divide by 2; you must normalize the result to match the broker's Lot Step (e.g., 0.01 or 0.1) and ensure it meets the Minimum Lot requirement.

MQL4 Code Implementation

Here is a robust function CloseHalfTrade that handles lot normalization and executes the partial close.

//+------------------------------------------------------------------+
//| Function to close half of a specific order                       |
//+------------------------------------------------------------------+
bool CloseHalfTrade(int ticket)
{
   // 1. Select the order
   if(!OrderSelect(ticket, SELECT_BY_TICKET))
   {
      Print("CloseHalfTrade: Failed to select ticket ", ticket, ". Error: ", GetLastError());
      return(false);
   }

   // 2. Check if it is a market order (OP_BUY or OP_SELL)
   int type = OrderType();
   if(type != OP_BUY && type != OP_SELL)
   {
      Print("CloseHalfTrade: Ticket ", ticket, " is not a market order.");
      return(false);
   }

   string symbol = OrderSymbol();
   
   // 3. Get Market Info for Lot calculations
   double lotStep = MarketInfo(symbol, MODE_LOTSTEP);
   double minLot  = MarketInfo(symbol, MODE_MINLOT);
   double currentLots = OrderLots();

   // 4. Calculate Half Lots
   double halfLots = currentLots / 2.0;

   // 5. Normalize Lots to the Broker's Step
   // We use MathFloor to ensure we don't round up to a value we don't possess if math is tight,
   // or MathRound for standard rounding. Here MathRound is generally safer for "half".
   halfLots = MathRound(halfLots / lotStep) * lotStep;

   // 6. Validation
   if(halfLots < minLot)
   {
      Print("CloseHalfTrade: Calculated half lots (", halfLots, ") is below minimum lot size.");
      return(false);
   }
   
   if(currentLots <= minLot)
   {
       Print("CloseHalfTrade: Order is already at minimum lot size, cannot split.");
       return(false);
   }

   // 7. Determine Closing Price
   // Note: We use MarketInfo to get the price of the Order's symbol, 
   // in case the EA is running on a different chart.
   double closePrice = 0.0;
   color arrowColor = clrNONE;

   RefreshRates(); // Ensure prices are current

   if(type == OP_BUY)
   {
      closePrice = MarketInfo(symbol, MODE_BID);
      arrowColor = clrRed;
   }
   else // OP_SELL
   {
      closePrice = MarketInfo(symbol, MODE_ASK);
      arrowColor = clrBlue;
   }

   // 8. Execute Partial Close
   // We pass 'halfLots' as the amount to close. The remaining volume stays open with the same ticket number.
   bool result = OrderClose(ticket, halfLots, closePrice, 3, arrowColor);

   if(!result)
   {
      Print("CloseHalfTrade: Failed to close half. Error: ", GetLastError());
      return(false);
   }

   Print("CloseHalfTrade: Successfully closed ", halfLots, " lots of ticket ", ticket);
   return(true);
}

Usage Example

You can call this function inside your OnTick() function or wherever your strategy logic dictates a partial exit.

void OnTick()
{
   // Example: Loop through open orders and close half if profit > 50 pips
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         // Check if the order belongs to this EA and Symbol
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == 12345)
         {
            // Calculate profit in pips
            double pips = 0;
            if(OrderType() == OP_BUY)
               pips = (Bid - OrderOpenPrice()) / Point;
            else if(OrderType() == OP_SELL)
               pips = (OrderOpenPrice() - Ask) / Point;

            // If profit is greater than 50 pips and lots > 0.01 (example check)
            if(pips > 50 && OrderLots() > 0.01)
            {
               // Call the function
               CloseHalfTrade(OrderTicket());
            }
         }
      }
   }
}

Key Considerations

  1. Lot Normalization: The line MathRound(halfLots / lotStep) * lotStep is crucial. If a broker has a lot step of 0.01 and you try to close 0.015 lots, the server will reject the order. This math forces the number to the nearest valid increment (e.g., 0.01 or 0.02).
  2. Minimum Lots: If you have a trade of 0.01 lots and the minimum allowed is 0.01, you cannot close half. The function includes a check for this (if(halfLots < minLot)).
  3. Ticket Numbers: In MT4, when you perform a partial close, the original ticket number is preserved for the remaining open portion of the trade. The closed portion is moved to history with a new ticket number, and the comment usually indicates "from #OriginalTicket".
  4. Symbol Context: The function uses MarketInfo(symbol, ...) instead of the predefined Bid or Ask variables. This ensures the code works correctly even if the EA is attached to a EURUSD chart but is managing a GBPUSD trade.

Q&A

Q: What happens to the Stop Loss and Take Profit after a partial close?
A: The Stop Loss and Take Profit levels remain attached to the remaining open portion of the trade. They are not removed or changed unless you explicitly modify the order using OrderModify().

Q: Can I close a specific percentage other than 50%?
A: Yes. Change the calculation line double halfLots = currentLots / 2.0; to double partLots = currentLots * 0.33; (to close 33%, for example). The normalization logic will handle the rest.

Q: Why do I need RefreshRates()?
A: MQL4 stores price data in a local cache. If your EA performs complex calculations that take time, the market price might change before OrderClose is called. RefreshRates() updates the local variables to the very latest server tick data to minimize slippage or "Invalid Price" errors.