π Get Market Depth Order Book DataΒΆ
Request: retrieve current Market Depth (order book) data for a subscribed symbol. Returns pending buy/sell orders at different price levels with volumes.
API Information:
- SDK wrapper:
MT5Account.marketBookGet(...)(from packageio.metarpc.mt5) - gRPC service:
mt5_term_api.MarketInfo - Proto definition:
MarketBookGet(defined inmt5-term-api-market-info.proto)
RPCΒΆ
- Service:
mt5_term_api.MarketInfo - Method:
MarketBookGet(MarketBookGetRequest) β MarketBookGetReply - Lowβlevel client (generated):
MarketInfoGrpc.MarketInfoBlockingStub.marketBookGet(request) - SDK wrapper (high-level):
package io.metarpc.mt5;
public class MT5Account {
/**
* Gets the current Market Depth (order book) data for a subscribed symbol.
* Returns pending buy and sell orders with prices and volumes from the order book.
* Use this to analyze liquidity, identify support/resistance levels, or optimize order placement.
*
* @param symbol Symbol name (must be subscribed via marketBookAdd first)
* @return Market book data containing arrays of buy and sell orders with prices and volumes
* @throws ApiExceptionMT5 if the call fails or connection is lost
*/
public Mt5TermApiMarketInfo.MarketBookGetReply marketBookGet(String symbol) throws ApiExceptionMT5;
}
Request message: MarketBookGetRequest { symbol: string }
Reply message: MarketBookGetReply { data: MarketBookGetData } or { error: Error }
π½ InputΒΆ
| Parameter | Type | Required | Description |
|---|---|---|---|
symbol |
String |
β | Symbol name (must be subscribed first) |
Important: Symbol must be subscribed via marketBookAdd() before calling this method.
β¬οΈ Output - MarketBookGetDataΒΆ
| Field | Type | Description |
|---|---|---|
mql_book_infos |
List<MrpcMqlBookInfo> |
List of order book entries (buy/sell orders) |
Access using reply.getData().getMqlBookInfosList().
Structure: MrpcMqlBookInfo (Order Book Entry)ΒΆ
Each order book entry contains:
| Field | Type | Description |
|---|---|---|
type |
BookType |
Order type (BUY, SELL, BUY_MARKET, SELL_MARKET) |
price |
double |
Price level |
volume |
long |
Volume in integer units |
volume_real |
double |
Volume in real units (lots) |
Access using:
- entry.getType() - Order type (enum)
- entry.getPrice() - Price level
- entry.getVolume() - Volume (integer)
- entry.getVolumeReal() - Volume in lots (double)
Enum: BookTypeΒΆ
| Value | Number | Description |
|---|---|---|
BOOK_TYPE_SELL |
0 | Sell limit order (Ask/Offer side) |
BOOK_TYPE_BUY |
1 | Buy limit order (Bid side) |
BOOK_TYPE_SELL_MARKET |
2 | Market sell order |
BOOK_TYPE_BUY_MARKET |
3 | Market buy order |
π¬ Just the essentialsΒΆ
- What it is. RPC to retrieve current order book (Level II) data for a symbol.
- Why you need it. See pending orders, analyze liquidity, identify price levels with high volume.
- Prerequisite. Must call
marketBookAdd()first to subscribe. - Data structure. Returns list of price levels with volumes, separated by buy/sell type.
- Use case. Liquidity analysis, support/resistance identification, large order optimization.
- Real-time. Data reflects current state at time of request.
π― PurposeΒΆ
Use this method when you need to:
- Analyze market liquidity at different price levels.
- Identify support/resistance zones from order concentration.
- Assess potential slippage before placing large orders.
- Monitor order flow changes.
- Find best prices with sufficient liquidity.
- Build advanced trading algorithms using Level II data.
- Visualize market depth for trading dashboards.
π§© Notes & TipsΒΆ
- Subscription required. Call
marketBookAdd()first, otherwise this will fail. - Snapshot data. Returns current state, not historical or streaming data.
- Price sorting. Entries are typically sorted by price (best prices first).
- Volume fields. Use
volume_real(double) for lot-based calculations. - Buy vs Sell. Filter by
BookTypeto separate bids from asks. - Market orders. Some brokers include executed market orders in the book.
- Empty book. Empty list doesn't mean error - symbol may have no pending orders.
- Polling. Call repeatedly for real-time monitoring (consider rate limits).
π Usage ExamplesΒΆ
1) Basic order book retrievalΒΆ
import io.metarpc.mt5.MT5Account;
import io.metarpc.mt5.exceptions.ApiExceptionMT5;
import mt5_term_api.Mt5TermApiMarketInfo;
public class Example {
public static void main(String[] args) {
MT5Account account = new MT5Account(12345678, "password");
try {
account.connect("demo.mt5server.com", 443, "EURUSD");
// Subscribe to Market Depth
account.marketBookAdd("EURUSD");
// Get order book data
Mt5TermApiMarketInfo.MarketBookGetReply reply =
account.marketBookGet("EURUSD");
var bookEntries = reply.getData().getMqlBookInfosList();
System.out.printf("Order book entries: %d%n%n", bookEntries.size());
// Display order book
for (var entry : bookEntries) {
String typeStr = entry.getType().name();
System.out.printf("%s | Price: %.5f | Volume: %.2f lots%n",
typeStr,
entry.getPrice(),
entry.getVolumeReal()
);
}
// Cleanup
account.marketBookRelease("EURUSD");
} catch (ApiExceptionMT5 e) {
System.err.println("Error: " + e.getMessage());
} finally {
account.close();
}
}
}
2) Separate bids and asksΒΆ
import java.util.*;
public class OrderBookAnalyzer {
public record OrderLevel(double price, double volume) {}
public record OrderBook(
List<OrderLevel> bids, // Buy orders
List<OrderLevel> asks // Sell orders
) {}
/**
* Parse order book into bids and asks
*/
public static OrderBook parseOrderBook(
MT5Account account,
String symbol) throws ApiExceptionMT5 {
var reply = account.marketBookGet(symbol);
var entries = reply.getData().getMqlBookInfosList();
List<OrderLevel> bids = new ArrayList<>();
List<OrderLevel> asks = new ArrayList<>();
for (var entry : entries) {
OrderLevel level = new OrderLevel(
entry.getPrice(),
entry.getVolumeReal()
);
switch (entry.getType()) {
case BOOK_TYPE_BUY:
case BOOK_TYPE_BUY_MARKET:
bids.add(level);
break;
case BOOK_TYPE_SELL:
case BOOK_TYPE_SELL_MARKET:
asks.add(level);
break;
}
}
// Sort: bids descending (best bid first), asks ascending (best ask first)
bids.sort((a, b) -> Double.compare(b.price(), a.price()));
asks.sort((a, b) -> Double.compare(a.price(), b.price()));
return new OrderBook(bids, asks);
}
/**
* Display formatted order book
*/
public static void displayOrderBook(OrderBook book) {
System.out.println("\nββββββββββββββββββββββββββββββββββββββββββββββ");
System.out.println("β ORDER BOOK (DOM) β");
System.out.println("β βββββββββββββββββββββββββββββββββββββββββββββ£");
System.out.println("β BIDS (Buy) β ASKS (Sell) β");
System.out.println("β Price β Volume β Price β Volume β");
System.out.println("β βββββββββββββββββββββββββββββββββββββββββββββ£");
int maxRows = Math.max(book.bids().size(), book.asks().size());
for (int i = 0; i < Math.min(maxRows, 10); i++) { // Show top 10 levels
String bidPrice = i < book.bids().size()
? String.format("%.5f", book.bids().get(i).price())
: " ";
String bidVol = i < book.bids().size()
? String.format("%.2f", book.bids().get(i).volume())
: " ";
String askPrice = i < book.asks().size()
? String.format("%.5f", book.asks().get(i).price())
: " ";
String askVol = i < book.asks().size()
? String.format("%.2f", book.asks().get(i).volume())
: " ";
System.out.printf("β %s β %s β %s β %s β%n",
bidPrice, bidVol, askPrice, askVol);
}
System.out.println("ββββββββββββββββββββββββββββββββββββββββββββββ");
}
}
// Usage
var book = OrderBookAnalyzer.parseOrderBook(account, "EURUSD");
OrderBookAnalyzer.displayOrderBook(book);
System.out.printf("\nBest Bid: %.5f (Volume: %.2f lots)%n",
book.bids().get(0).price(),
book.bids().get(0).volume()
);
System.out.printf("Best Ask: %.5f (Volume: %.2f lots)%n",
book.asks().get(0).price(),
book.asks().get(0).volume()
);
3) Calculate total liquidityΒΆ
public class LiquidityCalculator {
/**
* Calculate total liquidity on each side
*/
public static void analyzeLiquidity(
MT5Account account,
String symbol) throws ApiExceptionMT5 {
var reply = account.marketBookGet(symbol);
var entries = reply.getData().getMqlBookInfosList();
double totalBidVolume = 0;
double totalAskVolume = 0;
int bidLevels = 0;
int askLevels = 0;
for (var entry : entries) {
double volume = entry.getVolumeReal();
switch (entry.getType()) {
case BOOK_TYPE_BUY:
case BOOK_TYPE_BUY_MARKET:
totalBidVolume += volume;
bidLevels++;
break;
case BOOK_TYPE_SELL:
case BOOK_TYPE_SELL_MARKET:
totalAskVolume += volume;
askLevels++;
break;
}
}
double totalVolume = totalBidVolume + totalAskVolume;
double bidRatio = totalVolume > 0 ? (totalBidVolume / totalVolume) * 100 : 0;
double askRatio = totalVolume > 0 ? (totalAskVolume / totalVolume) * 100 : 0;
System.out.println("\nββββββββββββββββββββββββββββββββββββββββββ");
System.out.println("β LIQUIDITY ANALYSIS β");
System.out.println("β βββββββββββββββββββββββββββββββββββββββββ£");
System.out.printf("β Symbol: %-30s β%n", symbol);
System.out.println("β βββββββββββββββββββββββββββββββββββββββββ£");
System.out.printf("β Bid Levels: %-26d β%n", bidLevels);
System.out.printf("β Ask Levels: %-26d β%n", askLevels);
System.out.printf("β Total Bid Volume: %-19.2f β%n", totalBidVolume);
System.out.printf("β Total Ask Volume: %-19.2f β%n", totalAskVolume);
System.out.printf("β Bid Ratio: %-25.2f%% β%n", bidRatio);
System.out.printf("β Ask Ratio: %-25.2f%% β%n", askRatio);
System.out.println("ββββββββββββββββββββββββββββββββββββββββββ");
// Interpret imbalance
if (Math.abs(bidRatio - askRatio) > 20) {
String side = bidRatio > askRatio ? "BUY" : "SELL";
System.out.printf("\nβ οΈ Significant order imbalance: %s pressure detected%n", side);
}
}
}
// Usage
LiquidityCalculator.analyzeLiquidity(account, "EURUSD");
4) Find price levels with high volumeΒΆ
import java.util.*;
public class HighVolumeLevelFinder {
public record VolumeLevel(
double price,
double volume,
String type
) {}
/**
* Find top volume levels in order book
*/
public static List<VolumeLevel> findTopVolumeLevels(
MT5Account account,
String symbol,
int topN) throws ApiExceptionMT5 {
var reply = account.marketBookGet(symbol);
var entries = reply.getData().getMqlBookInfosList();
List<VolumeLevel> levels = new ArrayList<>();
for (var entry : entries) {
String typeStr = entry.getType() == Mt5TermApiMarketInfo.BookType.BOOK_TYPE_BUY ||
entry.getType() == Mt5TermApiMarketInfo.BookType.BOOK_TYPE_BUY_MARKET
? "BID" : "ASK";
levels.add(new VolumeLevel(
entry.getPrice(),
entry.getVolumeReal(),
typeStr
));
}
// Sort by volume descending
levels.sort((a, b) -> Double.compare(b.volume(), a.volume()));
return levels.subList(0, Math.min(topN, levels.size()));
}
/**
* Display high volume levels
*/
public static void displayHighVolumeLevels(
List<VolumeLevel> levels,
String symbol) {
System.out.printf("\nββββββββββββββββββββββββββββββββββββββββββ%n");
System.out.printf("β HIGH VOLUME LEVELS - %-16s β%n", symbol);
System.out.println("β βββββββββββββββββββββββββββββββββββββββββ£");
System.out.println("β Rank β Type β Price β Volume β");
System.out.println("β βββββββββββββββββββββββββββββββββββββββββ£");
for (int i = 0; i < levels.size(); i++) {
var level = levels.get(i);
System.out.printf("β %2d β %-4s β %9.5f β %10.2f β%n",
i + 1,
level.type(),
level.price(),
level.volume()
);
}
System.out.println("ββββββββββββββββββββββββββββββββββββββββββ");
}
}
// Usage
var topLevels = HighVolumeLevelFinder.findTopVolumeLevels(account, "EURUSD", 10);
HighVolumeLevelFinder.displayHighVolumeLevels(topLevels, "EURUSD");
5) Estimate slippage for large orderΒΆ
public class SlippageEstimator {
/**
* Estimate slippage for a large market order
*/
public static double estimateSlippage(
MT5Account account,
String symbol,
double volumeLots,
boolean isBuy) throws ApiExceptionMT5 {
var reply = account.marketBookGet(symbol);
var entries = reply.getData().getMqlBookInfosList();
// Get relevant side of book
var relevantSide = entries.stream()
.filter(entry -> {
if (isBuy) {
return entry.getType() == Mt5TermApiMarketInfo.BookType.BOOK_TYPE_SELL ||
entry.getType() == Mt5TermApiMarketInfo.BookType.BOOK_TYPE_SELL_MARKET;
} else {
return entry.getType() == Mt5TermApiMarketInfo.BookType.BOOK_TYPE_BUY ||
entry.getType() == Mt5TermApiMarketInfo.BookType.BOOK_TYPE_BUY_MARKET;
}
})
.sorted((a, b) -> {
// Sort by best price first
if (isBuy) {
return Double.compare(a.getPrice(), b.getPrice()); // Ascending for buys
} else {
return Double.compare(b.getPrice(), a.getPrice()); // Descending for sells
}
})
.toList();
if (relevantSide.isEmpty()) {
System.out.println("β οΈ No liquidity available on " + (isBuy ? "ask" : "bid") + " side");
return Double.MAX_VALUE;
}
double remainingVolume = volumeLots;
double totalCost = 0;
double bestPrice = relevantSide.get(0).getPrice();
System.out.printf("\nFilling %s order for %.2f lots:%n",
isBuy ? "BUY" : "SELL", volumeLots);
System.out.println("β".repeat(50));
for (var entry : relevantSide) {
if (remainingVolume <= 0) break;
double availableVolume = entry.getVolumeReal();
double fillVolume = Math.min(remainingVolume, availableVolume);
totalCost += fillVolume * entry.getPrice();
remainingVolume -= fillVolume;
System.out.printf(" Price: %.5f β Fill: %.2f/%.2f lots%n",
entry.getPrice(),
fillVolume,
availableVolume
);
}
if (remainingVolume > 0) {
System.out.printf("\nβ οΈ WARNING: Insufficient liquidity! %.2f lots unfilled%n",
remainingVolume);
return Double.MAX_VALUE;
}
double avgPrice = totalCost / volumeLots;
double slippage = Math.abs(avgPrice - bestPrice);
System.out.println("β".repeat(50));
System.out.printf("Best Price: %.5f%n", bestPrice);
System.out.printf("Avg Fill Price: %.5f%n", avgPrice);
System.out.printf("Estimated Slippage: %.5f (%.1f pips)%n",
slippage, slippage * 10000);
return slippage;
}
}
// Usage
double slippage = SlippageEstimator.estimateSlippage(
account,
"EURUSD",
10.0, // 10 lots
true // BUY order
);
6) Monitor order book changesΒΆ
public class OrderBookMonitor {
/**
* Monitor order book for changes
*/
public static void monitorChanges(
MT5Account account,
String symbol,
int durationSeconds) throws InterruptedException {
System.out.printf("Monitoring %s order book for %d seconds...%n",
symbol, durationSeconds);
System.out.println("β".repeat(50));
var prevBids = new ArrayList<Double>();
var prevAsks = new ArrayList<Double>();
long endTime = System.currentTimeMillis() + (durationSeconds * 1000L);
while (System.currentTimeMillis() < endTime) {
try {
var reply = account.marketBookGet(symbol);
var entries = reply.getData().getMqlBookInfosList();
var currentBids = entries.stream()
.filter(e -> e.getType() == Mt5TermApiMarketInfo.BookType.BOOK_TYPE_BUY)
.map(e -> e.getPrice())
.sorted((a, b) -> Double.compare(b, a)) // Descending
.toList();
var currentAsks = entries.stream()
.filter(e -> e.getType() == Mt5TermApiMarketInfo.BookType.BOOK_TYPE_SELL)
.map(e -> e.getPrice())
.sorted() // Ascending
.toList();
// Check for changes
if (!currentBids.equals(prevBids) || !currentAsks.equals(prevAsks)) {
System.out.printf("[%s] Order book changed%n",
java.time.LocalTime.now());
if (!currentBids.isEmpty()) {
System.out.printf(" Best Bid: %.5f%n", currentBids.get(0));
}
if (!currentAsks.isEmpty()) {
System.out.printf(" Best Ask: %.5f%n", currentAsks.get(0));
}
if (!currentBids.isEmpty() && !currentAsks.isEmpty()) {
double spread = currentAsks.get(0) - currentBids.get(0);
System.out.printf(" Spread: %.5f (%.1f pips)%n",
spread, spread * 10000);
}
System.out.println();
prevBids = new ArrayList<>(currentBids);
prevAsks = new ArrayList<>(currentAsks);
}
Thread.sleep(500); // Check every 500ms
} catch (ApiExceptionMT5 e) {
System.err.printf("Error: %s%n", e.getMessage());
}
}
System.out.println("Monitoring complete");
}
}
// Usage
account.marketBookAdd("EURUSD");
OrderBookMonitor.monitorChanges(account, "EURUSD", 30);
account.marketBookRelease("EURUSD");
7) Calculate bid-ask spread from order bookΒΆ
public class SpreadCalculator {
/**
* Calculate bid-ask spread from order book
*/
public static void calculateSpread(
MT5Account account,
String symbol) throws ApiExceptionMT5 {
var reply = account.marketBookGet(symbol);
var entries = reply.getData().getMqlBookInfosList();
// Find best bid and ask
OptionalDouble bestBid = entries.stream()
.filter(e -> e.getType() == Mt5TermApiMarketInfo.BookType.BOOK_TYPE_BUY)
.mapToDouble(e -> e.getPrice())
.max();
OptionalDouble bestAsk = entries.stream()
.filter(e -> e.getType() == Mt5TermApiMarketInfo.BookType.BOOK_TYPE_SELL)
.mapToDouble(e -> e.getPrice())
.min();
if (bestBid.isEmpty() || bestAsk.isEmpty()) {
System.out.println("β οΈ Incomplete order book (missing bid or ask)");
return;
}
double bid = bestBid.getAsDouble();
double ask = bestAsk.getAsDouble();
double spread = ask - bid;
double spreadPips = spread * 10000;
double spreadPercent = (spread / bid) * 100;
System.out.println("\nββββββββββββββββββββββββββββββββββββββββββ");
System.out.printf("β SPREAD ANALYSIS - %-19s β%n", symbol);
System.out.println("β βββββββββββββββββββββββββββββββββββββββββ£");
System.out.printf("β Best Bid: %-28.5f β%n", bid);
System.out.printf("β Best Ask: %-28.5f β%n", ask);
System.out.printf("β Spread: %-30.5f β%n", spread);
System.out.printf("β Spread (pips): %-23.1f β%n", spreadPips);
System.out.printf("β Spread (%%): %-26.4f β%n", spreadPercent);
System.out.println("ββββββββββββββββββββββββββββββββββββββββββ");
}
}
// Usage
SpreadCalculator.calculateSpread(account, "EURUSD");
8) Export order book to JSONΒΆ
import com.google.gson.*;
import java.io.*;
public class OrderBookExporter {
/**
* Export order book to JSON file
*/
public static void exportToJson(
MT5Account account,
String symbol,
String filename) throws ApiExceptionMT5, IOException {
var reply = account.marketBookGet(symbol);
var entries = reply.getData().getMqlBookInfosList();
// Build JSON structure
JsonObject root = new JsonObject();
root.addProperty("symbol", symbol);
root.addProperty("timestamp", java.time.Instant.now().toString());
JsonArray bids = new JsonArray();
JsonArray asks = new JsonArray();
for (var entry : entries) {
JsonObject level = new JsonObject();
level.addProperty("price", entry.getPrice());
level.addProperty("volume", entry.getVolumeReal());
boolean isBid = entry.getType() == Mt5TermApiMarketInfo.BookType.BOOK_TYPE_BUY ||
entry.getType() == Mt5TermApiMarketInfo.BookType.BOOK_TYPE_BUY_MARKET;
if (isBid) {
bids.add(level);
} else {
asks.add(level);
}
}
root.add("bids", bids);
root.add("asks", asks);
// Write to file
Gson gson = new GsonBuilder().setPrettyPrinting().create();
try (FileWriter writer = new FileWriter(filename)) {
gson.toJson(root, writer);
}
System.out.printf("β
Exported order book to %s%n", filename);
System.out.printf(" Bids: %d levels, Asks: %d levels%n",
bids.size(), asks.size());
}
}
// Usage
OrderBookExporter.exportToJson(account, "EURUSD", "eurusd_orderbook.json");
π Low-level gRPC call (for reference)ΒΆ
import io.grpc.*;
import mt5_term_api.*;
// Create request
Mt5TermApiMarketInfo.MarketBookGetRequest request =
Mt5TermApiMarketInfo.MarketBookGetRequest.newBuilder()
.setSymbol("EURUSD")
.build();
// Add metadata headers
Metadata headers = new Metadata();
Metadata.Key<String> idKey = Metadata.Key.of("id", Metadata.ASCII_STRING_MARSHALLER);
headers.put(idKey, instanceId.toString());
// Call service
Mt5TermApiMarketInfo.MarketBookGetReply reply = marketInfoClient
.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(headers))
.marketBookGet(request);
// Check for errors
if (reply.hasError()) {
throw new ApiExceptionMT5(reply.getError());
}
// Use data
List<Mt5TermApiMarketInfo.MrpcMqlBookInfo> bookEntries =
reply.getData().getMqlBookInfosList();
π Understanding Order Book DataΒΆ
Order Types: - BOOK_TYPE_BUY - Pending buy limit orders (bids) - BOOK_TYPE_SELL - Pending sell limit orders (asks/offers) - BOOK_TYPE_BUY_MARKET - Market buy orders (rare in retail) - BOOK_TYPE_SELL_MARKET - Market sell orders (rare in retail)
Price Levels: - Bid side: Buyers willing to buy at specific prices (below market) - Ask side: Sellers willing to sell at specific prices (above market) - Best bid: Highest bid price (top of bid side) - Best ask: Lowest ask price (top of ask side)
Volume Interpretation: - Higher volume at a level = more liquidity - Large volumes = potential support/resistance - Volume imbalance = potential price movement direction
Typical Workflow:
1. Subscribe with marketBookAdd(symbol)
2. Poll with marketBookGet(symbol) for current data
3. Parse and analyze bid/ask sides
4. Make trading decisions based on liquidity
5. Unsubscribe with marketBookRelease(symbol) when done