Beispiel #1
0
        protected static async Task TestRequest(IWebSocketClientConnection connection)
        {
            // Simple keep-alive mechanism using TestRequest/Heartbeat
            long nextExpectedCounter = 0;
            bool disconnect          = false;

            do
            {
                await Task.Delay(_testRequestDelay);

                if (!connection.IsConnected)
                {
                    break;
                }

                if (!connection.EnableTestRequest)
                {
                    continue;
                }

                if (nextExpectedCounter > connection.receivedMessageCounter)
                {
                    if (!disconnect)
                    {
                        connection.SendTestRequest();
                        disconnect = true;
                    }
                    else
                    {
                        // second chance before disconnecting
                        await Task.Delay(_testRequestDelay);

                        if (nextExpectedCounter > connection.receivedMessageCounter)
                        {
                            connection.OnLogEvent(LogStatusType.ERROR, "Websocket connection not responding");
                            connection.Shutdown();
                            break;
                        }
                        disconnect = false;
                    }
                }
                else
                {
                    disconnect = false;
                }

                // update expectation for next iteration
                nextExpectedCounter = connection.receivedMessageCounter + 1;
            } while (connection.IsConnected);
        }
        private void OnBrokerNotification(object sender, SystemEventArgs evt)
        {
            IWebSocketClientConnection webSocketConnection = (IWebSocketClientConnection)sender;

            try
            {
                switch (evt.evtType)
                {
                case SystemEventType.LOGIN_OK:
                    LogStatus(LogStatusType.INFO, "Processing after succesful LOGON");
                    this._myUserID = evt.json["UserID"].Value <ulong>();
                    // disable test request to avoid disconnection during the "slow" market data processing
                    webSocketConnection.EnableTestRequest = false;
                    StartInitialRequestsAfterLogon(webSocketConnection);
                    break;

                case SystemEventType.MARKET_DATA_REQUEST_REJECT:
                    LogStatus(LogStatusType.ERROR, "Unexpected Marketdata Request Reject");
                    webSocketConnection.Shutdown();
                    break;

                case SystemEventType.MARKET_DATA_FULL_REFRESH:
                {
                    string symbol = evt.json["Symbol"].Value <string>();
                    // dump the order book
                    LogStatus(LogStatusType.WARN, _allOrderBooks[symbol].ToString());
                    // bring back the testrequest keep-alive mechanism after processing the book
                    webSocketConnection.EnableTestRequest = true;
                    // run the trading strategy to buy and sell orders based on the top of the book
                    _tradingStrategy.runStrategy(webSocketConnection, symbol);
                    // TODO: remove the temp dump bellow
                    this._vwapForTradingSym.PrintTradesAndTheVWAP();
                    // example how to notify the application to start
                    //this._tradingStrategy.OnStart(webSocketConnection);
                }
                break;

                // --- Order Book Management Events ---
                case SystemEventType.ORDER_BOOK_CLEAR:
                {
                    string    symbol    = evt.json["Symbol"].Value <string>();
                    OrderBook orderBook = null;
                    if (_allOrderBooks.TryGetValue(symbol, out orderBook))
                    {
                        orderBook.Clear();
                    }
                    else
                    {
                        orderBook = new OrderBook(symbol);
                        _allOrderBooks.Add(symbol, orderBook);
                    }
                }
                break;

                case SystemEventType.ORDER_BOOK_NEW_ORDER:
                {
                    string    symbol    = evt.json["Symbol"].Value <string>();
                    OrderBook orderBook = null;
                    if (_allOrderBooks.TryGetValue(symbol, out orderBook))
                    {
                        orderBook.AddOrder(evt.json);
                    }
                    else
                    {
                        LogStatus(LogStatusType.ERROR,
                                  "Order Book not found for Symbol " + symbol + " @ " + evt.evtType.ToString());
                    }
                }
                break;

                case SystemEventType.ORDER_BOOK_DELETE_ORDERS_THRU:
                {
                    string    symbol    = evt.json["Symbol"].Value <string>();
                    OrderBook orderBook = null;
                    if (_allOrderBooks.TryGetValue(symbol, out orderBook))
                    {
                        orderBook.DeleteOrdersThru(evt.json);
                    }
                    else
                    {
                        LogStatus(LogStatusType.ERROR,
                                  "Order Book not found for Symbol " + symbol + " @ " + evt.evtType.ToString()
                                  );
                    }
                }
                break;

                case SystemEventType.ORDER_BOOK_DELETE_ORDER:
                {
                    string    symbol    = evt.json["Symbol"].Value <string>();
                    OrderBook orderBook = null;
                    if (_allOrderBooks.TryGetValue(symbol, out orderBook))
                    {
                        orderBook.DeleteOrder(evt.json);
                    }
                    else
                    {
                        LogStatus(LogStatusType.ERROR,
                                  "Order Book not found for Symbol " + symbol + " @ " + evt.evtType.ToString()
                                  );
                    }
                }
                break;

                case SystemEventType.ORDER_BOOK_UPDATE_ORDER:
                {
                    string    symbol    = evt.json["Symbol"].Value <string>();
                    OrderBook orderBook = null;
                    if (_allOrderBooks.TryGetValue(symbol, out orderBook))
                    {
                        orderBook.UpdateOrder(evt.json);
                    }
                    else
                    {
                        LogStatus(LogStatusType.ERROR,
                                  "Order Book not found for Symbol " + symbol + " @ " + evt.evtType.ToString()
                                  );
                    }
                }
                break;
                // ------------------------------------

                case SystemEventType.TRADE_CLEAR:
                    LogStatus(LogStatusType.WARN, "Receieved Market Data Event " + evt.evtType.ToString());
                    break;

                case SystemEventType.SECURITY_STATUS:
                {
                    LogStatus(LogStatusType.WARN,
                              "Receieved Market Data Event " +
                              evt.evtType.ToString() + " " +
                              (evt.json != null ? evt.json.ToString() : ".")
                              );

                    SecurityStatus securityStatus = new SecurityStatus();
                    securityStatus.Market = evt.json["Market"].Value <string>();
                    securityStatus.Symbol = evt.json["Symbol"].Value <string>();
                    securityStatus.LastPx = evt.json["LastPx"].Value <ulong>();
                    securityStatus.HighPx = evt.json["HighPx"].Value <ulong>();

                    if (evt.json["BestBid"].Type != JTokenType.Null)
                    {
                        securityStatus.BestBid = evt.json["BestBid"].Value <ulong>();
                    }
                    else
                    {
                        securityStatus.BestBid = 0;
                    }

                    if (evt.json["BestAsk"].Type != JTokenType.Null)
                    {
                        securityStatus.BestAsk = evt.json["BestAsk"].Value <ulong>();
                    }
                    else
                    {
                        securityStatus.BestAsk = 0;
                    }

                    if (evt.json["LowPx"].Type != JTokenType.Null)
                    {
                        securityStatus.LowPx = evt.json["LowPx"].Value <ulong>();
                    }
                    else
                    {
                        securityStatus.LowPx = 0;
                    }

                    securityStatus.SellVolume = evt.json["SellVolume"].Value <ulong>();
                    securityStatus.BuyVolume  = evt.json["BuyVolume"].Value <ulong>();

                    // update the security status information
                    string securityKey = securityStatus.Market + ":" + securityStatus.Symbol;
                    _securityStatusEntries[securityKey] = securityStatus;

                    // update the strategy when a new market information arrives
                    _tradingStrategy.runStrategy(webSocketConnection, _tradingSymbol);
                }
                break;

                case SystemEventType.TRADE:
                {
                    JObject msg = evt.json;
                    LogStatus(LogStatusType.WARN, "Receieved Market Data Event " + evt.evtType.ToString() + msg);

                    _vwapForTradingSym.pushTrade(
                        new ShortPeriodTickBasedVWAP.Trade(
                            msg["TradeID"].Value <ulong>(),
                            msg["Symbol"].Value <string>(),
                            msg["MDEntryPx"].Value <ulong>(),
                            msg["MDEntrySize"].Value <ulong>(),
                            String.Format("{0} {1}", msg["MDEntryDate"].Value <string>(), msg["MDEntryTime"].Value <string>())
                            )
                        );

                    /*
                     * LogStatus(
                     *      LogStatusType.INFO,
                     *      String.Format(
                     *              "New Trade : VWAP = {0} | LastPx = {1} - {2} | Size = {3}",
                     *              _vwapForTradingSym.calculateVWAP(),
                     *              _vwapForTradingSym.getLastPx(),
                     *              msg["MDEntryPx"].Value<ulong>(),
                     *              msg["MDEntrySize"].Value<ulong>()
                     *      )
                     * );
                     */
                }
                break;

                case SystemEventType.TRADING_SESSION_STATUS:
                    break;

                case SystemEventType.MARKET_DATA_INCREMENTAL_REFRESH:
                    LogStatus(LogStatusType.WARN, "Receieved Market Data Incremental Refresh : " + evt.evtType.ToString());
                    // update the strategy when an incremental message is processed
                    _tradingStrategy.runStrategy(webSocketConnection, _tradingSymbol);
                    break;

                // --- Order Entry Replies ---
                case SystemEventType.EXECUTION_REPORT:
                {
                    LogStatus(LogStatusType.WARN, "Receieved " + evt.evtType.ToString() + "\n" + evt.json.ToString());
                    MiniOMS.IOrder order = ProcessExecutionReport(evt.json);
                    _tradingStrategy.OnExecutionReport(webSocketConnection, order);
                }
                break;

                case SystemEventType.ORDER_LIST_RESPONSE:
                {
                    // process the requested list of orders
                    JObject msg = evt.json;
                    LogStatus(LogStatusType.WARN,
                              "Received " + evt.evtType.ToString() + " : " + "Page=" + msg["Page"].Value <string>()
                              );
                    JArray ordersLst = msg["OrdListGrp"].Value <JArray>();

                    if (ordersLst != null && ordersLst.Count > 0)
                    {
                        var columns = msg["Columns"].Value <JArray>();
                        Dictionary <string, int> indexOf = new Dictionary <string, int>();
                        int index = 0;
                        foreach (JToken col in columns)
                        {
                            indexOf.Add(col.Value <string>(), index++);
                        }

                        foreach (JArray data in ordersLst)
                        {
                            MiniOMS.Order order = new MiniOMS.Order();
                            order.ClOrdID     = data[indexOf["ClOrdID"]].Value <string>();
                            order.OrderID     = data[indexOf["OrderID"]].Value <ulong>();
                            order.Symbol      = data[indexOf["Symbol"]].Value <string>();
                            order.Side        = data[indexOf["Side"]].Value <char>();
                            order.OrdType     = data[indexOf["OrdType"]].Value <char>();
                            order.OrdStatus   = data[indexOf["OrdStatus"]].Value <char>();
                            order.AvgPx       = data[indexOf["AvgPx"]].Value <ulong>();
                            order.Price       = data[indexOf["Price"]].Value <ulong>();
                            order.OrderQty    = data[indexOf["OrderQty"]].Value <ulong>();
                            order.OrderQty    = data[indexOf["LeavesQty"]].Value <ulong>();
                            order.CumQty      = data[indexOf["CumQty"]].Value <ulong>();
                            order.CxlQty      = data[indexOf["CxlQty"]].Value <ulong>();
                            order.Volume      = data[indexOf["Volume"]].Value <ulong>();
                            order.OrderDate   = data[indexOf["OrderDate"]].Value <string>();
                            order.TimeInForce = data[indexOf["TimeInForce"]].Value <char>();
                            LogStatus(LogStatusType.WARN,
                                      "Adding Order to MiniOMS -> ClOrdID = " + order.ClOrdID.ToString() +
                                      " OrdStatus = " + order.OrdStatus
                                      );
                            try
                            {
                                _miniOMS.AddOrder(order);
                            }
                            catch (System.ArgumentException)
                            {
                            }
                        }

                        // check and request the next page
                        if (ordersLst.Count >= msg["PageSize"].Value <int>())
                        {
                            LogStatus(LogStatusType.INFO, "Requesting Page " + msg["Page"].Value <int>() + 1);
                            SendRequestForOpenOrders(webSocketConnection, msg["Page"].Value <int>() + 1);
                        }
                        else
                        {
                            LogStatus(LogStatusType.INFO, "EOT - no more Order List pages to process.");
                            // notify application that all requestes where replied,
                            // assuming the ORDER_LIST_REQUEST was the last in the StartInitialRequestsAfterLogon
                            //_tradingStrategy.OnStart(webSocketConnection);
                        }
                    }
                }
                break;

                case SystemEventType.BALANCE_RESPONSE:
                    if (evt.json != null)
                    {
                        //JObject receivedBalances = evt.json[_brokerId.ToString()].Value<JObject>();
                        foreach (var rb in evt.json[_brokerId.ToString()].Value <JObject>())
                        {
                            try
                            {
                                this._balances[rb.Key] = rb.Value.Value <ulong>();
                            }
                            catch (System.OverflowException)
                            {
                                // TODO: find a better solution for this kind of conversion problem
                                // {"4": {"BRL_locked": -1, "BTC_locked": 0, "BRL": 48460657965, "BTC": 50544897}, "MsgType": "U3", "ClientID": 90826379, "BalanceReqID": 3}
                                this._balances[rb.Key] = 0;
                            }
                        }
                        // update the strategy when the balance is updated
                        _tradingStrategy.runStrategy(webSocketConnection, _tradingSymbol);
                    }
                    break;

                case SystemEventType.TRADE_HISTORY_RESPONSE:
                {
                    JObject msg = evt.json;
                    LogStatus(LogStatusType.WARN,
                              "Received " + evt.evtType.ToString() + " : " + "Page=" + msg["Page"].Value <string>()
                              );

                    /*
                     * JArray all_trades = msg["TradeHistoryGrp"].Value<JArray>();
                     *
                     * if (all_trades != null && all_trades.Count > 0)
                     * {
                     *      var columns = msg["Columns"].Value<JArray>();
                     *      Dictionary<string, int> indexOf = new Dictionary<string, int>();
                     *      int index = 0;
                     *      foreach (JToken col in columns)
                     *      {
                     *              indexOf.Add(col.Value<string>(), index++);
                     *      }
                     *
                     *      foreach (JArray trade in all_trades)
                     *      {
                     *              _vwapForTradingSym.pushTrade(
                     *                      new ShortPeriodTickBasedVWAP.Trade(
                     *                              trade[indexOf["TradeID"]].Value<ulong>(),
                     *                              trade[indexOf["Market"]].Value<string>(),
                     *                              trade[indexOf["Price"]].Value<ulong>(),
                     *                              trade[indexOf["Size"]].Value<ulong>(),
                     *                              trade[indexOf["Created"]].Value<string>()
                     *                      )
                     *              );
                     *      }
                     *
                     *      // check and request the next page
                     *      if (all_trades.Count >= msg["PageSize"].Value<int>())
                     *      {
                     *              LogStatus(LogStatusType.INFO, "TODO: Requesting Page " + msg["Page"].Value<int>() + 1);
                     *              //TODO: create a function to call here and request a new page if requested period in minutes is not satified
                     *      }
                     *      else
                     *      {
                     *              LogStatus(LogStatusType.INFO, "EOT - no more Trade History pages to process.");
                     *      }
                     *
                     *      LogStatus(LogStatusType.INFO, String.Format("VWAP = {0}", _vwapForTradingSym.calculateVWAP()));
                     * }
                     */
                }
                    //
                    break;

                case SystemEventType.CLOSED:
                    // notify the application the connection was broken
                    //_tradingStrategy.OnClose(webSocketConnection);
                    break;

                // Following events are ignored because inheritted behaviour is sufficient for this prototype
                case SystemEventType.OPENED:

                case SystemEventType.ERROR:
                case SystemEventType.LOGIN_ERROR:
                case SystemEventType.HEARTBEAT:
                    break;

                default:
                    LogStatus(LogStatusType.WARN, "Unhandled Broker Notification Event : " + evt.evtType.ToString());
                    break;
                }
            }
            catch (Exception ex)
            {
                LogStatus(LogStatusType.ERROR,
                          " OnBrokerNotification Event Handler Error : " + ex.Message.ToString() + "\n" + ex.StackTrace
                          );
            }
        }
