Skip to content

Commit d2daf42

Browse files
authored
feature: map exchange string to primary exchange on level one ticks (#9488)
- add saleCondition/exchange overload to UpdateQuote - normalize exchange via GetPrimaryExchange in UpdateQuote/UpdateLastTrade - cover exchange mapping with combined test
1 parent 9046162 commit d2daf42

2 files changed

Lines changed: 54 additions & 3 deletions

File tree

Brokerages/LevelOneOrderBook/LevelOneMarketData.cs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ public LevelOneMarketData(Symbol symbol)
111111
/// <param name="bidSize">The size available at the best bid.</param>
112112
/// <param name="askPrice">The best ask price.</param>
113113
/// <param name="askSize">The size available at the best ask.</param>
114-
public void UpdateQuote(DateTime? quoteDateTimeUtc, decimal? bidPrice, decimal? bidSize, decimal? askPrice, decimal? askSize)
114+
/// <param name="saleCondition">The sale condition string.</param>
115+
/// <param name="exchange">The exchange identifier.</param>
116+
public void UpdateQuote(DateTime? quoteDateTimeUtc, decimal? bidPrice, decimal? bidSize, decimal? askPrice, decimal? askSize, string saleCondition, string exchange)
115117
{
116118
if (!IsValidTimestamp(quoteDateTimeUtc))
117119
{
@@ -141,12 +143,26 @@ public void UpdateQuote(DateTime? quoteDateTimeUtc, decimal? bidPrice, decimal?
141143

142144
if (isBidUpdated || isAskUpdated)
143145
{
144-
var lastQuoteTick = new Tick(quoteDateTimeUtc.Value.ConvertFromUtc(SymbolDateTimeZone), Symbol, BestBidSize, BestBidPrice, BestAskSize, BestAskPrice);
146+
var lastQuoteTick = new Tick(quoteDateTimeUtc.Value.ConvertFromUtc(SymbolDateTimeZone), Symbol, saleCondition, exchange.GetPrimaryExchange(Symbol.SecurityType), BestBidSize, BestBidPrice, BestAskSize, BestAskPrice);
145147

146148
BaseDataReceived?.Invoke(this, new(lastQuoteTick));
147149
}
148150
}
149151

152+
/// <summary>
153+
/// Updates the best bid and ask prices and sizes.
154+
/// Constructs and publishes a quote <see cref="Tick"/> to the <see cref="IDataAggregator"/>.
155+
/// </summary>
156+
/// <param name="quoteDateTimeUtc">The UTC timestamp when the quote was received.</param>
157+
/// <param name="bidPrice">The best bid price.</param>
158+
/// <param name="bidSize">The size available at the best bid.</param>
159+
/// <param name="askPrice">The best ask price.</param>
160+
/// <param name="askSize">The size available at the best ask.</param>
161+
public void UpdateQuote(DateTime? quoteDateTimeUtc, decimal? bidPrice, decimal? bidSize, decimal? askPrice, decimal? askSize)
162+
{
163+
UpdateQuote(quoteDateTimeUtc, bidPrice, bidSize, askPrice, askSize, string.Empty, string.Empty);
164+
}
165+
150166
/// <summary>
151167
/// Updates the last trade price and size.
152168
/// Constructs and publishes a trade <see cref="Tick"/> to the <see cref="Data.IDataAggregator"/>.
@@ -175,7 +191,7 @@ public void UpdateLastTrade(DateTime? tradeDateTimeUtc, decimal? lastQuantity, d
175191
tradeDateTimeUtc.Value.ConvertFromUtc(SymbolDateTimeZone),
176192
Symbol,
177193
saleCondition,
178-
exchange,
194+
exchange.GetPrimaryExchange(Symbol.SecurityType),
179195
LastTradeSize,
180196
LastTradePrice);
181197

Tests/Brokerages/LevelOneOrderBook/LevelOneMarketDataTests.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
using System;
1717
using NUnit.Framework;
18+
using QuantConnect.Data.Market;
1819
using QuantConnect.Brokerages.LevelOneOrderBook;
1920

2021
namespace QuantConnect.Tests.Brokerages.LevelOneOrderBook
@@ -176,6 +177,40 @@ public void UpdateQuoteShouldIgnoreZeroSizeUpdatesCorrectly(bool ignoreZeroSizeU
176177
}
177178
}
178179

180+
[TestCase("T", "NASDAQ")]
181+
[TestCase("Z", "BATS")]
182+
[TestCase("N", "NYSE")]
183+
[TestCase("P", "ARCA")]
184+
[TestCase("", "")]
185+
[TestCase(null, "")]
186+
public void LevelOneMarketDataShouldMapExchangeStringOnPublishedTicks(string exchange, string expectedExchange)
187+
{
188+
var levelOneMarketData = new LevelOneMarketData(_aapl);
189+
var quoteTick = default(Tick);
190+
var tradeTick = default(Tick);
191+
192+
levelOneMarketData.BaseDataReceived += (_, e) =>
193+
{
194+
var tick = (Tick)e.BaseData;
195+
if (tick.TickType == TickType.Quote)
196+
{
197+
quoteTick = tick;
198+
}
199+
else if (tick.TickType == TickType.Trade)
200+
{
201+
tradeTick = tick;
202+
}
203+
};
204+
205+
levelOneMarketData.UpdateQuote(_mockDateTime, 1, 1, 1, 1, saleCondition: string.Empty, exchange: exchange);
206+
levelOneMarketData.UpdateLastTrade(_mockDateTime, 1, 1, exchange: exchange);
207+
208+
Assert.IsNotNull(quoteTick, "Expected BaseDataReceived to publish a quote tick.");
209+
Assert.IsNotNull(tradeTick, "Expected BaseDataReceived to publish a trade tick.");
210+
Assert.AreEqual(expectedExchange, quoteTick.Exchange, "Quote tick exchange mismatch.");
211+
Assert.AreEqual(expectedExchange, tradeTick.Exchange, "Trade tick exchange mismatch.");
212+
}
213+
179214
[TestCase(true, 0, 2)]
180215
[TestCase(false, 0, 2)]
181216
public void UpdateLastTradeShouldIgnoreZeroSizeUpdatesCorrectly(bool ignoreZeroSizeUpdates, decimal? lastQuantity, decimal? lastPrice)

0 commit comments

Comments
 (0)