예제 #1
0
        private async ValueTask <SOrderBooks> createOrderbook(SOrderBooks cob)
        {
            cob.result.asks.ForEach(a => a.action = "insert");
            cob.result.bids.ForEach(a => a.action = "insert");

            return(await Task.FromResult(cob));
        }
예제 #2
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="cob"></param>
        /// <returns></returns>
        private async ValueTask <bool> mergeOrderbooks(SOrderBooks cob)
        {
            var _result = false;

            SOrderBooks _qob;

            {
                if (__qOrderBooks.TryGetValue(cob.symbol, out _qob) == true)
                {
                    _qob.stream = cob.stream;

                    var _settings = GetSettings(cob.symbol);
                    if (cob.action == "polling")
                    {
                        _result = await cleanOrderbooks(_qob, cob, _settings);
                    }
                    else
                    {
                        _result = await updateOrderbooks(_qob, cob, _settings);
                    }
                }
                else
                {
                    if (cob.action == "snapshot")
                    {
                        _result = await insertOrderbooks(cob);
                    }
                }
            }

            return(_result);
        }
예제 #3
0
        private async ValueTask publishOrderbook(SOrderBooks sob)
        {
            await Task.Delay(0);

            var _json_data = JsonConvert.SerializeObject(sob);

            __orderbook.Write(this, GMConfig.DealerName, _json_data);
        }
예제 #4
0
        private async ValueTask publishOrderbook(SOrderBooks sob)
        {
            await Task.Delay(0);

            if (sob.result.asks.Count + sob.result.bids.Count > 0)
            {
                var _json_data = JsonConvert.SerializeObject(sob);
                __orderbook.Write(this, BNConfig.DealerName, _json_data);
            }
        }
예제 #5
0
        private async ValueTask <bool> modifyOrderbook(SOrderBooks qob, SOrderBooks cob, Settings settings)
        {
            var _result = false;

            if (settings.last_orderbook_time < cob.result.timestamp)
            {
                settings.last_orderbook_time = cob.result.timestamp;

                var _current_ask_size = cob.result.askSumQty;
                var _current_bid_size = cob.result.bidSumQty;

                if (settings.last_order_ask_size != _current_ask_size || settings.last_order_bid_size != _current_bid_size)
                {
                    if (settings.before_trade_ask_size != _current_ask_size || settings.before_trade_bid_size != _current_bid_size)
                    {
                        if (settings.trades_flag == true)
                        {
                            var _t_ask = qob.result.asks.OrderByDescending(o => o.price).LastOrDefault();
                            var _t_bid = qob.result.bids.OrderByDescending(o => o.price).FirstOrDefault();

                            if (_t_ask != null && _t_bid != null)
                            {
                                cob.result.asks.RemoveAll(o => o.price < _t_ask.price);
                                cob.result.bids.RemoveAll(o => o.price > _t_bid.price);

                                var _c_ask = cob.result.asks.Where(o => o.price == _t_ask.price).SingleOrDefault();
                                var _c_bid = cob.result.bids.Where(o => o.price == _t_bid.price).SingleOrDefault();

                                if (_c_ask != null && _c_bid != null)
                                {
                                    if (_t_ask.quantity != _c_ask.quantity || _t_bid.quantity != _c_bid.quantity)
                                    {
                                        _t_ask.quantity = _c_ask.quantity;
                                        _t_bid.quantity = _c_bid.quantity;
                                    }
                                }
                            }

                            settings.trades_flag = false;
                        }

                        _result = await updateOrderbook(qob, cob, settings);

                        settings.last_order_ask_size = qob.result.asks.Sum(o => o.quantity);
                        settings.last_order_bid_size = qob.result.bids.Sum(o => o.quantity);
                    }
                }
            }

            return(_result);
        }
예제 #6
0
        private async ValueTask <bool> cleanOrderbooks(SOrderBooks qob, SOrderBooks cob, Settings settings)
        {
            qob.result.asks.Clear();
            qob.result.bids.Clear();

            cob.result.asks.ForEach(a => a.action = "insert");
            cob.result.bids.ForEach(a => a.action = "insert");

            qob.result.asks.AddRange(cob.result.asks);
            qob.result.bids.AddRange(cob.result.bids);

            settings.orderbook_count = 0;
            await snapshotOrderbook(qob);

            return(true);
        }
예제 #7
0
        private void cleanOrderbook(SOrderBooks qob, SOrderBooks nob, decimal price)
        {
            var _asks = qob.result.asks.Where(o => o.price < price);
            {
                foreach (var _ask in _asks)
                {
                    nob.result.asks.Add(new SOrderBookItem
                    {
                        action   = "delete",
                        id       = _ask.id,
                        price    = _ask.price,
                        quantity = _ask.quantity,
                        amount   = _ask.price * _ask.quantity,
                        count    = 1
                    });

                    nob.result.askSumQty -= _ask.quantity;

                    _ask.quantity = 0;
                    _ask.amount   = 0;
                }
            }

            var _bids = qob.result.bids.Where(o => o.price > price);
            {
                foreach (var _bid in _bids)
                {
                    nob.result.bids.Add(new SOrderBookItem
                    {
                        action   = "delete",
                        id       = _bid.id,
                        price    = _bid.price,
                        quantity = _bid.quantity,
                        amount   = _bid.price * _bid.quantity,
                        count    = 1
                    });

                    nob.result.bidSumQty -= _bid.quantity;

                    _bid.quantity = 0;
                    _bid.amount   = 0;
                }
            }
        }
예제 #8
0
        private async ValueTask snapshotOrderbook(string exchange, string symbol)
        {
            if (exchange == BNLogger.SNG.exchange_name)
            {
                SOrderBooks _sob = null;

                lock (__qOrderBooks)
                {
                    if (__qOrderBooks.ContainsKey(symbol) == true)
                    {
                        _sob        = __qOrderBooks[symbol];
                        _sob.stream = "snapshot";
                    }
                }

                if (_sob != null)
                {
                    await publishOrderbook(_sob);
                }
            }
        }
예제 #9
0
        private async ValueTask <bool> insertOrderbook(SOrderBooks cob)
        {
            var _result = false;

            var _sqo = await createOrderbook(cob);

            if (__qOrderBooks.TryAdd(cob.symbol, _sqo) == true)
            {
                var _settings = GetSettings(cob.symbol);
                {
                    _settings.last_orderbook_time = cob.result.timestamp;
                    _settings.orderbook_count     = 0;
                }

                await snapshotOrderbook(cob.symbol);

                _result = true;
            }

            return(_result);
        }