Beispiel #3
0
        public void OnMessage(string message, IWebSocketClientConnection connection)
        {
            JObject msg     = JsonConvert.DeserializeObject <JObject>(message);
            string  msgType = msg["MsgType"].Value <string>();

            switch (msgType)
            {
            case "BF":     //Login response:
            {
                if (msg.GetValue("UserReqTyp") != null && msg.GetValue("UserReqTyp").Value <int>() == 3)
                {
                    Debug.Assert(connection.IsLoggedOn);
                    DispatchEvent(SystemEventType.CHANGE_PASSWORD_RESPONSE, connection, msg);
                    break;
                }

                if (msg["UserStatus"].Value <int>() == 1)
                {
                    Debug.Assert(!connection.IsLoggedOn);
                    connection.IsLoggedOn = true;
                    OnLogEvent(LogStatusType.INFO, "Received LOGIN_OK response");
                    DispatchEvent(SystemEventType.LOGIN_OK, connection, msg);
                }
                else
                {
                    connection.IsLoggedOn = false;
                    OnLogEvent(LogStatusType.WARN,
                               "Received LOGIN_ERROR response : " + msg["UserStatusText"].Value <string>()
                               );
                    DispatchEvent(SystemEventType.LOGIN_ERROR, connection, msg);
                    connection.Shutdown();
                }
                break;
            }

            case "W":
                Debug.Assert(connection.IsLoggedOn);
                if (msg["MarketDepth"].Value <int>() != 1)    // Has Market Depth
                {
                    DispatchEvent(SystemEventType.ORDER_BOOK_CLEAR, connection, msg);
                    DispatchEvent(SystemEventType.TRADE_CLEAR, connection, msg);

                    foreach (JObject entry in msg["MDFullGrp"])
                    {
                        entry["MDReqID"] = msg["MDReqID"];
                        switch (entry["MDEntryType"].Value <char>())
                        {
                        case '0':         // Bid
                        case '1':         // Offer
                            entry["Symbol"] = msg["Symbol"];
                            DispatchEvent(SystemEventType.ORDER_BOOK_NEW_ORDER, connection, entry);
                            break;

                        case '2':         // Trade
                            DispatchEvent(SystemEventType.TRADE, connection, entry);
                            break;

                        case '4':         // Trading Session Status
                            DispatchEvent(SystemEventType.TRADING_SESSION_STATUS, connection, entry);
                            break;
                        }
                    }
                }

                DispatchEvent(SystemEventType.MARKET_DATA_FULL_REFRESH, connection, msg);
                break;

            case "X":
                if (msg["MDBkTyp"].Value <int>() == 3)    // Order Depth
                {
                    foreach (JObject entry in msg["MDIncGrp"])
                    {
                        entry["MDReqID"] = msg["MDReqID"];
                        switch (entry["MDEntryType"].Value <char>())
                        {
                        case '0':         // Bid
                        case '1':         // Offer
                            switch (entry["MDUpdateAction"].Value <char>())
                            {
                            case '0':
                                DispatchEvent(SystemEventType.ORDER_BOOK_NEW_ORDER, connection, entry);
                                break;

                            case '1':
                                DispatchEvent(SystemEventType.ORDER_BOOK_UPDATE_ORDER, connection, entry);
                                break;

                            case '2':
                                DispatchEvent(SystemEventType.ORDER_BOOK_DELETE_ORDER, connection, entry);
                                break;

                            case '3':
                                DispatchEvent(SystemEventType.ORDER_BOOK_DELETE_ORDERS_THRU, connection, entry);
                                break;
                            }
                            break;

                        case '2':         // Trade
                            DispatchEvent(SystemEventType.TRADE, connection, entry);
                            break;

                        case '4':         // Trading Session Status
                            DispatchEvent(SystemEventType.TRADING_SESSION_STATUS, connection, entry);
                            break;
                        }
                    }
                }
                else
                {
                    // TODO:  Top of the book handling.
                }
                DispatchEvent(SystemEventType.MARKET_DATA_INCREMENTAL_REFRESH, connection, msg);
                break;

            case "Y":
                DispatchEvent(SystemEventType.MARKET_DATA_REQUEST_REJECT, connection, msg);
                break;

            case "f":
                DispatchEvent(SystemEventType.SECURITY_STATUS, connection, msg);
                break;

            case "U3":
                DispatchEvent(SystemEventType.BALANCE_RESPONSE, connection, msg);
                break;

            case "U5":
                DispatchEvent(SystemEventType.ORDER_LIST_RESPONSE, connection, msg);
                break;

            case "8":      //Execution Report
                if (msg.GetValue("Volume") == null || msg.GetValue("Volume").Type == JTokenType.Null)
                {
                    if (msg.GetValue("AvgPx") != null && msg.GetValue("AvgPx").Type != JTokenType.Null && msg.GetValue("AvgPx").Value <ulong>() > 0)
                    {
                        msg["Volume"] = (ulong)(msg["CumQty"].Value <ulong>() * (float)(msg["AvgPx"].Value <ulong>() / 1e8));
                    }
                    else
                    {
                        msg["Volume"] = 0;
                    }
                }
                DispatchEvent(SystemEventType.EXECUTION_REPORT, connection, msg);
                break;

            case "U33":                     // Trade History Response
                DispatchEvent(SystemEventType.TRADE_HISTORY_RESPONSE, connection, msg);
                break;

            case "0":
                DispatchEvent(SystemEventType.HEARTBEAT, connection, msg);
                break;

            case "ERROR":
                OnLogEvent(LogStatusType.ERROR, msg.ToString());
                DispatchEvent(SystemEventType.ERROR, connection, msg);
                connection.Shutdown();
                break;

            default:
            {
                Debug.Assert(connection.IsLoggedOn);
                OnLogEvent(LogStatusType.WARN, "Unhandled message type : " + msgType);
                break;
            }
            }
        }