Skip to content

MT5Service - Position & Orders Methods (Mid-Level API)ΒΆ

5 methods for getting information about open positions, pending orders, and history

🧩 API Layer: MID-LEVEL - wrappers over MT5Account with clean Go types

Implementation:

These methods are implemented in examples/mt5/MT5Service.go, which wraps package/Helpers/MT5Account.go low-level API with convenient helpers for ease of use.

Demo files:

  • examples/demos/service/04_service_demo.go - comprehensive examples of all service wrapper methods
  • examples/demos/service/05_service_streaming.go - streaming methods examples

🎯 Why These Methods Exist¢

Problem: In MT5Account, to get positions and orders you need to work with protobuf structures:

// Low-level (MT5Account)
data, err := account.PositionsTotal(ctx)
total := data.TotalPositions  // ← unpacking

ordersData, err := account.OpenedOrders(ctx, &pb.OpenedOrdersRequest{
    InputSortMode: pb.BMT5_ENUM_OPENED_ORDER_SORT_TYPE_BMT5_OPENED_ORDER_SORT_BY_OPEN_TIME_ASC,
})
// ordersData.PositionInfos / ordersData.OpenedOrders - protobuf structures

Solution: MT5Service simplifies access and returns clean types:

// Mid-level (MT5Service)
total, err := service.GetPositionsTotal(ctx)  // βœ… directly int32

orders, err := service.GetOpenedOrders(ctx, sortMode)  // βœ… protobuf data directly

Advantages:

  • βœ… Less code (1 line instead of 2-3)
  • βœ… Direct access to data without .TotalPositions
  • βœ… Simplified method signatures
  • βœ… Automatic request structure creation
  • βœ… Pagination support for history

πŸ“‹ All 5 MethodsΒΆ

Method Returns Low-Level Equivalent
GetPositionsTotal(ctx) int32 PositionsTotal(ctx) + .TotalPositions
GetOpenedOrders(ctx, sortMode) *OpenedOrdersData OpenedOrders(ctx, req)
GetOpenedTickets(ctx) ([]int64, []int64) OpenedOrdersTickets(ctx, req) + unpacking
GetOrderHistory(ctx, from, to, sortMode, page, perPage) *OrdersHistoryData OrderHistory(ctx, req), from/to sent as InputFromUtc/InputToUtc
GetPositionsHistory(ctx, sortType, from, to, page, perPage) *PositionsHistoryData PositionsHistory(ctx, req), from/to sent as PositionOpenTimeFromUtc/ToUtc

πŸ“– Method SignaturesΒΆ

1) GetPositionsTotalΒΆ

func (s *MT5Service) GetPositionsTotal(ctx context.Context) (int32, error)

Returns the number of open positions.

Low-level equivalent: PositionsTotal(ctx) with .TotalPositions extraction.

Example:

total, err := service.GetPositionsTotal(ctx)
fmt.Printf("Open positions: %d\n", total)


2) GetOpenedOrdersΒΆ

func (s *MT5Service) GetOpenedOrders(
    ctx context.Context,
    sortMode pb.BMT5_ENUM_OPENED_ORDER_SORT_TYPE,
) (*pb.OpenedOrdersData, error)

Gets all open positions and pending orders.

Parameters:

  • sortMode - sorting mode from BMT5_ENUM_OPENED_ORDER_SORT_TYPE:
  • BMT5_OPENED_ORDER_SORT_BY_OPEN_TIME_ASC (0) - by open time, oldest first
  • BMT5_OPENED_ORDER_SORT_BY_OPEN_TIME_DESC (1) - by open time, newest first
  • BMT5_OPENED_ORDER_SORT_BY_ORDER_TICKET_ID_ASC (2) - by ticket ascending
  • BMT5_OPENED_ORDER_SORT_BY_ORDER_TICKET_ID_DESC (3) - by ticket descending

Returns:

  • *pb.OpenedOrdersData with lists:
  • PositionInfos []*pb.PositionInfo - open positions
  • OpenedOrders []*pb.OpenedOrderInfo - pending orders

Example:

data, err := service.GetOpenedOrders(ctx,
    pb.BMT5_ENUM_OPENED_ORDER_SORT_TYPE_BMT5_OPENED_ORDER_SORT_BY_OPEN_TIME_DESC)
if err != nil {
    return err
}

fmt.Printf("Opened positions: %d\n", len(data.PositionInfos))
for _, pos := range data.PositionInfos {
    fmt.Printf("  Position #%d: %s, Profit: %.2f\n",
        pos.Ticket, pos.Symbol, pos.Profit)
}

fmt.Printf("Pending orders: %d\n", len(data.OpenedOrders))
for _, order := range data.OpenedOrders {
    fmt.Printf("  Order #%d: %s, Type: %v\n",
        order.Ticket, order.Symbol, order.Type)
}

3) GetOpenedTicketsΒΆ

func (s *MT5Service) GetOpenedTickets(ctx context.Context) ([]int64, []int64, error)

Gets only ticket numbers of open positions and pending orders.

Lightweight alternative to GetOpenedOrders - returns only IDs, not full information.

Returns:

  • positionTickets []int64 - ticket numbers of open positions
  • orderTickets []int64 - ticket numbers of pending orders
  • error

Example:

posTickets, orderTickets, err := service.GetOpenedTickets(ctx)
if err != nil {
    return err
}

fmt.Printf("Open positions: %d\n", len(posTickets))
fmt.Printf("Position tickets: %v\n", posTickets)

fmt.Printf("Pending orders: %d\n", len(orderTickets))
fmt.Printf("Order tickets: %v\n", orderTickets)

When to use:

  • Need only ticket numbers (not full information)
  • Checking for open positions
  • Fast position count monitoring

4) GetOrderHistoryΒΆ

func (s *MT5Service) GetOrderHistory(
    ctx context.Context,
    from time.Time,
    to time.Time,
    sortMode pb.BMT5_ENUM_ORDER_HISTORY_SORT_TYPE,
    pageNumber int32,
    itemsPerPage int32,
) (*pb.OrdersHistoryData, error)

Gets historical orders and deals for a period with pagination.

Parameters:

  • from - period start: a real moment (time.Now().AddDate(...), time.Date(..., time.Local), ...). It is sent as protobuf InputFromUtc, so the server converts it to broker time itself - the result does not depend on the broker's time zone
  • to - period end (a real moment, sent as InputToUtc)
  • sortMode - sorting mode from BMT5_ENUM_ORDER_HISTORY_SORT_TYPE (BMT5_SORT_BY_OPEN_TIME_ASC/DESC, BMT5_SORT_BY_CLOSE_TIME_ASC/DESC, BMT5_SORT_BY_ORDER_TICKET_ID_ASC/DESC)
  • pageNumber - page number (1-based; 0 together with itemsPerPage 0 = no paging)
  • itemsPerPage - items per page

To pass a broker wall-clock time instead, call account.OrderHistory with InputFrom/InputTo (send either the plain fields or the ...Utc fields, not both).

Returns:

  • *pb.OrdersHistoryData with:
  • HistoryData []*pb.HistoryData - history rows; each row has HistoryOrder *pb.OrderHistoryData and/or HistoryDeal *pb.DealHistoryData
  • ArrayTotal int32 - total number of rows
  • PageNumber int32, ItemsPerPage int32 - the page returned

Example:

// History for last 7 days
from := time.Now().AddDate(0, 0, -7)
to := time.Now()

data, err := service.GetOrderHistory(ctx, from, to,
    pb.BMT5_ENUM_ORDER_HISTORY_SORT_TYPE_BMT5_SORT_BY_CLOSE_TIME_DESC,
    1, 100)  // first page, 100 items
if err != nil {
    return err
}

fmt.Printf("Total rows: %d (showing %d)\n",
    data.ArrayTotal, len(data.HistoryData))

for _, item := range data.HistoryData {
    if deal := item.HistoryDeal; deal != nil {
        fmt.Printf("Deal #%d: %s, Profit: %.2f, Time: %s (broker) / %s UTC\n",
            deal.Ticket, deal.Symbol, deal.Profit,
            deal.Time.AsTime().Format("2006-01-02 15:04:05"),
            deal.TimeUtc.AsTime().Format("2006-01-02 15:04:05"))
    }
}