예제 #10
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="cob"></param>
        /// <returns></returns>
        private async ValueTask <bool> mergeOrderbook(SOrderBooks cob)
        {
            var _result = false;

            SOrderBooks _qob;

            {
                if (__qOrderBooks.TryGetValue(cob.symbol, out _qob) == true)
                {
                    _qob.stream = cob.stream;

                    var _settings = GetSettings(cob.symbol);
                    _result = await modifyOrderbook(_qob, cob, _settings);
                }
                else
                {
                    _result = await insertOrderbook(cob);
                }
            }

            return(_result);
        }
예제 #11
0
        private async ValueTask <bool> insertOrderbooks(SOrderBooks cob)
        {
            var _result = false;

            cob.result.asks.ForEach(a => a.action = "insert");
            cob.result.bids.ForEach(a => a.action = "insert");

            if (__qOrderBooks.TryAdd(cob.symbol, cob) == true)
            {
                var _settings = GetSettings(cob.symbol);
                {
                    _settings.last_orderbook_time = cob.result.timestamp;
                    _settings.orderbook_count     = 0;
                }

                await snapshotOrderbook(cob);

                _result = true;
            }

            return(_result);
        }
예제 #12
0
        /// <summary>
        /// Fetch pending or registered order details
        /// </summary>
        /// <param name="base_name">The type of trading base-currency of which information you want to query for.</param>
        /// <param name="quote_name">The type of trading quote-currency of which information you want to query for.</param>
        /// <returns></returns>
        public async ValueTask <SOrderBooks> GetOrderBooks(string base_name, string quote_name)
        {
            var _result = new SOrderBooks();

            var _params = new Dictionary <string, object>();
            {
                _params.Add("market", $"{quote_name}-{base_name}");
            }

            var _response = await publicClient.CallApiGet2Async("/orderbook", _params);

            if (_response != null)
            {
#if RAWJSON
                _result.rawJson = _response.Content;
#endif
                if (_response.IsSuccessful == true)
                {
                    var _orderbooks = publicClient.DeserializeObject <List <UOrderBook> >(_response.Content);
                    {
                        var _orderbook = _orderbooks.FirstOrDefault();
                        if (_orderbook != null)
                        {
                            _result.result = _orderbook;
                            _result.SetSuccess();
                        }
                    }
                }
                else
                {
                    var _message = publicClient.GetResponseMessage(_response);
                    _result.SetFailure(_message.message);
                }
            }

            return(_result);
        }
예제 #13
0
        public async ValueTask Start(CancellationTokenSource cancelTokenSource)
        {
            DRLogger.SNG.WriteO(this, $"processing service start...");

            var _processing = Task.Run(async() =>
            {
                var _last_polling_trade = 0L;
                var _orderbook_size     = 25;

                while (true)
                {
                    try
                    {
                        await Task.Delay(0);

                        var _message = (QMessage)null;
                        if (ReceiveQ.TryDequeue(out _message) == false)
                        {
                            var _cancelled = cancelTokenSource.Token.WaitHandle.WaitOne(0);
                            if (_cancelled == true)
                            {
                                break;
                            }

                            await Task.Delay(10);
                            continue;
                        }

                        if (_message.command == "WS")
                        {
                            if (_message.stream == "trade")
                            {
                                var _w_trades = JsonConvert.DeserializeObject <List <DCompleteOrderItem> >(_message.payload ?? "");

                                var _s_trades = new SCompleteOrders
                                {
                                    exchange = _message.exchange,   // deribit
                                    stream   = _message.stream,     // trade
                                    symbol   = _message.symbol,     // BTC-PERPETUAL
                                    action   = _message.action,     // pushing

                                    sequentialId = _w_trades.Max(t => t.timestamp),

                                    result = _w_trades.Select(t =>
                                    {
                                        return(new SCompleteOrderItem
                                        {
                                            timestamp = t.timestamp,
                                            sideType = t.sideType,
                                            price = t.price,
                                            quantity = t.quantity
                                        });
                                    })
                                             .ToList()
                                };

                                if (_s_trades.result.Count() > 0)
                                {
                                    _last_polling_trade = _s_trades.sequentialId;
                                    await mergeTrades(_s_trades);
                                }
                            }
                            else if (_message.stream == "orderbook")
                            {
                                var _w_orderbooks = JsonConvert.DeserializeObject <DWsOrderBook>(_message.payload ?? "");
                                if (_w_orderbooks.asks.Count() > 0 || _w_orderbooks.bids.Count() > 0)
                                {
                                    var _timestamp = _w_orderbooks.timestamp;

                                    var _s_orderbooks = new SOrderBooks
                                    {
                                        exchange = _message.exchange,       // deribit
                                        symbol   = _message.symbol,         // BTC-PERPETUAL
                                        stream   = _message.stream,         // orderbook
                                        action   = _w_orderbooks.type,      // snapshot, change

                                        sequentialId = _timestamp,

                                        result = new SOrderBook
                                        {
                                            timestamp = _timestamp,

                                            askSumQty = 0,
                                            bidSumQty = 0,

                                            asks = new List <SOrderBookItem>(),
                                            bids = new List <SOrderBookItem>()
                                        }
                                    };

                                    foreach (var _a in _w_orderbooks.asks.OrderBy(a => a[1]).Take(_orderbook_size))
                                    {
                                        _s_orderbooks.result.asks.Add(new SOrderBookItem
                                        {
                                            action   = _a[0].ToString(),
                                            quantity = Convert.ToDecimal(_a[2]),
                                            price    = Convert.ToDecimal(_a[1]),
                                            amount   = 0,
                                            id       = 0,
                                            count    = 1
                                        });
                                    }

                                    foreach (var _b in _w_orderbooks.bids.OrderByDescending(b => b[1]).Take(_orderbook_size))
                                    {
                                        _s_orderbooks.result.bids.Add(new SOrderBookItem
                                        {
                                            action   = _b[0].ToString(),
                                            quantity = Convert.ToDecimal(_b[2]),
                                            price    = Convert.ToDecimal(_b[1]),
                                            amount   = 0,
                                            id       = 0,
                                            count    = 1
                                        });
                                    }

                                    await mergeOrderbooks(_s_orderbooks);
                                }
                            }
                        }
                        else if (_message.command == "AP")
                        {
                            if (_message.stream == "trade")
                            {
                                var _a_trades = JsonConvert.DeserializeObject <DRResults <DCompleteOrders> >(_message.payload ?? "");
                                if (_a_trades.result.trades.Count > 0)
                                {
                                    var _s_trades = new SCompleteOrders
                                    {
                                        exchange = _message.exchange,   // deribit
                                        symbol   = _message.symbol,     // BTC-PERPETUAL
                                        stream   = _message.stream,     // trade
                                        action   = _message.action,     // polling

                                        sequentialId = _a_trades.result.trades.Max(t => t.timestamp),

                                        result = _a_trades.result.trades.Where(t => t.timestamp > _last_polling_trade).Select(t =>
                                        {
                                            return(new SCompleteOrderItem
                                            {
                                                timestamp = t.timestamp,
                                                sideType = t.sideType,
                                                price = t.price,
                                                quantity = t.quantity
                                            });
                                        })
                                                 .ToList()
                                    };

                                    if (_s_trades.result.Count() > 0)
                                    {
                                        _last_polling_trade = _s_trades.sequentialId;
                                        await mergeTrades(_s_trades);
                                    }
                                }
                            }
                            else if (_message.stream == "orderbook")
                            {
                                var _a_orderbooks = JsonConvert.DeserializeObject <DRResults <DOrderBook> >(_message.payload ?? "");
                                if (_a_orderbooks.result.asks.Count > 0 || _a_orderbooks.result.bids.Count > 0)
                                {
                                    var _timestamp = _a_orderbooks.result.timestamp;

                                    var _s_orderbooks = new SOrderBooks
                                    {
                                        exchange = _message.exchange,   // deribit
                                        symbol   = _message.symbol,     // BTC-PERPETUAL
                                        stream   = _message.stream,     // orderbook
                                        action   = _message.action,     // polling

                                        sequentialId = _timestamp,

                                        result = new SOrderBook
                                        {
                                            timestamp = _timestamp,

                                            askSumQty = 0,
                                            bidSumQty = 0,

                                            asks = _a_orderbooks.result.asks.Select(o =>
                                            {
                                                return(new SOrderBookItem
                                                {
                                                    quantity = o.quantity,
                                                    price = o.price,
                                                    amount = 0,
                                                    id = 0,
                                                    count = 1
                                                });
                                            })
                                                   .ToList(),

                                            bids = _a_orderbooks.result.bids.Select(o =>
                                            {
                                                return(new SOrderBookItem
                                                {
                                                    quantity = o.quantity,
                                                    price = o.price,
                                                    amount = 0,
                                                    id = 0,
                                                    count = 1
                                                });
                                            })
                                                   .ToList()
                                        }
                                    };

                                    await mergeOrderbooks(_s_orderbooks);
                                }
                            }
                        }
                        else if (_message.command == "SS")
                        {
                            await snapshotOrderbook(_message.exchange);
                        }
#if DEBUG
                        else
                        {
                            DRLogger.SNG.WriteO(this, _message.payload);
                        }
#endif
                        if (cancelTokenSource.Token.IsCancellationRequested == true)
                        {
                            break;
                        }
                    }
                    catch (TaskCanceledException)
                    {
                    }
                    catch (Exception ex)
                    {
                        DRLogger.SNG.WriteX(this, ex.ToString());
                    }
                }
            },
                                       cancelTokenSource.Token
                                       );

            await Task.WhenAll(_processing);

            DRLogger.SNG.WriteO(this, $"processing service stop...");
        }
