Skip to content

❌ Close Position or Delete Pending Order

Request: close an open position (full or partial) or delete a pending order. The primary method for exiting trades.

API Information:

  • SDK wrapper: MT5Account.orderClose(...) (from package io.metarpc.mt5)
  • gRPC service: mt5_term_api.TradingHelper
  • Proto definition: OrderClose (defined in mt5-term-api-trading-helper.proto)

RPC

  • Service: mt5_term_api.TradingHelper
  • Method: OrderClose(OrderCloseRequest) → OrderCloseReply
  • Low‑level client (generated): TradingHelperGrpc.TradingHelperBlockingStub.orderClose(request)
  • SDK wrapper (high-level):
package io.metarpc.mt5;

public class MT5Account {
    /**
     * Closes an open position or deletes a pending order.
     * For positions, you can specify partial closure by providing a volume less than the total position size.
     *
     * @param ticket The ticket number of the order or position to close
     * @param volume Volume to close in lots (use position's full volume to close completely)
     * @param slippage Maximum acceptable price slippage in points
     * @return Response indicating success or failure of the closure
     * @throws ApiExceptionMT5 if the closure fails or connection is lost
     */
    public Mt5TermApiTradingHelper.OrderCloseReply orderClose(
        long ticket,
        double volume,
        int slippage) throws ApiExceptionMT5;
}

Request message: OrderCloseRequest { ticket, volume, slippage }

Reply message: OrderCloseReply { data: OrderCloseData } or { error: Error }


🔽 Input

Parameter Type Required Description
ticket long Order or position ticket number
volume double Volume to close in lots
slippage int Maximum price slippage in points

Volume Notes: - For full closure: use position's total volume - For partial closure: use volume less than total - For pending orders: volume is ignored (always deleted fully)


⬆️ Output - OrderCloseData

Field Type Description
returned_code int Operation return code (10009 = success)
returned_string_code String String representation of return code
returned_code_description String Human-readable description
close_mode MRPC_ORDER_CLOSE_MODE Type of closure performed

Enum: MRPC_ORDER_CLOSE_MODE

Value Number Description
MRPC_MARKET_ORDER_CLOSE 0 Full position closed
MRPC_MARKET_ORDER_PARTIAL_CLOSE 1 Partial position closed
MRPC_PENDING_ORDER_REMOVE 2 Pending order deleted

💬 Just the essentials

  • What it is. RPC to close positions or delete pending orders.
  • Full closure. Use position's full volume to close completely.
  • Partial closure. Use volume less than total for partial close.
  • Pending orders. Volume parameter ignored - always deleted fully.
  • Slippage. In points (not pips) - maximum acceptable price deviation.
  • Return code. 10009 = success.

🎯 Purpose

Use this method when you need to:

  • Close open positions (full or partial).
  • Take profits or cut losses manually.
  • Delete pending orders that are no longer needed.
  • Implement exit strategies.
  • Close positions based on signals or conditions.
  • Scale out of positions gradually.

🔗 Usage Examples

1) Close position completely

import io.metarpc.mt5.MT5Account;
import io.metarpc.mt5.exceptions.ApiExceptionMT5;
import mt5_term_api.Mt5TermApiTradingHelper;

public class Example {
    public static void main(String[] args) {
        MT5Account account = new MT5Account(12345678, "password");

        try {
            account.connect("demo.mt5server.com", 443, "EURUSD");

            long ticket = 123456789;
            double volume = 0.1;  // Full position volume
            int slippage = 10;    // 10 points

            Mt5TermApiTradingHelper.OrderCloseReply reply =
                account.orderClose(ticket, volume, slippage);

            var data = reply.getData();

            if (data.getReturnedCode() == 10009) {
                System.out.printf("✅ Position #%d closed%n", ticket);
                System.out.printf("   Mode: %s%n", data.getCloseMode());
            } else {
                System.out.printf("❌ Failed: %s (code: %d)%n",
                    data.getReturnedCodeDescription(),
                    data.getReturnedCode());
            }

        } catch (ApiExceptionMT5 e) {
            System.err.println("Error: " + e.getMessage());
        } finally {
            account.close();
        }
    }
}

