Skip to content

Commit d9c6c7c

Browse files
committed
Fix OrderBookIncrementManagerState dropping ALL@ALL QuoteChange messages
TryApply only looked in _byId dictionary, missing subscriptions registered via AddAllSecSubscription (_allSecSubscriptions). Added dynamic BookInfo creation for ALL@ALL subscriptions keyed by SecurityId. Added reproduction test TryApply_AllSecSubscription_OnlyBuildsBookDynamically.
1 parent 1d1c3e3 commit d9c6c7c

2 files changed

Lines changed: 46 additions & 1 deletion

File tree

Algo/OrderBookIncrementManagerState.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@ private class BookInfo(SecurityId securityId, ILogReceiver parent)
1717
private readonly HashSet<long> _passThrough = [];
1818
private readonly CachedSynchronizedSet<long> _allSecSubscriptions = [];
1919
private readonly CachedSynchronizedSet<long> _allSecSubscriptionsPassThrough = [];
20+
private ILogReceiver _builderParent;
2021

2122
/// <inheritdoc />
2223
public void AddSubscription(long transactionId, SecurityId securityId, ILogReceiver builderParent)
2324
{
2425
using (_sync.EnterScope())
2526
{
27+
_builderParent ??= builderParent;
28+
2629
var info = new BookInfo(securityId, builderParent);
2730
info.SubscriptionIds.Add(transactionId);
2831
_byId.Add(transactionId, info);
@@ -79,7 +82,24 @@ public QuoteChangeMessage TryApply(long subscriptionId, QuoteChangeMessage quote
7982
using (_sync.EnterScope())
8083
{
8184
if (!_byId.TryGetValue(subscriptionId, out var info))
82-
return null;
85+
{
86+
// For ALL@ALL subscriptions: dynamically create per-security builder
87+
if (_allSecSubscriptions.Count > 0 && _allSecSubscriptions.Contains(subscriptionId))
88+
{
89+
var secId = quoteMsg.SecurityId;
90+
91+
if (!_online.TryGetValue(secId, out info))
92+
{
93+
info = new(secId, _builderParent);
94+
info.SubscriptionIds.Add(subscriptionId);
95+
_online.Add(secId, info);
96+
}
97+
98+
_byId.Add(subscriptionId, info);
99+
}
100+
else
101+
return null;
102+
}
83103

84104
newQuoteMsg = info.Builder.TryApply(quoteMsg, subscriptionId);
85105

Tests/OrderBookIncrementManagerStateTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,31 @@ public void RemoveSubscription_ContainsSubscription_ReturnsFalse()
148148
state.ContainsSubscription(1).AssertFalse();
149149
}
150150

151+
[TestMethod]
152+
public void TryApply_AllSecSubscription_OnlyBuildsBookDynamically()
153+
{
154+
// Reproduces the bug: ALL@ALL subscription for MarketDepth
155+
// registers in _allSecSubscriptions but TryApply only looks in _byId,
156+
// so QuoteChange messages are dropped (TryApply returns null).
157+
var state = new OrderBookIncrementManagerState();
158+
159+
// Subscribe ALL@ALL (no per-security subscription)
160+
state.AddAllSecSubscription(42);
161+
162+
state.HasAnySubscriptions.AssertTrue();
163+
164+
var snapshot = CreateSnapshot(_secId, [42]);
165+
var result = state.TryApply(42, snapshot, out var subscriptionIds);
166+
167+
// BUG: result is null because _byId doesn't contain subscription 42
168+
// FIX: should dynamically create BookInfo for the security
169+
result.AssertNotNull();
170+
result.Bids.Length.AssertEqual(1);
171+
result.Asks.Length.AssertEqual(1);
172+
subscriptionIds.AssertNotNull();
173+
subscriptionIds.Any(id => id == 42L).AssertTrue();
174+
}
175+
151176
[TestMethod]
152177
public void Clear_RemovesAll()
153178
{

0 commit comments

Comments
 (0)