예제 #14
0
        private async ValueTask <bool> updateOrderbook(SOrderBooks qob, BAOrderBook orderbook, Settings settings)
        {
            var _dqo = new SOrderBooks
            {
                exchange     = BNLogger.SNG.exchange_name,
                stream       = "diffbooks",
                symbol       = orderbook.data.symbol,
                sequentialId = orderbook.data.lastId,
                result       = new SOrderBook()
            };

            lock (__qOrderBooks)
            {
                foreach (var _oi in orderbook.data.asks)
                {
                    var _ask = qob.result.asks.Where(o => o.price == _oi[0]).SingleOrDefault();
                    if (_ask == null)
                    {
                        var _aoi = new SOrderBookItem
                        {
                            action   = "insert",
                            price    = _oi[0],
                            quantity = _oi[1],
                            amount   = _oi[0] * _oi[1],
                            count    = 1
                        };

                        _dqo.result.asks.Add(_aoi);
                        qob.result.asks.Add(_aoi);
                    }
                    else if (_ask.quantity != _oi[1])
                    {
                        var _aoi = new SOrderBookItem
                        {
                            action   = "update",
                            price    = _oi[0],
                            quantity = _oi[1],
                            amount   = _oi[0] * _oi[1],
                            count    = 1
                        };

                        _dqo.result.asks.Add(_aoi);
                        _ask.quantity = _oi[1];
                    }
                }

                foreach (var _oi in orderbook.data.bids)
                {
                    var _bid = qob.result.bids.Where(o => o.price == _oi[0]).SingleOrDefault();
                    if (_bid == null)
                    {
                        var _boi = new SOrderBookItem
                        {
                            action   = "insert",
                            price    = _oi[0],
                            quantity = _oi[1],
                            amount   = _oi[0] * _oi[1],
                            count    = 1
                        };

                        _dqo.result.bids.Add(_boi);
                        qob.result.bids.Add(_boi);
                    }
                    else if (_bid.quantity != _oi[1])
                    {
                        var _boi = new SOrderBookItem
                        {
                            action   = "update",
                            price    = _oi[0],
                            quantity = _oi[1],
                            amount   = _oi[0] * _oi[1],
                            count    = 1
                        };

                        _dqo.result.bids.Add(_boi);
                        _bid.quantity = _oi[1];
                    }
                }

                foreach (var _qi in qob.result.asks)
                {
                    var _ask = orderbook.data.asks.Where(o => o[0] == _qi.price).SingleOrDefault();
                    if (_ask == null)
                    {
                        _dqo.result.asks.Add(new SOrderBookItem
                        {
                            action   = "delete",
                            price    = _qi.price,
                            quantity = _qi.quantity,
                            amount   = _qi.price * _qi.quantity,
                            count    = 1
                        });

                        _qi.quantity = 0;
                    }
                }

                foreach (var _qi in qob.result.bids)
                {
                    var _bid = orderbook.data.bids.Where(o => o[0] == _qi.price).SingleOrDefault();
                    if (_bid == null)
                    {
                        _dqo.result.bids.Add(new SOrderBookItem
                        {
                            action   = "delete",
                            price    = _qi.price,
                            quantity = _qi.quantity,
                            amount   = _qi.price * _qi.quantity,
                            count    = 1
                        });

                        _qi.quantity = 0;
                    }
                }

                qob.result.asks.RemoveAll(o => o.quantity == 0);
                qob.result.bids.RemoveAll(o => o.quantity == 0);
            }

            if (++settings.orderbook_count == __bnconfig.OrderBookCounter)
            {
                settings.orderbook_count = 0;

                qob.sequentialId = orderbook.data.lastId;
                await snapshotOrderbook(_dqo.exchange, _dqo.symbol);
            }
            else
            {
                await publishOrderbook(_dqo);
            }

            return(true);
        }
