public void CancelOrder(Order order, OrderOperationCallback callback = null) { string endPoint = "https://api.bitfinex.com/v1/order/cancel"; var client = new RestClient(endPoint, RestClient.HttpVerb.POST); string[] headers; string[] param = new string[] { $"\"order_id\":\"{order.OrderID}\"" }; BuildSignedMessage("/v1/order/cancel", param, out headers); try { var json = client.MakeRequest("", headers, false); var response = (JObject)JsonConvert.DeserializeObject(json); Log.Write($"Cancelled {order}", 2); callback?.Invoke(false); } catch (Exception e) { Log.Write($"Failed to cancel {order}", 0); Log.Write($"Exception: {e}", 0); callback?.Invoke(true); } }
public Order SubmitOrder(double price, double volume, char side, char type, OrderOperationCallback callback = null) { string endPoint = "https://api.bitfinex.com/v1/order/new"; var client = new RestClient(endPoint, RestClient.HttpVerb.POST); string[] headers; string[] param = new string[] { $"\"symbol\":\"{TradeSymbol}\"", $"\"amount\":\"{volume}\"", $"\"price\":\"{price}\"", $"\"side\":\"{(side.Equals(Side.BUY) ? "buy" : "sell")}\"", $"\"type\":\"exchange {(type.Equals(OrdType.LIMIT) ? "limit" : "market")}\"", "\"is_hidden\":false", "\"is_postonly\":false", "\"use_all_available\":false", "\"ocoorder\":false", "\"buy_price_oco\":\"0\"", "\"sell_price_oco\":\"0\"", }; BuildSignedMessage("/v1/order/new", param, out headers); try { var json = client.MakeRequest("", headers, false); var response = (JObject)JsonConvert.DeserializeObject(json); Order order = new Order() { OrderID = response["id"].ToString(), ClientOrderID = (long)response["id"], Price = price, Volume = volume, Side = side, OrderType = type, Time = new DateTime(1970, 1, 1).AddMilliseconds((double)response["timestamp"]) }; Log.Write($"Submitted {order}", 2); callback?.Invoke(false); return(order); } catch (Exception e) { Log.Write($"Failed to submit order", 0); Log.Write($"Exception: {e}", 0); callback?.Invoke(true); return(null); } }
public void CancelOrder(Order order, OrderOperationCallback callback = null) { string endPoint = "https://www.okcoin.com/api/v1/cancel_order.do"; RestClient client = new RestClient(endPoint, RestClient.HttpVerb.POST); Dictionary <string, string> param = new Dictionary <string, string>(); param["api_key"] = APIKey; param["symbol"] = TradeSymbolString; param["order_id"] = order.OrderID; var reqMsg = BuildSignedMessage(param); var json = client.MakeRequest(reqMsg); var response = (JObject)JsonConvert.DeserializeObject(json); if ((bool)response["result"]) { Log.Write($"Cancel {order}", 2); } else { Log.Write($"Cancel order error: {response["error_code"]}", 0); } callback?.Invoke(!(bool)response["result"]); }
public Order SubmitOrder(double price, double volume, char side, char type, OrderOperationCallback callback = null) { string endPoint = "https://www.okcoin.com/api/v1/trade.do"; RestClient client = new RestClient(endPoint, RestClient.HttpVerb.POST); Dictionary <string, string> param = new Dictionary <string, string>(); param["api_key"] = APIKey; param["symbol"] = TradeSymbolString; param["type"] = side == Side.BUY ? "buy" : "sell"; if (type == OrdType.MARKET) { param["type"] += "_market"; } param["price"] = price.ToString(); param["amount"] = volume.ToString(); var reqMsg = BuildSignedMessage(param); var json = client.MakeRequest(reqMsg); var response = (JObject)JsonConvert.DeserializeObject(json); var result = (bool)response["result"]; Order order = null; if (result) { order = new Order() { Price = price, Volume = volume, Time = DateTime.Now, Side = side, OrderType = type, OrderID = response["order_id"].ToString(), ClientOrderID = (long)response["order_id"] }; Log.Write($"Submit {order}", 2); } else { Log.Write($"Submit order error: {response["errorcode"]}", 0); } callback?.Invoke(!result); return(order); }
private void SocketTerminal_MessageReceived(object sender, MessageReceivedEventArgs e) { var dataArray = (JArray)JsonConvert.DeserializeObject(e.Message); for (int i = 0; i < dataArray.Count; i++) { Log.Write($"Message received: {dataArray[i]}\n", 3); var channel = dataArray[i]["channel"].ToString(); // wallet info if (channel.Equals("ok_sub_spotusd_userinfo")) { BalanceUSD = (double)dataArray[i]["data"]["info"]["free"]["usd"]; BalanceBTC = (double)dataArray[i]["data"]["info"]["free"]["btc"]; BalanceLTC = (double)dataArray[i]["data"]["info"]["free"]["ltc"]; } // wallet info else if (channel.Equals("ok_spotusd_userinfo")) { BalanceUSD = (double)dataArray[i]["data"]["info"]["funds"]["free"]["usd"]; BalanceBTC = (double)dataArray[i]["data"]["info"]["funds"]["free"]["btc"]; BalanceLTC = (double)dataArray[i]["data"]["info"]["funds"]["free"]["ltc"]; } // new order response else if (channel.Equals("ok_spotusd_trade")) { var result = (bool)dataArray[i]["data"]["result"]; if (result) { NewOrderID = (int)dataArray[i]["data"]["order_id"]; } else { NewOrderID = -1; Log.Write($"Submit order error: {dataArray[i]["data"]["error_code"]}", 0); } NewOrderWaitHandle.Set(); OrderSubmitCallback?.Invoke(!result); OrderSubmitCallback = null; } //cancel order response else if (channel.Equals("ok_spotusd_cancel_order")) { var result = (bool)dataArray[i]["data"]["result"]; if (result) { CurrentOrders.RemoveAll(o => o.OrderID.Equals(dataArray[i]["data"]["order_id"].ToString())); } else { Log.Write($"Cancel order error: {dataArray[i]["data"]["error_code"]}", 0); } NewOrderWaitHandle.Set(); OrderCancelCallback?.Invoke(!result); OrderSubmitCallback = null; } // order book data else if (channel.Equals("ok_sub_spot_btc_depth") || channel.Equals("ok_sub_spot_ltc_depth")) { var bids = (JArray)dataArray[i]["data"]["bids"]; foreach (JArray bid in bids) { if ((double)bid[1] > 0.0001) { CurrentOrderBook.ChangeOrder((double)bid[0], (double)bid[1], MDEntryType.BID); } else { CurrentOrderBook.RemoveOrder((double)bid[0], MDEntryType.BID); } } var asks = (JArray)dataArray[i]["data"]["asks"]; foreach (JArray ask in asks) { if ((double)ask[1] > 0.0001) { CurrentOrderBook.ChangeOrder((double)ask[0], (double)ask[1], MDEntryType.OFFER); } else { CurrentOrderBook.RemoveOrder((double)ask[0], MDEntryType.OFFER); } } } // live trade data else if (channel.Equals("ok_sub_spotusd_btc_trades") || channel.Equals("ok_sub_spotusd_ltc_trades")) { var trade = (JArray)dataArray[i]["data"]; LastTrade = new Trade() { Price = (double)trade.Last[1], Volume = (double)trade.Last[2] }; } // order info else if (channel.Equals("ok_spotusd_orderinfo")) { if ((bool)dataArray[i]["data"]["result"]) { var orders = (JArray)dataArray[i]["data"]["orders"]; foreach (var order in orders) { // add to existing order if it's not already there if (!CurrentOrders.Exists(o => o.OrderID.Equals(order["order_id"].ToString())) && ((int)order["status"] == 0) || (int)order["status"] == 1) { CurrentOrders.Add(new Order() { OrderID = order["order_id"].ToString(), Time = (DateTime)order["create_date"], Price = (double)order["price"], Volume = (double)order["amount"], Side = order["type"].ToString().Contains("buy") ? Side.BUY : Side.SELL, OrderType = order["type"].ToString().Contains("market") ? OrdType.MARKET : OrdType.LIMIT }); } } } else { Log.Write($"Error in requesting order info: {dataArray[i]["data"]}", 0); } } // sub order/trade info else if (channel.Equals("ok_sub_spotusd_trades")) { var status = (int)dataArray[i]["data"]["status"]; switch (status) { case -1: // cancelled case 2: // filled CurrentOrders.First(o => o.OrderID.Equals(dataArray[i]["data"]["orderId"])).FilledVolume = (double)dataArray[i]["data"]["completedTradeAmount"]; //CurrentOrders.RemoveAll(o => o.OrderID.Equals(dataArray[i]["data"]["orderId"])); break; case 1: // partial fill CurrentOrders.First(o => o.OrderID.Equals(dataArray[i]["data"]["orderId"])).FilledVolume = (double)dataArray[i]["data"]["completedTradeAmount"]; break; } } } }
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); } }
/// <summary> /// Report after a request about orders is made /// Possible cause: /// - New order /// - Cancel order /// - Rejected order /// - Requested order info /// - Order filled or partially filled /// </summary> /// <param name="msg"></param> /// <param name="sessionID"></param> public void OnMessage(QuickFix.FIX44.ExecutionReport msg, SessionID sessionID) { char executionReportType = msg.GetChar(Tags.ExecType); int clientOrderID = msg.GetInt(Tags.ClOrdID); switch (executionReportType) { case ExecType.NEW: { // New order execution report string avgPrice = msg.GetString(Tags.AvgPx); char side = msg.GetChar(Tags.Side); var order = new Order() { OrderID = msg.GetString(Tags.ExecID), ClientOrderID = clientOrderID, Price = double.Parse(avgPrice), Side = side }; CurrentOrders.Add(order); OrderSubmitCallback?.Invoke(false); OrderSubmitCallback = null; Log.Write($"Submission confirmed {order}", 2); break; } case ExecType.PARTIAL_FILL: { // Order executed double executionPrice = (double)msg.GetDecimal(Tags.AvgPx); int orderStatus = msg.GetInt(Tags.OrdStatus); char side = msg.GetChar(Tags.Side); double remainingQty = (double)msg.GetDecimal(Tags.LeavesQty); var order = CurrentOrders.First(o => o.ClientOrderID == clientOrderID); order.FilledVolume = order.Volume - remainingQty; Log.Write($"Partial fill {order}", 2); break; } case ExecType.FILL: { // Order executed double executionPrice = (double)msg.GetDecimal(Tags.AvgPx); int orderStatus = msg.GetInt(Tags.OrdStatus); char side = msg.GetChar(Tags.Side); var order = CurrentOrders.First(o => o.ClientOrderID == clientOrderID); order.FilledVolume = order.Volume; //CurrentOrders.Remove(order); Log.Write($"Fill {order}", 2); break; } case ExecType.CANCELED: { // Order cancel report string orderID = msg.GetString(Tags.ExecID); CurrentOrders.RemoveAll(o => o.ClientOrderID == clientOrderID); OrderCancelCallback?.Invoke(false); OrderCancelCallback = null; break; } case ExecType.REJECTED: { // Rejected order execution report OrderSubmitCallback?.Invoke(true); OrderSubmitCallback = null; string orderStatus = msg.GetString(Tags.OrdStatus); string text = msg.GetString(Tags.Text); Log.Write($"Order rejected | status: {orderStatus} | reason: {text}", 1); break; } case ExecType.PENDING_NEW: { // Requested Order info int numReports = msg.GetInt(Tags.TotNumReports); if (!CurrentOrders.Exists(o => o.ClientOrderID.Equals(clientOrderID))) { CurrentOrders.Add(new Order() { OrderID = msg.GetString(Tags.OrderID), ClientOrderID = clientOrderID, Price = (double)msg.GetDecimal(Tags.AvgPx), Side = msg.GetChar(Tags.Side) }); } break; } default: Log.Write($"Unknown execution report type {executionReportType}", 0); break; } }