Пример #1
0
        private async Task ProcessMessageAsync(ExecutionEvent message)
        {
            var trades = new List <TradeEntity>();

            foreach (var order in message.Orders)
            {
                var orderEntity = _mapper.Map <OrderEntity>(order);

                if (order.Trades == null)
                {
                    continue;
                }

                foreach (var trade in order.Trades)
                {
                    var tradeEntity = _mapper.Map <TradeEntity>(trade);
                    tradeEntity.AssetPairId  = orderEntity.AssetPairId;
                    tradeEntity.OrderId      = orderEntity.Id;
                    tradeEntity.PartitionKey = orderEntity.WalletId;
                    tradeEntity.WalletId     = orderEntity.WalletId;
                    trades.Add(tradeEntity);
                }
            }

            var tradesByWallet = trades.GroupBy(x => x.WalletId);

            var tasks = new List <Task>();

            foreach (var walletTrades in tradesByWallet)
            {
                var tradeUpdate = new TradeUpdate();

                tradeUpdate.Trades.AddRange(_mapper.Map <List <Lykke.HftApi.ApiContract.Trade> >(walletTrades.ToList()));
                tasks.Add(_tradeStream.WriteToStreamAsync(tradeUpdate, walletTrades.Key));
            }

            await Task.WhenAll(tasks);
        }
Пример #2
0
        /// <summary>
        /// Deserialize JSON and raise <see cref="UserDataEventArgs"/> event.
        /// </summary>
        /// <param name="json"></param>
        /// <param name="token"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        protected override void DeserializeJsonAndRaiseEvent(string json, CancellationToken token, Action <UserDataEventArgs> callback = null)
        {
            Throw.IfNullOrWhiteSpace(json, nameof(json));

            Logger?.LogDebug($"{nameof(UserDataWebSocketClient)}: \"{json}\"");

            try
            {
                var jObject = JObject.Parse(json);

                var eventType = jObject["e"].Value <string>();
                var eventTime = jObject["E"].Value <long>();

                // ReSharper disable once ConvertIfStatementToSwitchStatement
                if (eventType == "outboundAccountInfo")
                {
                    var commissions = new AccountCommissions(
                        jObject["m"].Value <int>(),  // maker
                        jObject["t"].Value <int>(),  // taker
                        jObject["b"].Value <int>(),  // buyer
                        jObject["s"].Value <int>()); // seller

                    var status = new AccountStatus(
                        jObject["T"].Value <bool>(),  // can trade
                        jObject["W"].Value <bool>(),  // can withdraw
                        jObject["D"].Value <bool>()); // can deposit

                    var balances = jObject["B"]
                                   .Select(entry => new AccountBalance(
                                               entry["a"].Value <string>(),   // asset
                                               entry["f"].Value <decimal>(),  // free amount
                                               entry["l"].Value <decimal>())) // locked amount
                                   .ToList();

                    var eventArgs = new AccountUpdateEventArgs(eventTime, token, new AccountInfo(User, commissions, status, jObject["u"].Value <long>(), balances));

                    try
                    {
                        callback?.Invoke(eventArgs);
                        AccountUpdate?.Invoke(this, eventArgs);
                    }
                    catch (OperationCanceledException) { }
                    catch (Exception e)
                    {
                        if (!token.IsCancellationRequested)
                        {
                            Logger?.LogError(e, $"{nameof(UserDataWebSocketClient)}: Unhandled account update event handler exception.");
                        }
                    }
                }
                else if (eventType == "executionReport")
                {
                    var order = new Order(User);

                    FillOrder(order, jObject);

                    var executionType    = ConvertOrderExecutionType(jObject["x"].Value <string>());
                    var rejectedReason   = ConvertOrderRejectedReason(jObject["r"].Value <string>());
                    var newClientOrderId = jObject["c"].Value <string>();

                    if (executionType == OrderExecutionType.Trade) // trade update event.
                    {
                        var trade = new AccountTrade(
                            jObject["s"].Value <string>(),  // symbol
                            jObject["t"].Value <long>(),    // ID
                            jObject["i"].Value <long>(),    // order ID
                            jObject["L"].Value <decimal>(), // price (price of last filled trade)
                            jObject["z"].Value <decimal>(), // quantity (accumulated quantity of filled trades)
                            jObject["n"].Value <decimal>(), // commission
                            jObject["N"].Value <string>(),  // commission asset
                            jObject["T"].Value <long>(),    // timestamp
                            order.Side == OrderSide.Buy,    // is buyer
                            jObject["m"].Value <bool>(),    // is buyer maker
                            jObject["M"].Value <bool>());   // is best price

                        var quantityOfLastFilledTrade = jObject["l"].Value <decimal>();

                        var eventArgs = new AccountTradeUpdateEventArgs(eventTime, token, order, rejectedReason, newClientOrderId, trade, quantityOfLastFilledTrade);

                        try
                        {
                            callback?.Invoke(eventArgs);
                            TradeUpdate?.Invoke(this, eventArgs);
                        }
                        catch (OperationCanceledException) { }
                        catch (Exception e)
                        {
                            if (!token.IsCancellationRequested)
                            {
                                Logger?.LogError(e, $"{nameof(UserDataWebSocketClient)}: Unhandled trade update event handler exception.");
                            }
                        }
                    }
                    else // order update event.
                    {
                        var eventArgs = new OrderUpdateEventArgs(eventTime, token, order, executionType, rejectedReason, newClientOrderId);

                        try
                        {
                            callback?.Invoke(eventArgs);
                            OrderUpdate?.Invoke(this, eventArgs);
                        }
                        catch (OperationCanceledException) { }
                        catch (Exception e)
                        {
                            if (!token.IsCancellationRequested)
                            {
                                Logger?.LogError(e, $"{nameof(UserDataWebSocketClient)}: Unhandled order update event handler exception.");
                            }
                        }
                    }
                }
                else
                {
                    Logger?.LogWarning($"{nameof(UserDataWebSocketClient)}.{nameof(DeserializeJsonAndRaiseEvent)}: Unexpected event type ({eventType}) - \"{json}\"");
                }
            }
            catch (OperationCanceledException) { }
            catch (Exception e)
            {
                if (!token.IsCancellationRequested)
                {
                    Logger?.LogError(e, $"{nameof(UserDataWebSocketClient)}.{nameof(DeserializeJsonAndRaiseEvent)}");
                }
            }
        }