예제 #15
0
        private async ValueTask <bool> updateCompleteOrder(SOrderBooks qob, SCompleteOrders cco, Settings settings)
        {
            var _nob = new SOrderBooks
            {
                exchange     = cco.exchange,
                stream       = "difftrade",
                symbol       = cco.symbol,
                action       = cco.action,
                sequentialId = cco.sequentialId,

                result = new SOrderBook
                {
                    timestamp = cco.result.Max(o => o.timestamp),
                    askSumQty = 0,
                    bidSumQty = 0
                }
            };

            lock (__qOrderBooks)
            {
                settings.before_trade_ask_size = qob.result.asks.Sum(o => o.quantity);
                settings.before_trade_bid_size = qob.result.bids.Sum(o => o.quantity);

                foreach (var _t in cco.result.OrderBy(t => t.timestamp))
                {
                    if (settings.last_trade_id >= _t.timestamp)
                    {
                        continue;
                    }

                    settings.last_trade_id = _t.timestamp;

                    var _ask = qob.result.asks.Where(o => o.price == _t.price).SingleOrDefault();
                    if (_ask != null)
                    {
                        if (_ask.quantity <= _t.quantity)
                        {
                            _nob.result.asks.Add(new SOrderBookItem
                            {
                                action   = "delete",
                                id       = _ask.id,
                                price    = _ask.price,
                                quantity = _ask.quantity,
                                amount   = _ask.price * _ask.quantity,
                                count    = 1
                            });

                            _nob.result.askSumQty -= _ask.quantity;

                            _ask.quantity = 0;
                            _ask.amount   = 0;
                        }
                        else
                        {
                            _ask.quantity -= _t.quantity;
                            _ask.amount    = _ask.price * _ask.quantity;

                            _nob.result.asks.Add(new SOrderBookItem
                            {
                                action   = "update",
                                id       = _ask.id,
                                price    = _ask.price,
                                quantity = _ask.quantity,
                                amount   = _ask.price * _ask.quantity,
                                count    = 1
                            });

                            _nob.result.askSumQty += _ask.quantity;
                        }
                    }

                    var _bid = qob.result.bids.Where(o => o.price == _t.price).SingleOrDefault();
                    if (_bid != null)
                    {
                        if (_bid.quantity <= _t.quantity)
                        {
                            _nob.result.bids.Add(new SOrderBookItem
                            {
                                action   = "delete",
                                id       = _bid.id,
                                price    = _bid.price,
                                quantity = _bid.quantity,
                                amount   = _bid.price * _bid.quantity,
                                count    = 1
                            });

                            _nob.result.bidSumQty -= _bid.quantity;

                            _bid.quantity = 0;
                            _bid.amount   = 0;
                        }
                        else
                        {
                            _bid.quantity -= _t.quantity;
                            _bid.amount    = _bid.price * _bid.quantity;

                            _nob.result.bids.Add(new SOrderBookItem
                            {
                                action   = "update",
                                id       = _bid.id,
                                price    = _bid.price,
                                quantity = _bid.quantity,
                                amount   = _bid.price * _bid.quantity,
                                count    = 1
                            });

                            _nob.result.bidSumQty += _bid.quantity;
                        }
                    }

                    //cleanOrderbook(qob, _nob, _t.price);
                }

                qob.sequentialId = _nob.sequentialId;

                qob.result.asks.RemoveAll(o => o.quantity == 0);
                qob.result.bids.RemoveAll(o => o.quantity == 0);
            }

            _nob.result.asks.RemoveAll(o => o.quantity == 0);
            _nob.result.bids.RemoveAll(o => o.quantity == 0);

            if (_nob.result.asks.Count + _nob.result.bids.Count > 0)
            {
                await publishOrderbook(_nob);

                settings.orderbook_count = 0;
                settings.trades_flag     = true;
            }

            return(true);
        }
예제 #16
0
        private async ValueTask <bool> updateOrderbook(SOrderBooks qob, SOrderBooks cob, Settings settings)
        {
            var _nob = new SOrderBooks
            {
                exchange     = cob.exchange,
                stream       = "diffbooks",
                symbol       = cob.symbol,
                action       = cob.action,
                sequentialId = cob.sequentialId,

                result = new SOrderBook
                {
                    timestamp = cob.result.timestamp,
                    askSumQty = 0,
                    bidSumQty = 0
                }
            };

            lock (__qOrderBooks)
            {
                foreach (var _ci in cob.result.asks)
                {
                    var _qi = qob.result.asks.Where(o => o.price == _ci.price).SingleOrDefault();
                    if (_qi == null)
                    {
                        var _ici = new SOrderBookItem
                        {
                            action   = "insert",
                            id       = _ci.id,
                            price    = _ci.price,
                            quantity = _ci.quantity,
                            amount   = _ci.price * _ci.quantity,
                            count    = 1
                        };

                        _nob.result.asks.Add(_ici);
                        qob.result.asks.Add(_ici);

                        _nob.result.askSumQty += _ci.quantity;
                    }
                    else if (_qi.quantity != _ci.quantity)
                    {
                        _nob.result.asks.Add(new SOrderBookItem
                        {
                            action   = "update",
                            id       = _qi.id,
                            price    = _qi.price,
                            quantity = _ci.quantity,
                            amount   = _qi.price * _ci.quantity,
                            count    = 1
                        });

                        _qi.quantity = _ci.quantity;
                        _qi.amount   = _qi.price * _qi.quantity;

                        _nob.result.askSumQty += _ci.quantity;
                    }
                }

                foreach (var _ci in cob.result.bids)
                {
                    var _qi = qob.result.bids.Where(o => o.price == _ci.price).SingleOrDefault();
                    if (_qi == null)
                    {
                        var _ici = new SOrderBookItem
                        {
                            action   = "insert",
                            id       = _ci.id,
                            price    = _ci.price,
                            quantity = _ci.quantity,
                            amount   = _ci.price * _ci.quantity,
                            count    = 1
                        };

                        _nob.result.bids.Add(_ici);
                        qob.result.bids.Add(_ici);

                        _nob.result.bidSumQty += _ci.quantity;
                    }
                    else if (_qi.quantity != _ci.quantity)
                    {
                        _nob.result.bids.Add(new SOrderBookItem
                        {
                            action   = "update",
                            id       = _qi.id,
                            price    = _qi.price,
                            quantity = _ci.quantity,
                            amount   = _qi.price * _ci.quantity,
                            count    = 1
                        });

                        _qi.quantity = _ci.quantity;
                        _qi.amount   = _qi.quantity * _qi.price;

                        _nob.result.bidSumQty += _ci.quantity;
                    }
                }

                foreach (var _qi in qob.result.asks)
                {
                    var _ci = cob.result.asks.Where(o => o.price == _qi.price).SingleOrDefault();
                    if (_ci == null)
                    {
                        _nob.result.asks.Add(new SOrderBookItem
                        {
                            action   = "delete",
                            id       = _qi.id,
                            price    = _qi.price,
                            quantity = _qi.quantity,
                            amount   = _qi.price * _qi.quantity,
                            count    = 1
                        });

                        _nob.result.askSumQty -= _qi.quantity;

                        _qi.quantity = 0;
                        _qi.amount   = 0;
                    }
                }

                foreach (var _qi in qob.result.bids)
                {
                    var _ci = cob.result.bids.Where(o => o.price == _qi.price).SingleOrDefault();
                    if (_ci == null)
                    {
                        _nob.result.bids.Add(new SOrderBookItem
                        {
                            action   = "delete",
                            id       = _qi.id,
                            price    = _qi.price,
                            quantity = _qi.quantity,
                            amount   = _qi.price * _qi.quantity,
                            count    = 1
                        });

                        _nob.result.bidSumQty -= _qi.quantity;

                        _qi.quantity = 0;
                        _qi.amount   = 0;
                    }
                }

                qob.result.asks.RemoveAll(o => o.quantity == 0);
                qob.result.bids.RemoveAll(o => o.quantity == 0);
            }

            if (++settings.orderbook_count == __gmconfig.SnapshotSkipCounter)
            {
                qob.sequentialId = cob.sequentialId;
                await snapshotOrderbook(_nob.symbol);
            }
            else
            {
                _nob.result.asks.RemoveAll(o => o.quantity == 0);
                _nob.result.bids.RemoveAll(o => o.quantity == 0);

                if (_nob.result.asks.Count > 0 || _nob.result.bids.Count > 0)
                {
                    await publishOrderbook(_nob);
                }
            }

            return(true);
        }
