Skip to content

MT5Service - Market Depth Methods (Mid-Level API)ΒΆ

3 methods for working with Depth of Market (DOM) / "order book"

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


🎯 Why These Methods Exist¢

Problem: In MT5Account, working with DOM requires calls with protobuf structures:

// Low-level (MT5Account)
addData, err := account.MarketBookAdd(ctx, &pb.MarketBookAddRequest{Symbol: "EURUSD"})
success := addData.OpenedSuccessfully  // ← unpacking

bookData, err := account.MarketBookGet(ctx, &pb.MarketBookGetRequest{Symbol: "EURUSD"})
for _, b := range bookData.MqlBookInfos {
    // working with protobuf MqlBookInfo
}

releaseData, err := account.MarketBookRelease(ctx, &pb.MarketBookReleaseRequest{Symbol: "EURUSD"})
closed := releaseData.ClosedSuccessfully  // ← unpacking

Solution: MT5Service simplifies the API and returns clean Go types:

// Mid-level (MT5Service)
success, err := service.SubscribeMarketDepth(ctx, "EURUSD")  // βœ… bool directly

books, err := service.GetMarketDepth(ctx, "EURUSD")  // βœ… []BookInfo
for _, b := range books {
    fmt.Printf("Type: %v, Price: %.5f, Volume: %.2f\n", b.Type, b.Price, b.VolumeReal)
}

success, err = service.UnsubscribeMarketDepth(ctx, "EURUSD")  // βœ… bool directly

Advantages:

  • βœ… Direct bool return instead of .OpenedSuccessfully / .ClosedSuccessfully
  • βœ… Clean []BookInfo DTO instead of []*pb.MqlBookInfo
  • βœ… Clear method names (Subscribe/Unsubscribe)
  • βœ… Automatic request structure creation

πŸ“‹ All 3 MethodsΒΆ

Method Returns Low-Level Equivalent
SubscribeMarketDepth(ctx, symbol) bool MarketBookAdd(ctx, req) + .OpenedSuccessfully
GetMarketDepth(ctx, symbol) []BookInfo MarketBookGet(ctx, req) + conversion to DTO
UnsubscribeMarketDepth(ctx, symbol) bool MarketBookRelease(ctx, req) + .ClosedSuccessfully

πŸ“¦ DTO StructureΒΆ

BookInfoΒΆ

type BookInfo struct {
    Type       pb.BookType // SELL (ask) or BUY (bid)
    Price      float64     // Price level
    Volume     int64       // Volume in lots (integer)
    VolumeReal float64     // Volume with decimal precision
}

Advantage: Clean Go structure instead of protobuf MqlBookInfo.

Type Values:

  • pb.BookType_BOOK_TYPE_SELL - ask (sell offer)
  • pb.BookType_BOOK_TYPE_BUY - bid (buy demand)

πŸ“– Method SignaturesΒΆ

1) SubscribeMarketDepthΒΆ

func (s *MT5Service) SubscribeMarketDepth(
    ctx context.Context,
    symbol string,
) (bool, error)

Subscribes to Depth of Market (DOM) updates for a symbol.

IMPORTANT: Must be called before GetMarketDepth.

Parameters:

  • symbol - symbol name (e.g., "EURUSD")

Returns:

  • true - subscription successful
  • false - subscription failed (symbol doesn't support DOM)
  • error - execution error

Example:

success, err := service.SubscribeMarketDepth(ctx, "EURUSD")
if err != nil {
    return err
}
if !success {
    return fmt.Errorf("failed to subscribe to DOM for EURUSD")
}
fmt.Println("βœ… Subscribed to EURUSD market depth")

2) GetMarketDepthΒΆ

func (s *MT5Service) GetMarketDepth(
    ctx context.Context,
    symbol string,
) ([]BookInfo, error)

Retrieves current Depth of Market (DOM) snapshot for a symbol.

IMPORTANT: Requires prior SubscribeMarketDepth call.

Parameters:

  • symbol - symbol name (e.g., "EURUSD")

Returns:

  • []BookInfo - array of price levels (bid and ask)
  • error - execution error

