Skip to content

✅ Close Positions and Delete Orders

Request: close an open position at current market price or delete a pending order. Supports partial position closing.

API Information:

  • Low-level API: MT5Account.OrderClose(...) (from Go package github.com/MetaRPC/GoMT5/package/Helpers)
  • 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): TradingHelperClient.OrderClose(ctx, request, opts...)

💬 Just the essentials

  • What it is. Closes market positions or cancels pending orders.
  • Why you need it. Exit trades, take profits, cut losses, cancel pending orders.
  • Partial closing. Supports closing part of a position via Volume parameter.

🎯 Purpose

Use it to:

  • Close market positions at current price
  • Cancel pending orders
  • Partially close positions
  • Exit trades programmatically
  • Implement exit strategies

📚 Tutorial

For a detailed line-by-line explanation with examples, see: OrderClose - How it works


package mt5

type MT5Account struct {
    // ...
}

// OrderClose closes an existing market position or deletes a pending order.
// Supports partial closing via Volume parameter.
func (a *MT5Account) OrderClose(
    ctx context.Context,
    req *pb.OrderCloseRequest,
) (*pb.OrderCloseData, error)

Request message:

OrderCloseRequest {
  uint64 ticket = 1;      // Order ticket to close (REQUIRED)
  double volume = 2;      // Volume to close (0 = close all)
  int32 slippage = 5;     // Maximum price deviation (slippage) in points
}

🔽 Input

Parameter Type Description
ctx context.Context Context for deadline/timeout and cancellation
req *pb.OrderCloseRequest Request with ticket and close parameters

Request fields:

Field Type Required Description
Ticket uint64 Order ticket number to close
Volume double Volume to close (0 = close all)
Slippage int32 Maximum price deviation (slippage) in points

⬆️ Output — OrderCloseData

Field Type Description
ReturnedCode uint32 Operation return code (10009 = success)
ReturnedStringCode string String representation of return code
ReturnedCodeDescription string Description of return code
CloseMode MRPC_ORDER_CLOSE_MODE Close mode (see enum below)

📘 Enum: MRPC_ORDER_CLOSE_MODE

Value Constant Description
0 MRPC_MARKET_ORDER_CLOSE Full market position close
1 MRPC_MARKET_ORDER_PARTIAL_CLOSE Partial market position close
2 MRPC_PENDING_ORDER_REMOVE Pending order removal

🧩 Notes & Tips

  • Automatic reconnection: All MT5Account methods have built-in protection against transient gRPC errors with automatic reconnection via ExecuteWithReconnect.
  • Default timeout: If context has no deadline, a default 30s timeout is applied automatically.
  • Nil context: If you pass nil context, context.Background() is used automatically.
  • Full close: Set Volume to 0 or full position volume to close entire position.
  • Pending orders: Deletes pending orders (no market execution).

🔗 Usage Examples

1) Close position completely

package main

import (
    "context"
    "fmt"
    "time"

    pb "github.com/MetaRPC/GoMT5/package"
    "github.com/MetaRPC/GoMT5/package/Helpers"
)

func main() {
    account, _ := mt5.NewMT5Account(12345, "password", "mt5.mrpc.pro:443", uuid.New())
    defer account.Close()

    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    ticket := uint64(12345678)

    data, err := account.OrderClose(ctx, &pb.OrderCloseRequest{
        Ticket:   ticket,
        Volume:   0,  // Close full position
        Slippage: 20,
    })
    if err != nil {
        panic(err)
    }

    // Check close result using helper from errors.go
    if mt5.IsRetCodeSuccess(data.ReturnedCode) {
        fmt.Printf("Position %d closed successfully\n", ticket)
    } else {
        fmt.Printf("Close failed: %s (code: %d)\n",
            mt5.GetRetCodeMessage(data.ReturnedCode),
            data.ReturnedCode)
    }
}

2) Partial position close

func PartialClose(account *mt5.MT5Account, ticket uint64, closeVolume float64) error {
    ctx := context.Background()

    data, err := account.OrderClose(ctx, &pb.OrderCloseRequest{
        Ticket:   ticket,
        Volume:   closeVolume,
        Slippage: 20,
    })
    if err != nil {
        return fmt.Errorf("failed to close: %w", err)
    }

    // Validate close result using helper from errors.go
    if !mt5.IsRetCodeSuccess(data.ReturnedCode) {
        return fmt.Errorf("close unsuccessful: %s (code: %d)",
            mt5.GetRetCodeMessage(data.ReturnedCode),
            data.ReturnedCode)
    }

    fmt.Printf("Partially closed position %d (%.2f lots)\n", ticket, closeVolume)
    return nil
}

// Usage:
// PartialClose(account, 12345678, 0.05) // Close 0.05 lots

3) Close all positions