예제 #17
0
        public async ValueTask Start(CancellationToken cancelToken)
        {
            BTLogger.SNG.WriteO(this, $"processing service start...");

            var _processing = Task.Run(async() =>
            {
                var _last_polling_trade = 0L;

                while (true)
                {
                    try
                    {
                        await Task.Delay(0);

                        var _message = (QMessage)null;
                        if (ReceiveQ.TryDequeue(out _message) == false)
                        {
                            var _cancelled = cancelToken.WaitHandle.WaitOne(0);
                            if (_cancelled == true)
                            {
                                break;
                            }

                            await Task.Delay(10);
                            continue;
                        }

                        if (_message.command == "WS")
                        {
                            if (_message.stream == "trade")
                            {
                                var _w_trade = JsonConvert.DeserializeObject <UWCompleteOrderItem>(_message.payload ?? "");

                                var _s_trade = new SCompleteOrders
                                {
                                    exchange     = _message.exchange,
                                    symbol       = _message.symbol,
                                    stream       = _message.stream,
                                    action       = _message.action,
                                    sequentialId = _w_trade.timestamp,

                                    result = new List <SCompleteOrderItem>
                                    {
                                        new SCompleteOrderItem
                                        {
                                            timestamp = _w_trade.timestamp,
                                            sideType  = _w_trade.sideType,
                                            price     = _w_trade.price,
                                            quantity  = _w_trade.quantity
                                        }
                                    }
                                };

                                if (_s_trade.result.Count() > 0)
                                {
                                    _last_polling_trade = _s_trade.sequentialId;
                                    await mergeCompleteOrder(_s_trade);
                                }
                            }
                            else if (_message.stream == "orderbook")
                            {
                                var _w_orderbook = JsonConvert.DeserializeObject <UWOrderBook>(_message.payload ?? "");

                                var _s_orderbooks = new SOrderBooks
                                {
                                    exchange     = _message.exchange,
                                    symbol       = _message.symbol,
                                    stream       = _message.stream,
                                    action       = _message.action,
                                    sequentialId = _w_orderbook.timestamp,

                                    result = new SOrderBook
                                    {
                                        timestamp = _w_orderbook.timestamp,

                                        askSumQty = _w_orderbook.askSumQty,
                                        bidSumQty = _w_orderbook.bidSumQty,

                                        asks = _w_orderbook.asks,
                                        bids = _w_orderbook.bids
                                    }
                                };

                                await mergeOrderbook(_s_orderbooks);
                            }
                        }
                        else if (_message.command == "AP")
                        {
                            if (_message.stream == "trade")
                            {
                                var _a_trades = JsonConvert.DeserializeObject <List <UACompleteOrderItem> >(_message.payload ?? "");

                                var _s_trade = new SCompleteOrders
                                {
                                    exchange     = _message.exchange,
                                    symbol       = _message.symbol,
                                    stream       = _message.stream,
                                    action       = _message.action,
                                    sequentialId = _a_trades.Max(t => t.timestamp),

                                    result = _a_trades.Where(t => t.timestamp > _last_polling_trade).Select(t =>
                                    {
                                        return(new SCompleteOrderItem
                                        {
                                            timestamp = t.timestamp,
                                            sideType = t.sideType,
                                            price = t.price,
                                            quantity = t.quantity
                                        });
                                    })
                                             .ToList()
                                };

                                if (_s_trade.result.Count() > 0)
                                {
                                    _last_polling_trade = _s_trade.sequentialId;
                                    await mergeCompleteOrder(_s_trade);
                                }
                            }
                            else if (_message.stream == "orderbook")
                            {
                                var _a_orderbooks = JsonConvert.DeserializeObject <List <UAOrderBook> >(_message.payload ?? "");

                                var _timestamp = _a_orderbooks.Max(o => o.timestamp);
                                var _asks      = _a_orderbooks[0].asks;
                                var _bids      = _a_orderbooks[0].bids;

                                var _s_orderbooks = new SOrderBooks
                                {
                                    exchange     = _message.exchange,
                                    symbol       = _message.symbol,
                                    stream       = _message.stream,
                                    action       = _message.action,
                                    sequentialId = _a_orderbooks.Max(t => t.timestamp),

                                    result = new SOrderBook
                                    {
                                        timestamp = _timestamp,

                                        askSumQty = _asks.Sum(o => o.quantity),
                                        bidSumQty = _bids.Sum(o => o.quantity),

                                        asks = _asks,
                                        bids = _bids
                                    }
                                };

                                await mergeOrderbook(_s_orderbooks);
                            }
                            else if (_message.stream == "ticker")
                            {
                                var _a_ticker_data = JsonConvert.DeserializeObject <List <UAOrderBook> >(_message.payload ?? "");

                                await publishTicker(new STickers
                                {
                                    exchange     = _message.exchange,
                                    symbol       = _message.symbol,
                                    stream       = _message.stream,
                                    action       = _message.action,
                                    sequentialId = _message.sequentialId,

                                    timestamp    = _a_ticker_data.Max(o => o.timestamp),
                                    totalAskSize = _a_ticker_data.Sum(o => o.askSumQty),
                                    totalBidSize = _a_ticker_data.Sum(o => o.bidSumQty),

                                    result = _a_ticker_data.Select(o =>
                                    {
                                        var _ask = o.asks.OrderBy(a => a.price).First();
                                        var _bid = o.bids.OrderBy(a => a.price).Last();

                                        return(new STickerItem
                                        {
                                            askPrice = _ask.price,
                                            askSize = _ask.quantity,
                                            bidPrice = _bid.price,
                                            bidSize = _bid.quantity
                                        });
                                    })
                                             .ToList()
                                });
                            }
                        }
                        else if (_message.command == "SS")
                        {
                            await snapshotOrderbook(_message.symbol);
                        }
#if DEBUG
                        else
                        {
                            BTLogger.SNG.WriteO(this, _message.payload);
                        }
#endif
                        if (cancelToken.IsCancellationRequested == true)
                        {
                            break;
                        }
                    }
                    catch (TaskCanceledException)
                    {
                    }
                    catch (Exception ex)
                    {
                        BTLogger.SNG.WriteX(this, ex.ToString());
                    }
                }
            },
                                       cancelToken
                                       );

            await Task.WhenAll(_processing);

            BTLogger.SNG.WriteO(this, $"processing service stop...");
        }