Пример #3
0
        protected override void HandleMessage(IEnumerable <Action <UserDataEventArgs> > callbacks, string stream, string json)
        {
            if (!Users.ContainsKey(stream))
            {
                Logger?.LogError($"{nameof(UserDataClient)}.{nameof(HandleMessage)}: Unknown listen key (\"{stream}\").  [thread: {Thread.CurrentThread.ManagedThreadId}]");
                return; // ignore.
            }

            var user = Users[stream];

            try
            {
                var jObject = JObject.Parse(json);

                var eventType = jObject["e"].Value <string>();
                var eventTime = jObject["E"].Value <long>().ToDateTime();

                // ReSharper disable once ConvertIfStatementToSwitchStatement
                if (eventType == "outboundAccountInfo")
                {
                    var commissions = new AccountCommissions(
                        jObject["m"].Value <int>(),  // maker
                        jObject["t"].Value <int>(),  // taker
                        jObject["b"].Value <int>(),  // buyer
                        jObject["s"].Value <int>()); // seller

                    var status = new AccountStatus(
                        jObject["T"].Value <bool>(),  // can trade
                        jObject["W"].Value <bool>(),  // can withdraw
                        jObject["D"].Value <bool>()); // can deposit

                    var balances = jObject["B"]
                                   .Select(entry => new AccountBalance(
                                               entry["a"].Value <string>(),   // asset
                                               entry["f"].Value <decimal>(),  // free amount
                                               entry["l"].Value <decimal>())) // locked amount
                                   .ToList();

                    var eventArgs = new AccountUpdateEventArgs(eventTime, new AccountInfo(user, commissions, status, jObject["u"].Value <long>().ToDateTime(), balances));

                    try
                    {
                        // ReSharper disable once InconsistentlySynchronizedField
                        if (_accountUpdateSubscribers.TryGetValue(stream, out var subscribers))
                        {
                            foreach (var subcriber in subscribers)
                            {
                                subcriber(eventArgs);
                            }
                        }

                        if (callbacks != null)
                        {
                            foreach (var callback in callbacks)
                            {
                                callback(eventArgs);
                            }
                        }

                        AccountUpdate?.Invoke(this, eventArgs);
                    }
                    catch (OperationCanceledException) { /* ignore */ }
                    catch (Exception e)
                    {
                        Logger?.LogWarning(e, $"{nameof(UserDataClient)}.{nameof(HandleMessage)}: Unhandled account update event handler exception.");
                    }
                }
                else if (eventType == "executionReport")
                {
                    var order = new Order(user);

                    FillOrder(order, jObject);

                    var executionType    = ConvertOrderExecutionType(jObject["x"].Value <string>());
                    var rejectedReason   = jObject["r"].Value <string>();
                    var newClientOrderId = jObject["c"].Value <string>();

                    if (executionType == OrderExecutionType.Trade) // trade update event.
                    {
                        var trade = new AccountTrade(
                            jObject["s"].Value <string>(),  // symbol
                            jObject["t"].Value <long>(),    // ID
                            jObject["i"].Value <long>(),    // order ID
                            jObject["L"].Value <decimal>(), // price (price of last filled trade)
                            jObject["z"].Value <decimal>(), // quantity (accumulated quantity of filled trades)
                            jObject["n"].Value <decimal>(), // commission
                            jObject["N"].Value <string>(),  // commission asset
                            jObject["T"].Value <long>()
                            .ToDateTime(),                  // time
                            order.Side == OrderSide.Buy,    // is buyer
                            jObject["m"].Value <bool>(),    // is buyer maker
                            jObject["M"].Value <bool>());   // is best price

                        var quantityOfLastFilledTrade = jObject["l"].Value <decimal>();

                        var eventArgs = new AccountTradeUpdateEventArgs(eventTime, order, rejectedReason, newClientOrderId, trade, quantityOfLastFilledTrade);

                        try
                        {
                            // ReSharper disable once InconsistentlySynchronizedField
                            if (_accountTradeUpdateSubscribers.TryGetValue(stream, out var subscribers))
                            {
                                foreach (var subcriber in subscribers)
                                {
                                    subcriber(eventArgs);
                                }
                            }

                            if (callbacks != null)
                            {
                                foreach (var callback in callbacks)
                                {
                                    callback(eventArgs);
                                }
                            }

                            TradeUpdate?.Invoke(this, eventArgs);
                        }
                        catch (OperationCanceledException) { /* ignore */ }
                        catch (Exception e)
                        {
                            Logger?.LogWarning(e, $"{nameof(UserDataClient)}.{nameof(HandleMessage)}: Unhandled trade update event handler exception.");
                        }
                    }
                    else // order update event.
                    {
                        var eventArgs = new OrderUpdateEventArgs(eventTime, order, executionType, rejectedReason, newClientOrderId);

                        try
                        {
                            // ReSharper disable once InconsistentlySynchronizedField
                            if (_orderUpdateSubscribers.TryGetValue(stream, out var subscribers))
                            {
                                foreach (var subcriber in subscribers)
                                {
                                    subcriber(eventArgs);
                                }
                            }

                            if (callbacks != null)
                            {
                                foreach (var callback in callbacks)
                                {
                                    callback(eventArgs);
                                }
                            }

                            OrderUpdate?.Invoke(this, eventArgs);
                        }
                        catch (OperationCanceledException) { /* ignore */ }
                        catch (Exception e)
                        {
                            Logger?.LogWarning(e, $"{nameof(UserDataClient)}.{nameof(HandleMessage)}: Unhandled order update event handler exception.");
                        }
                    }
                }
                else
                {
                    Logger?.LogWarning($"{nameof(UserDataClient)}.{nameof(HandleMessage)}: Unexpected event type ({eventType}).");
                }
            }
            catch (OperationCanceledException) { /* ignore */ }
            catch (Exception e)
            {
                Logger?.LogError(e, $"{nameof(UserDataClient)}.{nameof(HandleMessage)}");
            }
        }
