Subscribe to Market Book (SubscribeToMarketBookAsync)¶
Sugar method: Subscribes to Market Depth (order book) with automatic cleanup via IDisposable pattern.
API Information:
- Extension method:
MT5Service.SubscribeToMarketBookAsync(...)(fromMT5ServiceExtensions) - Package: Part of
mt5_term_apilibrary - Region: [12] MARKET DEPTH (DOM)
- Underlying calls:
MarketBookAddAsync()+ customMarketBookSubscriptionwrapper
Method Signature¶
public static async Task<IDisposable> SubscribeToMarketBookAsync(
this MT5Service svc,
string symbol,
int timeoutSec = 15,
CancellationToken ct = default)
🔽 Input¶
| Parameter | Type | Description |
|---|---|---|
svc |
MT5Service |
MT5Service instance (extension method) |
symbol |
string |
Symbol to subscribe (e.g., "EURUSD", "XAUUSD") |
timeoutSec |
int |
RPC timeout in seconds (default: 15) |
ct |
CancellationToken |
Cancellation token |
⬆️ Output¶
| Type | Description |
|---|---|
Task<IDisposable> |
Disposable subscription that auto-releases when disposed |
Key behavior:
- When disposed, automatically calls
MarketBookReleaseAsync() - Use with
usingstatement for automatic cleanup
💬 Just the essentials¶
- What it is: Subscribes to real-time order book (DOM) data for a symbol with automatic cleanup.
- Why you need it: Access Level II market data for scalping, liquidity analysis, and advanced order placement.
- Pattern: Use with
usingblock - automatically unsubscribes when block exits.
🎯 Purpose¶
Use it for:
- Scalping - See real liquidity and order flow
- Large order execution - Check available liquidity before placing big orders
- Market microstructure analysis - Study bid/ask dynamics
- Iceberg order detection - Find hidden large orders
- Support/Resistance detection - Identify order clusters
🔧 Under the Hood¶
// Step 1: Subscribe to market book
await svc.MarketBookAddAsync(symbol, Dl(timeoutSec), ct);
// Step 2: Return disposable wrapper
return new MarketBookSubscription(svc, symbol, timeoutSec);
// Internal MarketBookSubscription class:
private class MarketBookSubscription : IDisposable
{
private readonly MT5Service _svc;
private readonly string _symbol;
private readonly int _timeoutSec;
private bool _disposed;
public void Dispose()
{
if (!_disposed)
{
_disposed = true;
// Fire-and-forget release (cannot await in Dispose)
_ = _svc.MarketBookReleaseAsync(_symbol, Dl(_timeoutSec), default);
}
}
}
What it improves:
- Automatic cleanup - No manual unsubscribe needed
- Resource safety - Guaranteed cleanup even on exception
- Clean syntax -
usingblock pattern - Fire-and-forget release - Non-blocking dispose
🔗 Usage Examples¶
Example 1: Basic Subscription¶
// Subscribe to EURUSD order book
using (await svc.SubscribeToMarketBookAsync("EURUSD"))
{
var book = await svc.GetMarketBookSnapshotAsync("EURUSD");
Console.WriteLine($"Order book entries: {book.MqlBookInfos.Count}");
foreach (var entry in book.MqlBookInfos.Take(5))
{
string side = entry.Type == BookType.Buy ? "BID" : "ASK";
Console.WriteLine($"{side}: {entry.Price:F5} - {entry.Volume} lots");
}
} // Auto-unsubscribes here
// Output:
// Order book entries: 20
// BID: 1.08520 - 100 lots
// BID: 1.08519 - 250 lots
// BID: 1.08518 - 150 lots
// ASK: 1.08523 - 120 lots
// ASK: 1.08524 - 180 lots
Example 2: Find Best Bid/Ask from Book¶
using (await svc.SubscribeToMarketBookAsync("EURUSD"))
{
var (bestBid, bestAsk) = await svc.GetBestBidAskFromBookAsync("EURUSD");
double spread = bestAsk - bestBid;
Console.WriteLine($"Best Bid: {bestBid:F5}");
Console.WriteLine($"Best Ask: {bestAsk:F5}");
Console.WriteLine($"Spread: {spread:F5}");
}
// Output:
// Best Bid: 1.08520
// Best Ask: 1.08523
// Spread: 0.00003
Example 3: Check Liquidity Before Large Order¶
public async Task<bool> HasEnoughLiquidity(
MT5Service svc,
string symbol,
double targetVolume)
{
using (await svc.SubscribeToMarketBookAsync(symbol))
{
var book = await svc.GetMarketBookSnapshotAsync(symbol);
// Sum top 3 ask levels
long totalAskVolume = book.MqlBookInfos
.Where(b => b.Type == BookType.Sell)
.OrderBy(b => b.Price)
.Take(3)
.Sum(b => b.Volume);
Console.WriteLine($"Available liquidity (top 3 levels): {totalAskVolume} lots");
Console.WriteLine($"Target volume: {targetVolume} lots");
return totalAskVolume >= targetVolume;
}
}
// Usage:
bool canExecute = await HasEnoughLiquidity(svc, "EURUSD", targetVolume: 500);
if (canExecute)
{
Console.WriteLine("✅ Sufficient liquidity - placing order");
}
else
{
Console.WriteLine("⚠️ Insufficient liquidity - may cause slippage");
}
Example 4: Find Order Book Imbalance¶
using (await svc.SubscribeToMarketBookAsync("EURUSD"))
{
var book = await svc.GetMarketBookSnapshotAsync("EURUSD");
long totalBidVolume = book.MqlBookInfos
.Where(b => b.Type == BookType.Buy)
.Sum(b => b.Volume);
long totalAskVolume = book.MqlBookInfos
.Where(b => b.Type == BookType.Sell)
.Sum(b => b.Volume);
double imbalance = (double)totalBidVolume / totalAskVolume;
Console.WriteLine($"Total Bid Volume: {totalBidVolume}");
Console.WriteLine($"Total Ask Volume: {totalAskVolume}");
Console.WriteLine($"Bid/Ask Imbalance: {imbalance:F2}");
if (imbalance > 1.5)
{
Console.WriteLine("🔼 Strong buying pressure");
}
else if (imbalance < 0.67)
{
Console.WriteLine("🔽 Strong selling pressure");
}
else
{
Console.WriteLine("➡️ Balanced order book");
}
}
Example 5: Monitor Order Book in Real-Time Loop¶
using (await svc.SubscribeToMarketBookAsync("EURUSD"))
{
for (int i = 0; i < 10; i++)
{
var (bid, ask) = await svc.GetBestBidAskFromBookAsync("EURUSD");
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Bid: {bid:F5}, Ask: {ask:F5}");
await Task.Delay(1000); // Update every second
}
} // Auto-cleanup after loop
// Output:
// [14:30:00] Bid: 1.08520, Ask: 1.08523
// [14:30:01] Bid: 1.08521, Ask: 1.08523
// [14:30:02] Bid: 1.08520, Ask: 1.08524
// ...
Example 6: Find Support/Resistance from Order Clusters¶
using (await svc.SubscribeToMarketBookAsync("EURUSD"))
{
var book = await svc.GetMarketBookSnapshotAsync("EURUSD");
// Find largest bid cluster (potential support)
var largestBid = book.MqlBookInfos
.Where(b => b.Type == BookType.Buy)
.OrderByDescending(b => b.Volume)
.First();
// Find largest ask cluster (potential resistance)
var largestAsk = book.MqlBookInfos
.Where(b => b.Type == BookType.Sell)
.OrderByDescending(b => b.Volume)
.First();
Console.WriteLine($"🛡️ Support (largest bid): {largestBid.Price:F5} ({largestBid.Volume} lots)");
Console.WriteLine($"🚧 Resistance (largest ask): {largestAsk.Price:F5} ({largestAsk.Volume} lots)");
}
Example 7: Multiple Symbol Subscriptions¶
// Subscribe to multiple symbols simultaneously
var symbols = new[] { "EURUSD", "GBPUSD", "USDJPY" };
var subscriptions = new List<IDisposable>();
try
{
// Subscribe to all
foreach (var symbol in symbols)
{
subscriptions.Add(await svc.SubscribeToMarketBookAsync(symbol));
}
// Analyze all books
foreach (var symbol in symbols)
{
var (bid, ask) = await svc.GetBestBidAskFromBookAsync(symbol);
Console.WriteLine($"{symbol}: {bid:F5} / {ask:F5}");
}
}
finally
{
// Cleanup all subscriptions
foreach (var sub in subscriptions)
{
sub.Dispose();
}
}
Example 8: Exception Handling with Guaranteed Cleanup¶
try
{
using (await svc.SubscribeToMarketBookAsync("EURUSD"))
{
var book = await svc.GetMarketBookSnapshotAsync("EURUSD");
if (book.MqlBookInfos.Count == 0)
{
throw new InvalidOperationException("Empty order book!");
}
// Process book...
}
} // Dispose called even if exception occurs
catch (Exception ex)
{
Console.WriteLine($"❌ Error: {ex.Message}");
}
Example 9: Calculate Weighted Average Price from Book¶
using (await svc.SubscribeToMarketBookAsync("EURUSD"))
{
var book = await svc.GetMarketBookSnapshotAsync("EURUSD");
// Calculate VWAP (Volume Weighted Average Price) from ask side
var asks = book.MqlBookInfos.Where(b => b.Type == BookType.Sell).ToList();
double totalValue = asks.Sum(a => a.Price * a.Volume);
long totalVolume = asks.Sum(a => a.Volume);
double vwap = totalValue / totalVolume;
Console.WriteLine($"Ask-side VWAP: {vwap:F5}");
Console.WriteLine($"Based on {totalVolume} lots across {asks.Count} levels");
}
Example 10: Async Using Declaration (C# 8.0+)¶
// Modern C# 8.0+ syntax - auto-disposes at end of scope
await using var subscription = await svc.SubscribeToMarketBookAsync("EURUSD");
var book = await svc.GetMarketBookSnapshotAsync("EURUSD");
Console.WriteLine($"Book depth: {book.MqlBookInfos.Count} levels");
// Process book data...
// Auto-disposes here (end of method/scope)
🔗 Related Methods¶
📦 Methods used internally:
MarketBookAddAsync()- Low-level subscriptionMarketBookReleaseAsync()- Low-level unsubscription
🍬 Related Sugar methods:
GetMarketBookSnapshotAsync()- Get current order book snapshotGetBestBidAskFromBookAsync()- Extract best bid/ask from bookCalculateLiquidityAtLevelAsync()- Calculate volume at price level
📊 Alternative approaches:
MarketBookAddAsync()+ manualMarketBookReleaseAsync()- More control, but manual cleanup
⚠️ Common Pitfalls¶
-
Forgetting to dispose:
// ❌ WRONG: Memory leak - never unsubscribes! var sub = await svc.SubscribeToMarketBookAsync("EURUSD"); var book = await svc.GetMarketBookSnapshotAsync("EURUSD"); // sub never disposed! // ✅ CORRECT: Use using block using (await svc.SubscribeToMarketBookAsync("EURUSD")) { var book = await svc.GetMarketBookSnapshotAsync("EURUSD"); } // Auto-cleanup -
Using book data after unsubscribe:
MarketBookGetData book; using (await svc.SubscribeToMarketBookAsync("EURUSD")) { book = await svc.GetMarketBookSnapshotAsync("EURUSD"); } // Unsubscribed here // ⚠️ Book data may be stale - subscription is closed Console.WriteLine(book.MqlBookInfos.Count); // ✅ CORRECT: Process data inside using block -
Not checking if broker supports DOM:
// ⚠️ Not all brokers provide Level II data try { using (await svc.SubscribeToMarketBookAsync("EURUSD")) { var book = await svc.GetMarketBookSnapshotAsync("EURUSD"); if (book.MqlBookInfos.Count == 0) { Console.WriteLine("⚠️ Broker doesn't provide DOM data"); } } } catch (Exception ex) { Console.WriteLine($"❌ DOM not supported: {ex.Message}"); } -
Subscribing to same symbol multiple times:
// ⚠️ Redundant - MT5 handles multiple subscriptions, but wasteful using (await svc.SubscribeToMarketBookAsync("EURUSD")) using (await svc.SubscribeToMarketBookAsync("EURUSD")) // Redundant { // Only need one subscription per symbol } // ✅ CORRECT: One subscription per symbol using (await svc.SubscribeToMarketBookAsync("EURUSD")) { // Use single subscription } -
Assuming real-time updates without polling:
// ⚠️ GetMarketBookSnapshotAsync returns snapshot, not live stream using (await svc.SubscribeToMarketBookAsync("EURUSD")) { var book = await svc.GetMarketBookSnapshotAsync("EURUSD"); // This book is a snapshot - won't update automatically // Must poll to see changes: await Task.Delay(1000); book = await svc.GetMarketBookSnapshotAsync("EURUSD"); // New snapshot }
💡 Summary¶
SubscribeToMarketBookAsync provides clean DOM subscription with automatic cleanup:
- ✅ IDisposable pattern for automatic unsubscribe
- ✅ Use with
usingblock for guaranteed cleanup - ✅ Access to Level II market data
- ✅ Essential for scalping and large order execution
- ✅ Works with all other DOM methods
// Clean pattern:
using (await svc.SubscribeToMarketBookAsync("EURUSD"))
{
var book = await svc.GetMarketBookSnapshotAsync("EURUSD");
// Analyze order book...
} // Auto-cleanup guaranteed
Professional DOM access made simple! 📊