예제 #18
0
        private async ValueTask <bool> mergeOrderbook(BAOrderBook orderbook)
        {
            var _result = false;

            var _settings = __qSettings.ContainsKey(orderbook.data.symbol)
                          ? __qSettings[orderbook.data.symbol]
                          : __qSettings[orderbook.data.symbol] = new Settings();

            if (__qOrderBooks.ContainsKey(orderbook.data.symbol) == false)
            {
                _settings.last_orderbook_id = orderbook.data.lastId;

                var _sqo = new SOrderBooks
                {
                    exchange     = BNLogger.SNG.exchange_name,
                    stream       = "snapshot",
                    symbol       = orderbook.data.symbol,
                    sequentialId = orderbook.data.lastId,
                    result       = new SOrderBook()
                };

                foreach (var _oi in orderbook.data.asks)
                {
                    _sqo.result.asks.Add(new SOrderBookItem
                    {
                        action   = "insert",
                        price    = _oi[0],
                        quantity = _oi[1],
                        amount   = _oi[0] * _oi[1],
                        count    = 1
                    });
                }

                foreach (var _oi in orderbook.data.bids)
                {
                    _sqo.result.bids.Add(new SOrderBookItem
                    {
                        action   = "insert",
                        price    = _oi[0],
                        quantity = _oi[1],
                        amount   = _oi[0] * _oi[1],
                        count    = 1
                    });
                }

                __qOrderBooks[_sqo.symbol] = _sqo;

                await publishOrderbook(_sqo);

                _settings.orderbook_count = 0;

                _result = true;
            }
            else if (_settings.last_orderbook_id < orderbook.data.lastId)
            {
                _settings.last_orderbook_id = orderbook.data.lastId;

                var _qob = __qOrderBooks[orderbook.data.symbol];

                var _current_ask_size = orderbook.data.asks.Sum(o => o[1]);
                var _current_bid_size = orderbook.data.bids.Sum(o => o[1]);

                if (_settings.last_order_ask_size != _current_ask_size || _settings.last_order_bid_size != _current_bid_size)
                {
                    if (_settings.before_trade_ask_size != _current_ask_size || _settings.before_trade_bid_size != _current_bid_size)
                    {
                        _result = await updateOrderbook(_qob, orderbook, _settings);

                        _settings.last_order_ask_size = _qob.result.asks.Sum(o => o.quantity);
                        _settings.last_order_bid_size = _qob.result.bids.Sum(o => o.quantity);
#if DEBUG
                        // modified check
                        if (_current_ask_size != _settings.last_order_ask_size || _current_bid_size != _settings.last_order_bid_size)
                        {
                            BNLogger.SNG.WriteQ(this, $"diffb-{orderbook.stream}: timestamp => {_settings.last_orderbook_id}, symbol => {orderbook.data.symbol}, ask_size => {_current_ask_size}, {_settings.last_order_ask_size}, bid_size => {_current_bid_size}, {_settings.last_order_bid_size}");
                        }

                        if (_qob.result.asks.Count + _qob.result.bids.Count != 40)
                        {
                            var _ask_count = _qob.result.asks.Count();
                            var _bid_count = _qob.result.bids.Count();

                            BNLogger.SNG.WriteQ(this, $"diffb-{orderbook.stream}: timestamp => {_settings.last_orderbook_id}, symbol => {orderbook.data.symbol}, ask_count => {_ask_count}, bid_count => {_bid_count}");
                        }
#endif
                    }
                    else
                    {
                        BNLogger.SNG.WriteQ(this, $"trade-{orderbook.stream}: timestamp => {_settings.last_orderbook_id}, symbol => {orderbook.data.symbol}, ask_size => {_current_ask_size}, bid_size => {_current_bid_size}");
                    }
                }
                else
                {
                    BNLogger.SNG.WriteQ(this, $"equal-{orderbook.stream}: timestamp => {_settings.last_orderbook_id}, symbol => {orderbook.data.symbol}, ask_size => {_current_ask_size}, bid_size => {_current_bid_size}");
                }
            }

            return(_result);
        }
예제 #19
0
 private async ValueTask snapshotOrderbook(SOrderBooks qob)
 {
     qob.stream = "snapshot";
     await publishOrderbook(qob);
 }