Example:

books, err := service.GetMarketDepth(ctx, "EURUSD")
if err != nil {
    return err
}

fmt.Println("Market Depth for EURUSD:")
for i, b := range books {
    typeStr := "BID"
    if b.Type == pb.BookType_BOOK_TYPE_SELL {
        typeStr = "ASK"
    }
    fmt.Printf("  %d. %s: Price=%.5f, Volume=%.2f\n",
        i+1, typeStr, b.Price, b.VolumeReal)
}

3) UnsubscribeMarketDepthΒΆ

func (s *MT5Service) UnsubscribeMarketDepth(
    ctx context.Context,
    symbol string,
) (bool, error)

Unsubscribes from Depth of Market updates.

Call this to stop receiving DOM updates and free resources.

Parameters:

  • symbol - symbol name (e.g., "EURUSD")

Returns:

  • true - unsubscription successful
  • false - unsubscription failed
  • error - execution error

Example:

success, err := service.UnsubscribeMarketDepth(ctx, "EURUSD")
if err != nil {
    return err
}
if success {
    fmt.Println("βœ… Unsubscribed from EURUSD market depth")
}

πŸ’‘ Usage ExamplesΒΆ

Example 1: Basic DOM OperationsΒΆ

// βœ… MT5Service - clean and concise
ctx := context.Background()

// 1. Subscribe to market depth
success, err := service.SubscribeMarketDepth(ctx, "EURUSD")
if err != nil || !success {
    return fmt.Errorf("subscription failed")
}
defer service.UnsubscribeMarketDepth(ctx, "EURUSD")

// 2. Get current DOM snapshot
books, err := service.GetMarketDepth(ctx, "EURUSD")
if err != nil {
    return err
}

// 3. Process DOM data
for _, b := range books {
    fmt.Printf("Price: %.5f, Volume: %.2f\n", b.Price, b.VolumeReal)
}

Advantage: Returns clean []BookInfo DTO instead of protobuf []*pb.MqlBookInfo, with direct bool returns instead of unpacking .OpenedSuccessfully/.ClosedSuccessfully fields.


Example 2: Liquidity Analysis (Bid/Ask Volumes)ΒΆ

// βœ… MT5Service - analyze bid and ask liquidity
func AnalyzeLiquidity(service *mt5.MT5Service, symbol string) error {
    ctx := context.Background()

    // 1. Subscribe to DOM
    success, err := service.SubscribeMarketDepth(ctx, symbol)
    if err != nil || !success {
        return fmt.Errorf("failed to subscribe to DOM: %w", err)
    }
    defer service.UnsubscribeMarketDepth(ctx, symbol)

    // 2. Get DOM snapshot
    books, err := service.GetMarketDepth(ctx, symbol)
    if err != nil {
        return err
    }

    // 3. Separate bid and ask levels
    var bidVolume, askVolume float64
    var bidLevels, askLevels []BookInfo

    for _, b := range books {
        if b.Type == pb.BookType_BOOK_TYPE_BUY {
            bidLevels = append(bidLevels, b)
            bidVolume += b.VolumeReal
        } else {
            askLevels = append(askLevels, b)
            askVolume += b.VolumeReal
        }
    }

    // 4. Display liquidity analysis
    fmt.Printf("πŸ“Š Liquidity Analysis for %s\n", symbol)
    fmt.Printf("Bid levels: %d, Total volume: %.2f\n", len(bidLevels), bidVolume)
    fmt.Printf("Ask levels: %d, Total volume: %.2f\n", len(askLevels), askVolume)

    // 5. Determine market pressure
    if bidVolume > askVolume {
        fmt.Println("⬆️ More buying pressure (bid volume > ask volume)")
    } else {
        fmt.Println("⬇️ More selling pressure (ask volume > bid volume)")
    }

    return nil
}

Example 3: Monitor Spread from DOMΒΆ

