Exemple #1
0
 internal static void Display(AccountTrade trade)
 {
     lock (ConsoleSync)
     {
         Console.WriteLine($"  {trade.Time.ToLocalTime()} - {trade.Symbol.PadLeft(8)} - {(trade.IsBuyer ? "Buy" : "Sell").PadLeft(4)} - {(trade.IsMaker ? "Maker" : "Taker")} - {trade.Quantity:0.00000000} @ {trade.Price:0.00000000}{(trade.IsBestPriceMatch ? "*" : " ")} - Fee: {trade.Commission:0.00000000} {trade.CommissionAsset.PadRight(5)} ID: {trade.Id}");
     }
 }
Exemple #2
0
        public void Throws()
        {
            var time = DateTimeOffset.FromUnixTimeMilliseconds(DateTime.UtcNow.ToTimestamp()).UtcDateTime;

            var           symbol = Symbol.BTC_USDT;
            const decimal price  = 4999;

            const string orderRejectedReason = OrderRejectedReason.None;
            const string newClientOrderId    = "new-test-order";

            const long    tradeId          = 12345;
            const long    orderId          = 54321;
            const decimal quantity         = 1;
            const decimal commission       = 10;
            const string  commissionAsset  = "BNB";
            const bool    isBuyer          = true;
            const bool    isMaker          = true;
            const bool    isBestPriceMatch = true;

            var trade = new AccountTrade(symbol, tradeId, orderId, price, quantity, commission, commissionAsset, time, isBuyer, isMaker, isBestPriceMatch);

            decimal quantityOfLastFilledTrade = 1;

            Assert.Throws <ArgumentNullException>("order", () => new AccountTradeUpdateEventArgs(time, null, orderRejectedReason, newClientOrderId, trade, quantityOfLastFilledTrade));
        }
Exemple #3
0
        public void Serialization()
        {
            var           symbol           = Symbol.BTC_USDT;
            const long    id               = 12345;
            const long    orderId          = 54321;
            const decimal price            = 5000;
            const decimal quantity         = 1;
            const decimal quoteQuantity    = price * quantity;
            const decimal commission       = 10;
            var           commissionAsset  = Asset.BNB;
            var           time             = DateTimeOffset.FromUnixTimeMilliseconds(DateTime.UtcNow.ToTimestamp()).UtcDateTime;
            const bool    isBuyer          = true;
            const bool    isMaker          = true;
            const bool    isBestPriceMatch = true;

            var trade = new AccountTrade(symbol, id, orderId, price, quantity, quoteQuantity, commission, commissionAsset, time, isBuyer, isMaker, isBestPriceMatch);

            var json = JsonConvert.SerializeObject(trade);

            trade = JsonConvert.DeserializeObject <AccountTrade>(json);

            Assert.Equal(symbol, trade.Symbol);
            Assert.Equal(id, trade.Id);
            Assert.Equal(orderId, trade.OrderId);
            Assert.Equal(price, trade.Price);
            Assert.Equal(quantity, trade.Quantity);
            Assert.Equal(quoteQuantity, trade.QuoteQuantity);
            Assert.Equal(commission, trade.Commission);
            Assert.Equal(commissionAsset, trade.CommissionAsset);
            Assert.Equal(time, trade.Time);
            Assert.Equal(isBuyer, trade.IsBuyer);
            Assert.Equal(isMaker, trade.IsMaker);
            Assert.Equal(isBestPriceMatch, trade.IsBestPriceMatch);
        }
Exemple #4
0
        public void Equality()
        {
            var           symbol           = Symbol.BTC_USDT;
            const long    id               = 12345;
            const long    orderId          = 54321;
            const decimal price            = 5000;
            const decimal quantity         = 1;
            const decimal quoteQuantity    = price * quantity;
            const decimal commission       = 10;
            var           commissionAsset  = Asset.BNB;
            var           time             = DateTimeOffset.FromUnixTimeMilliseconds(DateTime.UtcNow.ToTimestamp()).UtcDateTime;
            const bool    isBuyer          = true;
            const bool    isMaker          = true;
            const bool    isBestPriceMatch = true;

            var trade = new AccountTrade(symbol, id, orderId, price, quantity, quoteQuantity, commission, commissionAsset, time, isBuyer, isMaker, isBestPriceMatch);

            var serializer = new AccountTradeSerializer();

            var json = serializer.Serialize(trade);

            var other = serializer.Deserialize(json);

            Assert.True(trade.Equals(other));
        }