func CloseAllPositions(account *mt5.MT5Account) error {
    ctx := context.Background()

    // Get all open positions
    positions, err := account.OpenedOrders(ctx, &pb.OpenedOrdersRequest{})
    if err != nil {
        return err
    }

    for _, order := range positions.Orders {
        data, err := account.OrderClose(ctx, &pb.OrderCloseRequest{
            Ticket:   order.Ticket,
            Volume:   0, // Full close
            Slippage: 50,
        })
        if err != nil {
            fmt.Printf("Failed to close %d: %v\n", order.Ticket, err)
            continue
        }
        // Check close result using helper from errors.go
        if mt5.IsRetCodeSuccess(data.ReturnedCode) {
            fmt.Printf("Closed position %d\n", order.Ticket)
        } else {
            fmt.Printf("Failed to close %d: %s (code: %d)\n",
                order.Ticket,
                mt5.GetRetCodeMessage(data.ReturnedCode),
                data.ReturnedCode)
        }
        time.Sleep(100 * time.Millisecond) // Small delay between closes
    }

    return nil
}

4) Close losing positions only

func CloseLosingPositions(account *mt5.MT5Account) error {
    ctx := context.Background()

    positions, err := account.OpenedOrders(ctx, &pb.OpenedOrdersRequest{})
    if err != nil {
        return err
    }

    for _, order := range positions.Orders {
        if order.Profit < 0 { // Only losing positions
            data, err := account.OrderClose(ctx, &pb.OrderCloseRequest{
                Ticket:   order.Ticket,
                Volume:   0,
                Slippage: 30,
            })
            if err != nil {
                fmt.Printf("Failed to close losing position %d: %v\n", order.Ticket, err)
                continue
            }
            if data.ReturnedCode == 10009 {
                fmt.Printf("Closed losing position %d (P&L: %.2f)\n", order.Ticket, order.Profit)
            }
        }
    }

    return nil
}

5) Close positions for specific symbol

func CloseSymbolPositions(account *mt5.MT5Account, symbol string) error {
    ctx := context.Background()

    positions, err := account.OpenedOrders(ctx, &pb.OpenedOrdersRequest{
        Symbol: symbol,
    })
    if err != nil {
        return err
    }

    closedCount := 0
    for _, order := range positions.Orders {
        data, err := account.OrderClose(ctx, &pb.OrderCloseRequest{
            Ticket:   order.Ticket,
            Volume:   0,
            Slippage: 30,
        })
        if err != nil {
            fmt.Printf("Failed to close %d: %v\n", order.Ticket, err)
            continue
        }
        if data.ReturnedCode == 10009 {
            closedCount++
        }
        time.Sleep(100 * time.Millisecond)
    }

    fmt.Printf("Closed %d %s positions\n", closedCount, symbol)
    return nil
}

6) Close with confirmation

func ClosePositionWithConfirmation(account *mt5.MT5Account, ticket uint64) error {
    ctx := context.Background()

    // Get position details first
    positions, err := account.OpenedOrders(ctx, &pb.OpenedOrdersRequest{})
    if err != nil {
        return err
    }

    var order *pb.Order
    for _, o := range positions.Orders {
        if o.Ticket == ticket {
            order = o
            break
        }
    }

    if order == nil {
        return fmt.Errorf("position %d not found", ticket)
    }

    fmt.Printf("Closing position %d: %s, Volume: %.2f, P&L: %.2f\n",
        order.Ticket, order.Symbol, order.Volume, order.Profit)

    // Close position
    data, err := account.OrderClose(ctx, &pb.OrderCloseRequest{
        Ticket:   ticket,
        Volume:   0,
        Slippage: 20,
    })
    if err != nil {
        return err
    }

    if data.ReturnedCode != 10009 {
        return fmt.Errorf("close unsuccessful: %s", data.ReturnedCodeDescription)
    }

    fmt.Printf("Position closed successfully\n")

    // Wait and verify it's closed
    time.Sleep(1 * time.Second)
    isOpen, _ := IsTicketOpen(account, ticket)
    if isOpen {
        return fmt.Errorf("position %d still open after close", ticket)
    }

    fmt.Println("Close confirmed")
    return nil
}

func IsTicketOpen(account *mt5.MT5Account, ticket uint64) (bool, error) {
    ctx := context.Background()
    data, err := account.OpenedOrdersTickets(ctx, &pb.OpenedOrdersTicketsRequest{})
    if err != nil {
        return false, err
    }

    for _, t := range data.Tickets {
        if t == ticket {
            return true, nil
        }
    }
    return false, nil
}

🔧 Common Patterns

Close with retry

func CloseWithRetry(account *mt5.MT5Account, ticket uint64, maxRetries int) error {
    ctx := context.Background()

    for i := 0; i < maxRetries; i++ {
        data, err := account.OrderClose(ctx, &pb.OrderCloseRequest{
            Ticket:   ticket,
            Volume:   0,
            Slippage: 50,
        })

        if err == nil && data.ReturnedCode == 10009 {
            fmt.Printf("Position %d closed successfully\n", ticket)
            return nil
        }

        if err != nil {
            fmt.Printf("Close attempt %d failed: %v\n", i+1, err)
        } else {
            fmt.Printf("Close attempt %d failed: %s\n", i+1, data.ReturnedCodeDescription)
        }
        time.Sleep(500 * time.Millisecond)
    }

    return fmt.Errorf("failed to close after %d retries", maxRetries)
}

📚 See Also