Пример #4
0
 private static void addToUpdatersList(
     TradeUpdate[] updaters,
     MamaFieldDescriptor fieldDesc,
     TradeUpdate updater)
 {
     if (fieldDesc == null) return;
     int fieldId = fieldDesc.getFid();
     if (fieldId <= mMaxFid)
     {
         updaters[fieldId] = updater;
     }
 }
Пример #5
0
        private static TradeUpdate[] createUpdaters()
        {
            mMaxFid = MamdaTradeFields.getMaxFid();
            TradeUpdate[] updaters = new TradeUpdate[mMaxFid + 1];

            addToUpdatersList(updaters, MamdaTradeFields.SYMBOL, new MamdaTradeSymbol());
            addToUpdatersList(updaters, MamdaTradeFields.ISSUE_SYMBOL, new MamdaTradeIssueSymbol());
            addToUpdatersList(updaters, MamdaTradeFields.PART_ID, new TradeLastPartId());
            addToUpdatersList(updaters, MamdaTradeFields.TRADE_ID, new TradeId());
            addToUpdatersList(updaters, MamdaTradeFields.ORIG_TRADE_ID, new OrigTradeId());
            addToUpdatersList(updaters, MamdaTradeFields.CORR_TRADE_ID, new CorrTradeId());
            addToUpdatersList(updaters, MamdaTradeFields.SRC_TIME, new TradeSrcTime());
            addToUpdatersList(updaters, MamdaTradeFields.ACTIVITY_TIME, new TradeActivityTime());
            addToUpdatersList(updaters, MamdaTradeFields.LINE_TIME, new TradeLineTime());
            addToUpdatersList(updaters, MamdaTradeFields.SEND_TIME, new TradeSendTime());
            addToUpdatersList(updaters, MamdaTradeFields.PUB_ID, new TradePubId());
            addToUpdatersList(updaters, MamdaTradeFields.TRADE_PRICE, new TradeLastPrice());
            addToUpdatersList(updaters, MamdaTradeFields.TRADE_SIZE, new TradeLastVolume());
            addToUpdatersList(updaters, MamdaTradeFields.TRADE_TIME, new TradeLastTime());
            addToUpdatersList(updaters, MamdaTradeFields.TRADE_DATE, new TradeTradeDate());
            addToUpdatersList(updaters, MamdaTradeFields.TRADE_DIRECTION, new TradeDirection());
            addToUpdatersList(updaters, MamdaTradeFields.AGGRESSOR_SIDE, new AggressorSide());
            addToUpdatersList(updaters, MamdaTradeFields.TRADE_SIDE, new TradeSide());
            addToUpdatersList(updaters, MamdaTradeFields.NET_CHANGE, new TradeNetChange());
            addToUpdatersList(updaters, MamdaTradeFields.PCT_CHANGE, new TradePctChange());
            addToUpdatersList(updaters, MamdaTradeFields.TOTAL_VOLUME, new TradeAccVolume());
            addToUpdatersList(updaters, MamdaTradeFields.OFF_EXCHANGE_TOTAL_VOLUME, new TradeOffExAccVolume());
            addToUpdatersList(updaters, MamdaTradeFields.ON_EXCHANGE_TOTAL_VOLUME, new TradeOnExAccVolume());
            addToUpdatersList(updaters, MamdaTradeFields.HIGH_PRICE, new TradeHighPrice());
            addToUpdatersList(updaters, MamdaTradeFields.LOW_PRICE, new TradeLowPrice());
            addToUpdatersList(updaters, MamdaTradeFields.OPEN_PRICE, new TradeOpenPrice());
            addToUpdatersList(updaters, MamdaTradeFields.CLOSE_PRICE, new TradeClosePrice());
            addToUpdatersList(updaters, MamdaTradeFields.PREV_CLOSE_PRICE, new TradePrevClosePrice());
            addToUpdatersList(updaters, MamdaTradeFields.TRADE_SEQNUM, new TradeEventSeqNum());
            addToUpdatersList(updaters, MamdaTradeFields.SHORT_SALE_CIRCUIT_BREAKER, new MamdaShortSaleCircuitBreaker());
            addToUpdatersList(updaters, MamdaTradeFields.ORIG_SHORT_SALE_CIRCUIT_BREAKER, new MamdaOrigShortSaleCircuitBreaker());
            addToUpdatersList(updaters, MamdaTradeFields.CORR_SHORT_SALE_CIRCUIT_BREAKER, new MamdaCorrShortSaleCircuitBreaker());
            addToUpdatersList(updaters, MamdaTradeFields.TRADE_QUALIFIER, new TradeQualStr());
            addToUpdatersList(updaters, MamdaTradeFields.SALE_CONDITION, new TradeQualNativeStr());
            addToUpdatersList(updaters, MamdaTradeFields.TRADE_PART_ID, new TradeLastPartId());
            addToUpdatersList(updaters, MamdaTradeFields.TOTAL_VALUE, new TradeTotalValue());
            addToUpdatersList(updaters, MamdaTradeFields.OFF_EXCHANGE_TOTAL_VALUE, new TradeOffExTotalValue());
            addToUpdatersList(updaters, MamdaTradeFields.ON_EXCHANGE_TOTAL_VALUE, new TradeOnExTotalValue());
            addToUpdatersList(updaters, MamdaTradeFields.VWAP, new TradeVWap());
            addToUpdatersList(updaters, MamdaTradeFields.OFF_EXCHANGE_VWAP, new TradeOffExVWap());
            addToUpdatersList(updaters, MamdaTradeFields.ON_EXCHANGE_VWAP, new TradeOnExVWap());
            addToUpdatersList(updaters, MamdaTradeFields.STD_DEV, new TradeStdDev());
            addToUpdatersList(updaters, MamdaTradeFields.STD_DEV_SUM, new TradeStdDevSum());
            addToUpdatersList(updaters, MamdaTradeFields.STD_DEV_SUM_SQUARES, new TradeStdDevSumSquares());
            addToUpdatersList(updaters, MamdaTradeFields.ORDER_ID, new TradeOrderId());
            addToUpdatersList(updaters, MamdaTradeFields.SETTLE_PRICE, new TradeSettlePrice());
            addToUpdatersList(updaters, MamdaTradeFields.SETTLE_DATE, new TradeSettleDate());
            addToUpdatersList(updaters, MamdaTradeFields.SELLERS_SALE_DAYS, new TradeSellerSalesDays());
            addToUpdatersList(updaters, MamdaTradeFields.STOP_STOCK_IND, new TradeStopStockInd());
            addToUpdatersList(updaters, MamdaTradeFields.TRADE_EXEC_VENUE, new TradeExecVenue());
            addToUpdatersList(updaters, MamdaTradeFields.OFF_EXCHANGE_TRADE_PRICE, new OffExTradePrice());
            addToUpdatersList(updaters, MamdaTradeFields.ON_EXCHANGE_TRADE_PRICE, new OnExTradePrice());
            addToUpdatersList(updaters, MamdaTradeFields.IS_IRREGULAR, new TradeIsIrregular());
            addToUpdatersList(updaters, MamdaTradeFields.ORIG_PART_ID, new TradeOrigPartId());
            addToUpdatersList(updaters, MamdaTradeFields.ORIG_PRICE, new TradeOrigPrice());
            addToUpdatersList(updaters, MamdaTradeFields.ORIG_SIZE, new TradeOrigVolume());
            addToUpdatersList(updaters, MamdaTradeFields.ORIG_SEQNUM, new TradeOrigSeqNum());
            addToUpdatersList(updaters, MamdaTradeFields.ORIG_TRADE_QUALIFIER, new TradeOrigQualStr());
            addToUpdatersList(updaters, MamdaTradeFields.ORIG_SALE_CONDITION, new TradeOrigQualNativeStr());
            addToUpdatersList(updaters, MamdaTradeFields.ORIG_SELLERS_SALE_DAYS, new TradeOrigSellersSaleDays());
            addToUpdatersList(updaters, MamdaTradeFields.ORIG_STOP_STOCK_IND, new TradeOrigStopStockInd());
            addToUpdatersList(updaters, MamdaTradeFields.CORR_PART_ID, new TradeCorrPartId());
            addToUpdatersList(updaters, MamdaTradeFields.CORR_PRICE, new TradeCorrPrice());
            addToUpdatersList(updaters, MamdaTradeFields.CORR_SIZE, new TradeCorrVolume());
            addToUpdatersList(updaters, MamdaTradeFields.CORR_TRADE_QUALIFIER, new TradeCorrQualStr());
            addToUpdatersList(updaters, MamdaTradeFields.CORR_SALE_CONDITION, new TradeCorrQualNativeStr());
            addToUpdatersList(updaters, MamdaTradeFields.CORR_SELLERS_SALE_DAYS, new TradeCorrSellersSaleDays());
            addToUpdatersList(updaters, MamdaTradeFields.CORR_STOP_STOCK_IND, new TradeCorrStopStockInd());
            addToUpdatersList(updaters, MamdaTradeFields.CORR_TIME, new TradeCorrTime());
            addToUpdatersList(updaters, MamdaTradeFields.CANCEL_TIME, new TradeCancelTime());
            addToUpdatersList(updaters, MamdaTradeFields.TRADE_COUNT, new TradeCount());
            addToUpdatersList(updaters, MamdaTradeFields.CONFLATE_COUNT, new TradeConflateCount());
            addToUpdatersList(updaters, MamdaTradeFields.TRADE_UNITS, new TradeUnits());
            addToUpdatersList(updaters, MamdaTradeFields.HIGH_SEQNUM, new TradeHighSeqNum());
            addToUpdatersList(updaters, MamdaTradeFields.LOW_SEQNUM, new TradeLowSeqNum());
            addToUpdatersList(updaters, MamdaTradeFields.LAST_SEQNUM, new TradeLastSeqNum());
            addToUpdatersList(updaters, MamdaTradeFields.TOTAL_VOLUME_SEQNUM, new TradeTotalVolumeSeqNum());
            addToUpdatersList(updaters, MamdaTradeFields.CURRENCY_CODE, new TradeCurrencyCode());
            addToUpdatersList(updaters, MamdaTradeFields.BLOCK_COUNT, new TradeBlockCount());
            addToUpdatersList(updaters, MamdaTradeFields.BLOCK_VOLUME, new TradeBlockVolume());
            addToUpdatersList(updaters, MamdaTradeFields.PREV_CLOSE_DATE, new TradePrevCloseDate());
            addToUpdatersList(updaters, MamdaTradeFields.ADJ_PREV_CLOSE, new TradeAdjPrevClose());
            addToUpdatersList(updaters, MamdaTradeFields.IRREG_PRICE, new TradeIrregPrice());
            addToUpdatersList(updaters, MamdaTradeFields.IRREG_SIZE, new TradeIrregVolume());
            addToUpdatersList(updaters, MamdaTradeFields.IRREG_PART_ID, new TradeIrregPartId());
            addToUpdatersList(updaters, MamdaTradeFields.IRREG_TIME, new TradeIrregTime());
            addToUpdatersList(updaters, MamdaTradeFields.UPDATE_AS_TRADE, new TradeUpdateAsTrade());

            return updaters;
        }