2) Close with position lookup

public class PositionCloser {
    /**
     * Close position by ticket with automatic volume lookup
     */
    public static boolean closePosition(
            MT5Account account,
            long ticket) throws ApiExceptionMT5 {

        // Get position volume
        var positions = account.openedOrders(
            Mt5TermApiAccountHelper.BMT5_ENUM_OPENED_ORDER_SORT_TYPE.BMT5_SORT_BY_TICKET_ASC
        ).getData().getPositionInfosList();

        var position = positions.stream()
            .filter(p -> p.getTicket() == ticket)
            .findFirst()
            .orElseThrow(() -> new ApiExceptionMT5("Position not found: " + ticket));

        double volume = position.getVolume();
        System.out.printf("Closing position #%d (%s %.2f lots)%n",
            ticket, position.getSymbol(), volume);

        var reply = account.orderClose(ticket, volume, 10);
        var data = reply.getData();

        if (data.getReturnedCode() == 10009) {
            System.out.println("✅ Position closed successfully");
            return true;
        } else {
            System.out.printf("❌ Failed: %s%n",
                data.getReturnedCodeDescription());
            return false;
        }
    }
}

// Usage
boolean success = PositionCloser.closePosition(account, 123456789);

3) Partial position close

public class PartialCloser {
    /**
     * Close part of position
     */
    public static boolean closePartial(
            MT5Account account,
            long ticket,
            double volumeToClose) throws ApiExceptionMT5 {

        // Get current position
        var positions = account.openedOrders(
            Mt5TermApiAccountHelper.BMT5_ENUM_OPENED_ORDER_SORT_TYPE.BMT5_SORT_BY_TICKET_ASC
        ).getData().getPositionInfosList();

        var position = positions.stream()
            .filter(p -> p.getTicket() == ticket)
            .findFirst()
            .orElseThrow(() -> new ApiExceptionMT5("Position not found"));

        double totalVolume = position.getVolume();

        if (volumeToClose >= totalVolume) {
            System.out.println("⚠️ Volume >= total, closing fully");
            volumeToClose = totalVolume;
        }

        System.out.printf("Closing %.2f of %.2f lots (%.1f%%)%n",
            volumeToClose,
            totalVolume,
            (volumeToClose / totalVolume) * 100);

        var reply = account.orderClose(ticket, volumeToClose, 10);
        var data = reply.getData();

        if (data.getReturnedCode() == 10009) {
            if (data.getCloseMode() == Mt5TermApiTradingHelper.MRPC_ORDER_CLOSE_MODE.MRPC_MARKET_ORDER_PARTIAL_CLOSE) {
                System.out.printf("✅ Partial close: %.2f lots closed, %.2f remains%n",
                    volumeToClose,
                    totalVolume - volumeToClose);
            } else {
                System.out.println("✅ Full position closed");
            }
            return true;
        } else {
            System.out.printf("❌ Failed: %s%n", data.getReturnedCodeDescription());
            return false;
        }
    }
}

// Usage - close half the position
PartialCloser.closePartial(account, 123456789, 0.05);

4) Delete pending order

public class PendingOrderDeleter {
    /**
     * Delete pending order
     */
    public static boolean deletePendingOrder(
            MT5Account account,
            long orderTicket) throws ApiExceptionMT5 {

        System.out.printf("Deleting pending order #%d...%n", orderTicket);

        // For pending orders, volume doesn't matter
        var reply = account.orderClose(orderTicket, 0.0, 0);
        var data = reply.getData();

        if (data.getReturnedCode() == 10009) {
            System.out.println("✅ Pending order deleted");
            return true;
        } else {
            System.out.printf("❌ Failed: %s (code: %d)%n",
                data.getReturnedCodeDescription(),
                data.getReturnedCode());
            return false;
        }
    }
}

// Usage
PendingOrderDeleter.deletePendingOrder(account, 123456);

5) Close all positions for symbol