Exemple #5
0
        public void Properties()
        {
            var           symbol           = Symbol.BTC_USDT;
            const long    id               = 12345;
            const long    orderId          = 54321;
            const decimal price            = 5000;
            const decimal quantity         = 1;
            const decimal commission       = 10;
            var           commissionAsset  = Asset.BNB;
            var           timestamp        = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
            const bool    isBuyer          = true;
            const bool    isMaker          = true;
            const bool    isBestPriceMatch = true;

            var trade = new AccountTrade(symbol, id, orderId, price, quantity, commission, commissionAsset, timestamp, isBuyer, isMaker, isBestPriceMatch);

            Assert.Equal(symbol, trade.Symbol);
            Assert.Equal(id, trade.Id);
            Assert.Equal(orderId, trade.OrderId);
            Assert.Equal(price, trade.Price);
            Assert.Equal(quantity, trade.Quantity);
            Assert.Equal(commission, trade.Commission);
            Assert.Equal(commissionAsset, trade.CommissionAsset);
            Assert.Equal(timestamp, trade.Timestamp);
            Assert.Equal(isBuyer, trade.IsBuyer);
            Assert.Equal(isMaker, trade.IsMaker);
            Assert.Equal(isBestPriceMatch, trade.IsBestPriceMatch);
        }
Exemple #6
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="timestamp">The event time.</param>
        /// <param name="token">The cancellation token.</param>
        /// <param name="order">The order.</param>
        /// <param name="rejectedReason">The order rejected reason.</param>
        /// <param name="newClientOrderId">The new client order ID.</param>
        /// <param name="trade">The trade.</param>
        /// <param name="quantityOfLastFilledTrade">The quantity of last filled trade.</param>
        public AccountTradeUpdateEventArgs(long timestamp, CancellationToken token, Order order, OrderRejectedReason rejectedReason, string newClientOrderId, AccountTrade trade, decimal quantityOfLastFilledTrade)
            : base(timestamp, token, order, OrderExecutionType.Trade, rejectedReason, newClientOrderId)
        {
            Throw.IfNull(trade, nameof(trade));

            Trade = trade;
            QuantityOfLastFilledTrade = quantityOfLastFilledTrade;
        }
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="time">The event time.</param>
        /// <param name="order">The order.</param>
        /// <param name="rejectedReason">The order rejected reason.</param>
        /// <param name="newClientOrderId">The new client order ID.</param>
        /// <param name="trade">The trade.</param>
        /// <param name="quantityOfLastFilledTrade">The quantity of last filled trade.</param>
        public AccountTradeUpdateEventArgs(DateTime time, Order order, string rejectedReason, string newClientOrderId, AccountTrade trade, decimal quantityOfLastFilledTrade)
            : base(time, order, OrderExecutionType.Trade, rejectedReason, newClientOrderId)
        {
            Throw.IfNull(trade, nameof(trade));

            Trade = trade;
            QuantityOfLastFilledTrade = quantityOfLastFilledTrade;
        }
        public void Properties()
        {
            var time = DateTimeOffset.FromUnixTimeMilliseconds(DateTime.UtcNow.ToTimestamp()).UtcDateTime;

            var               user             = new BinanceApiUser("api-key");
            var               symbol           = Symbol.BTC_USDT;
            const int         id               = 123456;
            const string      clientOrderId    = "test-order";
            const decimal     price            = 4999;
            const decimal     originalQuantity = 1;
            const decimal     executedQuantity = 0.5m;
            const OrderStatus status           = OrderStatus.PartiallyFilled;
            const TimeInForce timeInForce      = TimeInForce.IOC;
            const OrderType   orderType        = OrderType.Market;
            const OrderSide   orderSide        = OrderSide.Sell;
            const decimal     stopPrice        = 5000;
            const decimal     icebergQuantity  = 0.1m;
            const bool        isWorking        = true;

            var order = new Order(user, symbol, id, clientOrderId, price, originalQuantity, executedQuantity, status, timeInForce, orderType, orderSide, stopPrice, icebergQuantity, time, isWorking);

            const OrderRejectedReason orderRejectedReason = OrderRejectedReason.None;
            const string newClientOrderId = "new-test-order";

            const long    tradeId          = 12345;
            const long    orderId          = 54321;
            const decimal quantity         = 1;
            const decimal commission       = 10;
            const string  commissionAsset  = "BNB";
            const bool    isBuyer          = true;
            const bool    isMaker          = true;
            const bool    isBestPriceMatch = true;

            var trade = new AccountTrade(symbol, tradeId, orderId, price, quantity, commission, commissionAsset, time, isBuyer, isMaker, isBestPriceMatch);

            const decimal quantityOfLastFilledTrade = 1;

            using (var cts = new CancellationTokenSource())
            {
                var args = new AccountTradeUpdateEventArgs(time, cts.Token, order, orderRejectedReason, newClientOrderId, trade, quantityOfLastFilledTrade);

                Assert.Equal(time, args.Time);
                Assert.Equal(order, args.Order);
                Assert.Equal(OrderExecutionType.Trade, args.OrderExecutionType);
                Assert.Equal(orderRejectedReason, args.OrderRejectedReason);
                Assert.Equal(newClientOrderId, args.NewClientOrderId);
                Assert.Equal(trade, args.Trade);
                Assert.Equal(quantityOfLastFilledTrade, args.QuantityOfLastFilledTrade);
            }
        }