// βœ… MT5Service - calculate spread from DOM
func MonitorDOMSpread(service *mt5.MT5Service, symbol string, interval time.Duration) {
    ctx := context.Background()

    // 1. Subscribe to market depth
    success, _ := service.SubscribeMarketDepth(ctx, symbol)
    if !success {
        fmt.Println("❌ Cannot subscribe to DOM")
        return
    }
    defer service.UnsubscribeMarketDepth(ctx, symbol)

    // 2. Setup periodic ticker
    ticker := time.NewTicker(interval)
    defer ticker.Stop()

    for range ticker.C {
        // 3. Get current DOM snapshot
        books, err := service.GetMarketDepth(ctx, symbol)
        if err != nil {
            fmt.Printf("❌ Error: %v\n", err)
            continue
        }

        // 4. Find best bid and ask prices
        var bestBid, bestAsk float64
        for _, b := range books {
            if b.Type == pb.BookType_BOOK_TYPE_BUY && (bestBid == 0 || b.Price > bestBid) {
                bestBid = b.Price
            }
            if b.Type == pb.BookType_BOOK_TYPE_SELL && (bestAsk == 0 || b.Price < bestAsk) {
                bestAsk = b.Price
            }
        }

        // 5. Calculate and display spread
        if bestBid > 0 && bestAsk > 0 {
            spread := bestAsk - bestBid
            fmt.Printf("[%s] %s: Bid=%.5f, Ask=%.5f, Spread=%.5f\n",
                time.Now().Format("15:04:05"), symbol, bestBid, bestAsk, spread)
        }
    }
}

Example 4: Visualize DOM (Order Book)ΒΆ

// βœ… MT5Service - beautiful DOM visualization
func PrintDOMBook(service *mt5.MT5Service, symbol string) error {
    ctx := context.Background()

    // 1. Subscribe to market depth
    success, err := service.SubscribeMarketDepth(ctx, symbol)
    if err != nil || !success {
        return fmt.Errorf("failed to subscribe: %w", err)
    }
    defer service.UnsubscribeMarketDepth(ctx, symbol)

    // 2. Get DOM snapshot
    books, err := service.GetMarketDepth(ctx, symbol)
    if err != nil {
        return err
    }

    // 3. Separate bids and asks
    var bids, asks []BookInfo
    for _, b := range books {
        if b.Type == pb.BookType_BOOK_TYPE_BUY {
            bids = append(bids, b)
        } else {
            asks = append(asks, b)
        }
    }

    // 4. Sort (asks ascending, bids descending)
    sort.Slice(asks, func(i, j int) bool {
        return asks[i].Price < asks[j].Price
    })
    sort.Slice(bids, func(i, j int) bool {
        return bids[i].Price > bids[j].Price
    })

    // 5. Display formatted order book
    fmt.Printf("\nπŸ“– Market Depth for %s\n", symbol)
    fmt.Println("=" + strings.Repeat("=", 50))

    fmt.Println("ASK (Sell Orders):")
    for i := len(asks) - 1; i >= 0; i-- {
        a := asks[i]
        fmt.Printf("  %.5f | %8.2f\n", a.Price, a.VolumeReal)
    }

    fmt.Println(strings.Repeat("-", 50))

    fmt.Println("BID (Buy Orders):")
    for _, b := range bids {
        fmt.Printf("  %.5f | %8.2f\n", b.Price, b.VolumeReal)
    }

    fmt.Println("=" + strings.Repeat("=", 50))

    return nil
}

Output:

πŸ“– Market Depth for EURUSD
==================================================
ASK (Sell Orders):
  1.08905 |   150.00
  1.08904 |   200.00
  1.08903 |   300.00
--------------------------------------------------
BID (Buy Orders):
  1.08902 |   250.00
  1.08901 |   180.00
  1.08900 |   120.00
==================================================


Example 5: Find Large Orders in DOM (Walls)ΒΆ