Пример #6
0
 private static void MarketDataSource_TradeUpdateEvent(TradeUpdate tradeUpdate)
 {
     Console.WriteLine("Client Id " + tradeUpdate.ClientId + " Product Type " + tradeUpdate.Product + " Order Type " + tradeUpdate.OrderType + " Trade Price " + tradeUpdate.TradePrice + " Traded Qty " + tradeUpdate.TradeQuantity + " Filled Qty " + tradeUpdate.FilledQty);
 }
Пример #7
0
        public void Start()
        {
            _pricesReader.SubscribeToChanges(prices =>
            {
                foreach (var price in prices)
                {
                    _priceStraem.WriteToStream(_mapper.Map <PriceUpdate>(price));
                }
            });

            _tickerReader.SubscribeToChanges(tickers =>
            {
                foreach (var ticker in tickers)
                {
                    _tickerStream.WriteToStream(_mapper.Map <TickerUpdate>(ticker));
                }
            });

            _orderbookReader.SubscribeToChanges(orderbooks =>
            {
                foreach (var orderbook in orderbooks)
                {
                    var item = _mapper.Map <Orderbook>(orderbook);
                    item.Asks.AddRange(_mapper.Map <List <Orderbook.Types.PriceVolume> >(orderbook.Asks));
                    item.Bids.AddRange(_mapper.Map <List <Orderbook.Types.PriceVolume> >(orderbook.Bids));
                    _orderbookStream.WriteToStream(item, orderbook.AssetPairId);
                }
            });

            _balanceReader.SubscribeToChanges(balances =>
            {
                var balancesByWallet = balances.GroupBy(x => x.WalletId);

                foreach (var walletBalanes in balancesByWallet)
                {
                    var balanceUpdate = new BalanceUpdate();
                    balanceUpdate.Balances.AddRange(_mapper.Map <List <Balance> >(walletBalanes.ToList()));
                    _balanceStream.WriteToStream(balanceUpdate, walletBalanes.Key);
                }
            });

            _orderReader.SubscribeToChanges(ordersEntities =>
            {
                var ordersByWallet = ordersEntities.GroupBy(x => x.WalletId);

                foreach (var walletOrders in ordersByWallet)
                {
                    var orderUpdate = new OrderUpdate();
                    orderUpdate.Orders.AddRange(_mapper.Map <List <Order> >(walletOrders.ToList()));
                    _orderStream.WriteToStream(orderUpdate, walletOrders.Key);
                }
            });

            _tradeReader.SubscribeToChanges(tradeEntities =>
            {
                var tradesByWallet = tradeEntities.GroupBy(x => x.WalletId);

                foreach (var walletTrades in tradesByWallet)
                {
                    var tradeUpdate = new TradeUpdate();

                    tradeUpdate.Trades.AddRange(_mapper.Map <List <Trade> >(walletTrades.ToList()));
                    _tradeStream.WriteToStream(tradeUpdate, walletTrades.Key);
                }
            });

            Console.WriteLine("Stream services started.");
        }