예제 #1
0
        internal void ParseFromMarketOrderPriceZero(OrderSide side)
        {
            var order = new OrderUpdate(
                orderId: 0,
                tradeId: 0,
                orderType: Market,
                orderStatus: New,
                createdTimestamp: 0,
                setPrice: 0.4M,
                side: side,
                pair: TradingPair.Parse("EOSETH"),
                setQuantity: 40M)
            {
                AverageFilledPrice = 0M,
                FilledQuantity     = 10M,
            };

            var exec = TradeExecution.FromOrder(order);

            Assert.Equal(exec.From.Locked, decimal.Zero);

            if (side == OrderSide.Sell)
            {
                Assert.Equal(10M, exec.From.Free);
                Assert.Equal(decimal.Zero, exec.To.Free);
            }
            else
            {
                Assert.Equal(decimal.Zero, exec.From.Free);
                Assert.Equal(10M, exec.To.Free);
            }

            Assert.Equal(exec.To.Locked, decimal.Zero);
        }
예제 #2
0
 public void ToExternalOrderSideConversion(OrderSide side)
 {
     Binance.Net.Objects.OrderSide converted = BinanceUtilities.ToExternal(side);
     Binance.Net.Objects.OrderSide check     = side == OrderSide.Buy
         ? Binance.Net.Objects.OrderSide.Buy
         : Binance.Net.Objects.OrderSide.Sell;
     Assert.Equal(converted, check);
 }
예제 #3
0
        /// <summary>
        /// Convert SpreadShare.Models to Binance.Net.
        /// </summary>
        /// <param name="side">SpreadShare.Models.OrderSide.</param>
        /// <returns>Binance.Net.OrderSide.</returns>
        public static Binance.Net.Objects.OrderSide ToExternal(OrderSide side)
        {
            switch (side)
            {
            case OrderSide.Buy:
                return(Binance.Net.Objects.OrderSide.Buy);

            case OrderSide.Sell:
                return(Binance.Net.Objects.OrderSide.Sell);

            default:
                throw new ArgumentException($"{side} not a known order side");
            }
        }
예제 #4
0
        internal void ParseFromMarketCancelledOrderInvalid(OrderSide side)
        {
            var order = new OrderUpdate(
                orderId: 0,
                tradeId: 0,
                orderType: Market,
                orderStatus: Cancelled,
                createdTimestamp: 0,
                setPrice: 0.2M,
                side: side,
                pair: TradingPair.Parse("EOSETH"),
                setQuantity: 100M);

            Assert.Throws <ArgumentException>(() => TradeExecution.FromOrder(order));
        }
예제 #5
0
        internal void ParseFromMarketFilled(OrderSide side)
        {
            var order = new OrderUpdate(
                orderId: 0,
                tradeId: 0,
                orderType: Market,
                orderStatus: Filled,
                createdTimestamp: 0,
                setPrice: 0.2M,
                side: side,
                pair: TradingPair.Parse("EOSETH"),
                setQuantity: 100M);
            var exec = TradeExecution.FromOrder(order);

            // Assert that the order is calculated as a complete market order
            Assert.NotEqual(exec.From.Symbol, exec.To.Symbol);
            Assert.Equal(0M, exec.From.Locked);
            Assert.Equal(0M, exec.To.Locked);
        }
예제 #6
0
        internal void ParseFromMarketOrderForeignCommission(OrderSide side)
        {
            var order = new OrderUpdate(
                orderId: 0,
                tradeId: 0,
                orderType: OrderUpdate.OrderTypes.Market,
                orderStatus: Filled,
                createdTimestamp: 0,
                setPrice: 0.2M,
                side: side,
                pair: TradingPair.Parse("EOSETH"),
                setQuantity: 100M)
            {
                FilledQuantity     = 100M,
                AverageFilledPrice = 0.15M,
                LastFillIncrement  = 100M,
                LastFillPrice      = 0.15M,
                Commission         = 42.69M,
                CommissionAsset    = new Currency("BNB"),
            };

            var exec = TradeExecution.FromOrder(order);

            if (side == OrderSide.Buy)
            {
                Assert.Equal(100M * 0.15M, exec.From.Free);
                Assert.Equal(100M, exec.To.Free);
            }
            else
            {
                Assert.Equal(100M, exec.From.Free);
                Assert.Equal(100M * 0.15M, exec.To.Free);
            }

            Assert.Equal(0, exec.To.Locked);
            Assert.Equal(0, exec.From.Locked);
        }
