// mika // debug code for check build algo //private readonly Dictionary<long, decimal> _pendingMatch = new Dictionary<long, decimal>(); //private readonly Dictionary<long, Tuple<Sides, decimal, decimal>> _activeOrders = new Dictionary<long, Tuple<Sides, decimal, decimal>>(); //private QuoteChangeMessage BuildDepth() //{ // var bids = new SortedDictionary<decimal, QuoteChange>(new BackwardComparer<decimal>()); // var asks = new SortedDictionary<decimal, QuoteChange>(); // foreach (var pair in _activeOrders) // { // var quotes = pair.Value.Item1 == Sides.Buy ? bids : asks; // var quote = quotes.TryGetValue(pair.Value.Item2); // if (quote == null) // { // quote = new QuoteChange(pair.Value.Item1, pair.Value.Item2, pair.Value.Item3); // quotes.Add(pair.Value.Item2, quote); // } // else // quote.Volume += pair.Value.Item3; // } // return new QuoteChangeMessage // { // Bids = bids.Values.ToArray(), // Asks = asks.Values.ToArray() // }; //} /// <summary> /// Добавить новую строчку из лога заявок к стакану. /// </summary> /// <param name="item">Строчка лога заявок.</param> /// <returns>Был ли изменен стакан.</returns> public bool Update(ExecutionMessage item) { if (item == null) throw new ArgumentNullException("item"); if (item.ExecutionType != ExecutionTypes.OrderLog) throw new ArgumentException("item"); // mika // debug code for check build algo //var orderId = item.OrderId.Value; //var orderVol = item.Volume.Value; //if (item.IsOrderLogRegistered()) //{ // var vol = _pendingMatch.TryGetValue2(orderId); // if (vol != null) // vol -= orderVol; // else // vol = orderVol; // if (vol > 0) // _activeOrders.Add(orderId, Tuple.Create(item.Side, item.Price, vol.Value)); //} //else if (item.IsOrderLogMatched()) //{ // var t = _activeOrders.TryGetValue(orderId); // if (t != null) // { // var vol = t.Item3; // vol -= orderVol; // if (vol < 0) // throw new Exception(); // if (vol == 0) // _activeOrders.Remove(orderId); // else // _activeOrders[orderId] = Tuple.Create(item.Side, item.Price, vol); // } // else // { // var vol = _pendingMatch.TryGetValue2(orderId); // if (vol == null) // vol = orderVol; // else // vol += orderVol; // _pendingMatch[orderId] = vol.Value; // } //} //else if (item.IsOrderLogCanceled()) // _activeOrders.Remove(orderId); var volume = item.GetVolume(); var changed = false; try { // Очистить стакан в вечерний клиринг if (item.ServerTime.TimeOfDay >= _clearingBeginTime) { // Garic - переделал // Очищаем только в рабочие дни поскольку в субботу/воскресенье допустима отмена заявок if (_lastUpdateTime != null && _lastUpdateTime.Value.TimeOfDay < _clearingBeginTime && _exchange.WorkingTime.IsTradeDate(item.ServerTime.LocalDateTime, true)) { _depth.ServerTime = item.ServerTime; _depth.Bids = Enumerable.Empty<QuoteChange>(); _depth.Asks = Enumerable.Empty<QuoteChange>(); _matchingOrder = null; changed = true; } } _lastUpdateTime = item.ServerTime.LocalDateTime; if (item.IsSystem == false || item.TradePrice != null || item.Price == 0 /* нулевая цена может появится при поставке опционов */) return changed; if (item.IsOrderLogRegistered()) { changed = TryApplyTrades(null); if ( (item.Side == Sides.Buy && (_depth.Asks.IsEmpty() || item.Price < _depth.Asks.First().Price)) || (item.Side == Sides.Sell && (_depth.Bids.IsEmpty() || item.Price > _depth.Bids.First().Price)) ) { if (item.TimeInForce == TimeInForce.PutInQueue) { var quotes = (item.Side == Sides.Buy ? _bids : _asks); var quote = quotes.TryGetValue(item.Price); if (quote == null) { quote = new QuoteChange { Side = item.Side, Price = item.Price, Volume = volume, }; quotes.Add(item.Price, quote); if (item.Side == Sides.Buy) _depth.Bids = GetArray(quotes); else _depth.Asks = GetArray(quotes); } else quote.Volume += volume; changed = true; } } else { _matchingOrder = (ExecutionMessage)item.Clone(); // mika // из-за того, что могут быть кросс-сделки, матчинг только по заявкам невозможен // (сначала идет регистрация вглубь стакана, затем отмена по причине кросс-сделки) // http://forum.rts.ru/viewtopic.asp?t=24197 // } } else if (item.IsOrderLogCanceled()) { var isSame = _matchingOrder != null && _matchingOrder.OrderId == item.OrderId; changed = TryApplyTrades(item); if (!isSame && item.TimeInForce == TimeInForce.PutInQueue) { // http://forum.rts.ru/viewtopic.asp?t=24197 if (item.GetOrderLogCancelReason() != OrderLogCancelReasons.CrossTrade) { var quotes = (item.Side == Sides.Buy ? _bids : _asks); var quote = quotes.TryGetValue(item.Price); if (quote != null) { quote.Volume -= volume; if (quote.Volume <= 0) { quotes.Remove(item.Price); if (item.Side == Sides.Buy) _depth.Bids = GetArray(quotes); else _depth.Asks = GetArray(quotes); } } } changed = true; } } else { throw new ArgumentException(LocalizedStrings.Str943Params.Put(item), "item"); // для одной сделки соответствуют две строчки в ОЛ //_trades[item.Trade.Id] = item; } } finally { if (changed) _depth.ServerTime = item.ServerTime; } return changed; }
bool IOrderLogMarketDepthBuilder.Update(ExecutionMessage item) { if (item == null) { throw new ArgumentNullException(nameof(item)); } if (item.ExecutionType != ExecutionTypes.OrderLog) { throw new ArgumentException(nameof(item)); } if (item.OrderPrice == 0) { return(false); } var changed = false; var quotes = item.Side == Sides.Buy ? _bids : _asks; try { if (item.IsOrderLogRegistered()) { if (item.OrderId != null) { var quote = quotes.SafeAdd(item.OrderPrice, key => new QuoteChange(item.Side, key, 0)); var id = item.OrderId.Value; if (item.OrderVolume != null) { var volume = item.OrderVolume.Value; if (_orders.TryGetValue(id, out var prevVolume)) { quote.Volume += (volume - prevVolume); _orders[id] = volume; } else { quote.Volume += volume; _orders.Add(id, volume); } changed = true; } } } else if (item.IsOrderLogMatched()) { if (item.OrderId != null) { var id = item.OrderId.Value; var volume = item.TradeVolume ?? item.OrderVolume; if (volume != null) { if (_orders.TryGetValue(id, out var prevVolume)) { var quote = quotes.TryGetValue(item.OrderPrice); if (quote != null) { quote.Volume -= volume.Value; if (quote.Volume <= 0) { quotes.Remove(item.OrderPrice); } } _orders[id] = prevVolume - volume.Value; changed = true; } } } } else if (item.IsOrderLogCanceled()) { if (item.OrderId != null) { var id = item.OrderId.Value; if (_orders.TryGetValue(id, out var prevVolume)) { var quote = quotes.TryGetValue(item.OrderPrice); if (quote != null) { quote.Volume -= prevVolume; if (quote.Volume <= 0) { quotes.Remove(item.OrderPrice); } } _orders.Remove(id); changed = true; } } } } finally { if (changed) { _depth.ServerTime = item.ServerTime; _depth.LocalTime = item.LocalTime; _depth.Bids = _bids.Values.ToArray(); _depth.Asks = _asks.Values.ToArray(); } } return(changed); }
QuoteChangeMessage IOrderLogMarketDepthBuilder.Update(ExecutionMessage item) { if (item == null) { throw new ArgumentNullException(nameof(item)); } if (item.ExecutionType != ExecutionTypes.OrderLog) { throw new ArgumentException(nameof(item)); } if (item.OrderPrice == 0) { return(null); } _depth.ServerTime = item.ServerTime; _depth.LocalTime = item.LocalTime; QuoteChange?changedQuote = null; var quotes = item.Side == Sides.Buy ? _bids : _asks; if (item.IsOrderLogRegistered()) { if (item.OrderVolume != null) { QuoteChange ProcessRegister <T>(T id, Dictionary <T, decimal> orders) { var quote = quotes.SafeAdd(item.OrderPrice, key => new QuoteChange(key, 0)); var volume = item.OrderVolume.Value; if (orders.TryGetValue(id, out var prevVolume)) { quote.Volume += (volume - prevVolume); orders[id] = volume; } else { quote.Volume += volume; orders.Add(id, volume); } quotes[item.OrderPrice] = quote; return(quote); } if (item.OrderId != null) { changedQuote = ProcessRegister(item.OrderId.Value, _ordersByNum); } else if (!item.OrderStringId.IsEmpty()) { changedQuote = ProcessRegister(item.OrderStringId, _ordersByString); } } } else if (item.IsOrderLogMatched()) { var volume = item.TradeVolume ?? item.OrderVolume; if (volume != null) { QuoteChange?ProcessMatched <T>(T id, Dictionary <T, decimal> orders) { if (orders.TryGetValue(id, out var prevVolume)) { orders[id] = prevVolume - volume.Value; if (quotes.TryGetValue(item.OrderPrice, out var quote)) { quote.Volume -= volume.Value; if (quote.Volume <= 0) { quotes.Remove(item.OrderPrice); } quotes[item.OrderPrice] = quote; return(quote); } } return(null); } if (item.OrderId != null) { changedQuote = ProcessMatched(item.OrderId.Value, _ordersByNum); } else if (!item.OrderStringId.IsEmpty()) { changedQuote = ProcessMatched(item.OrderStringId, _ordersByString); } } } else if (item.IsOrderLogCanceled()) { QuoteChange?ProcessCanceled <T>(T id, Dictionary <T, decimal> orders) { if (orders.TryGetAndRemove(id, out var prevVolume)) { if (quotes.TryGetValue(item.OrderPrice, out var quote)) { quote.Volume -= prevVolume; if (quote.Volume <= 0) { quotes.Remove(item.OrderPrice); } quotes[item.OrderPrice] = quote; return(quote); } } return(null); } if (item.OrderId != null) { changedQuote = ProcessCanceled(item.OrderId.Value, _ordersByNum); } else if (!item.OrderStringId.IsEmpty()) { changedQuote = ProcessCanceled(item.OrderStringId, _ordersByString); } } if (changedQuote == null) { return(null); } var increment = new QuoteChangeMessage { ServerTime = item.ServerTime, LocalTime = item.LocalTime, SecurityId = _depth.SecurityId, State = QuoteChangeStates.Increment, }; var q = changedQuote.Value; if (item.Side == Sides.Buy) { increment.Bids = new[] { q } } ; else { increment.Asks = new[] { q } }; return(increment); } }