public OrderBook GetOrderBook() { string endPoint = "https://www.okcoin.com/api/v1/depth.do"; RestClient client = new RestClient(endPoint, RestClient.HttpVerb.GET); string reqMsg = $"?symbol={TradeSymbolString}&size=5"; var json = client.MakeRequest(reqMsg); var response = (JObject)JsonConvert.DeserializeObject(json); var asks = (JArray)response["asks"]; var bids = (JArray)response["bids"]; OrderBook orderBook = new OrderBook(); foreach (var ask in asks) { orderBook.AddOrder((double)ask[0], (double)ask[1], MDEntryType.OFFER); } foreach (var bid in bids) { orderBook.AddOrder((double)bid[0], (double)bid[1], MDEntryType.BID); } return(orderBook); }
public void RemoveOffer() { OrderBook book = new OrderBook(); book.AddOrder(10, 1, MDEntryType.OFFER); book.AddOrder(11, 2, MDEntryType.OFFER); book.AddOrder(12, 3, MDEntryType.OFFER); book.RemoveOrder(10, MDEntryType.OFFER); Assert.AreEqual(11, book.BestOffer.Price); Assert.AreEqual(5, book.CumulativeVolume(12, MDEntryType.OFFER)); }
public void RemoveBid() { OrderBook book = new OrderBook(); book.AddOrder(10, 1, MDEntryType.BID); book.AddOrder(11, 2, MDEntryType.BID); book.AddOrder(12, 3, MDEntryType.BID); book.RemoveOrder(12, MDEntryType.BID); Assert.AreEqual(11, book.BestBid.Price); Assert.AreEqual(3, book.CumulativeVolume(10, MDEntryType.BID)); }
/// <summary> /// Initial market data snapshot /// </summary> /// <param name="msg"></param> /// <param name="sessionID"></param> public void OnMessage(QuickFix.FIX44.MarketDataSnapshotFullRefresh msg, SessionID sessionID) { var numMDEntries = msg.GetInt(Tags.NoMDEntries); for (int i = 1; i <= numMDEntries; i++) { var entry = msg.GetGroup(i, Tags.NoMDEntries); var entryType = entry.GetChar(Tags.MDEntryType); if (entryType.Equals(MDEntryType.BID) || entryType.Equals(MDEntryType.OFFER)) { CurrentOrderBook.AddOrder((double)entry.GetDecimal(Tags.MDEntryPx), (double)entry.GetDecimal(Tags.MDEntrySize), entry.GetChar(Tags.MDEntryType)); } else if (entryType.Equals(MDEntryType.TRADE)) { LastTrade = new Trade() { Price = (double)entry.GetDecimal(Tags.MDEntryPx), Volume = (double)entry.GetDecimal(Tags.MDEntrySize) }; } else { Log.Write($"Unknown entry type {entryType}", 0); } } }
public OrderBook GetOrderBook() { var endPoint = $"https://api.bitfinex.com/v1/book/{TradeSymbol}"; var client = new RestClient(endPoint, RestClient.HttpVerb.GET); var json = client.MakeRequest("?limit_bids=2&limit_asks=2&group=1", null, false); var response = (JObject)JsonConvert.DeserializeObject(json); OrderBook orderBook = new OrderBook(); foreach (var data in response["bids"]) { orderBook.AddOrder((double)data["price"], (double)data["amount"], MDEntryType.BID); } foreach (var data in response["asks"]) { orderBook.AddOrder((double)data["price"], (double)data["amount"], MDEntryType.OFFER); } return(orderBook); }
public void ChangeBid() { OrderBook book = new OrderBook(); book.AddOrder(10, 1, MDEntryType.BID); book.AddOrder(11, 2, MDEntryType.BID); book.AddOrder(12, 3, MDEntryType.BID); book.ChangeOrder(12.5, 4, MDEntryType.BID); Assert.AreEqual(12.5, book.BestBid.Price); Assert.AreEqual(4, book.BestBid.Volume); Assert.AreEqual(12, book.BestBid.Next.Price); Assert.AreEqual(3, book.BestBid.Next.Volume); Assert.AreEqual(4, book.NumBids); Assert.AreEqual(7, book.CumulativeVolume(11.2, MDEntryType.BID)); Assert.AreEqual(10, book.CumulativeVolume(10, MDEntryType.BID)); Assert.IsNull(book.BestOffer); }
public void AddOffers() { OrderBook book = new OrderBook(); book.AddOrder(10, 1, MDEntryType.OFFER); book.AddOrder(11, 2, MDEntryType.OFFER); book.AddOrder(12, 3, MDEntryType.OFFER); Assert.AreEqual(10, book.BestOffer.Price); Assert.AreEqual(1, book.BestOffer.Volume); Assert.AreEqual(11, book.BestOffer.Next.Price); Assert.AreEqual(2, book.BestOffer.Next.Volume); Assert.AreEqual(3, book.NumOffers); Assert.AreEqual(6, book.CumulativeVolume(12, MDEntryType.OFFER)); Assert.AreEqual(1, book.CumulativeVolume(10, MDEntryType.OFFER)); Assert.AreEqual(11, book.PriceDepth(2, MDEntryType.OFFER)); Assert.AreEqual(12, book.PriceDepth(5, MDEntryType.OFFER)); Assert.IsNull(book.BestBid); }
public void AddBids() { OrderBook book = new OrderBook(); book.AddOrder(10, 1, MDEntryType.BID); book.AddOrder(11, 2, MDEntryType.BID); book.AddOrder(12, 3, MDEntryType.BID); Assert.AreEqual(12, book.BestBid.Price); Assert.AreEqual(3, book.BestBid.Volume); Assert.AreEqual(11, book.BestBid.Next.Price); Assert.AreEqual(2, book.BestBid.Next.Volume); Assert.AreEqual(3, book.NumBids); Assert.AreEqual(3, book.CumulativeVolume(12, MDEntryType.BID)); Assert.AreEqual(6, book.CumulativeVolume(10, MDEntryType.BID)); Assert.AreEqual(11, book.PriceDepth(4, MDEntryType.BID)); Assert.AreEqual(12, book.PriceDepth(3, MDEntryType.BID)); Assert.IsNull(book.BestOffer); }
public IHttpActionResult Post([FromBody] AddOrderRequestModel request) { OrderBook.AddOrder(request.IsBuyOrder, request.Amount, request.Count, request.UserId); return(Ok()); }
// QUESTION: Consider making async: https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap public OrderResponseBL SubmitOrder(IOrderContext context, ISubscriber subscriber, IAccountServiceRpcClient accountService, OrderBL order) { #if (PERF || PERF_FINE || PERF_FINEST) long start = Now; #endif OrderResponseBL insertResponse = InsertOrder(context, subscriber, order); if (insertResponse.HasErrors) { return(insertResponse); } OrderBook book = order.IsMarketOrder ? order.IsBuyOrder ? SellBook : BuyBook : order.IsBuyOrder ? BuyBook : SellBook; if (order.IsMarketOrder) { // QUESTION: Could potentially optimize to not do an initial save of a Market order // but can we allow order entry during market close hours? What's MarketOnOpen? // The actual outcome of the match will be responded back asynchronously OrderTransactionResponseBL fillResponse = FillMarketOrder(accountService, order, book); if (fillResponse.HasErrors) { return(insertResponse); } fillResponse = SaveOrderTransaction(context, subscriber, fillResponse.Data); if (fillResponse.HasErrors) { return(insertResponse); } #if DIAGNOSTICS DiagnosticsWriteDetails(fillResponse.Data); #endif #if (PERF || PERF_FINE || PERF_FINEST) Logger.Here().Information(String.Format("SubmitOrder: Market order executed in {0} milliseconds", ((Now - start) / TimeSpan.TicksPerMillisecond))); #endif return(new OrderResponseBL(order)); } book.AddOrder(order); bool done = false; while (!done) { if (BuyBook.IsEmpty || SellBook.IsEmpty) { throw new SystemException("Order book is empty"); // TODO: Handle order book is empty } // The new order triggered a match // We'll try to match it here, but respond back with the insertResponse regardless // The actual outcome of the match will be responded back asynchronously if (BuyBook.First.StrikePrice >= SellBook.First.StrikePrice) { OrderTransactionResponseBL fillResponse = TryFillOrderBook(accountService); if (fillResponse.HasErrors) { return(insertResponse); } fillResponse = SaveOrderTransaction(context, subscriber, fillResponse.Data); if (fillResponse.HasErrors) { return(insertResponse); } #if DIAGNOSTICS DiagnosticsWriteDetails(fillResponse.Data); #endif } else { done = true; } } #if (PERF || PERF_FINE || PERF_FINEST) Logger.Here().Information(String.Format("SubmitOrder: Processed in {0} milliseconds", ((Now - start) / TimeSpan.TicksPerMillisecond))); #endif return(insertResponse); }
private void SocketTerminal_OnMessageReceived(object sender, MessageReceivedEventArgs e) { Log.Write($"Message received: {e.Message}", 3); var data = JsonConvert.DeserializeObject(e.Message); if (data is JObject) { var dataObject = (JObject)data; var eventType = dataObject["event"].ToString(); if (eventType.Equals("subscribed")) { ChannelID.Add(dataObject["channel"].ToString(), (int)dataObject["chanId"]); } else if (eventType.Equals("auth")) { var errorCode = dataObject["code"]; if (errorCode != null) { Log.Write($"Authentication failure with code {errorCode} | msg: {dataObject["msg"]}", 0); } } else if (eventType.Equals("error")) { Log.Write($"Error code: {dataObject["code"]} | msg: {dataObject["msg"]}", 0); } } else if (data is JArray) { var dataArray = (JArray)data; var channelID = (int)dataArray[0]; if (channelID == ChannelID["auth"]) { var msgType = dataArray[1].ToString(); if (msgType.Equals("ws")) // wallet snapshot { var wallets = dataArray[2]; for (var wal = wallets.First; wal != null; wal = wal.Next) { if (wal[0].ToString().Equals("exchange")) { var currency = wal[1].ToString(); if (currency.Equals("USD")) { BalanceFiat = (double)wal[2]; } else if (currency.Equals(Symbol)) { BalanceSecurity = (double)wal[2]; } } } } else if (msgType.Equals("wu")) // wallet update { // Only consider exchange wallets if (dataArray[2][0].ToString().Equals("exchange")) { var currency = dataArray[2][1].ToString(); if (currency.Equals("USD")) { BalanceFiat = (double)dataArray[2][2]; } else if (currency.Equals(Symbol)) { BalanceSecurity = (double)dataArray[2][2]; } } } else if (msgType.Equals("on")) // new order confirmation { string symbol = dataArray[2][3].ToString(); if (symbol.Equals(TradeSymbol)) { var order = new Order() { OrderID = dataArray[2][0].ToString(), ClientOrderID = (long)dataArray[2][2], Price = (double)dataArray[2][16], Time = new DateTime(1970, 1, 1).AddMilliseconds((double)dataArray[2][4]), Volume = Math.Abs((double)dataArray[2][7]), FilledVolume = Math.Abs((double)dataArray[2][6] - (double)dataArray[2][7]), Side = (double)dataArray[2][6] > 0 ? Side.BUY : Side.SELL, OrderType = dataArray[2][8].ToString().Contains("LIMIT") ? OrdType.LIMIT : OrdType.MARKET }; CurrentOrders.Add(order); OrderSubmitCallback?.Invoke(false); OrderSubmitCallback = null; Log.Write($"Submitted {order}", 2); } } else if (msgType.Equals("oc")) // cancel order confirmation { string symbol = dataArray[2][3].ToString(); if (symbol.Equals(TradeSymbol)) { string status = dataArray[2][13].ToString(); string orderId = dataArray[2][0].ToString(); var order = CurrentOrders.FirstOrDefault(o => o.OrderID.Equals(orderId)); if (order != null) { if (status.Contains("EXECUTED")) // executed { order.FilledVolume = Math.Abs((double)dataArray[2][6] - (double)dataArray[2][7]); Log.Write($"Executed {order}", 2); } else if (status.Contains("PARTIALLY FILLED")) { order.FilledVolume = Math.Abs((double)dataArray[2][6] - (double)dataArray[2][7]); Log.Write($"Partial fill {order}", 2); } else // cancelled { CurrentOrders.Remove(order); OrderCancelCallback?.Invoke(false); OrderCancelCallback = null; Log.Write($"Cancelled {order}", 2); } } else { // unregistered order = new Order() { OrderID = dataArray[2][0].ToString(), ClientOrderID = (long)dataArray[2][2], Price = (double)dataArray[2][16], Time = new DateTime(1970, 1, 1).AddMilliseconds((double)dataArray[2][4]), Volume = Math.Abs((double)dataArray[2][7]), FilledVolume = Math.Abs((double)dataArray[2][6] - (double)dataArray[2][7]), Side = (double)dataArray[2][6] > 0 ? Side.BUY : Side.SELL, OrderType = dataArray[2][8].ToString().Contains("LIMIT") ? OrdType.LIMIT : OrdType.MARKET }; if (status.Contains("EXECUTED")) { Log.Write($"Executed unregistered {orderId} (This should happen VERY rarily)", 2); } else { Log.Write($"Cancelled unregistered {orderId}", 2); } } } } else if (msgType.Equals("os")) // order info snapshot { var orders = dataArray[2]; foreach (var order in orders) { string symbol = order[3].ToString(); string orderId = order[0].ToString(); // If order is not yet registered locally if (symbol.Equals(TradeSymbol) && !CurrentOrders.Any(o => o.OrderID.Equals(orderId))) { CurrentOrders.Add(new Order() { OrderID = orderId, ClientOrderID = (long)order[2], Price = (double)order[12], Volume = Math.Abs((double)order[7]), FilledVolume = Math.Abs((double)order[6] - (double)order[7]), Side = (double)order[6] > 0 ? Side.BUY : Side.SELL, OrderType = order[8].ToString().Contains("LIMIT") ? OrdType.LIMIT : OrdType.MARKET, Time = new DateTime(1970, 1, 1).AddMilliseconds((double)order[4]), }); } } } else if (msgType.Equals("ou")) // order info update { string symbol = dataArray[2][3].ToString(); string orderId = dataArray[2][0].ToString(); if (symbol.Equals(TradeSymbol)) { var order = CurrentOrders.FirstOrDefault(o => o.OrderID.Equals(orderId)); if (order == null) { order = new Order() { OrderID = dataArray[2][0].ToString(), ClientOrderID = (int)dataArray[2][2], Time = new DateTime(1970, 1, 1).AddMilliseconds((double)dataArray[2][4]), Volume = Math.Abs((double)dataArray[2][7]), FilledVolume = Math.Abs((double)dataArray[2][6] - (double)dataArray[2][7]), Side = (double)dataArray[2][6] > 0 ? Side.BUY : Side.SELL, OrderType = dataArray[2][8].ToString().Contains("LIMIT") ? OrdType.LIMIT : OrdType.MARKET }; CurrentOrders.Add(order); } else { order.FilledVolume = Math.Abs((double)dataArray[2][6] - (double)dataArray[2][7]); Log.Write($"Update {order}", 2); } } } else if (msgType.Equals("tu")) // trades { string symbol = dataArray[2][1].ToString(); if (TradeSymbol.Contains(symbol)) { string orderId = dataArray[2][3].ToString(); var order = CurrentOrders.FirstOrDefault(o => o.OrderID.Equals(orderId)); if (order != null) { order.FilledVolume = Math.Abs((double)dataArray[2][4]); Log.Write($"Trade info about {order}", 2); if (order.FilledVolume >= order.Volume) { CurrentOrders.Remove(order); } } else { Log.Write($"Trade info about unregistered order", 1); } } } else if (msgType.Equals("n")) // notification { string status = dataArray[2][6].ToString(); if (status.Equals("ERROR")) { Log.Write($"Error: {dataArray[2][7].ToString()}", 0); string req = dataArray[2][1].ToString(); if (req.Equals("on-req")) // new order error { OrderSubmitCallback?.Invoke(true); } else if (req.Equals("oc-req")) // cancel order error { OrderCancelCallback?.Invoke(true); } } else if (status.Equals("FAILURE")) { Log.Write($"Failure: {dataArray[2][7].ToString()}", 0); } else if (status.Equals("SUCCESS")) { Log.Write($"Notification: {dataArray[2][7].ToString()}", 2); } else if (status.Equals("INFO")) { Log.Write($"Information: {dataArray[2][7].ToString()}", 2); } } } else if (ChannelID.ContainsKey("trades") && channelID == ChannelID["trades"]) { if (dataArray[1] is JArray) // snapshot { var trades = dataArray[1]; var lastTrade = trades.First; LastTrade = new Trade() { Price = (double)lastTrade[3], Volume = (double)lastTrade[2] }; } else // update { if (dataArray[1].ToString().Equals("te")) { LastTrade = new Trade() { Price = (double)dataArray[2][3], Volume = (double)dataArray[2][2] }; } } } else if (ChannelID.ContainsKey("book") && channelID == ChannelID["book"]) { // heartbeat if (dataArray[1].ToString().Equals("hb")) { } // Snapshot else if (dataArray[1][0] is JArray) { foreach (var entry in dataArray[1]) { int count = (int)entry[1]; if (count == 0) { Log.Write("Error: zero count in order book snapshot", 0); } else { double volume = (double)entry[2]; CurrentOrderBook.AddOrder((double)entry[0], volume, volume > 0 ? MDEntryType.BID : MDEntryType.OFFER); } } } // Update else { int count = (int)dataArray[1][1]; if (count > 0) // change bid { CurrentOrderBook.ChangeOrder((double)dataArray[1][0], (double)dataArray[1][2], (double)dataArray[1][2] > 0 ? MDEntryType.BID : MDEntryType.OFFER); } else // remove { CurrentOrderBook.RemoveOrder((double)dataArray[1][0], (double)dataArray[1][2] > 0 ? MDEntryType.BID : MDEntryType.OFFER); } } } } else { Log.Write($"Unknown message json type: {data.GetType()}", 0); } }