예제 #7
0
        internal void ParseFromLimitFilledDifferentPrice(OrderSide side)
        {
            var order = new OrderUpdate(
                orderId: 0,
                tradeId: 0,
                orderType: Limit,
                orderStatus: Filled,
                createdTimestamp: 0,
                setPrice: 0.2M,
                side: side,
                pair: TradingPair.Parse("EOSETH"),
                setQuantity: 100M)
            {
                FilledQuantity     = 100M,
                AverageFilledPrice = 0.15M,
                LastFillIncrement  = 100M,
                LastFillPrice      = 0.15M,
            };

            var exec = TradeExecution.FromOrder(order);

            Assert.Equal(0M, exec.From.Free);
            if (side == OrderSide.Buy)
            {
                // Allocation was locked using the set price, and should be freed as such
                Assert.Equal(100M * 0.2M, exec.From.Locked);
                Assert.Equal(100M, exec.To.Free);
            }
            else
            {
                Assert.Equal(100M, exec.From.Locked);
                Assert.Equal(100M * 0.15M, exec.To.Free);
            }

            Assert.Equal(0, exec.To.Locked);
        }
예제 #8
0
 private static OrderUpdate IsSide(this OrderUpdate order, OrderSide side)
 => order.Side == side
         ? order
         : throw new UnexpectedOrderSideException(
           $"Order {order.OrderId} has unexpected side, expected {side}, got {order.Side}");
예제 #9
0
        /// <inheritdoc />
        public override ResponseObject <OrderUpdate> PlaceLimitOrder(TradingPair pair, OrderSide side, decimal quantity, decimal price, long tradeId)
        {
            // Add the order to the watchlist
            OrderUpdate order = new OrderUpdate(
                _mockOrderCounter++,
                tradeId,
                OrderUpdate.OrderStatus.New,
                OrderUpdate.OrderTypes.Limit,
                Timer.CurrentTime.ToUnixTimeMilliseconds(),
                price,
                side,
                pair,
                quantity);

            // Add to order cache to confirm placement
            _orderCache.Enqueue(order);

            // Add to watch list to check if filled
            WatchList.Add(order.OrderId, order);

            return(new ResponseObject <OrderUpdate>(ResponseCode.Success, order));
        }
예제 #10
0
        /// <inheritdoc />
        public override ResponseObject <OrderUpdate> ExecuteMarketOrder(TradingPair pair, OrderSide side, decimal quantity, long tradeId)
        {
            decimal priceEstimate = _dataProvider.GetCurrentPriceTopBid(pair).Data;

            var order = new OrderUpdate(
                _mockOrderCounter++,
                tradeId,
                OrderUpdate.OrderStatus.Filled,
                OrderUpdate.OrderTypes.Market,
                Timer.CurrentTime.ToUnixTimeMilliseconds(),
                priceEstimate,
                side,
                pair,
                quantity)
            {
                AverageFilledPrice = priceEstimate,
                FilledQuantity     = quantity,
                FilledTimestamp    = Timer.CurrentTime.ToUnixTimeMilliseconds(),
            };

            // Add to order cache to confirm filled
            _orderCache.Enqueue(order);

            // Write the trade to the logger
            LogOrder(order);

            return(new ResponseObject <OrderUpdate>(
                       ResponseCode.Success,
                       order));
        }
예제 #11
0
        /// <inheritdoc />
        public override ResponseObject <OrderUpdate> PlaceStoplossOrder(TradingPair pair, OrderSide side, decimal quantity, decimal price, long tradeId)
        {
            // Add the order to the watchlist
            OrderUpdate order = new OrderUpdate(
                orderId: _mockOrderCounter++,
                tradeId: tradeId,
                orderStatus: OrderUpdate.OrderStatus.New,
                orderType: OrderUpdate.OrderTypes.StopLoss,
                createdTimestamp: Timer.CurrentTime.ToUnixTimeMilliseconds(),
                setPrice: 0,
                side: side,
                pair: pair,
                setQuantity: quantity)
            {
                StopPrice = price,
            };

            // Add to order cache to confirm placement.
            _orderCache.Enqueue(order);

            // Add to watchlist to check if filled
            WatchList.Add(order.OrderId, order);

            return(new ResponseObject <OrderUpdate>(ResponseCode.Success, order));
        }
예제 #12
0
        public void ToInternalOrderSideConversion(Binance.Net.Objects.OrderSide side)
        {
            OrderSide converted = BinanceUtilities.ToInternal(side);

            Assert.Equal(converted, side == Binance.Net.Objects.OrderSide.Buy ? OrderSide.Buy : OrderSide.Sell);
        }