Exemple #9
0
        public void Throws()
        {
            var timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();

            var               user             = new BinanceApiUser("api-key");
            var               symbol           = Symbol.BTC_USDT;
            const int         id               = 123456;
            const string      clientOrderId    = "test-order";
            const decimal     price            = 4999;
            const decimal     originalQuantity = 1;
            const decimal     executedQuantity = 0.5m;
            const OrderStatus status           = OrderStatus.PartiallyFilled;
            const TimeInForce timeInForce      = TimeInForce.IOC;
            const OrderType   orderType        = OrderType.Market;
            const OrderSide   orderSide        = OrderSide.Sell;
            const decimal     stopPrice        = 5000;
            const decimal     icebergQuantity  = 0.1m;
            const bool        isWorking        = true;

            var order = new Order(user, symbol, id, clientOrderId, price, originalQuantity, executedQuantity, status, timeInForce, orderType, orderSide, stopPrice, icebergQuantity, timestamp, isWorking);

            const OrderRejectedReason orderRejectedReason = OrderRejectedReason.None;
            const string newClientOrderId = "new-test-order";

            const long    tradeId          = 12345;
            const long    orderId          = 54321;
            const decimal quantity         = 1;
            const decimal commission       = 10;
            const string  commissionAsset  = "BNB";
            const bool    isBuyer          = true;
            const bool    isMaker          = true;
            const bool    isBestPriceMatch = true;

            var trade = new AccountTrade(symbol, tradeId, orderId, price, quantity, commission, commissionAsset, timestamp, isBuyer, isMaker, isBestPriceMatch);

            decimal quantityOfLastFilledTrade = 1;

            using (var cts = new CancellationTokenSource())
            {
                Assert.Throws <ArgumentException>("timestamp", () => new AccountTradeUpdateEventArgs(-1, cts.Token, order, orderRejectedReason, newClientOrderId, trade, quantityOfLastFilledTrade));
                Assert.Throws <ArgumentException>("timestamp", () => new AccountTradeUpdateEventArgs(0, cts.Token, order, orderRejectedReason, newClientOrderId, trade, quantityOfLastFilledTrade));
                Assert.Throws <ArgumentNullException>("order", () => new AccountTradeUpdateEventArgs(timestamp, cts.Token, null, orderRejectedReason, newClientOrderId, trade, quantityOfLastFilledTrade));
            }
        }
Exemple #10
0
 private Interface.Model.AccountTrade NewAccountTrade(AccountTrade t)
 {
     return(new Interface.Model.AccountTrade
     {
         Symbol = t.Symbol,
         Id = t.Id,
         Price = t.Price,
         Quantity = t.Quantity,
         Time = t.Time,
         BuyerOrderId = t.BuyerOrderId,
         SellerOrderId = t.SellerOrderId,
         IsBuyerMaker = t.IsBuyerMaker,
         IsBestPriceMatch = t.IsBestPriceMatch,
         OrderId = t.OrderId,
         QuoteQuantity = t.QuoteQuantity,
         Commission = t.Commission,
         CommissionAsset = t.CommissionAsset,
         IsBuyer = t.IsBuyer,
         IsMaker = t.IsMaker
     });
 }
