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); }
/// <summary> /// Queue a trade based on a proposal, the callback must return the trade execution /// which will be used to update the allocation. /// </summary> /// <param name="p">TradeProposal to be verified.</param> /// <param name="tradeCallback">Trade callback to be executed if verification was successful.</param> /// <returns>Boolean indicating successful execution of the callback.</returns> public virtual ResponseObject <OrderUpdate> QueueTrade(TradeProposal p, Func <OrderUpdate> tradeCallback) { Guard.Argument(_allocation).NotNull("Initialise allocations first"); Guard.Argument(p).NotNull(); var alloc = GetAvailableFunds(p.From.Symbol); if (alloc.Free < p.From.Free || alloc.Locked < p.From.Locked) { _logger.LogCritical($"Got trade proposal for ({p.From}, but allocation " + $"showed only ({alloc}) was available\n" + "Trade will not be executed."); return(ResponseObject.OrderRefused); } // Let the provider execute the trade and save the execution report var order = tradeCallback(); // TradingProvider can give a null execution report when it decides not to trade. // if this happens the portfolio will be checked against the remote using an 'empty' or 'monoid' trade execution. if (order is null) { _logger.LogWarning("TradingProvider implementation returned a null OrderUpdate"); return(ResponseObject.OrderPlacementFailed("Implementation returned a null OrderUpdate")); } TradeExecution exec = TradeExecution.FromOrder(order); // Update the local information UpdateAllocation(exec); return(new ResponseObject <OrderUpdate>(order)); }
internal void ParseFromMarketSellOrderNewHappyFlow() { var order = new OrderUpdate( orderId: 0, tradeId: 0, orderType: Market, orderStatus: New, createdTimestamp: 0, setPrice: 0.4M, side: OrderSide.Sell, pair: TradingPair.Parse("EOSETH"), setQuantity: 40M) { FilledQuantity = 40M, AverageFilledPrice = 0.401M, }; var exec = TradeExecution.FromOrder(order); Assert.Equal(order.Pair.Left, exec.From.Symbol); Assert.Equal(order.Pair.Right, exec.To.Symbol); Assert.Equal(40M, exec.From.Free); Assert.Equal(0, exec.From.Locked); Assert.Equal(40M * 0.401M, exec.To.Free); Assert.Equal(0, exec.To.Locked); }
internal void ParseFromNonMarketBuyOrderForeignCommission(OrderUpdate.OrderTypes type) { var order = new OrderUpdate( orderId: 0, tradeId: 0, orderType: type, orderStatus: Filled, createdTimestamp: 0, setPrice: 0.2M, side: OrderSide.Buy, pair: TradingPair.Parse("EOSETH"), setQuantity: 100M) { FilledQuantity = 100M, AverageFilledPrice = 0.15M, LastFillIncrement = 100M, LastFillPrice = 0.15M, Commission = 96.42M, CommissionAsset = new Currency("BNB"), }; var exec = TradeExecution.FromOrder(order); Assert.Equal(0M, exec.From.Free); Assert.Equal(100M * 0.2M, exec.From.Locked); Assert.Equal(100M, exec.To.Free); Assert.Equal(0M, exec.To.Locked); }
internal void ParseFromNonMarketSellOrderCancelledHappyFlow(OrderUpdate.OrderTypes orderType) { var order = new OrderUpdate( orderId: 0, tradeId: 0, orderType: orderType, orderStatus: Cancelled, createdTimestamp: 0, setPrice: 0.2M, side: OrderSide.Sell, pair: TradingPair.Parse("EOSETH"), setQuantity: 100M); var exec = TradeExecution.FromOrder(order); Assert.NotNull(exec); var symbol = new Currency("EOS"); Assert.Equal(symbol, exec.From.Symbol); Assert.Equal(symbol, exec.To.Symbol); Assert.Equal(0M, exec.From.Free); Assert.Equal(100M, exec.From.Locked); Assert.Equal(100M, exec.To.Free); Assert.Equal(0M, exec.To.Locked); }
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); }
private void UpdateAllocation(OrderUpdate order) { Guard.Argument(order).NotNull(nameof(order)); // Skip Market orders, there are filled at execution time. if (order.OrderType == OrderUpdate.OrderTypes.Market) { return; } // Skip new orders, their allocation is already processed if (order.Status == OrderUpdate.OrderStatus.New) { return; } var exec = TradeExecution.FromOrder(order); _allocationManager.UpdateAllocation(exec); }
internal void ParseFromMarketOrderCommission(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) { FilledQuantity = 100M, AverageFilledPrice = 0.15M, LastFillIncrement = 100M, LastFillPrice = 0.15M, Commission = 2.3M, CommissionAsset = new Currency("EOS"), }; var exec = TradeExecution.FromOrder(order); if (side == OrderSide.Buy) { Assert.Equal(100M * 0.15M, exec.From.Free); Assert.Equal(100M - 2.3M, exec.To.Free); } else { Assert.Equal(100M, exec.From.Free); Assert.Equal((100M * 0.15M) - (2.3M * 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); }