예제 #13
0
        /// <inheritdoc />
        public override ResponseObject <OrderUpdate> ExecuteMarketOrder(TradingPair pair, OrderSide side, decimal quantity, long tradeId)
        {
            var client       = _communications.Client;
            var realQuantity = pair.RoundToTradable(quantity);

            // Attempt to place the order on Binance
            var query = client.PlaceOrder(
                symbol: pair.ToString(),
                side: BinanceUtilities.ToExternal(side),
                type: OrderType.Market,
                quantity: realQuantity,
                newClientOrderId: null,
                price: null,
                timeInForce: null,
                stopPrice: null,
                icebergQty: null,
                orderResponseType: null,
                (int)_communications.ReceiveWindow);

            // Report failure of placing market order
            if (!query.Success)
            {
                Logger.LogError($"Placing market order {side} {realQuantity} {pair.Left} failed! --> {query.Error.Message}");
                return(new ResponseObject <OrderUpdate>(ResponseCode.Error, query.Error.Message));
            }

            var order = query.Data;

            // Create an order update with known information
            OrderUpdate result = new OrderUpdate(
                orderId: order.OrderId,
                tradeId: tradeId,
                orderStatus: OrderUpdate.OrderStatus.Filled,
                orderType: BinanceUtilities.ToInternal(order.Type),
                createdTimestamp: DateTimeOffset.Now.ToUnixTimeMilliseconds(),
                setPrice: 0, // This information is unknown for market orders
                side: side,
                pair: pair,
                setQuantity: realQuantity)
            {
                FilledQuantity     = order.ExecutedQuantity,
                FilledTimestamp    = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
                AverageFilledPrice = HelperMethods.SafeDiv(order.CummulativeQuoteQuantity, order.ExecutedQuantity),
            };

            return(new ResponseObject <OrderUpdate>(ResponseCode.Success, result));
        }
예제 #14
0
        /// <inheritdoc />
        public override ResponseObject <OrderUpdate> PlaceStoplossOrder(TradingPair pair, OrderSide side, decimal quantity, decimal price, long tradeId)
        {
            var     client = _communications.Client;
            decimal limitPrice;

            if (side == OrderSide.Sell)
            {
                // Set the limit price extremely low -> sell immediately for the best price.
                // 5% is an arbitrary number that is probably more than the spread, but is not
                // rejected by Binance for deviating too much from the current price.
                limitPrice = price * 0.95M;
            }
            else
            {
                // Skew the quantity and the price -> buy immediately for the best price.
                // Quantity must scale inverse because (quantity * price) is the amount that needs to
                // be locked. You cannot lock more assets than you have.
                // 2% is hardcoded on purpose because it is unlikely to change.
                limitPrice = price * 1.02M;
                quantity  /= 1.02M;
            }

            var realQuantity   = pair.RoundToTradable(quantity);
            var realLimitPrice = pair.RoundToPriceable(limitPrice);
            var realStopPrice  = pair.RoundToPriceable(price);

            lock (_orderCache)
            {
                var query = client.PlaceOrder(
                    symbol: pair.ToString(),
                    side: BinanceUtilities.ToExternal(side),
                    type: OrderType.StopLossLimit,
                    quantity: realQuantity,
                    newClientOrderId: null,
                    price: realLimitPrice,
                    timeInForce: TimeInForce.GoodTillCancel,
                    stopPrice: realStopPrice,
                    icebergQty: null,
                    orderResponseType: null,
                    receiveWindow: (int)_communications.ReceiveWindow);

                if (query.Success)
                {
                    var order = new OrderUpdate(
                        query.Data.OrderId,
                        tradeId,
                        OrderUpdate.OrderStatus.New,
                        OrderUpdate.OrderTypes.StopLoss,
                        DateTimeOffset.Now.ToUnixTimeMilliseconds(),
                        realLimitPrice,
                        side,
                        pair,
                        realQuantity)
                    {
                        StopPrice = realStopPrice,
                    };

                    // Enter middleware instance to make sure this order is
                    // also converted to a stoploss order when the exchange reports updates.
                    _transformMiddleWare.Add(order.OrderId, x => x.OrderType = OrderUpdate.OrderTypes.StopLoss);

                    return(new ResponseObject <OrderUpdate>(order));
                }

                return(ResponseObject.OrderPlacementFailed(BinanceUtilities.ToInternalError(query.Error.Code), query.Error.Message));
            }
        }
예제 #15
0
        /// <inheritdoc />
        public override ResponseObject <OrderUpdate> PlaceLimitOrder(TradingPair pair, OrderSide side, decimal quantity, decimal price, long tradeId)
        {
            var client       = _communications.Client;
            var realQuantity = pair.RoundToTradable(quantity);
            var realPrice    = pair.RoundToPriceable(price);

            var query = client.PlaceOrder(
                symbol: pair.ToString(),
                side: BinanceUtilities.ToExternal(side),
                type: OrderType.Limit,
                quantity: realQuantity,
                newClientOrderId: null,
                price: realPrice,
                timeInForce: TimeInForce.GoodTillCancel,
                stopPrice: null,
                icebergQty: null,
                orderResponseType: null,
                receiveWindow: (int)_communications.ReceiveWindow);

            return(query.Success
                ? new ResponseObject <OrderUpdate>(
                       ResponseCode.Success,
                       new OrderUpdate(
                           query.Data.OrderId,
                           tradeId,
                           OrderUpdate.OrderStatus.New,
                           OrderUpdate.OrderTypes.Limit,
                           DateTimeOffset.Now.ToUnixTimeMilliseconds(),
                           realPrice,
                           side,
                           pair,
                           realQuantity))
                : ResponseObject.OrderPlacementFailed(BinanceUtilities.ToInternalError(query.Error.Code), query.Error.Message));
        }