예제 #20
0
        private async ValueTask <bool> updateTradeItem(SOrderBooks qob, List <BTradeItem> tradeItems, string stream)
        {
            var _rqo = new SOrderBooks
            {
                exchange     = BNLogger.SNG.exchange_name,
                stream       = stream,
                symbol       = qob.symbol,
                sequentialId = tradeItems.Max(t => t.timestamp),
                result       = new SOrderBook()
            };

            var _settings = __qSettings.ContainsKey(qob.symbol)
                          ? __qSettings[qob.symbol]
                          : __qSettings[qob.symbol] = new Settings();

            lock (__qOrderBooks)
            {
                _settings.before_trade_ask_size = qob.result.asks.Sum(o => o.quantity);
                _settings.before_trade_bid_size = qob.result.bids.Sum(o => o.quantity);

                foreach (var _t in tradeItems.OrderBy(t => t.timestamp))
                {
                    if (_settings.last_trade_time >= _t.timestamp)
                    {
                        continue;
                    }

                    _settings.last_trade_time = _t.timestamp;

                    var _ask = qob.result.asks.Where(o => o.price == _t.price).SingleOrDefault();
                    if (_ask != null)
                    {
                        if (_ask.quantity <= _t.quantity)
                        {
                            var _aoi = new SOrderBookItem
                            {
                                action   = "delete",
                                price    = _ask.price,
                                quantity = _ask.quantity,
                                amount   = _ask.price * _ask.quantity,
                                count    = 1
                            };

                            _rqo.result.asks.Add(_aoi);
                            _ask.quantity = 0;
                        }
                        else
                        {
                            _ask.quantity -= _t.quantity;

                            var _aoi = new SOrderBookItem
                            {
                                action   = "update",
                                price    = _ask.price,
                                quantity = _ask.quantity,
                                amount   = _ask.price * _ask.quantity,
                                count    = 1
                            };

                            _rqo.result.asks.Add(_aoi);
                        }
                    }

                    var _bid = qob.result.bids.Where(o => o.price == _t.price).SingleOrDefault();
                    if (_bid != null)
                    {
                        if (_bid.quantity <= _t.quantity)
                        {
                            var _aoi = new SOrderBookItem
                            {
                                action   = "delete",
                                price    = _bid.price,
                                quantity = _bid.quantity,
                                amount   = _bid.price * _bid.quantity,
                                count    = 1
                            };

                            _rqo.result.bids.Add(_aoi);
                            _bid.quantity = 0;
                        }
                        else
                        {
                            _bid.quantity -= _t.quantity;

                            var _aoi = new SOrderBookItem
                            {
                                action   = "update",
                                price    = _bid.price,
                                quantity = _bid.quantity,
                                amount   = _bid.price * _bid.quantity,
                                count    = 1
                            };

                            _rqo.result.bids.Add(_aoi);
                        }
                    }

                    // orderbook에 해당 하는 price-level 보다 안쪽에 위치한 level들은 삭제 해야 한다.
                    var _strange_asks = qob.result.asks.Where(o => o.price < _t.price);
                    if (_strange_asks.Count() > 0)
                    {
                        foreach (var _qox in _strange_asks)
                        {
                            var _aoi = new SOrderBookItem
                            {
                                action   = "delete",
                                price    = _qox.price,
                                quantity = _qox.quantity,
                                amount   = _qox.price * _qox.quantity,
                                count    = 1
                            };

                            _rqo.result.asks.Add(_aoi);
                            _qox.quantity = 0;
                        }
                    }

                    var _strange_bids = qob.result.bids.Where(o => o.price > _t.price);
                    if (_strange_bids.Count() > 0)
                    {
                        foreach (var _qox in _strange_bids)
                        {
                            var _aoi = new SOrderBookItem
                            {
                                action   = "delete",
                                price    = _qox.price,
                                quantity = _qox.quantity,
                                amount   = _qox.price * _qox.quantity,
                                count    = 1
                            };

                            _rqo.result.bids.Add(_aoi);
                            _qox.quantity = 0;
                        }
                    }
                }

                qob.sequentialId = _rqo.sequentialId;

                qob.result.asks.RemoveAll(o => o.quantity == 0);
                qob.result.bids.RemoveAll(o => o.quantity == 0);
            }

            if (_rqo.result.asks.Count + _rqo.result.bids.Count > 0)
            {
                await publishOrderbook(_rqo);

                //_settings.orderbook_count = 0;
            }

            return(true);
        }
