/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.Linq; namespace QuantConnect.Brokerages { /// /// Represents a full order book for a security. /// It contains prices and order sizes for each bid and ask level. /// The best bid and ask prices are also kept up to date. /// public class DefaultOrderBook : IOrderBookUpdater { private decimal _bestBidPrice; private decimal _bestBidSize; private decimal _bestAskPrice; private decimal _bestAskSize; private readonly object _locker = new object(); /// /// Represents bid prices and sizes /// protected SortedDictionary Bids { get; init; } = new SortedDictionary(); /// /// Represents ask prices and sizes /// protected SortedDictionary Asks { get; init; } = new SortedDictionary(); /// /// Represents a unique security identifier of current Order Book /// public Symbol Symbol { get; } /// /// Event fired each time or are changed /// public event EventHandler BestBidAskUpdated; /// /// The best bid price /// public decimal BestBidPrice { get { lock (_locker) { return _bestBidPrice; } } } /// /// The best bid size /// public decimal BestBidSize { get { lock (_locker) { return _bestBidSize; } } } /// /// The best ask price /// public decimal BestAskPrice { get { lock (_locker) { return _bestAskPrice; } } } /// /// The best ask size /// public decimal BestAskSize { get { lock (_locker) { return _bestAskSize; } } } /// /// Initializes a new instance of the class /// /// The symbol for the order book public DefaultOrderBook(Symbol symbol) { Symbol = symbol; } /// /// Clears all bid/ask levels and prices. /// public void Clear() { lock (_locker) { _bestBidPrice = 0; _bestBidSize = 0; _bestAskPrice = 0; _bestAskSize = 0; Bids.Clear(); Asks.Clear(); } } /// /// Updates or inserts a bid price level in the order book /// /// The bid price level to be inserted or updated /// The new size at the bid price level public void UpdateBidRow(decimal price, decimal size) { lock (_locker) { Bids[price] = size; if (_bestBidPrice == 0 || price >= _bestBidPrice) { _bestBidPrice = price; _bestBidSize = size; BestBidAskUpdated?.Invoke(this, new BestBidAskUpdatedEventArgs(Symbol, _bestBidPrice, _bestBidSize, _bestAskPrice, _bestAskSize)); } } } /// /// Updates or inserts an ask price level in the order book /// /// The ask price level to be inserted or updated /// The new size at the ask price level public void UpdateAskRow(decimal price, decimal size) { lock (_locker) { Asks[price] = size; if (_bestAskPrice == 0 || price <= _bestAskPrice) { _bestAskPrice = price; _bestAskSize = size; BestBidAskUpdated?.Invoke(this, new BestBidAskUpdatedEventArgs(Symbol, _bestBidPrice, _bestBidSize, _bestAskPrice, _bestAskSize)); } } } /// /// Removes a bid price level from the order book /// /// The bid price level to be removed public void RemoveBidRow(decimal price) { lock (_locker) { Bids.Remove(price); if (price == _bestBidPrice) { var priceLevel = Bids.LastOrDefault(); _bestBidPrice = priceLevel.Key; _bestBidSize = priceLevel.Value; BestBidAskUpdated?.Invoke(this, new BestBidAskUpdatedEventArgs(Symbol, _bestBidPrice, _bestBidSize, _bestAskPrice, _bestAskSize)); } } } /// /// Removes an ask price level from the order book /// /// The ask price level to be removed public void RemoveAskRow(decimal price) { lock (_locker) { Asks.Remove(price); if (price == _bestAskPrice) { var priceLevel = Asks.FirstOrDefault(); _bestAskPrice = priceLevel.Key; _bestAskSize = priceLevel.Value; BestBidAskUpdated?.Invoke(this, new BestBidAskUpdatedEventArgs(Symbol, _bestBidPrice, _bestBidSize, _bestAskPrice, _bestAskSize)); } } } /// /// Common price level removal method /// /// public void RemovePriceLevel(decimal priceLevel) { lock (_locker) { if (Asks.ContainsKey(priceLevel)) { RemoveAskRow(priceLevel); } else if (Bids.ContainsKey(priceLevel)) { RemoveBidRow(priceLevel); } } } } }