5) GetPositionsHistoryΒΆ

func (s *MT5Service) GetPositionsHistory(
    ctx context.Context,
    sortType pb.AH_ENUM_POSITIONS_HISTORY_SORT_TYPE,
    from *time.Time,
    to *time.Time,
    pageNumber *int32,
    itemsPerPage *int32,
) (*pb.PositionsHistoryData, error)

Gets closed positions with aggregated P&L data.

Parameters (all optional, use nil for defaults):

  • sortType - sort type from AH_ENUM_POSITIONS_HISTORY_SORT_TYPE (AH_POSITION_OPEN_TIME_ASC/DESC, AH_POSITION_TICKET_ASC/DESC) - required
  • from - position OPEN time period start (nil = no filter); a real moment, sent as PositionOpenTimeFromUtc
  • to - position OPEN time period end (nil = no filter); sent as PositionOpenTimeToUtc
  • pageNumber - page number (nil = no paging)
  • itemsPerPage - items per page (nil = no paging: staging answered all positions of the range; compare len(HistoryPositions) with TotalCount). For long histories prefer cursor paging (PageToken / PageSize of pb.PositionsHistoryRequest through account.PositionsHistory, repeat with NextPageToken until it is empty) - the MT5Sugar history methods do that.

Returns:

  • *pb.PositionsHistoryData with:
  • HistoryPositions []*pb.PositionHistoryInfo - closed positions
  • TotalCount int32 - number of positions in the selected range, over all pages

Example:

// Get all closed positions
data, err := service.GetPositionsHistory(ctx,
    pb.AH_ENUM_POSITIONS_HISTORY_SORT_TYPE_AH_POSITION_OPEN_TIME_DESC,
    nil, nil, nil, nil)  // all parameters default
if err != nil {
    return err
}

fmt.Printf("Total closed positions: %d\n", data.TotalCount)
for _, pos := range data.HistoryPositions {
    fmt.Printf("Position ID: %d, Symbol: %s\n", pos.PositionTicket, pos.Symbol)
    fmt.Printf("  Volume: %.2f, Profit: %.2f, Commission: %.2f\n",
        pos.Volume, pos.Profit, pos.Commission)
    fmt.Printf("  Opened: %s, Closed: %s (broker server time)\n",
        pos.OpenTime.AsTime().Format("2006-01-02 15:04:05"),
        pos.CloseTime.AsTime().Format("2006-01-02 15:04:05"))
}

With time filtering:

// Closed positions opened during the last month (the window is sent as UTC)
from := time.Now().AddDate(0, -1, 0)
to := time.Now()
page := int32(1)
perPage := int32(50)

data, err := service.GetPositionsHistory(ctx,
    pb.AH_ENUM_POSITIONS_HISTORY_SORT_TYPE_AH_POSITION_OPEN_TIME_DESC,
    &from, &to, &page, &perPage)


πŸ’‘ Usage ExamplesΒΆ

Example 1: Checking for Open PositionsΒΆ

// ❌ BEFORE (MT5Account) - 2 calls:
data, err := account.PositionsTotal(ctx)
if err != nil {
    return err
}
total := data.TotalPositions
if total > 0 {
    fmt.Printf("You have %d open positions\n", total)
}

// βœ… AFTER (MT5Service) - 1 call:
total, err := service.GetPositionsTotal(ctx)
if err != nil {
    return err
}
if total > 0 {
    fmt.Printf("You have %d open positions\n", total)
}

Code reduction: 20% (5 lines β†’ 4 lines)


Example 2: Getting List of Positions with Their ProfitΒΆ

// βœ… MT5Service - get positions (newest first) with their profit
data, err := service.GetOpenedOrders(ctx,
    pb.BMT5_ENUM_OPENED_ORDER_SORT_TYPE_BMT5_OPENED_ORDER_SORT_BY_OPEN_TIME_DESC)