public class SymbolCloser {
    /**
     * Close all positions for a specific symbol
     */
    public static int closeAllForSymbol(
            MT5Account account,
            String symbol) throws ApiExceptionMT5 {

        var positions = account.openedOrders(
            Mt5TermApiAccountHelper.BMT5_ENUM_OPENED_ORDER_SORT_TYPE.BMT5_SORT_BY_TICKET_ASC
        ).getData().getPositionInfosList();

        var symbolPositions = positions.stream()
            .filter(p -> p.getSymbol().equals(symbol))
            .toList();

        if (symbolPositions.isEmpty()) {
            System.out.printf("No positions found for %s%n", symbol);
            return 0;
        }

        System.out.printf("Closing %d positions for %s...%n",
            symbolPositions.size(), symbol);

        int closedCount = 0;

        for (var position : symbolPositions) {
            try {
                var reply = account.orderClose(
                    position.getTicket(),
                    position.getVolume(),
                    10
                );

                if (reply.getData().getReturnedCode() == 10009) {
                    System.out.printf("✅ Closed #%d (%.2f lots)%n",
                        position.getTicket(),
                        position.getVolume());
                    closedCount++;
                } else {
                    System.out.printf("❌ Failed #%d: %s%n",
                        position.getTicket(),
                        reply.getData().getReturnedCodeDescription());
                }

            } catch (ApiExceptionMT5 e) {
                System.err.printf("Error closing #%d: %s%n",
                    position.getTicket(), e.getMessage());
            }
        }

        System.out.printf("Closed: %d/%d positions%n",
            closedCount, symbolPositions.size());

        return closedCount;
    }
}

// Usage
int closed = SymbolCloser.closeAllForSymbol(account, "EURUSD");

6) Close all losing positions

public class LossCloser {
    /**
     * Close all positions with losses
     */
    public static int closeAllLosing(MT5Account account) throws ApiExceptionMT5 {
        var positions = account.openedOrders(
            Mt5TermApiAccountHelper.BMT5_ENUM_OPENED_ORDER_SORT_TYPE.BMT5_SORT_BY_TICKET_ASC
        ).getData().getPositionInfosList();

        var losingPositions = positions.stream()
            .filter(p -> p.getProfit() < 0)
            .toList();

        if (losingPositions.isEmpty()) {
            System.out.println("No losing positions");
            return 0;
        }

        System.out.printf("Closing %d losing positions...%n", losingPositions.size());

        int closedCount = 0;
        double totalLoss = 0;

        for (var position : losingPositions) {
            try {
                var reply = account.orderClose(
                    position.getTicket(),
                    position.getVolume(),
                    10
                );

                if (reply.getData().getReturnedCode() == 10009) {
                    System.out.printf("✅ Closed #%d: %.2f loss%n",
                        position.getTicket(),
                        position.getProfit());
                    closedCount++;
                    totalLoss += position.getProfit();
                }

            } catch (ApiExceptionMT5 e) {
                System.err.printf("Error: %s%n", e.getMessage());
            }
        }

        System.out.printf("Closed: %d positions, Total loss: %.2f%n",
            closedCount, totalLoss);

        return closedCount;
    }
}

// Usage
LossCloser.closeAllLosing(account);

7) Scale out (close in steps)

