π Unsubscribe from Market Depth (DOM)ΒΆ
Request: unsubscribe from Market Depth (DOM / Level II) updates for a symbol. Stops receiving order book data and releases associated resources.
API Information:
- SDK wrapper:
MT5Account.marketBookRelease(...)(from packageio.metarpc.mt5) - gRPC service:
mt5_term_api.MarketInfo - Proto definition:
MarketBookRelease(defined inmt5-term-api-market-info.proto)
RPCΒΆ
- Service:
mt5_term_api.MarketInfo - Method:
MarketBookRelease(MarketBookReleaseRequest) β MarketBookReleaseReply - Lowβlevel client (generated):
MarketInfoGrpc.MarketInfoBlockingStub.marketBookRelease(request) - SDK wrapper (high-level):
package io.metarpc.mt5;
public class MT5Account {
/**
* Unsubscribes from Market Depth updates for a specified symbol.
* Stops receiving order book data and releases associated resources.
* Use this when you no longer need DOM data for a symbol to free up resources.
*
* @param symbol Symbol name (e.g., "EURUSD")
* @return Unsubscription confirmation response
* @throws ApiExceptionMT5 if the call fails or connection is lost
*/
public Mt5TermApiMarketInfo.MarketBookReleaseReply marketBookRelease(String symbol) throws ApiExceptionMT5;
}
Request message: MarketBookReleaseRequest { symbol: string }
Reply message: MarketBookReleaseReply { data: MarketBookReleaseData } or { error: Error }
π½ InputΒΆ
| Parameter | Type | Required | Description |
|---|---|---|---|
symbol |
String |
β | Symbol name (e.g., "EURUSD", "XAUUSD") |
β¬οΈ Output - MarketBookReleaseDataΒΆ
| Field | Type | Description |
|---|---|---|
closed_successfully |
boolean |
true if unsubscription successful, false otherwise |
Access using reply.getData().getClosedSuccessfully().
π¬ Just the essentialsΒΆ
- What it is. RPC to unsubscribe from Market Depth (order book) updates.
- Why you need it. Free up resources when you no longer need DOM data.
- Resource cleanup. Always call this when done with Market Depth monitoring.
- Idempotent. Safe to call even if not subscribed (will return success).
- Best practice. Use in
finallyblocks or cleanup code to ensure resource release. - Performance. Reduces memory/bandwidth usage by stopping unused subscriptions.
π― PurposeΒΆ
Use this method when you need to:
- Stop receiving Market Depth updates for a symbol.
- Free up terminal/server resources after analysis is complete.
- Clean up subscriptions before disconnecting.
- Implement proper resource management in trading applications.
- Release unused subscriptions to avoid resource leaks.
π§© Notes & TipsΒΆ
- Always cleanup. Call this method when done with DOM data to free resources.
- Idempotent. Safe to call multiple times or on unsubscribed symbols.
- Use finally. Place in
finallyblocks to ensure cleanup even on errors. - Success flag. Check
closed_successfullyto verify release worked. - No side effects. Won't affect other subscriptions or market data.
- Auto-reconnect. Uses
executeWithReconnect()for reliability. - Best practice. Always pair
marketBookAdd()withmarketBookRelease().
π Usage ExamplesΒΆ
1) Basic unsubscriptionΒΆ
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");
// Use Market Depth data...
var bookData = account.marketBookGet("EURUSD");
System.out.printf("Order book entries: %d%n",
bookData.getData().getMqlBookInfosList().size());
// Unsubscribe when done
Mt5TermApiMarketInfo.MarketBookReleaseReply reply =
account.marketBookRelease("EURUSD");
if (reply.getData().getClosedSuccessfully()) {
System.out.println("β
Successfully unsubscribed from EURUSD Market Depth");
} else {
System.out.println("β οΈ Failed to unsubscribe");
}
} catch (ApiExceptionMT5 e) {
System.err.println("Error: " + e.getMessage());
} finally {
account.close();
}
}
}
2) Proper resource cleanup with try-finallyΒΆ
public class SafeCleanup {
/**
* Use Market Depth with guaranteed cleanup
*/
public static void useMarketDepth(
MT5Account account,
String symbol) {
boolean subscribed = false;
try {
// Subscribe
var addReply = account.marketBookAdd(symbol);
subscribed = addReply.getData().getOpenedSuccessfully();
if (!subscribed) {
System.out.printf("Failed to subscribe to %s%n", symbol);
return;
}
System.out.printf("β
Subscribed to %s Market Depth%n", symbol);
// Use Market Depth data
var bookReply = account.marketBookGet(symbol);
System.out.printf("Order book entries: %d%n",
bookReply.getData().getMqlBookInfosList().size());
// Your analysis logic here...
} catch (ApiExceptionMT5 e) {
System.err.printf("Error: %s%n", e.getMessage());
} finally {
// Always cleanup
if (subscribed) {
try {
var releaseReply = account.marketBookRelease(symbol);
if (releaseReply.getData().getClosedSuccessfully()) {
System.out.printf("β
Cleaned up %s subscription%n", symbol);
}
} catch (ApiExceptionMT5 e) {
System.err.printf("Cleanup error: %s%n", e.getMessage());
}
}
}
}
}
// Usage
SafeCleanup.useMarketDepth(account, "EURUSD");
3) Unsubscribe from multiple symbolsΒΆ
import java.util.*;
public class MultiSymbolCleanup {
/**
* Unsubscribe from multiple symbols
*/
public static Map<String, Boolean> releaseMultiple(
MT5Account account,
String... symbols) {
Map<String, Boolean> results = new LinkedHashMap<>();
System.out.printf("Unsubscribing from %d symbols...%n", symbols.length);
System.out.println("β".repeat(50));
for (String symbol : symbols) {
try {
var reply = account.marketBookRelease(symbol);
boolean success = reply.getData().getClosedSuccessfully();
results.put(symbol, success);
String status = success ? "β
RELEASED" : "β οΈ FAILED";
System.out.printf("%s: %s%n", symbol, status);
} catch (ApiExceptionMT5 e) {
results.put(symbol, false);
System.out.printf("%s: β ERROR - %s%n", symbol, e.getMessage());
}
}
long successCount = results.values().stream()
.filter(success -> success)
.count();
System.out.println("β".repeat(50));
System.out.printf("Released: %d/%d subscriptions%n", successCount, symbols.length);
return results;
}
}
// Usage
MultiSymbolCleanup.releaseMultiple(account,
"EURUSD", "GBPUSD", "USDJPY", "XAUUSD"
);
4) Auto-closing resource (try-with-resources pattern)ΒΆ
public class MarketDepthResource implements AutoCloseable {
private final MT5Account account;
private final String symbol;
private boolean subscribed = false;
public MarketDepthResource(MT5Account account, String symbol) throws ApiExceptionMT5 {
this.account = account;
this.symbol = symbol;
// Subscribe on creation
var reply = account.marketBookAdd(symbol);
this.subscribed = reply.getData().getOpenedSuccessfully();
if (!subscribed) {
throw new ApiExceptionMT5("Failed to subscribe to Market Depth for " + symbol);
}
System.out.printf("β
Subscribed to %s Market Depth%n", symbol);
}
/**
* Get current order book
*/
public Mt5TermApiMarketInfo.MarketBookGetReply getOrderBook() throws ApiExceptionMT5 {
if (!subscribed) {
throw new IllegalStateException("Not subscribed to Market Depth");
}
return account.marketBookGet(symbol);
}
@Override
public void close() {
if (subscribed) {
try {
var reply = account.marketBookRelease(symbol);
if (reply.getData().getClosedSuccessfully()) {
System.out.printf("β
Released %s subscription%n", symbol);
subscribed = false;
}
} catch (ApiExceptionMT5 e) {
System.err.printf("Failed to release %s: %s%n", symbol, e.getMessage());
}
}
}
}
// Usage with try-with-resources (automatic cleanup!)
try (var marketDepth = new MarketDepthResource(account, "EURUSD")) {
var book = marketDepth.getOrderBook();
System.out.printf("Order book size: %d%n",
book.getData().getMqlBookInfosList().size());
// Analyze order book...
} // Automatic unsubscription happens here
5) Subscription manager with trackingΒΆ
public class SubscriptionManager {
private final MT5Account account;
private final Set<String> activeSubscriptions = new HashSet<>();
public SubscriptionManager(MT5Account account) {
this.account = account;
}
/**
* Subscribe with tracking
*/
public boolean subscribe(String symbol) throws ApiExceptionMT5 {
if (activeSubscriptions.contains(symbol)) {
System.out.printf("Already subscribed to %s%n", symbol);
return true;
}
var reply = account.marketBookAdd(symbol);
boolean success = reply.getData().getOpenedSuccessfully();
if (success) {
activeSubscriptions.add(symbol);
System.out.printf("β
Subscribed to %s (%d active)%n",
symbol, activeSubscriptions.size());
}
return success;
}
/**
* Unsubscribe with tracking
*/
public boolean unsubscribe(String symbol) throws ApiExceptionMT5 {
if (!activeSubscriptions.contains(symbol)) {
System.out.printf("Not subscribed to %s%n", symbol);
return true;
}
var reply = account.marketBookRelease(symbol);
boolean success = reply.getData().getClosedSuccessfully();
if (success) {
activeSubscriptions.remove(symbol);
System.out.printf("β
Unsubscribed from %s (%d remaining)%n",
symbol, activeSubscriptions.size());
}
return success;
}
/**
* Cleanup all subscriptions
*/
public void cleanupAll() {
if (activeSubscriptions.isEmpty()) {
System.out.println("No active subscriptions to cleanup");
return;
}
System.out.printf("Cleaning up %d subscriptions...%n",
activeSubscriptions.size());
var symbols = new ArrayList<>(activeSubscriptions);
int successCount = 0;
for (String symbol : symbols) {
try {
if (unsubscribe(symbol)) {
successCount++;
}
} catch (ApiExceptionMT5 e) {
System.err.printf("Failed to release %s: %s%n",
symbol, e.getMessage());
}
}
System.out.printf("β
Cleaned up %d/%d subscriptions%n",
successCount, symbols.size());
}
/**
* Get active subscription count
*/
public int getActiveCount() {
return activeSubscriptions.size();
}
/**
* Get active subscriptions
*/
public Set<String> getActiveSubscriptions() {
return new HashSet<>(activeSubscriptions);
}
/**
* Print status
*/
public void printStatus() {
System.out.println("\nββββββββββββββββββββββββββββββββββββββββββ");
System.out.println("β MARKET DEPTH SUBSCRIPTIONS β");
System.out.println("β βββββββββββββββββββββββββββββββββββββββββ£");
System.out.printf("β Active: %-30d β%n", activeSubscriptions.size());
if (!activeSubscriptions.isEmpty()) {
System.out.println("β βββββββββββββββββββββββββββββββββββββββββ£");
for (String symbol : activeSubscriptions) {
System.out.printf("β β
%-36s β%n", symbol);
}
}
System.out.println("ββββββββββββββββββββββββββββββββββββββββββ");
}
}
// Usage
var manager = new SubscriptionManager(account);
// Subscribe to symbols
manager.subscribe("EURUSD");
manager.subscribe("GBPUSD");
manager.subscribe("XAUUSD");
manager.printStatus();
// Use market depth data...
// Cleanup when done
manager.cleanupAll();
6) Cleanup on application shutdownΒΆ
public class ShutdownHookCleanup {
/**
* Register shutdown hook for cleanup
*/
public static void registerCleanupHook(
MT5Account account,
Set<String> subscribedSymbols) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("\nπ Shutdown detected - cleaning up subscriptions...");
for (String symbol : subscribedSymbols) {
try {
var reply = account.marketBookRelease(symbol);
if (reply.getData().getClosedSuccessfully()) {
System.out.printf("β
Released %s%n", symbol);
}
} catch (Exception e) {
System.err.printf("Failed to release %s: %s%n",
symbol, e.getMessage());
}
}
System.out.println("Cleanup complete");
}));
}
}
// Usage
Set<String> subscriptions = new HashSet<>();
// Register cleanup hook
ShutdownHookCleanup.registerCleanupHook(account, subscriptions);
// Subscribe to symbols
account.marketBookAdd("EURUSD");
subscriptions.add("EURUSD");
account.marketBookAdd("GBPUSD");
subscriptions.add("GBPUSD");
// Application runs...
// On shutdown (Ctrl+C or exit), cleanup happens automatically
7) Retry release with exponential backoffΒΆ
public class RetryRelease {
/**
* Release subscription with retry logic
*/
public static boolean releaseWithRetry(
MT5Account account,
String symbol,
int maxRetries) {
System.out.printf("Releasing %s (max retries: %d)...%n", symbol, maxRetries);
int retryDelay = 100; // Start with 100ms
for (int attempt = 1; attempt <= maxRetries; attempt++) {
try {
var reply = account.marketBookRelease(symbol);
if (reply.getData().getClosedSuccessfully()) {
System.out.printf("β
Released successfully (attempt %d/%d)%n",
attempt, maxRetries);
return true;
}
System.out.printf("β οΈ Release failed (attempt %d/%d)%n",
attempt, maxRetries);
} catch (ApiExceptionMT5 e) {
System.out.printf("β Error on attempt %d/%d: %s%n",
attempt, maxRetries, e.getMessage());
}
if (attempt < maxRetries) {
try {
System.out.printf("β³ Waiting %dms before retry...%n", retryDelay);
Thread.sleep(retryDelay);
retryDelay = Math.min(retryDelay * 2, 5000); // Cap at 5s
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
}
System.out.printf("β Failed to release after %d attempts%n", maxRetries);
return false;
}
}
// Usage
RetryRelease.releaseWithRetry(account, "EURUSD", 3);
8) Batch cleanup with progressΒΆ
public class BatchCleanup {
/**
* Cleanup multiple subscriptions with progress tracking
*/
public static void cleanupWithProgress(
MT5Account account,
Collection<String> symbols) {
if (symbols.isEmpty()) {
System.out.println("No subscriptions to cleanup");
return;
}
int total = symbols.size();
int completed = 0;
int successful = 0;
System.out.printf("Cleaning up %d subscriptions...%n", total);
System.out.println("β".repeat(50));
for (String symbol : symbols) {
completed++;
int progress = (completed * 100) / total;
System.out.printf("[%3d%%] Releasing %s... ", progress, symbol);
try {
var reply = account.marketBookRelease(symbol);
if (reply.getData().getClosedSuccessfully()) {
successful++;
System.out.println("β
OK");
} else {
System.out.println("β οΈ FAILED");
}
} catch (ApiExceptionMT5 e) {
System.out.printf("β ERROR: %s%n", e.getMessage());
}
// Small delay between releases
if (completed < total) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
System.out.println("β".repeat(50));
System.out.printf("β
Successfully released: %d/%d%n", successful, total);
if (successful < total) {
System.out.printf("β οΈ Failed to release: %d%n", total - successful);
}
}
}
// Usage
Set<String> subscriptions = Set.of(
"EURUSD", "GBPUSD", "USDJPY", "AUDUSD",
"USDCHF", "NZDUSD", "USDCAD"
);
BatchCleanup.cleanupWithProgress(account, subscriptions);
π Low-level gRPC call (for reference)ΒΆ
import io.grpc.*;
import mt5_term_api.*;
// Create request
Mt5TermApiMarketInfo.MarketBookReleaseRequest request =
Mt5TermApiMarketInfo.MarketBookReleaseRequest.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.MarketBookReleaseReply reply = marketInfoClient
.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(headers))
.marketBookRelease(request);
// Check for errors
if (reply.hasError()) {
throw new ApiExceptionMT5(reply.getError());
}
// Check success
boolean success = reply.getData().getClosedSuccessfully();
π Best PracticesΒΆ
Resource Management:
1. Always call marketBookRelease() after marketBookAdd()
2. Use try-finally blocks to ensure cleanup
3. Consider try-with-resources pattern for automatic cleanup
4. Register shutdown hooks for application-level cleanup
Error Handling:
- Check closed_successfully flag to verify release
- Handle exceptions gracefully (release may fail after disconnect)
- Safe to call even if not subscribed (idempotent)
- Don't throw exceptions in cleanup code
Performance: - Release unused subscriptions promptly - Avoid accumulating subscriptions over time - Track active subscriptions to prevent leaks - Clean up before application exit
Typical Lifecycle:
// 1. Subscribe
account.marketBookAdd("EURUSD");
// 2. Use data (multiple times)
for (int i = 0; i < 10; i++) {
var book = account.marketBookGet("EURUSD");
// Analyze...
Thread.sleep(1000);
}
// 3. Release (in finally)
account.marketBookRelease("EURUSD");
Common Patterns: - Scoped usage: Subscribe β Use β Release immediately - Long-lived: Subscribe at startup β Use throughout session β Release at shutdown - On-demand: Subscribe when needed β Use briefly β Release after use