예제 #21
0
        public async ValueTask Start(CancellationToken cancelToken)
        {
            BMLogger.SNG.WriteO(this, $"processing service start...");

            var _processing = Task.Run(async() =>
            {
                var _last_polling_trade = 0L;

                while (true)
                {
                    try
                    {
                        await Task.Delay(0);

                        var _message = (QMessage)null;
                        if (ReceiveQ.TryDequeue(out _message) == false)
                        {
                            var _cancelled = cancelToken.WaitHandle.WaitOne(0);
                            if (_cancelled == true)
                            {
                                break;
                            }

                            await Task.Delay(10);
                            continue;
                        }

                        if (_message.command == "WS")
                        {
                            if (_message.stream == "order")
                            {
                                var _w_orders = JsonConvert.DeserializeObject <List <BMyOrderItem> >(_message.payload ?? "");

                                var _s_order = new SMyOrders
                                {
                                    exchange     = _message.exchange,
                                    stream       = _message.stream,
                                    symbol       = _message.symbol,
                                    action       = _message.action,
                                    sequentialId = _w_orders.Max(t => t.timestamp),

                                    result = _w_orders.Select(o =>
                                    {
                                        return(new SMyOrderItem
                                        {
                                            orderId = o.orderId ?? "",
                                            symbol = o.symbol ?? "",
                                            sideType = o.sideType,

                                            timestamp = o.timestamp,

                                            makerType = MakerType.Maker,
                                            orderStatus = o.orderStatus,
                                            orderType = o.orderType,

                                            quantity = o.quantity,
                                            price = o.price,
                                            amount = o.amount,
                                            filled = o.filled,
                                            remaining = o.remaining,

                                            workingIndicator = o.workingIndicator,
                                            avgPx = o.avgPx.HasValue ? o.avgPx.Value : 0,
                                            fee = o.fee,
                                            cost = o.cost,

                                            count = o.count
                                        });
                                    })
                                             .ToList <ISMyOrderItem>()
                                };

                                await mergeMyOrder(_s_order);
                            }
                            else if (_message.stream == "trade")
                            {
                                var _w_trades = JsonConvert.DeserializeObject <List <BCompleteOrderItem> >(_message.payload ?? "");

                                var _s_trade = new SCompleteOrders
                                {
                                    exchange     = _message.exchange,
                                    stream       = _message.stream,
                                    symbol       = _message.symbol,
                                    action       = _message.action,
                                    sequentialId = _w_trades.Max(t => t.timestamp),

                                    result = _w_trades.Select(t =>
                                    {
                                        return(new SCompleteOrderItem
                                        {
                                            timestamp = t.timestamp,
                                            sideType = t.sideType,
                                            price = t.price,
                                            quantity = t.quantity
                                        });
                                    })
                                             .ToList()
                                };

                                if (_s_trade.result.Count() > 0)
                                {
                                    _last_polling_trade = _s_trade.sequentialId;
                                    await mergeCompleteOrder(_s_trade);
                                }
                            }
                            else if (_message.stream == "orderbook")
                            {
                                var _w_orderbooks = JsonConvert.DeserializeObject <List <BOrderBookItem> >(_message.payload ?? "");

                                var _timestamp = CUnixTime.NowMilli;
                                var _asks      = _w_orderbooks.Where(o => o.sideType == SideType.Ask);
                                var _bids      = _w_orderbooks.Where(o => o.sideType == SideType.Bid);

                                var _s_orderbooks = new SOrderBooks
                                {
                                    exchange     = _message.exchange,
                                    symbol       = _message.symbol,
                                    stream       = _message.stream,
                                    action       = _message.action,
                                    sequentialId = _timestamp,

                                    result = new SOrderBook
                                    {
                                        timestamp = _timestamp,
                                        askSumQty = _asks.Sum(o => o.quantity),
                                        bidSumQty = _bids.Sum(o => o.quantity),

                                        asks = _asks.Select(o =>
                                        {
                                            return(new SOrderBookItem
                                            {
                                                quantity = o.quantity,
                                                price = o.price,
                                                amount = o.quantity * o.price,
                                                id = o.id,
                                                count = 1
                                            });
                                        }).ToList(),
                                        bids = _bids.Select(o =>
                                        {
                                            return(new SOrderBookItem
                                            {
                                                quantity = o.quantity,
                                                price = o.price,
                                                amount = o.quantity * o.price,
                                                id = o.id,
                                                count = 1
                                            });
                                        }).ToList()
                                    }
                                };

                                await mergeOrderbook(_s_orderbooks);
                            }
                        }
                        else if (_message.command == "AP")
                        {
                            if (_message.stream == "order")
                            {
                                var _w_orders = JsonConvert.DeserializeObject <List <BMyOrderItem> >(_message.payload ?? "");

                                var _s_order = new SMyOrders
                                {
                                    exchange     = _message.exchange,
                                    stream       = _message.stream,
                                    symbol       = _message.symbol,
                                    action       = _message.action,
                                    sequentialId = _w_orders.Max(t => t.timestamp),

                                    result = _w_orders.Select(o =>
                                    {
                                        return(new SMyOrderItem
                                        {
                                            orderId = o.orderId ?? "",
                                            symbol = o.symbol ?? "",
                                            sideType = o.sideType,

                                            timestamp = o.timestamp,

                                            makerType = o.makerType,
                                            orderStatus = o.orderStatus,
                                            orderType = o.orderType,

                                            quantity = o.quantity,
                                            price = o.price,
                                            amount = o.amount,
                                            filled = o.filled,
                                            remaining = o.remaining,

                                            workingIndicator = o.workingIndicator,
                                            avgPx = o.avgPx.HasValue ? o.avgPx.Value : 0,
                                            fee = o.fee,
                                            cost = o.cost,

                                            count = o.count
                                        });
                                    })
                                             .ToList <ISMyOrderItem>()
                                };

                                await mergeMyOrder(_s_order);
                            }
                            else if (_message.stream == "trade")
                            {
                                var _a_trades = JsonConvert.DeserializeObject <List <BCompleteOrderItem> >(_message.payload ?? "");

                                var _s_trade = new SCompleteOrders
                                {
                                    exchange     = _message.exchange,
                                    symbol       = _message.symbol,
                                    stream       = _message.stream,
                                    action       = _message.action,
                                    sequentialId = _a_trades.Max(t => t.timestamp),

                                    result = _a_trades.Where(t => t.timestamp > _last_polling_trade).Select(t =>
                                    {
                                        return(new SCompleteOrderItem
                                        {
                                            timestamp = t.timestamp,
                                            sideType = t.sideType,
                                            price = t.price,
                                            quantity = t.quantity
                                        });
                                    })
                                             .ToList()
                                };

                                if (_s_trade.result.Count() > 0)
                                {
                                    _last_polling_trade = _s_trade.sequentialId;
                                    await mergeCompleteOrder(_s_trade);
                                }
                            }
                            else if (_message.stream == "orderbook")
                            {
                                var _a_orderbooks = JsonConvert.DeserializeObject <List <BOrderBookItem> >(_message.payload ?? "");

                                var _timestamp = CUnixTime.NowMilli;
                                var _asks      = _a_orderbooks.Where(o => o.sideType == SideType.Ask);
                                var _bids      = _a_orderbooks.Where(o => o.sideType == SideType.Bid);

                                var _s_orderbooks = new SOrderBooks
                                {
                                    exchange     = _message.exchange,
                                    symbol       = _message.symbol,
                                    stream       = _message.stream,
                                    action       = _message.action,
                                    sequentialId = _timestamp,

                                    result = new SOrderBook
                                    {
                                        timestamp = _timestamp,
                                        askSumQty = _asks.Sum(o => o.quantity),
                                        bidSumQty = _bids.Sum(o => o.quantity),

                                        asks = _asks.Select(o =>
                                        {
                                            return(new SOrderBookItem
                                            {
                                                quantity = o.quantity,
                                                price = o.price,
                                                amount = o.quantity * o.price,
                                                id = o.id,
                                                count = 1
                                            });
                                        }).ToList(),
                                        bids = _bids.Select(o =>
                                        {
                                            return(new SOrderBookItem
                                            {
                                                quantity = o.quantity,
                                                price = o.price,
                                                amount = o.quantity * o.price,
                                                id = o.id,
                                                count = 1
                                            });
                                        }).ToList()
                                    }
                                };

                                await mergeOrderbook(_s_orderbooks);
                            }
                        }
                        else if (_message.command == "SS")
                        {
                            await snapshotOrderbook(_message.exchange);
                        }
#if DEBUG
                        else
                        {
                            BMLogger.SNG.WriteO(this, _message.payload);
                        }
#endif
                        if (cancelToken.IsCancellationRequested == true)
                        {
                            break;
                        }
                    }
                    catch (TaskCanceledException)
                    {
                    }
                    catch (Exception ex)
                    {
                        BMLogger.SNG.WriteX(this, ex.ToString());
                    }
                }
            },
                                       cancelToken
                                       );

            await Task.WhenAll(_processing);

            BMLogger.SNG.WriteO(this, $"processing service stop...");
        }