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