Exemple #11
0
        public virtual string Serialize(AccountTrade trade)
        {
            Throw.IfNull(trade, nameof(trade));

            var jObject = new JObject
            {
                new JProperty(Key_Symbol, trade.Symbol),
                new JProperty(Key_Id, trade.Id),
                new JProperty(Key_OrderId, trade.OrderId),
                new JProperty(Key_Price, trade.Price.ToString(CultureInfo.InvariantCulture)),
                new JProperty(Key_Quantity, trade.Quantity.ToString(CultureInfo.InvariantCulture)),
                new JProperty(Key_Commission, trade.Commission.ToString(CultureInfo.InvariantCulture)),
                new JProperty(Key_CommissionAsset, trade.CommissionAsset),
                new JProperty(Key_Time, trade.Timestamp),
                new JProperty(Key_IsBuyer, trade.IsBuyer),
                new JProperty(Key_IsMaker, trade.IsMaker),
                new JProperty(Key_IsBestPriceMatch, trade.IsBestPriceMatch)
            };

            return(jObject.ToString(Formatting.None));
        }
Exemple #12
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)}");
                }
            }
        }
Exemple #13
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)}");
            }
        }
Exemple #14
0
        static void Main(string[] args)
        {
            CX cx = new CX(CX.Production); //Create CX instance.

            //Subscribe to GetProducts response.
            cx.OnGetProducts += (sender, e) => {
                foreach (Product product in e.Products)
                    Console.WriteLine(
                        $"{product.ProductId, -2} " +
                        $"{product.Symbol, -4} " +
                        $"{product.ProductFullName, -20} " +
                        $"{product.ProductType, -20} " +
                        $"{product.DecimalPlaces, -2}"
                    );
            };

            //Subscribe to GetInstruments response.
            cx.OnGetInstruments += (sender, e) =>
            {
                foreach (Instrument instrument in e.Instruments)
                    Console.WriteLine(
                        $"{instrument.InstrumentId, -2} " +
                        $"{instrument.Symbol, -4} " +
                        $"{instrument.Product1, -2} " +
                        $"{instrument.Product1Symbol, -4} " +
                        $"{instrument.Product2, -2} " +
                        $"{instrument.Product2Symbol, -4} " +
                        $"{instrument.InstrumentType, -10}"
                    );
            };

            //Subscribe to WebAuthenticateuser response.
            cx.OnWebAuthenticateUser += (sender, e) =>
            {
                Console.WriteLine(e.Authenticated);
                Console.WriteLine(e.SessionToken);
                Console.WriteLine(e.UserId);
                Console.WriteLine(e.ErrorMessage);
            };

            //Subscribe to GetUserAccounts response.
            cx.OnGetUserAccounts += (sender, e) =>
            {
                foreach (var account in e.AccountIds)
                    Console.WriteLine(account);
            };

            //Subscribe to GetAccountTransaction response.
            cx.OnGetAccountTransactions += (sender, e) =>
            {
                foreach (AccountTransaction transaction in e.Transactions)
                    Console.WriteLine(
                        $"{transaction.TransactionId, -4} " +
                        $"{transaction.AccountId, -2} " +
                        $"{transaction.Cr, -5} " +
                        $"{transaction.Dr, -5} " +
                        $"{transaction.TransactionType, -6}" +
                        $" {transaction.ReferenceId, -4} " +
                        $"{transaction.ProductId, -2} " +
                        $"{transaction.Balance, -7} " +
                        $"{transaction.ReferenceType, -14} " +
                        $"{transaction.TimeStamp, -15}"
                    );
            };

            //Subscribe to GetAccountPosition response.
            cx.OnGetAccountPositions += (sender, e) =>
            {
                foreach (AccountPosition position in e.Positions)
                    Console.WriteLine(
                        $"{position.AccountId, -2} " +
                        $"{position.ProductId, -2} " +
                        $"{position.ProductSymbol, -4} " +
                        $"{position.Amount, -10} " +
                        $"{position.Hold, -10} " +
                        $"{position.PendingDeposits, -10} " +
                        $"{position.PendingWithdraws, -10}"
                    );
            };

            //Subscribe to GetAccountTrades response.
            cx.OnGetAccountTrades += (sender, e) =>
            {
                foreach (AccountTrade trade in e.Trades)
                    Console.WriteLine(
                        $"{trade.TradeId, -4} " +
                        $"{trade.OrderId, -5} " +
                        $"{trade.AccountId, -2} " +
                        $"{trade.ClientOrderId, -4} " +
                        $"{trade.InstrumentId, -2} " +
                        $"{trade.Side, -5} " +
                        $"{trade.Quantity, -7} " +
                        $"{trade.Price, -7} " +
                        $"{trade.Value, -7} " +
                        $"{trade.TradeTime, -15}"
                    );
            };

            //Subscribe to SendOrder response.
            cx.OnSendOrder += (sender, e) =>
            {
                Console.WriteLine(e.Status);
                Console.WriteLine(e.ErrorMessage);
                Console.WriteLine(e.OrderId);
            };

            //Send SendOrder request.
            cx.SendOrder(new Order {
                AccountId = 4,
                ClientOrderId = 99,
                OMSId = 1,
                UseDisplayQuantity = true,
                Quantity = 1,
                DisplayQuantity = 0,
                LimitPrice = 95,
                OrderIdOCO = 0,
                OrderType = 2,                  // 1 (Market), 2 (Limit), 3 (StopMarket), 4 (StopLimit), 5 (TrailingStopMarket), 6 (TrailingStopLimit)
                PegPriceType = 1,               // 1 (Last), 2 (Bid), 3 (Ask)
                InstrumentId = 1,
                TrailingAmount = 1,
                LimitOffset = 2,
                Side = 0,                       // 0 (Buy) or 1 (Sell)
                StopPrice = 96, 
                TimeInForce = 1,                // 1 (Good 'til Canceled), 3 (Immediate or Cancel), 4 (Fill or Kill)
            });

            //Subscribe to CancelOrder response.
            cx.OnCancelOrder += (sender, e) =>
            {
                Console.WriteLine(e.Status);
                Console.WriteLine(e.ErrorMessage);
                Console.WriteLine(e.Detail);
                Console.WriteLine(e.ErrorCode);
            };

            //Subscribe to GetOrderStatus response.
            cx.OnGetOrderStatus += (sender, e) =>
            {
                OrderStatus orderStatus = e.OrderStatus;
                Console.WriteLine(orderStatus.Account);
                Console.WriteLine(orderStatus.Quantity);
                Console.WriteLine(orderStatus.OrderType);
                Console.WriteLine(orderStatus.Instrument); // 1 (Market), 2 (Limit), 3 (StopMarket), 4 (StopLimit), 5 (TrailingStopMarket), 6 (TrailingStopLimit)
                Console.WriteLine(orderStatus.Side);
                Console.WriteLine(orderStatus.OrderId);
                Console.WriteLine(orderStatus.Price);
                Console.WriteLine(orderStatus.OrderState);
                Console.WriteLine(orderStatus.OrigQuantity);
                Console.WriteLine(orderStatus.QuantityExecuted);
                Console.WriteLine(orderStatus.RejectReason);
                Console.WriteLine(orderStatus.OrigOrderId);
                Console.WriteLine(orderStatus.OrigClOrdId);
                Console.WriteLine(orderStatus.ReceiveTime);
            };

            //Subscribe to GetOrderFee
            cx.OnGetOrderFee += (sender, e) =>
            {
                Console.WriteLine(e.OrderFee);
                Console.WriteLine(e.ProductId);
            };

            //Subscribe to GetOrderHistory
            cx.OnGetOrderHistory += (sender, e) =>
            {
                foreach (OrderStatus order in e.Orders)
                    Console.WriteLine(
                        $"{order.Account, -5} " +
                        $"{order.ClientOrderId, -3} " +
                        $"{order.Quantity, -2} " +
                        $"{order.OrderType, -2} " +
                        $"{order.Instrument, -2} " +
                        $"{order.Side, -5}" +
                        $"{order.OrderId, -4} " +
                        $"{order.Price, -5} " +
                        $"{order.OrderState, -15} " +
                        $"{order.Quantity, -3} " +
                        $"{order.QuantityExecuted, -3}" +
                        $"{order.RejectReason, -10} " +
                        $"{order.OrigOrderId, -5} " +
                        $"{order.OrigClOrdId, -2} " +
                        $"{order.ReceiveTime, -15}"
                    );
            };

            //Subscribe to GetOpenOrders response.
            cx.OnGetOpenOrders += (sender, e) =>
            {
                foreach (OrderStatus order in e.Orders)
                    Console.WriteLine(
                        $"{order.Account, -5} " +
                        $"{order.ClientOrderId, -3} " +
                        $"{order.Quantity, -2} " +
                        $"{order.OrderType, -2} " +
                        $"{order.Instrument, -2} " +
                        $"{order.Side, -5}" +
                        $"{order.OrderId, -4} " +
                        $"{order.Price, -5} " +
                        $"{order.OrderState, -15} " +
                        $"{order.Quantity, -3} " +
                        $"{order.QuantityExecuted, -3}" +
                        $"{order.RejectReason, -10} " +
                        $"{order.OrigOrderId, -5} " +
                        $"{order.OrigClOrdId, -2} " +
                        $"{order.ReceiveTime, -15}"
                    );
            };

            //Subscribe to CreateWithdrawTicket response.
            cx.OnCreateWithdrawTicket += (sender, e) =>
            {
                Console.WriteLine(e.Result);
                Console.WriteLine(e.ErrorMessage);
                Console.WriteLine(e.ErrorCode);
            };

            //Subscribe to SubscribeLevel 1 response.
            cx.OnSubscribeLevel1 += (sender, e) =>
            {
                MarketDataLevel1 marketData = e.MarketData;
                Console.WriteLine(marketData.InstrumentId);
                Console.WriteLine(marketData.BestBid);
                Console.WriteLine(marketData.BestOffer);
                Console.WriteLine(marketData.LastTradedPx);
                Console.WriteLine(marketData.LastTradedQty);
                Console.WriteLine(marketData.LastTradeTime);
                Console.WriteLine(marketData.SessionOpen);
                Console.WriteLine(marketData.SessionHigh);
                Console.WriteLine(marketData.SessionLow);
                Console.WriteLine(marketData.SessionClose);
                Console.WriteLine(marketData.Volume);
                Console.WriteLine(marketData.CurrentDayVolume);
                Console.WriteLine(marketData.CurrentDayNumTrades);
                Console.WriteLine(marketData.CurrentDayPxChange);
                Console.WriteLine(marketData.Rolling24HrVolume);
                Console.WriteLine(marketData.Rolling24NumTrades);
                Console.WriteLine(marketData.Rolling24HrPxChange);
                Console.WriteLine(marketData.TimeStamp);
            };

            //Subscribe to Level1UpdateEvent event.
            cx.Level1UpdateEvent += (sender, e) =>
            {
                MarketDataLevel1 marketData = e.MarketData;
                Console.WriteLine(marketData.InstrumentId);
                Console.WriteLine(marketData.BestBid);
                Console.WriteLine(marketData.BestOffer);
                Console.WriteLine(marketData.LastTradedPx);
                Console.WriteLine(marketData.LastTradedQty);
                Console.WriteLine(marketData.LastTradeTime);
                Console.WriteLine(marketData.SessionOpen);
                Console.WriteLine(marketData.SessionHigh);
                Console.WriteLine(marketData.SessionLow);
                Console.WriteLine(marketData.SessionClose);
                Console.WriteLine(marketData.Volume);
                Console.WriteLine(marketData.CurrentDayVolume);
                Console.WriteLine(marketData.CurrentDayNumTrades);
                Console.WriteLine(marketData.CurrentDayPxChange);
                Console.WriteLine(marketData.Rolling24HrVolume);
                Console.WriteLine(marketData.Rolling24NumTrades);
                Console.WriteLine(marketData.Rolling24HrPxChange);
                Console.WriteLine(marketData.TimeStamp);
            };

            //Subscribe to UnsubscribeLevel1 response.
            cx.OnUnsubscribeLevel1 += (sender, e) =>
            {
                Console.WriteLine(e.Result);
                Console.WriteLine(e.ErrorMessage);
                Console.WriteLine(e.ErrorCode);
                Console.WriteLine(e.Detail);
            };

            //Subscribe to Subscribelevel2 response.
            cx.OnSubscribeLevel2 += (sender, e) =>
            {
                foreach (MarketDataLevel2 marketData in e.MarketData)
                {
                    Console.WriteLine(
                        $"{marketData.SequenceNumber,-7} " +
                        $"{marketData.NumTraders - 7} " +
                        $"{marketData.Timestamp,-15} " +
                        $"{marketData.ChangeType,-7} " + // 0 (New), 1 (Update), 2 (Delete)
                        $"{marketData.LastTradedPx,-7} " +
                        $"{marketData.NumOrders,-7} " +
                        $"{marketData.Price,-7} " +
                        $"{marketData.InstrumenId,-2} " +
                        $"{marketData.Quantity,-7} " +
                        $"{marketData.Side,-5} "  // 0 (Buy), 1 (Sell)
                    );
                }
            };

            //Subscribe to Level2UpdateEvent event.
            cx.Level2UpdateEvent += (sender, e) =>
            {
                foreach (MarketDataLevel2 marketData in e.MarketData)
                {
                    Console.WriteLine(
                        $"{marketData.SequenceNumber, -7} " +
                        $"{marketData.NumTraders -7} " +
                        $"{marketData.Timestamp, -15} " +
                        $"{marketData.ChangeType, -7} " + // 0 (New), 1 (Update), 2 (Delete)
                        $"{marketData.LastTradedPx, -7} " +
                        $"{marketData.NumOrders, -7} " +
                        $"{marketData.Price, -7} " +
                        $"{marketData.InstrumenId, -2} " +
                        $"{marketData.Quantity, -7} " +
                        $"{marketData.Side, -5} "  // 0 (Buy), 1 (Sell)
                    );
                }
            };

            //Subscribe to UnsubscribeLevel2 response.
            cx.OnUnsubscribeLevel2 += (sender, e) =>
            {
                Console.WriteLine(e.Result);
                Console.WriteLine(e.ErrorMessage);
                Console.WriteLine(e.ErrorCode);
                Console.WriteLine(e.Detail);
            };

            //Subscribe to SubscribeTrades response.
            cx.OnSubscribeTrades += (sender, e) =>
            {
                foreach (MarketTrade trade in e.MarketTrades)
                {
                    Console.WriteLine(
                        $"{trade.TradeId, -4} " +
                        $"{trade.InstrumentId, -2} " +
                        $"{trade.Quantity, -7} " +
                        $"{trade.Price, -7} " +
                        $"{trade.Timestamp, -15} " +
                        $"{trade.Direction, -10} " + //0 (NoChange), 1 (Uptick), 2 (Downtick)
                        $"{trade.TakerSide, -7} "
                    );
                }
            };

            //Subscribe to TradeDataUpdateEvent event.
            cx.TradeDataUpdateEvent += (sender, e) =>
            {
                foreach (MarketTrade trade in e.MarketTrades)
                {
                    Console.WriteLine(
                        $"{trade.TradeId,-4} " +
                        $"{trade.InstrumentId,-2} " +
                        $"{trade.Quantity,-7} " +
                        $"{trade.Price,-7} " +
                        $"{trade.Timestamp,-15} " +
                        $"{trade.Direction,-10} " + //0 (NoChange), 1 (Uptick), 2 (Downtick)
                        $"{trade.TakerSide,-7} "
                    );
                }
            };

            //Subscribe to UnsubscribeTrades response.
            cx.OnUnsubscribeTrades += (sender, e) =>
            {
                Console.WriteLine(e.Result);
                Console.WriteLine(e.ErrorMessage);
                Console.WriteLine(e.ErrorCode);
                Console.WriteLine(e.Detail);
            };

            //Subscribe to AccountPositionEvent
            cx.AccountPositionEvent += (sender, e) =>
            {
                AccountPosition position = e.Position;
                Console.WriteLine(
                    $"{position.AccountId, -2} " +
                    $"{position.ProductId, -2} " +
                    $"{position.ProductSymbol, -4} " +
                    $"{position.Amount, -7} " +
                    $"{position.Hold, -7} " +
                    $"{position.PendingDeposits, -7} " +
                    $"{position.PendingWithdraws, -7} " +
                    $"{position.TotalDayDeposits, -7} " +
                    $"{position.TotalDayWithdraws , -7} "
                );
            };

            //Subscribe to OrderStateEvent
            cx.OrderStateEvent += (sender, e) =>
            {
                OrderStatus order = e.OrderStatus;
                Console.WriteLine(
                    $"{order.Account, -2} " +
                    $"{order.ClientOrderId, -2} " +
                    $"{order.Quantity, -7} " +
                    $"{order.OrderType, -15}" +           // 1 (Market), 2 (Limit), 3 (StopMarket), 4 (StopLimit), 5 (TrailingStopMarket), 6 (TrailingStopLimit)
                    $"{order.Instrument, -2} " +
                    $"{order.Side, -5} " +
                    $"{order.OrderId, -4} " +
                    $"{order.Price, -4} " +
                    $"{order.OrderState, -14} " +
                    $"{order.QuantityExecuted, -7} " +
                    $"{order.ChangeReason, -42} " +
                    $"{order.ReceiveTime, -15} "
                );
            };

            //Subscribe OrderTradeEvent
            cx.OrderTradeEvent += (sender, e) =>
            {
                AccountTrade trade = e.Trade;
                Console.WriteLine(
                        $"{trade.TradeId, -4} " +
                        $"{trade.OrderId, -5} " +
                        $"{trade.AccountId, -2} " +
                        $"{trade.ClientOrderId, -4} " +
                        $"{trade.InstrumentId, -2} " +
                        $"{trade.Side, -5} " +
                        $"{trade.Quantity, -7} " +
                        $"{trade.Price, -7} " +
                        $"{trade.Value, -7} " +
                        $"{trade.TradeTime, -15}"
                    );
            };

            //Subscribe NewOrderRejectEvent
            cx.NewOrderRejectEvent += (sender, e) =>
            {
                Console.WriteLine(e.AccountId);
                Console.WriteLine(e.ClientOrderId);
                Console.WriteLine(e.Status);
                Console.WriteLine(e.RejectReason);
            };

            //Subscribe CancelOrderRejectEvent
            cx.CancelOrderRejectEvent += (sender, e) =>
            {
                Console.WriteLine(e.AccountId);
                Console.WriteLine(e.OrderId);
                Console.WriteLine(e.OrderRevision);
                Console.WriteLine(e.OrderType);
                Console.WriteLine(e.InstrumentId);
                Console.WriteLine(e.Status);
                Console.WriteLine(e.RejectReason);
            };

            //Subscribe MarketStateUpdate
            cx.MarketStateUpdate += (sender, e) =>
            {
                Console.WriteLine(e.EchangeId);
                Console.WriteLine(e.VenueAdapterId);
                Console.WriteLine(e.Action);
                Console.WriteLine(e.PreviousStatus);
                Console.WriteLine(e.NewStatus);
                Console.WriteLine(e.ExchangeDateTime);
            };

            //Subscribe to SubscribeAccountEvents response.
            cx.OnSubscribeAccountEvents += (sender, e) =>
            {
                Console.WriteLine(e.Result);
                Console.WriteLine(e.ErrorMessage);
                Console.WriteLine(e.ErrorCode);
                Console.WriteLine(e.Detail);
            };

            while (true)
            {
                var command = Console.ReadLine();
                switch (command)
                {
                    case "get products":
                        cx.GetProducts();            
                        break;
                    case "get instruments":
                        cx.GetInstruments();
                        break;
                    case "web authenticate user":
                        Console.Write("web authenticate user > enter username : "******"web authenticate user > enter password : "******"get user accounts":
                        cx.GetUserAccounts();
                        break;
                    case "get account transactions":
                        Console.Write("get account transactions > account id : ");
                        int accountId = int.Parse(Console.ReadLine());
                        cx.GetAccountTransactions(accountId, 10);
                        break;
                    case "help":
                    case "/?":
                    default:
                        Console.WriteLine("C# Coins Pro Command Line 0.0.1");
                        Console.WriteLine();
                        Console.WriteLine($"{"help",-25} {"Prints the help."}");
                        Console.WriteLine($"{"/?",-25} {"Prints the help."}");
                        Console.WriteLine($"{"get products",-25} {"Send request to get products."}");
                        Console.WriteLine($"{"get instruments",-25} {"Send request to get instruments."}");
                        Console.WriteLine($"{"web authenticate user",-25} {"Send request to log in to API."}");
                        Console.WriteLine($"{"get user accounts",-25} {"Send request to get user accounts."}");
                        break;
                }
            }
        }