if err != nil {
    return err
}

fmt.Println("Open positions (newest first):")
totalProfit := 0.0
for _, pos := range data.PositionInfos {
    fmt.Printf("  #%d: %s %.2f lots, Profit: %.2f\n",
        pos.Ticket, pos.Symbol, pos.Volume, pos.Profit)
    totalProfit += pos.Profit
}
fmt.Printf("Total P&L: %.2f\n", totalProfit)

Example 3: Lightweight Position MonitoringΒΆ

// βœ… MT5Service - use GetOpenedTickets for fast checking
func MonitorPositions(service *mt5.MT5Service, interval time.Duration) {
    ticker := time.NewTicker(interval)
    defer ticker.Stop()

    previousCount := 0

    for range ticker.C {
        ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)

        posTickets, orderTickets, err := service.GetOpenedTickets(ctx)
        cancel()

        if err != nil {
            fmt.Printf("❌ Error: %v\n", err)
            continue
        }

        currentCount := len(posTickets)
        if currentCount != previousCount {
            fmt.Printf("[%s] Positions changed: %d β†’ %d\n",
                time.Now().Format("15:04:05"), previousCount, currentCount)
            fmt.Printf("  Position tickets: %v\n", posTickets)
            fmt.Printf("  Pending orders: %v\n", orderTickets)
            previousCount = currentCount
        }
    }
}

Example 4: Getting Deal History for a PeriodΒΆ

// βœ… MT5Service - history for last month
func PrintMonthlyReport(service *mt5.MT5Service) error {
    ctx := context.Background()

    from := time.Now().AddDate(0, -1, 0)  // month ago
    to := time.Now()

    data, err := service.GetOrderHistory(ctx, from, to,
        pb.BMT5_ENUM_ORDER_HISTORY_SORT_TYPE_BMT5_SORT_BY_CLOSE_TIME_DESC,
        1, 1000)  // first page, up to 1000 rows
    if err != nil {
        return err
    }

    fmt.Printf("πŸ“Š Monthly Report (%s - %s)\n",
        from.Format("2006-01-02"), to.Format("2006-01-02"))
    fmt.Printf("Total rows: %d\n", data.ArrayTotal)

    totalProfit := 0.0
    winCount := 0
    lossCount := 0

    for _, item := range data.HistoryData {
        deal := item.HistoryDeal
        if deal == nil {
            continue
        }
        totalProfit += deal.Profit
        if deal.Profit > 0 {
            winCount++
        } else if deal.Profit < 0 {
            lossCount++
        }
    }

    fmt.Printf("Total P&L: %.2f\n", totalProfit)
    fmt.Printf("Win rate: %.1f%% (%d wins / %d losses)\n",
        float64(winCount)/(float64(winCount+lossCount))*100,
        winCount, lossCount)

    return nil
}

Example 5: Analyzing Closed Positions with PaginationΒΆ

// βœ… MT5Service - processing large history volumes with pagination
func AnalyzeClosedPositions(service *mt5.MT5Service) error {
    ctx := context.Background()

    page := int32(1)
    perPage := int32(100)

    for {
        data, err := service.GetPositionsHistory(ctx,
            pb.AH_ENUM_POSITIONS_HISTORY_SORT_TYPE_AH_POSITION_OPEN_TIME_DESC,
            nil, nil, &page, &perPage)
        if err != nil {
            return err
        }

        fmt.Printf("Page %d: %d positions (total: %d)\n",
            page, len(data.HistoryPositions), data.TotalCount)

        for _, pos := range data.HistoryPositions {
            duration := pos.CloseTime.AsTime().Sub(pos.OpenTime.AsTime())
            fmt.Printf("  Position %d: %s, Duration: %v, Profit: %.2f\n",
                pos.PositionTicket, pos.Symbol, duration, pos.Profit)
        }

        // Check if there are more pages
        if int32(len(data.HistoryPositions)) < perPage {
            break  // this was the last page
        }

        page++
    }

    return nil
}

πŸ”§ When to UseΒΆ

βœ… GetPositionsTotalΒΆ