// βœ… MT5Service - find large orders in the book
func FindLargeOrders(service *mt5.MT5Service, symbol string, minVolume float64) error {
    ctx := context.Background()

    // 1. Subscribe to market depth
    success, err := service.SubscribeMarketDepth(ctx, symbol)
    if err != nil || !success {
        return fmt.Errorf("failed to subscribe: %w", err)
    }
    defer service.UnsubscribeMarketDepth(ctx, symbol)

    // 2. Get DOM snapshot
    books, err := service.GetMarketDepth(ctx, symbol)
    if err != nil {
        return err
    }

    // 3. Find and display large orders
    fmt.Printf("πŸ” Large orders (volume >= %.2f) in %s:\n", minVolume, symbol)

    for _, b := range books {
        if b.VolumeReal >= minVolume {
            typeStr := "BID 🟒"
            if b.Type == pb.BookType_BOOK_TYPE_SELL {
                typeStr = "ASK πŸ”΄"
            }
            fmt.Printf("  %s: Price=%.5f, Volume=%.2f\n",
                typeStr, b.Price, b.VolumeReal)
        }
    }

    return nil
}

πŸ”§ When to UseΒΆ

βœ… SubscribeMarketDepthΒΆ

Use when:

  • Starting to work with DOM for a symbol
  • Need real-time order book updates
  • Analyzing market liquidity

IMPORTANT: Always call before GetMarketDepth.


βœ… GetMarketDepthΒΆ

Use when:

  • Getting current DOM snapshot
  • Analyzing bid/ask levels
  • Finding large orders (walls)
  • Calculating average price by levels

IMPORTANT: Requires active subscription via SubscribeMarketDepth.


βœ… UnsubscribeMarketDepthΒΆ

Use when:

  • Finishing work with DOM
  • Freeing resources
  • Use defer for automatic unsubscription

πŸ“Š Performance ComparisonΒΆ

Task Low-Level (lines of code) Mid-Level (lines of code) Reduction
Subscribe to DOM 3-4 lines 1-2 lines 40-50%
Get DOM 2-3 lines 1 line 33-50%
Unsubscribe from DOM 2-3 lines 1 line 33-50%
Full cycle (sub+get+unsub) 8-10 lines 4-5 lines 40-50%

πŸ’‘ RecommendationsΒΆ

  1. Always use defer for UnsubscribeMarketDepth:

    success, _ := service.SubscribeMarketDepth(ctx, symbol)
    if success {
        defer service.UnsubscribeMarketDepth(ctx, symbol)
    }
    

  2. Check success when subscribing:

    if !success {
        return fmt.Errorf("DOM not available for %s", symbol)
    }
    

  3. Not all symbols support DOM - verify subscription result

  4. GetMarketDepth returns a snapshot - for real-time use polling or streaming

  5. Handle errors - symbol may become unavailable during operation

"Subscribe-Defer-Get" Pattern:

func WorkWithDOM(service *mt5.MT5Service, symbol string) error {
    ctx := context.Background()

    // 1. Subscribe
    success, err := service.SubscribeMarketDepth(ctx, symbol)
    if err != nil || !success {
        return fmt.Errorf("subscription failed")
    }

    // 2. Defer unsubscribe (automatic cleanup)
    defer service.UnsubscribeMarketDepth(ctx, symbol)

    // 3. Get data
    books, err := service.GetMarketDepth(ctx, symbol)
    if err != nil {
        return err
    }

    // 4. Process
    for _, b := range books {
        // ...
    }

    return nil
}


MT5Service (Mid-Level):

MT5Account (Low-Level):


🎯 Summary¢

MT5Service Market Depth methods solve the main task - simplifying DOM operations:

  • ❌ No need to create requests manually (MarketBookAddRequest, etc.)
  • ❌ No need to extract .OpenedSuccessfully / .ClosedSuccessfully
  • ❌ No need to work with []*pb.MqlBookInfo
  • βœ… Get bool directly (success/fail)
  • βœ… Clean []BookInfo DTO instead of protobuf
  • βœ… Clear names (Subscribe/Get/Unsubscribe)
  • βœ… Code reads like standard Go

Typical workflow:

// Subscribe β†’ Get β†’ Process β†’ Unsubscribe
service.SubscribeMarketDepth(ctx, "EURUSD")
defer service.UnsubscribeMarketDepth(ctx, "EURUSD")

books, _ := service.GetMarketDepth(ctx, "EURUSD")
// Analyze bid/ask levels...