public class ScaleOutCloser {
    /**
     * Scale out of position in multiple steps
     */
    public static void scaleOut(
            MT5Account account,
            long ticket,
            int steps) throws ApiExceptionMT5, InterruptedException {

        // Get position
        var positions = account.openedOrders(
            Mt5TermApiAccountHelper.BMT5_ENUM_OPENED_ORDER_SORT_TYPE.BMT5_SORT_BY_TICKET_ASC
        ).getData().getPositionInfosList();

        var position = positions.stream()
            .filter(p -> p.getTicket() == ticket)
            .findFirst()
            .orElseThrow(() -> new ApiExceptionMT5("Position not found"));

        double totalVolume = position.getVolume();
        double volumePerStep = totalVolume / steps;

        // Round to 0.01
        volumePerStep = Math.round(volumePerStep * 100.0) / 100.0;

        System.out.printf("Scaling out of position #%d in %d steps%n",
            ticket, steps);
        System.out.printf("Total: %.2f lots, Per step: %.2f lots%n",
            totalVolume, volumePerStep);

        double remainingVolume = totalVolume;

        for (int i = 1; i <= steps; i++) {
            double volumeToClose = Math.min(volumePerStep, remainingVolume);

            System.out.printf("\nStep %d/%d: Closing %.2f lots...%n",
                i, steps, volumeToClose);

            var reply = account.orderClose(ticket, volumeToClose, 10);

            if (reply.getData().getReturnedCode() == 10009) {
                remainingVolume -= volumeToClose;
                System.out.printf("✅ Closed %.2f lots, %.2f remains%n",
                    volumeToClose, remainingVolume);

                if (remainingVolume <= 0.01) {
                    System.out.println("Position fully closed");
                    break;
                }
            } else {
                System.out.printf("❌ Failed: %s%n",
                    reply.getData().getReturnedCodeDescription());
                break;
            }

            // Wait before next step
            if (i < steps) {
                Thread.sleep(1000);
            }
        }
    }
}

// Usage - close position in 3 steps
ScaleOutCloser.scaleOut(account, 123456789, 3);

8) Emergency close all

public class EmergencyCloser {
    /**
     * Emergency close all positions and delete all pending orders
     */
    public static void closeAll(MT5Account account) {
        System.out.println("🚨 EMERGENCY CLOSE ALL 🚨");
        System.out.println("═".repeat(50));

        try {
            var data = account.openedOrders(
                Mt5TermApiAccountHelper.BMT5_ENUM_OPENED_ORDER_SORT_TYPE.BMT5_SORT_BY_TICKET_ASC
            ).getData();

            int positionsCount = data.getPositionInfosCount();
            int ordersCount = data.getOpenedOrdersCount();

            System.out.printf("Found: %d positions, %d pending orders%n",
                positionsCount, ordersCount);

            // Close all positions
            int positionsClosed = 0;
            for (var position : data.getPositionInfosList()) {
                try {
                    var reply = account.orderClose(
                        position.getTicket(),
                        position.getVolume(),
                        50  // Allow high slippage
                    );

                    if (reply.getData().getReturnedCode() == 10009) {
                        System.out.printf("✅ Closed position #%d%n",
                            position.getTicket());
                        positionsClosed++;
                    }
                } catch (Exception e) {
                    System.err.printf("❌ Failed to close #%d: %s%n",
                        position.getTicket(), e.getMessage());
                }
            }

            // Delete all pending orders
            int ordersDeleted = 0;
            for (var order : data.getOpenedOrdersList()) {
                try {
                    var reply = account.orderClose(order.getTicket(), 0.0, 0);

                    if (reply.getData().getReturnedCode() == 10009) {
                        System.out.printf("✅ Deleted order #%d%n",
                            order.getTicket());
                        ordersDeleted++;
                    }
                } catch (Exception e) {
                    System.err.printf("❌ Failed to delete #%d: %s%n",
                        order.getTicket(), e.getMessage());
                }
            }

            System.out.println("═".repeat(50));
            System.out.printf("Results: %d/%d positions closed, %d/%d orders deleted%n",
                positionsClosed, positionsCount,
                ordersDeleted, ordersCount);

        } catch (ApiExceptionMT5 e) {
            System.err.println("Fatal error: " + e.getMessage());
        }
    }
}

// Usage - EMERGENCY ONLY!
EmergencyCloser.closeAll(account);

📌 Important Notes

Slippage: - In points, not pips (10 points = 1 pip for EURUSD) - 0 = no slippage allowed (may cause rejections) - 10-50 typical for normal conditions - Higher slippage for emergency closes

Partial Closure: - Creates new ticket for remaining position - Original ticket is closed - Useful for scaling out of positions

Pending Orders: - Always deleted fully (volume ignored) - No slippage applies to pending orders

Return Codes: - 10009 = SUCCESS - Other codes indicate failure

Best Practices: - Always check return code - Use reasonable slippage values - Handle errors gracefully - Consider partial closes for large positions