public async Task <OrderBook> GetOrderBookAsync(OrderBookContext context) { var api = ApiProvider.GetApi(context); var pairCode = context.Pair.ToTicker(this, "").ToLower(); var r = await api.GetOrderBookAsync(pairCode).ConfigureAwait(false); var orderBook = new OrderBook(Network, context.Pair); var date = r.timestamp.ToUtcDateTime(); var asks = context.MaxRecordsCount.HasValue ? r.asks.Take(context.MaxRecordsCount.Value / 2) : r.asks; var bids = context.MaxRecordsCount.HasValue ? r.bids.Take(context.MaxRecordsCount.Value / 2) : r.bids; foreach (var i in bids.Select(GetBidAskData)) { orderBook.Add(new OrderBookRecord(OrderType.Bid, new Money(i.Price, context.Pair.Asset2), i.Amount)); } foreach (var i in asks.Select(GetBidAskData)) { orderBook.Add(new OrderBookRecord(OrderType.Ask, new Money(i.Price, context.Pair.Asset2), i.Amount)); } return(orderBook); }
public async Task <OrderBook> GetOrderBookAsync(OrderBookContext context) { var api = ApiProvider.GetApi(context); var pairCode = context.Pair.ToTicker(this, '_'); var r = context.MaxRecordsCount.HasValue ? await api.GetOrderBookAsync(pairCode, context.MaxRecordsCount.Value / 2).ConfigureAwait(false) : await api.GetOrderBookAsync(pairCode).ConfigureAwait(false); if (r.bids == null || r.asks == null) { throw new NoAssetPairException(context.Pair, this); } var orderBook = new OrderBook(Network, context.Pair); foreach (var i in r.bids) { orderBook.Add(new OrderBookRecord(OrderType.Bid, new Money(i[0], context.Pair.Asset2), i[1])); } foreach (var i in r.asks) { orderBook.Add(new OrderBookRecord(OrderType.Ask, new Money(i[0], context.Pair.Asset2), i[1])); } return(orderBook); }
private async Task <OrderBook> GetOrderBookLocalAsync(IKrakenApi api, AssetPair assetPair, int?maxCount) { var pair = assetPair; var remotePair = new AssetPair(pair.Asset1.ToRemoteCode(this), pair.Asset2.ToRemoteCode(this)); var r = await api.GetOrderBookAsync(remotePair.ToTicker(this), maxCount ?? 0).ConfigureAwait(false); CheckResponseErrors(r); var data = r.result.FirstOrDefault(); var orderBook = new OrderBook(Network, assetPair); var asks = maxCount.HasValue ? data.Value.asks.Take(maxCount.Value / 2).ToArray() : data.Value.asks; var bids = maxCount.HasValue ? data.Value.bids.Take(maxCount.Value / 2).ToArray() : data.Value.bids; foreach (var i in bids.Select(GetBidAskData)) { orderBook.Add(OrderType.Bid, i.Price, i.Volume); } foreach (var i in asks.Select(GetBidAskData)) { orderBook.Add(OrderType.Ask, i.Price, i.Volume); } return(orderBook); }
public Task HandleExternalOrderBookAsync( string source, string assetPairId, IReadOnlyCollection <Order> buyOrders, IReadOnlyCollection <Order> sellOrders) { if (!_matchExternalOrderBooks) { return(Task.CompletedTask); } OrderBook orderBook = _orderBooks.GetOrAdd(assetPairId, new OrderBook(assetPairId, _balancesService)); orderBook.Remove(o => o.IsExternal); foreach (Order order in buyOrders.OrderByDescending(o => o.Price)) { orderBook.Add(order); } foreach (Order order in sellOrders.OrderBy(o => o.Price)) { orderBook.Add(order); } return(PublishOrderBookAsync(orderBook)); }
public async Task <OrderBook> GetOrderBookAsync(OrderBookContext context) { var api = ApiProvider.GetApi(context); var pairCode = context.Pair.ToTicker(this, '_'); var r = await api.GetBoardAsync(pairCode).ConfigureAwait(false); var bids = context.MaxRecordsCount.HasValue ? r.bids.Take(context.MaxRecordsCount.Value / 2) : r.bids; var asks = context.MaxRecordsCount.HasValue ? r.asks.Take(context.MaxRecordsCount.Value / 2) : r.asks; var orderBook = new OrderBook(Network, context.Pair); foreach (var i in bids) { orderBook.Add(new OrderBookRecord(OrderType.Bid, new Money(i.price, context.Pair.Asset2), i.size)); } foreach (var i in asks) { orderBook.Add(new OrderBookRecord(OrderType.Ask, new Money(i.price, context.Pair.Asset2), i.size)); } return(orderBook); }
public async Task <OrderBook> GetOrderBookAsync(OrderBookContext context) { CheckOrderRecordsInputNumber(context.MaxRecordsCount); var api = ApiProvider.GetApi(context); var pairCode = context.Pair.ToTicker(this, ""); var r = context.MaxRecordsCount.HasValue ? await api.GetOrderBookAsync(pairCode, context.MaxRecordsCount.Value / 2).ConfigureAwait(false) : await api.GetOrderBookAsync(pairCode).ConfigureAwait(false); var orderBook = new OrderBook(Network, context.Pair); foreach (var i in r.bids.Select(GetOrderBookRecordData)) { orderBook.Add(new OrderBookRecord(OrderType.Bid, new Money(i.Price, context.Pair.Asset2), i.Volume)); } foreach (var i in r.asks.Select(GetOrderBookRecordData)) { orderBook.Add(new OrderBookRecord(OrderType.Ask, new Money(i.Price, context.Pair.Asset2), i.Volume)); } return(orderBook); }
public void TestTradeCallbacks() { var order0 = new SimpleOrder(false, 3250, 100); var order1 = new SimpleOrder(true, 3250, 800); var order2 = new SimpleOrder(false, 3230, 0); var order3 = new SimpleOrder(false, 3240, 200); var order4 = new SimpleOrder(true, 3250, 600); var listener = new TradeCbListener(); var order_book = new OrderBook(); order_book.SetTradeListener(listener); // Add order, should be accepted order_book.Add(order0); Assert.Equal(0, listener.quantities_.Count); listener.Reset(); // Add matching order, should result in a trade order_book.Add(order1); Assert.Equal(1, listener.quantities_.Count); Assert.Equal(1, listener.costs_.Count); Assert.Equal((Quantity)100, listener.quantities_[0]); Assert.Equal(100 * 3250, listener.costs_[0]); listener.Reset(); // Add invalid order, should be rejected order_book.Add(order2); Assert.Equal(0, listener.quantities_.Count); listener.Reset(); // Cancel only valid order, should be cancelled order_book.Cancel(order1); Assert.Equal(0, listener.quantities_.Count); listener.Reset(); // Cancel filled order, should be rejected order_book.Cancel(order0); Assert.Equal(0, listener.quantities_.Count); listener.Reset(); // Add a new order and replace it, should be replaced order_book.Add(order3); order_book.Replace(order3, 0, 3250); Assert.Equal(0, listener.quantities_.Count); listener.Reset(); // Add matching order, should be accepted, followed by a fill order_book.Add(order4); Assert.Equal(1, listener.quantities_.Count); Assert.Equal(1, listener.costs_.Count); listener.Reset(); // Replace matched order, with too large of a size decrease, replace // should be rejected order_book.Replace(order3, -500, 0); Assert.Equal(0, listener.quantities_.Count); }
public void TestOrderCallbacks() { var order0 = new SimpleOrder(false, 3250, 100); var order1 = new SimpleOrder(true, 3250, 800); var order2 = new SimpleOrder(false, 3230, 0); var order3 = new SimpleOrder(false, 3240, 200); var order4 = new SimpleOrder(true, 3250, 600); var listener = new OrderCbListener(); var order_book = new OrderBook(); order_book.SetOrderListener(listener); // Add order, should be accepted order_book.Add(order0); Assert.AreEqual(1, listener.accepts_.Count); listener.Reset(); // Add matching order, should be accepted, followed by a fill order_book.Add(order1); Assert.AreEqual(1, listener.accepts_.Count); Assert.AreEqual(1, listener.fills_.Count); listener.Reset(); // Add invalid order, should be rejected order_book.Add(order2); Assert.AreEqual(1, listener.rejects_.Count); listener.Reset(); // Cancel only valid order, should be cancelled order_book.Cancel(order1); Assert.AreEqual(1, listener.cancels_.Count); listener.Reset(); // Cancel filled order, should be rejected order_book.Cancel(order0); Assert.AreEqual(1, listener.cancel_rejects_.Count); listener.Reset(); // Add a new order and replace it, should be replaced order_book.Add(order3); order_book.Replace(order3, 0, 3250); Assert.AreEqual(1, listener.accepts_.Count); Assert.AreEqual(1, listener.replaces_.Count); listener.Reset(); // Add matching order, should be accepted, followed by a fill order_book.Add(order4); Assert.AreEqual(1, listener.accepts_.Count); Assert.AreEqual(1, listener.fills_.Count); listener.Reset(); // Replace matched order, with too large of a size decrease, replace // should be rejected order_book.Replace(order3, -500, 0); Assert.AreEqual(0, listener.replaces_.Count); Assert.AreEqual(1, listener.replace_rejects_.Count); }
internal static bool AddAndVerify(OrderBook orderBook, IOrder order, bool matchExpected, bool completeExpected = false, OrderConditions conditions = 0) { var matched = orderBook.Add(order, conditions); if (matched == matchExpected) { if (completeExpected) { return(OrderState.Complete == (order as SimpleOrder)?.State); } else if ((conditions & OrderConditions.ImmediateOrCancel) != 0) { return(OrderState.Cancelled == (order as SimpleOrder)?.State); } else { return(OrderState.Accepted == (order as SimpleOrder)?.State); } } else { return(false); } }
public async Task <Guid> CreateOrderAsync(Order order) { _clientOrdersService.Add(order); OrderBook orderBook = _orderBooks.GetOrAdd(order.Pair, new OrderBook(order.Pair, _balancesService)); orderBook.Add(order); await PublishOrderBookAsync(orderBook); return(order.Id); }
public void ClientHasInsufficientFunds() { var balancesService = new Mock <IBalancesService>(); balancesService.Setup(x => x.UserHasEnoughBalanceForOrder(It.IsAny <Order>())).Returns(false); var ob = new OrderBook(Pair, balancesService.Object); var sell = Order.CreateLimit(ClientId1, TradeType.Sell, Pair, 100, 60); Assert.Throws <InsufficientBalanceException>(() => ob.Add(sell)); Assert.Equal(OrderStatus.Rejected, sell.OrderStatus); }
static void Main(string[] args) { //create order book OrderBook orderBook = new OrderBook(); //start pumping orders //that will be concurrently processed by sweeper thread for (int ctr = 0; ctr <= 10; ctr++) { orderBook.Add(new Order()); } Console.ReadLine(); }
public async Task <OrderBook> GetOrderBookAsync(OrderBookContext context) { var api = ApiProvider.GetApi(context); var pairCode = context.Pair.ToTicker(this, ""); var r = context.MaxRecordsCount.HasValue ? await api.GetOrderBookAsync(pairCode, context.MaxRecordsCount.Value).ConfigureAwait(false) : await api.GetOrderBookAsync(pairCode, 0).ConfigureAwait(false); var buyAction = "buy"; var sellAction = "sell"; var buys = context.MaxRecordsCount.HasValue ? r.Where(x => x.side.ToLower().Equals(buyAction)).OrderBy(x => x.id) .Take(context.MaxRecordsCount.Value / 2).ToList() : r.Where(x => x.side.ToLower().Equals(buyAction)).OrderBy(x => x.id).ToList(); var sells = context.MaxRecordsCount.HasValue ? r.Where(x => x.side.ToLower().Equals(sellAction)).OrderBy(x => x.id) .Take(context.MaxRecordsCount.Value / 2).ToList() : r.Where(x => x.side.ToLower().Equals(sellAction)).OrderBy(x => x.id).ToList(); var orderBook = new OrderBook(Network, context.Pair); foreach (var i in buys) { orderBook.Add(new OrderBookRecord(OrderType.Bid, new Money(i.price, context.Pair.Asset2), i.size)); } foreach (var i in sells) { orderBook.Add(new OrderBookRecord(OrderType.Ask, new Money(i.price, context.Pair.Asset2), i.size)); } return(orderBook); }
/// <summary>Determine the exchange rate based on volume</summary> private static OrderBook MergeRates(OrderBook rates, OrderBook offers, Unit <decimal> balance, bool invert) // Worker thread context { // 'rates' is a table of volumes in the current coin currency (i.e. the current // coin in the loop) along with the accumulated exchange rate for each volume. // 'orders' is a table of 'Base' currency volumes and the offer prices for // converting those volumes to 'Quote' currency. // If 'invert' is true, the 'orders' table is the offers for converting Quote // currency to Base currency, however the volumes and prices are still in Base // and Quote respectively. var new_coin = invert ? offers.Base : offers.Quote; var ret = new OrderBook(new_coin, new_coin, rates.TradeType); // Volume accumulators for the 'rates' and 'orders' order books. var R_vol = 0m._(rates.Base.Symbol); var O_vol = 0m._(rates.Base.Symbol); // The maximum volume available to trade is the minimum of the 'rates' and 'orders' // volumes, hence this loop ends when the last order in either set is reached. for (int r = 0, o = 0; r != rates.Count && o != offers.Count;) { var rate = rates[r]; var offr = offers[o]; // Get the offer price and volume to convert to the current coin currency var price = invert ? 1m / offr.PriceQ2B : offr.PriceQ2B; var volume = invert ? offr.PriceQ2B * offr.AmountBase : offr.AmountBase; // Get the volume available to be traded at 'price' var vol0 = rate.AmountBase < volume ? rate.AmountBase : volume; // Convert this volume to the new currency using 'price' var vol1 = vol0 * price; // Record the volume and the combined rate ret.Add(new Offer(rate.PriceQ2B * price, vol1), validate: false); // Move to the next order in the set with the lowest accumulative volume. // If the same accumulative volume is in both sets, move to the next order in both sets. // Need to be careful with overflow, because special case values use decimal.MaxValue. // Only advance to the next order if the accumulative volume is less than MaxValue. var adv_R = (R_vol < decimal.MaxValue - rate.AmountBase) && // if 'R_vol + rate.AmountBase' does not overflow (R_vol - O_vol <= volume - rate.AmountBase); // and 'R_vol + rate.AmountBase' <= 'O_vol + volume' var adv_O = (O_vol < decimal.MaxValue - volume) && // if 'O_vol + volume' does not overflow (R_vol - O_vol >= volume - rate.AmountBase); // and 'R_vol + rate.AmountBase' >= 'O_vol + volume' if (adv_R) { ++r; R_vol += rate.AmountBase; } if (adv_O) { ++o; O_vol += volume; } if (!adv_R && !adv_O) { break; } // Don't bother calculating for volumes that exceed the current balance if (rates.Count > 5 && R_vol > balance) { break; } } return(ret); }