/// <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));
        }
        /// <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));
            }
        }