Use when:

  • Need a quick check for positions
  • Checking number of positions before actions
  • Monitoring without details

Example:

total, _ := service.GetPositionsTotal(ctx)
if total == 0 {
    fmt.Println("No open positions")
}

βœ… GetOpenedOrdersΒΆ

Use when:

  • Need full information about positions/orders
  • Calculating total profit/loss
  • Building a dashboard with position details

Example:

data, _ := service.GetOpenedOrders(ctx, pb.BMT5_ENUM_OPENED_ORDER_SORT_TYPE_BMT5_OPENED_ORDER_SORT_BY_OPEN_TIME_ASC)
for _, pos := range data.PositionInfos {
    fmt.Printf("Position: %s, P&L: %.2f\n", pos.Symbol, pos.Profit)
}

βœ… GetOpenedTicketsΒΆ

Use when:

  • Need only ticket numbers (not details)
  • Lightweight monitoring of changes
  • Checking for a specific ticket

Example:

posTickets, _, _ := service.GetOpenedTickets(ctx)
hasPosition := slices.Contains(posTickets, int64(12345))

βœ… GetOrderHistoryΒΆ

Use when:

  • Need order and deal history
  • Building reports for a period
  • Analyzing trading activity

Example:

data, _ := service.GetOrderHistory(ctx, from, to,
    pb.BMT5_ENUM_ORDER_HISTORY_SORT_TYPE_BMT5_SORT_BY_CLOSE_TIME_DESC, 1, 100)
fmt.Printf("History rows: %d\n", len(data.HistoryData))


βœ… GetPositionsHistoryΒΆ

Use when:

  • Need aggregated information about positions
  • Analyzing closed positions with P&L
  • Building trading statistics

Example:

data, _ := service.GetPositionsHistory(ctx,
    pb.AH_ENUM_POSITIONS_HISTORY_SORT_TYPE_AH_POSITION_OPEN_TIME_DESC, nil, nil, nil, nil)
for _, pos := range data.HistoryPositions {
    fmt.Printf("Position: %s, Profit: %.2f\n", pos.Symbol, pos.Profit)
}

πŸ“Š Performance ComparisonΒΆ

Task Low-Level (lines of code) Mid-Level (lines of code) Reduction
Get position count 2-3 lines 1 line 33-50%
Get position list 3-4 lines 2-3 lines 25-33%
Get tickets 2-3 lines 1 line 33-50%
History for period 5-6 lines 3-4 lines 25-40%

πŸ’‘ RecommendationsΒΆ

  1. For quick checks β†’ use GetPositionsTotal or GetOpenedTickets
  2. For position details β†’ use GetOpenedOrders with sorting
  3. For history β†’ use GetOrderHistory (deals+orders) or GetPositionsHistory (aggregated positions)
  4. For large volumes β†’ use pagination (page, perPage parameters)
  5. For monitoring β†’ GetOpenedTickets is lighter than GetOpenedOrders

"Simple to Complex" Pattern:

// 1. Quick check for presence
total, _ := service.GetPositionsTotal(ctx)
if total == 0 {
    return nil
}

// 2. Get tickets (lightweight)
posTickets, _, _ := service.GetOpenedTickets(ctx)
fmt.Printf("Position tickets: %v\n", posTickets)

// 3. Full information (if needed)
data, _ := service.GetOpenedOrders(ctx, pb.BMT5_ENUM_OPENED_ORDER_SORT_TYPE_BMT5_OPENED_ORDER_SORT_BY_OPEN_TIME_ASC)
for _, pos := range data.PositionInfos {
    // Detailed processing
}


🎯 Summary¢

MT5Service Position & Orders methods solve the main task - simplify data access:

  • ❌ No need to extract .TotalPositions
  • ❌ No need to manually create complex request structures
  • ❌ No need to convert time.Time β†’ timestamppb (automatic)
  • βœ… Get data directly (int32, []int64, *protobuf.Data)
  • βœ… Pagination support for large volumes
  • βœ… Code reads like normal Go
  • βœ… Lightweight variants (GetOpenedTickets) for monitoring