Beispiel #1
0
        private void sendOrder(IWebSocketClientConnection webSocketConnection, string symbol, char side, ulong qty, ulong price)
        {
            Debug.Assert(side == OrderSide.BUY || side == OrderSide.SELL);

            if (side != OrderSide.BUY && side != OrderSide.SELL)
            {
                throw new ArgumentException();
            }

            if (qty >= _minTradeSize)
            {
                // send order (Participate don't initiate - aka book or cancel) and keep track of the ClOrdId
                string clorid = _tradeclient.SendOrder(
                    webSocketConnection,
                    symbol,
                    qty,
                    price,
                    side,
                    _tradeclient.BrokerId,
                    MakeClOrdId(),
                    OrdType.LIMIT,
                    ExecInst.PARTICIPATE_DONT_INITIATE
                    );

                if (side == OrderSide.BUY)
                {
                    _strategyBuyOrderClorid = clorid;
                }
                else
                {
                    _strategySellOrderClorid = clorid;
                }
            }
        }
        public void SendRequestToCancelAllOrders(IWebSocketClientConnection connection)
        {
            JObject order_cancel_request = new JObject();

            order_cancel_request["MsgType"]     = "F";
            order_cancel_request["FingerPrint"] = connection.Device.FingerPrint;
            order_cancel_request["STUNTIP"]     = connection.Device.Stuntip;
            connection.SendMessage(order_cancel_request.ToString());
        }
        //

        public string SendOrder(IWebSocketClientConnection connection,
                                string symbol,
                                ulong qty,
                                ulong price,
                                char side,
                                int broker_id,
                                string client_order_id,
                                char order_type = '2',
                                char execInst   = default(char))
        {
            // add pending new order to the OMS
            MiniOMS.Order orderToSend = new MiniOMS.Order();
            orderToSend.Symbol    = symbol;
            orderToSend.OrderQty  = qty;
            orderToSend.Price     = price;
            orderToSend.Side      = side;
            orderToSend.ClOrdID   = client_order_id;
            orderToSend.OrdType   = order_type;
            orderToSend.OrdStatus = 'A'; // PENDING_NEW according to FIX std
            try
            {
                _miniOMS.AddOrder(orderToSend);
            }
            catch (Exception ex)
            {
                LogStatus(LogStatusType.ERROR,
                          "The MiniOMS Rejected the Order : " +
                          orderToSend.ClOrdID + ";" +
                          orderToSend.OrderQty + ";" +
                          orderToSend.Price.ToString() + ";\n"
                          + ex.Message.ToString() + "\n"
                          + ex.StackTrace
                          );
                return(null);
            }

            // send the order to the broker
            JObject new_order_single = new JObject();

            new_order_single["MsgType"]  = "D";
            new_order_single["ClOrdID"]  = orderToSend.ClOrdID;
            new_order_single["Symbol"]   = orderToSend.Symbol;
            new_order_single["Side"]     = orderToSend.Side.ToString();
            new_order_single["OrdType"]  = orderToSend.OrdType.ToString();
            new_order_single["Price"]    = orderToSend.Price;
            new_order_single["OrderQty"] = orderToSend.OrderQty;
            new_order_single["BrokerID"] = broker_id;
            if (execInst != default(char))
            {
                new_order_single["ExecInst"] = execInst.ToString();
            }
            new_order_single["FingerPrint"] = connection.Device.FingerPrint;
            new_order_single["STUNTIP"]     = connection.Device.Stuntip;
            connection.SendMessage(new_order_single.ToString());
            return(orderToSend.ClOrdID);
        }
        private void SendRequestForOpenOrders(IWebSocketClientConnection connection, int page = 0)
        {
            JObject orders_list_request = new JObject();

            orders_list_request["MsgType"]     = "U4";
            orders_list_request["OrdersReqID"] = connection.NextOutgoingSeqNum();
            orders_list_request["Page"]        = page;
            orders_list_request["PageSize"]    = 20;
            orders_list_request["Filter"]      = new JArray("has_leaves_qty eq 1" /*"has_cum_qty eq 1"*/);
            connection.SendMessage(orders_list_request.ToString());
        }
Beispiel #5
0
        private void replaceOrder(IWebSocketClientConnection webSocketConnection, string symbol, char side, ulong price, ulong qty = 0)
        {
            Debug.Assert(side == OrderSide.BUY || side == OrderSide.SELL);

            string existingClorId = side == OrderSide.BUY ? this._strategyBuyOrderClorid : this._strategySellOrderClorid;

            var orderToReplace = _tradeclient.miniOMS.GetOrderByClOrdID(existingClorId);

            if (qty == 0)
            {
                qty = calculateOrderQty(symbol, side, price);
            }

            if (qty < _minOrderSize)
            {
                return;                 // order is too small to send
            }

            // cancel the previous sent order since it is not possible to modify the order
            if (orderToReplace != null)
            {
                switch (orderToReplace.OrdStatus)
                {
                case OrdStatus.PENDING_NEW:     // client control - in the case no response was received
                case OrdStatus.PENDING_CANCEL:
                    LogStatus(
                        LogStatusType.WARN,
                        String.Format(
                            "WAITING ORDER STATE CHANGE : {0} CLORDID {1} SIDE {2}",
                            orderToReplace.OrdStatus.ToString(),
                            orderToReplace.ClOrdID,
                            side)
                        );
                    return;     // wait the confirmation of the NEW or CANCEL (TODO: create a timeout to avoid ad-eternum wait)

                case OrdStatus.NEW:
                case OrdStatus.PARTIALLY_FILLED:
                    // cancel the order to replace it
                    if (orderToReplace.Price == price && orderToReplace.LeavesQty == qty)
                    {
                        return;                                 // order is essencially the same and should not be replaced
                    }
                    _tradeclient.CancelOrderByClOrdID(webSocketConnection, orderToReplace.ClOrdID);
                    break;
                //return;

                default:
                    break;
                }
            }

            // send a new order
            sendOrder(webSocketConnection, symbol, side, qty, price);
        }
Beispiel #6
0
        public void SendTestRequest(IWebSocketClientConnection connection)
        {
            JObject test_request = new JObject();

            test_request["MsgType"]     = "1";
            test_request["FingerPrint"] = connection.Device.FingerPrint;
            test_request["STUNTIP"]     = connection.Device.Stuntip;
            test_request["TestReqID"]   = connection.NextOutgoingSeqNum().ToString();
            test_request["SendTime"]    = Util.ConvertToUnixTimestamp(DateTime.Now).ToString();
            string test_request_msg = test_request.ToString();

            connection.SendMessage(test_request_msg);
        }
Beispiel #7
0
        public void OnClose(IWebSocketClientConnection webSocketConn)
        {
            Debug.Assert(!webSocketConn.IsConnected);
            webSocketConn.IsLoggedOn = false;
            OnLogEvent(LogStatusType.ERROR, "WebSocket closed.");
            DispatchEvent(SystemEventType.CLOSED, webSocketConn);
            bool bRetVal = _connections.Remove(webSocketConn);

            if (bRetVal)
            {
                OnLogEvent(LogStatusType.INFO, "Removed connection : " + webSocketConn.ToString());
            }
        }
Beispiel #8
0
 protected virtual void DispatchEvent(
     SystemEventType evtType,
     IWebSocketClientConnection connection,
     JObject json = null)
 {
     if (SystemEvent != null)
     {
         SystemEventArgs args = new SystemEventArgs();
         args.evtType = evtType;
         args.json    = json;
         SystemEvent(connection, args);
     }
 }
Beispiel #9
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);
        }
Beispiel #10
0
 public void OnExecutionReport(IWebSocketClientConnection webSocketConnection, MiniOMS.IOrder order)
 {
     if (_priceType == PriceType.PEGGED && _strategySide == OrderSide.SELL)
     {
         if (order.OrdStatus == OrdStatus.FILLED || order.OrdStatus == OrdStatus.PARTIALLY_FILLED)
         {
             ulong theSoldAmount = _tradeclient.GetSoldAmount();
             if (theSoldAmount >= _maxAmountToSell)
             {
                 LogStatus(LogStatusType.WARN, String.Format("[OnExecutionReport] Cannot exceed the allowed max amount to sell : {0} {1}", theSoldAmount, _maxAmountToSell));
                 _tradeclient.CancelOrderByClOrdID(webSocketConnection, _strategySellOrderClorid);
             }
         }
     }
 }
Beispiel #11
0
        private void replaceOrder(IWebSocketClientConnection webSocketConnection, string symbol, char side, ulong price, ulong qty = 0)
        {
            Debug.Assert(side == OrderSide.BUY || side == OrderSide.SELL);

            string existingClorId = side == OrderSide.BUY ? this._strategyBuyOrderClorid : this._strategySellOrderClorid;

            var orderToReplace = _tradeclient.miniOMS.GetOrderByClOrdID(existingClorId);

            // cancel the previous sent order since it is not possible to modify the order
            if (orderToReplace != null)
            {
                switch (orderToReplace.OrdStatus)
                {
                case OrdStatus.PENDING_NEW:     // client control - in the case no response was received
                case OrdStatus.PENDING_CANCEL:
                    LogStatus(
                        LogStatusType.WARN,
                        String.Format(
                            "WAITING ORDER STATE CHANGE : {0} CLORDID {1} SIDE {2}",
                            orderToReplace.OrdStatus.ToString(),
                            orderToReplace.ClOrdID,
                            side)
                        );
                    return;     // wait the confirmation

                case OrdStatus.NEW:
                case OrdStatus.PARTIALLY_FILLED:
                    // cancel the order to replace it
                    _tradeclient.CancelOrderByClOrdID(webSocketConnection, orderToReplace.ClOrdID);
                    break;

                default:
                    break;
                }
            }

            if (qty == 0)
            {
                qty = calculateOrderQty(symbol, side, price);
            }

            // send a new buy order
            sendOrder(webSocketConnection, symbol, side, qty, price);
        }
 public bool CancelOrderByClOrdID(IWebSocketClientConnection connection, string clOrdID)
 {
     MiniOMS.Order orderToCancel = _miniOMS.GetOrderByClOrdID(clOrdID);
     if (orderToCancel != null)
     {
         if (orderToCancel.OrdStatus == OrdStatus.NEW || orderToCancel.OrdStatus == OrdStatus.PARTIALLY_FILLED)
         {
             orderToCancel.OrdStatus = OrdStatus.PENDING_CANCEL;
             JObject order_cancel_request = new JObject();
             order_cancel_request["MsgType"]     = "F";
             order_cancel_request["ClOrdID"]     = clOrdID;
             order_cancel_request["FingerPrint"] = connection.Device.FingerPrint;
             order_cancel_request["STUNTIP"]     = connection.Device.Stuntip;
             connection.SendMessage(order_cancel_request.ToString());
             return(true);
         }
     }
     return(false);
 }
Beispiel #13
0
        public void OnOpen(IWebSocketClientConnection connection)
        {
            Debug.Assert(connection.IsConnected);
            Debug.Assert(!connection.IsLoggedOn);

            OnLogEvent(LogStatusType.INFO, "Connection Succeeded");

            _connections.Add(connection);

            // dispatch the connection opened
            DispatchEvent(SystemEventType.OPENED, connection);

            // build the json Login Request Message
            JObject login_request = new JObject();

            login_request["MsgType"]            = "BE";
            login_request["UserReqID"]          = connection.NextOutgoingSeqNum();
            login_request["UserReqTyp"]         = "1";
            login_request["Username"]           = connection.UserAccount.Username;
            login_request["Password"]           = connection.UserAccount.Password;
            login_request["BrokerID"]           = connection.UserAccount.BrokerId;
            login_request["CancelOnDisconnect"] = "1";             // enabled so that all session orders are automatically cancelled upon a disconnection (should work in next backend version)
            if (connection.UserAccount.SecondFactor != null && connection.UserAccount.SecondFactor != string.Empty)
            {
                login_request["SecondFactor"] = connection.UserAccount.SecondFactor;
            }
            login_request["UserAgent"]               = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36";
            login_request["UserAgentLanguage"]       = "en-US";
            login_request["UserAgentTimezoneOffset"] = ":180,";
            login_request["UserAgentPlatform"]       = "Linux x86_64";
            login_request["FingerPrint"]             = connection.Device.FingerPrint;
            login_request["STUNTIP"] = connection.Device.Stuntip;

            // send the login request Message on wire
            connection.SendMessage(login_request.ToString());
        }
Beispiel #14
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;
            }
            }
        }
Beispiel #15
0
 public void OnError(string ErrorMessage, IWebSocketClientConnection webSocketConn)
 {
     OnLogEvent(LogStatusType.ERROR, ErrorMessage);
     DispatchEvent(SystemEventType.ERROR, webSocketConn);
 }
Beispiel #16
0
        /*abstract*/ public void runStrategy(IWebSocketClientConnection webSocketConnection, string symbol)
        {
            // Run the strategy to try to have an order on at least one side of the book according to fixed price range
            // but never executing as a taker

            if (!_enabled)               // strategy cannot run when disabled
            {
                LogStatus(LogStatusType.WARN, "Strategy is disabled and will not run");
                return;
            }

            // get the BTC Price in dollar
            SecurityStatus btcusd_quote = _tradeclient.GetSecurityStatus("BITSTAMP", "BTCUSD");

            if (btcusd_quote == null || btcusd_quote.LastPx == 0)
            {
                if (_priceType == PriceType.TRAILING_STOP || _priceType == PriceType.PEGGED)
                {
                    LogStatus(LogStatusType.WARN, "BITSTAMP:BTCUSD not available");
                    return;
                }
            }

            // get the dollar price
            SecurityStatus usd_official_quote = _tradeclient.GetSecurityStatus("UOL", "USDBRL"); // use USDBRT for the turism quote

            if (usd_official_quote == null || usd_official_quote.BestAsk == 0 || usd_official_quote.BestBid == 0)
            {
                if (_priceType == PriceType.TRAILING_STOP || _priceType == PriceType.PEGGED)
                {
                    LogStatus(LogStatusType.WARN, "UOL:USDBRL not available");
                    return;
                }
            }

            OrderBook orderBook = _tradeclient.GetOrderBook(symbol);

            if (orderBook == null)
            {
                LogStatus(LogStatusType.ERROR, "Order Book not available for " + symbol);
                return;
            }


            if (_priceType == PriceType.TRAILING_STOP)
            {
                if (_strategySide == OrderSide.SELL && _stop_price > 0)
                {
                    if (btcusd_quote.LastPx <= _stop_price)
                    {
                        // trigger the stop when the price goes down
                        ulong stop_price_floor = (ulong)(Math.Round(_stop_price / 1e8 * usd_official_quote.BestAsk / 1e8 * _stop_price_adjustment_factor, 2) * 1e8);
                        ulong best_bid_price   = orderBook.BestBid != null ? orderBook.BestBid.Price : 0;
                        ulong availableQty     = calculateOrderQty(symbol, OrderSide.SELL, stop_price_floor, ulong.MaxValue);
                        Console.WriteLine("DEBUG ** Triggered Trailing Stop and should execute ASAP ** [{0}],[{1}],[{2}],[{3}]", btcusd_quote.LastPx, best_bid_price, stop_price_floor, availableQty);
                        if (best_bid_price >= stop_price_floor)
                        {
                            // force a minimal execution as maker to get e-mail notification when the trailing stop is triggered
                            availableQty = availableQty > _minOrderSize ? availableQty - _minOrderSize : availableQty;
                            sendOrder(webSocketConnection, symbol, OrderSide.SELL, availableQty, stop_price_floor, OrdType.LIMIT, 0, ExecInst.DEFAULT, TimeInForce.IMMEDIATE_OR_CANCEL);
                        }
                        // change the strategy so that the bot might negociate the leaves qty as a maker applying another limit factor as a sell floor
                        _priceType        = PriceType.PEGGED;
                        _sell_floor_price = stop_price_floor;
                        Console.WriteLine("DEBUG Changed Strategy to FLOAT with SELL_FLOOR_PRICE=[{0}]", _sell_floor_price);
                    }
                    else
                    {
                        // when the market goes up - adjust the stop in case of a new high price in BTCUSD
                        if (btcusd_quote.LastPx > _trailing_stop_high_price)
                        {
                            _stop_price = (ulong)(Math.Round(btcusd_quote.LastPx / 1e8 * (1 - _stoppx_offset_percentage / 100), 3) * 1e8);
                            _trailing_stop_high_price = btcusd_quote.LastPx;
                            Console.WriteLine("DEBUG Trailing STOP [StopPx={0}] [HighPx=[{1}] [EntryPx={2}] [CapPx={3}]", _stop_price, _trailing_stop_high_price, _trailing_stop_entry_price, _trailing_stop_cap_price);
                        }
                        // and check we should make profit
                        if (_trailing_stop_cap_price > 0 && btcusd_quote.LastPx > _trailing_stop_cap_price)
                        {
                            ulong  best_bid_price        = orderBook.BestBid != null ? orderBook.BestBid.Price : 0;
                            double local_exchange_spread = (best_bid_price / 1e8) / (double)(btcusd_quote.LastPx / 1e8 * usd_official_quote.BestBid / 1e8);
                            if (local_exchange_spread > 1)
                            {
                                _sell_floor_price = (ulong)(Math.Round(btcusd_quote.LastPx / 1e8 * usd_official_quote.BestBid / 1e8 * _stop_price_adjustment_factor, 2) * 1e8);
                                ulong availableQty = calculateOrderQty(symbol, OrderSide.SELL, _sell_floor_price, ulong.MaxValue);
                                Console.WriteLine("DEBUG ** Reached Trailing Stop CAP and should execute ASAP ** [{0}],[{1}],[{2}],[{3}]", btcusd_quote.LastPx, best_bid_price, _sell_floor_price, availableQty);
                                if (best_bid_price >= _sell_floor_price)
                                {
                                    availableQty = availableQty > _minOrderSize ? availableQty - _minOrderSize : availableQty; // force minimal execution as maker
                                    sendOrder(webSocketConnection, symbol, OrderSide.SELL, availableQty, _sell_floor_price, OrdType.LIMIT, 0, ExecInst.DEFAULT, TimeInForce.IMMEDIATE_OR_CANCEL);
                                }
                                // change the strategy so that the bot might negociate the leaves qty as a maker
                                _priceType = PriceType.PEGGED;
                                Console.WriteLine("DEBUG Changed Strategy to FLOAT with SELL_FLOOR_PRICE=[{0}]", _sell_floor_price);
                            }
                            else
                            {
                                Console.WriteLine("DEBUG ** Reached Trailing Stop CAP [but without local exchange spread] ** [{0}],[{1}],[{2}],[{3}]", btcusd_quote.LastPx, best_bid_price, usd_official_quote.BestBid, local_exchange_spread);
                            }
                        }
                    }
                }
                return;
            }

            // ** temporary workaround to support market pegged sell order strategy without plugins**
            if (_priceType == PriceType.PEGGED && _strategySide == OrderSide.SELL)
            {
                // make the price float according to the MID Price

                // gather the data to calculate the midprice
                // instead of bestAsk let's use the Price reached if one decides to buy X BTC
                ulong maxPriceToBuyXBTC = orderBook.MaxPriceForAmountWithoutSelfOrders(
                    OrderBook.OrdSide.SELL,
                    (ulong)(1 * 1e8),                                                                                             // TODO: make it a parameter
                    _tradeclient.UserId);


                // let's use the VWAP as the market price (short period tick based i.e last 30 min.)
                ulong marketPrice = _tradeclient.CalculateVWAP();

                // calculate the mid price
                ulong midprice = (ulong)((orderBook.BestBid.Price + maxPriceToBuyXBTC + marketPrice) / 3);
                _sellTargetPrice = midprice + _pegOffsetValue;

                // check the selling FLOOR
                if (_sellTargetPrice < _sell_floor_price)
                {
                    _sellTargetPrice = _sell_floor_price;
                }
            }

            // another workaround for sending a single stop order and disable the strategy
            if (_priceType == PriceType.STOP)
            {
                if (_strategySide == OrderSide.BUY)
                {
                    char  ordType   = (_buyTargetPrice == 0 ? OrdType.STOP_MARKET : OrdType.STOP_LIMIT);
                    ulong ref_price = (_buyTargetPrice > _stop_price ? _buyTargetPrice : _stop_price);
                    ulong qty       = calculateOrderQty(symbol, _strategySide, ref_price);
                    sendOrder(webSocketConnection, symbol, OrderSide.BUY, qty, _buyTargetPrice, ordType, _stop_price, default(char));
                }

                if (_strategySide == OrderSide.SELL)
                {
                    char  ordType = (_sellTargetPrice == 0 ? OrdType.STOP_MARKET : OrdType.STOP_LIMIT);
                    ulong qty     = calculateOrderQty(symbol, OrderSide.SELL);
                    sendOrder(webSocketConnection, symbol, OrderSide.SELL, qty, _sellTargetPrice, ordType, _stop_price, default(char));
                }
                // disable strategy after sending the stop order...
                this._enabled = false;
                return;
            }

            // run the strategy that change orders in the book
            if (_maxOrderSize > 0)
            {
                webSocketConnection.EnableTestRequest = false;
                if (_strategySide == OrderSide.BUY || _strategySide == default(char)) // buy or both
                {
                    runBuyStrategy(webSocketConnection, symbol);
                }

                if (_strategySide == OrderSide.SELL || _strategySide == default(char)) // sell or both
                {
                    runSellStrategy(webSocketConnection, symbol);
                }
                webSocketConnection.EnableTestRequest = true;
            }
        }
Beispiel #17
0
        private void runSellStrategy(IWebSocketClientConnection webSocketConnection, string symbol)
        {
            OrderBook.IOrder bestOffer = _tradeclient.GetOrderBook(symbol).BestOffer;
            if (bestOffer != null)
            {
                if (bestOffer.UserId != _tradeclient.UserId)
                {
                    // sell @ 1 cent bellow the best price (TODO: parameter for price increment)
                    ulong sellPrice = bestOffer.Price - (ulong)(0.01 * 1e8);
                    if (sellPrice >= _sellTargetPrice)
                    {
                        replaceOrder(webSocketConnection, symbol, OrderSide.SELL, sellPrice);
                    }
                    else
                    {
                        // cannot fight for the first position thus try to find a visible position in the book
                        OrderBook orderBook             = _tradeclient.GetOrderBook(symbol);
                        List <OrderBook.Order> sellside = orderBook.GetOfferOrders();
                        int i = sellside.BinarySearch(
                            new OrderBook.Order(OrderBook.OrdSide.SELL, _sellTargetPrice + (ulong)(0.01 * 1e8)),
                            new OrderBook.OrderPriceComparer()
                            );
                        int position = (i < 0 ? ~i : i);
                        Debug.Assert(position > 0);

                        // verificar se a profundidade vale a pena: (TODO: parameters for max_pos_depth and max_amount_depth)
                        if (position > 5 + 1 && orderBook.DoesAmountExceedsLimit(
                                OrderBook.OrdSide.SELL,
                                position - 1, (ulong)(10 * 1e8)))
                        {
                            _tradeclient.CancelOrderByClOrdID(webSocketConnection, _strategySellOrderClorid);
                            return;
                        }

                        var pivotOrder = sellside[position];
                        if (pivotOrder.UserId == _tradeclient.UserId)
                        {
                            // ordem ja e minha : pega + recursos disponiveis e cola no preco no vizinho se já nao estiver
                            ulong price_delta  = sellside[position + 1].Price - pivotOrder.Price;
                            ulong newSellPrice = (price_delta > (ulong)(0.01 * 1e8) ?
                                                  pivotOrder.Price + price_delta - (ulong)(0.01 * 1e8) :
                                                  pivotOrder.Price);
                            ulong availableQty = calculateOrderQty(symbol, OrderSide.SELL);
                            if (newSellPrice > pivotOrder.Price || availableQty > pivotOrder.Qty)
                            {
                                replaceOrder(webSocketConnection, symbol, OrderSide.SELL, newSellPrice, availableQty);
                            }
                        }
                        else
                        {
                            // estabelece preco de venda 1 centavo menor do que nesta posicao
                            ulong newSellPrice = pivotOrder.Price - (ulong)(0.01 * 1e8);
                            replaceOrder(webSocketConnection, symbol, OrderSide.SELL, newSellPrice);
                        }
                    }
                }
                else
                {
                    // check and replace the order to get closer to the order in the second position and gather more available funds
                    List <OrderBook.Order> sellside = _tradeclient.GetOrderBook(symbol).GetOfferOrders();
                    ulong price_delta  = sellside.Count > 1 ? sellside[1].Price - sellside[0].Price : 0;
                    ulong newSellPrice = (price_delta > (ulong)(0.01 * 1e8) ?
                                          bestOffer.Price + price_delta - (ulong)(0.01 * 1e8) :
                                          bestOffer.Price);
                    ulong availableQty = calculateOrderQty(symbol, OrderSide.SELL);
                    if (newSellPrice > bestOffer.Price || availableQty > bestOffer.Qty)
                    {
                        replaceOrder(webSocketConnection, symbol, OrderSide.SELL, newSellPrice, availableQty);
                    }
                }
            }
            else
            {
                // TODO: empty book scenario
            }
        }
Beispiel #18
0
        private void runSellStrategy(IWebSocketClientConnection webSocketConnection, string symbol)
        {
            OrderBook.IOrder bestBid   = _tradeclient.GetOrderBook(symbol).BestBid;
            OrderBook.IOrder bestOffer = _tradeclient.GetOrderBook(symbol).BestOffer;

            if (_priceType == PriceType.MARKET_AS_MAKER)
            {
                ulong sellPrice = 0;
                if (bestBid != null)
                {
                    sellPrice = bestBid.Price + (ulong)(0.01 * 1e8);
                }
                else if (bestOffer != null)
                {
                    sellPrice = bestOffer.Price;
                }

                if (sellPrice > 0 || _sell_floor_price > 0)
                {
                    if (sellPrice >= _sell_floor_price)
                    {
                        replaceOrder(webSocketConnection, symbol, OrderSide.SELL, sellPrice);
                        return;
                    }

                    _sellTargetPrice = _sell_floor_price; // find the best position as maker for the sell floor price
                }
                else
                {
                    // empty book scenario without a user defined sell floor price
                    _tradeclient.CancelOrderByClOrdID(webSocketConnection, _strategySellOrderClorid);
                    return;
                }
            }

            // available funds with a target price should execute ASAP even as liquidity takers whenever possible
            if (_sellTargetPrice > 0 && bestBid != null && bestBid.Price >= _sellTargetPrice && bestBid.UserId != _tradeclient.UserId)
            {
                ulong availableQty = calculateOrderQty(symbol, OrderSide.SELL, bestBid.Price, ulong.MaxValue);
                if (availableQty > _minOrderSize)
                {
                    ulong sell_qty = Math.Min(availableQty, bestBid.Qty);
                    // execute the order as taker and emulate IOC instruction
                    sendOrder(webSocketConnection, symbol, OrderSide.SELL, sell_qty, bestBid.Price, OrdType.LIMIT, 0, ExecInst.DEFAULT, TimeInForce.IMMEDIATE_OR_CANCEL);
                    return;
                }
                else
                {
                    // cancel current order to free balance to be used as taker
                    MiniOMS.IOrder own_sell_order = _tradeclient.miniOMS.GetOrderByClOrdID(_strategySellOrderClorid);
                    if (own_sell_order != null && (own_sell_order.OrdStatus == OrdStatus.NEW || own_sell_order.OrdStatus == OrdStatus.PARTIALLY_FILLED))
                    {
                        if (own_sell_order.LeavesQty > _minOrderSize)
                        {
                            ulong sell_qty = Math.Min(own_sell_order.LeavesQty, bestBid.Qty);
                            _tradeclient.CancelOrderByClOrdID(webSocketConnection, own_sell_order.ClOrdID);
                            sendOrder(webSocketConnection, symbol, OrderSide.SELL, sell_qty, bestBid.Price, OrdType.LIMIT, 0, ExecInst.DEFAULT, TimeInForce.IMMEDIATE_OR_CANCEL);
                            return;
                        }
                    }
                }
            }

            // post the order in the order book
            if (bestOffer != null)
            {
                if (bestOffer.UserId != _tradeclient.UserId)
                {
                    // sell @ 1 cent bellow the best price (TODO: parameter for price increment)
                    ulong sellPrice = bestOffer.Price - (ulong)(0.01 * 1e8);
                    if (sellPrice >= _sellTargetPrice)
                    {
                        // TODO: Become a Taker when the spread is "small" (i.e for stop trailing converted to pegged or for any pegged)
                        if (sellPrice > bestBid.Price)
                        {
                            replaceOrder(webSocketConnection, symbol, OrderSide.SELL, sellPrice);
                        }
                        else
                        {
                            // avoid being a taker or receiving a reject when using ExecInst=6 but stay in the book with max price
                            ulong max_sell_price = bestBid.Price + (ulong)(0.01 * 1e8);
                            var   own_order      = _tradeclient.miniOMS.GetOrderByClOrdID(_strategySellOrderClorid);
                            ulong availableQty   = calculateOrderQty(symbol, OrderSide.SELL);
                            if (own_order == null || own_order.Price != max_sell_price || availableQty > own_order.OrderQty)
                            {
                                replaceOrder(webSocketConnection, symbol, OrderSide.SELL, max_sell_price);
                            }
                        }
                    }
                    else
                    {
                        // cannot fight for the first position thus try to find a visible position in the book
                        OrderBook orderBook             = _tradeclient.GetOrderBook(symbol);
                        List <OrderBook.Order> sellside = orderBook.GetOfferOrders();
                        int i = sellside.BinarySearch(
                            new OrderBook.Order(OrderBook.OrdSide.SELL, _sellTargetPrice + (ulong)(0.01 * 1e8)),
                            new OrderBook.OrderPriceComparer()
                            );
                        int position = (i < 0 ? ~i : i);
                        Debug.Assert(position > 0);

                        // verificar se a profundidade vale a pena: (TODO: parameters for max_pos_depth and max_amount_depth)
                        if (position > 15 + 1 && orderBook.DoesAmountExceedsLimit(
                                OrderBook.OrdSide.SELL,
                                position - 1, (ulong)(20 * 1e8)))
                        {
                            _tradeclient.CancelOrderByClOrdID(webSocketConnection, _strategySellOrderClorid);
                            return;
                        }

                        var pivotOrder = sellside[position];
                        if (pivotOrder.UserId == _tradeclient.UserId)
                        {
                            // make sure the order is the same or from another client instance
                            MiniOMS.IOrder own_sell_order = _tradeclient.miniOMS.GetOrderByClOrdID(_strategySellOrderClorid);
                            if (sellside[position].OrderId == own_sell_order.OrderID)
                            {
                                // ordem ja e minha : pega + recursos disponiveis e cola no preco do vizinho se já nao estiver
                                ulong price_delta  = sellside.Count > position + 1 ? sellside[position + 1].Price - pivotOrder.Price : 0;
                                ulong newSellPrice = (price_delta > (ulong)(0.01 * 1e8) ?
                                                      pivotOrder.Price + price_delta - (ulong)(0.01 * 1e8) :
                                                      pivotOrder.Price);
                                ulong availableQty = calculateOrderQty(symbol, OrderSide.SELL);
                                if (newSellPrice > pivotOrder.Price || availableQty > pivotOrder.Qty)
                                {
                                    replaceOrder(webSocketConnection, symbol, OrderSide.SELL, newSellPrice, availableQty);
                                }
                            }
                        }
                        else
                        {
                            // estabelece preco de venda 1 centavo menor do que nesta posicao
                            ulong newSellPrice = pivotOrder.Price - (ulong)(0.01 * 1e8);
                            replaceOrder(webSocketConnection, symbol, OrderSide.SELL, newSellPrice);
                        }
                    }
                }
                else
                {
                    // check and replace the order on the top to get closer to the order in the second position and gather more available funds
                    MiniOMS.IOrder         own_sell_order = _tradeclient.miniOMS.GetOrderByClOrdID(_strategySellOrderClorid);
                    List <OrderBook.Order> sellside       = _tradeclient.GetOrderBook(symbol).GetOfferOrders();
                    if (sellside[0].OrderId == own_sell_order.OrderID)
                    {
                        ulong price_delta  = sellside.Count > 1 ? sellside[1].Price - sellside[0].Price : 0;
                        ulong newSellPrice = (price_delta > (ulong)(0.01 * 1e8) ?
                                              bestOffer.Price + price_delta - (ulong)(0.01 * 1e8) :
                                              bestOffer.Price);
                        ulong availableQty = calculateOrderQty(symbol, OrderSide.SELL);
                        if (newSellPrice > bestOffer.Price || availableQty > bestOffer.Qty)
                        {
                            replaceOrder(webSocketConnection, symbol, OrderSide.SELL, newSellPrice, availableQty);
                        }
                    }
                }
            }
            else
            {
                // empty book scenario
                ulong availableQty = calculateOrderQty(symbol, OrderSide.SELL);
                Debug.Assert(_sellTargetPrice > 0);
                ulong sell_price = Math.Max(_sellTargetPrice, bestBid != null ? bestBid.Price + (ulong)(0.01 * 1e8) : 0);
                sendOrder(webSocketConnection, symbol, OrderSide.SELL, availableQty, sell_price);
            }
        }
Beispiel #19
0
        private void runBuyStrategy(IWebSocketClientConnection webSocketConnection, string symbol)
        {
            OrderBook.IOrder bestBid   = _tradeclient.GetOrderBook(symbol).BestBid;
            OrderBook.IOrder bestOffer = _tradeclient.GetOrderBook(symbol).BestOffer;

            if (_priceType == PriceType.MARKET_AS_MAKER)
            {
                ulong buyPrice = 0;
                if (bestOffer != null)
                {
                    buyPrice = bestOffer.Price - (ulong)(0.01 * 1e8);
                }
                else if (bestBid != null)
                {
                    buyPrice = bestBid.Price;
                }

                Debug.Assert(_buy_cap_price > 0);
                if (_buy_cap_price == 0)
                {
                    _buy_cap_price = ulong.MaxValue;
                }

                if (buyPrice > 0 && buyPrice <= _buy_cap_price)
                {
                    replaceOrder(webSocketConnection, symbol, OrderSide.BUY, buyPrice);
                    return;
                }

                if (_buy_cap_price == ulong.MaxValue)
                {
                    _tradeclient.CancelOrderByClOrdID(webSocketConnection, _strategyBuyOrderClorid);
                    return;
                }

                _buyTargetPrice = _buy_cap_price; // find the best position as maker at the buy cap price
            }

            if (_priceType == PriceType.EXPLORE_BOOK_DEPTH)
            {
                // set the price based on the depth (this is a lot inefficent but I don't care)
                OrderBook orderBook = _tradeclient.GetOrderBook(symbol);
                ulong     max_price = orderBook.MaxPriceForAmountWithoutSelfOrders(OrderBook.OrdSide.BUY, _minBookDepth, _tradeclient.UserId);
                ulong     min_price = orderBook.MaxPriceForAmountWithoutSelfOrders(OrderBook.OrdSide.BUY, _maxBookDepth, _tradeclient.UserId);
                max_price = max_price < ulong.MaxValue ? max_price : min_price;
                var myOrder = _tradeclient.miniOMS.GetOrderByClOrdID(this._strategyBuyOrderClorid);

                if (max_price < ulong.MaxValue)
                {
                    min_price = min_price < ulong.MaxValue ? min_price : max_price;
                    if (myOrder == null || myOrder.Price > max_price || myOrder.Price < min_price)
                    {
                        //LogStatus (LogStatusType.WARN, String.Format ("[DT] must change order price not in expected position {0} {1} {2}", myOrder != null ? myOrder.Price : 0, max_price, min_price));
                        if (min_price < max_price)
                        {
                            _buyTargetPrice = min_price + (ulong)(0.01 * 1e8); // 1 pip better than min_price
                        }
                        else
                        {
                            _buyTargetPrice = min_price - (ulong)(0.01 * 1e8); // 1 pip worse than min_price
                        }
                    }
                    else
                    {
                        return;                         // don't change the order because it is still in an acceptable position
                    }
                }
                else
                {
                    // no reference found in the book
                    SecurityStatus usd_official_quote = _tradeclient.GetSecurityStatus("UOL", "USDBRL");                      // use USDBRT for the turism quote
                    SecurityStatus btcusd_quote       = _tradeclient.GetSecurityStatus("BITSTAMP", "BTCUSD");
                    if (usd_official_quote != null && usd_official_quote.BestBid > 0 && btcusd_quote != null && btcusd_quote.BestBid > 0)
                    {
                        ulong market_price   = _tradeclient.CalculateVWAP();
                        ulong lastPrice      = _tradeclient.GetLastPrice();
                        ulong off_sale_price = (ulong)(usd_official_quote.BestBid / 1e8 * btcusd_quote.BestBid / 1e8 * 0.5 * 1e8);
                        _buyTargetPrice = Math.Min(Math.Min(market_price, lastPrice), off_sale_price);
                    }
                    else
                    {
                        return;
                    }
                }
            }

            if (bestBid != null)
            {
                if (bestBid.UserId != _tradeclient.UserId)
                {
                    // buy @ 1 cent above the best price (TODO: parameter for price increment)
                    ulong buyPrice = bestBid.Price + (ulong)(0.01 * 1e8);
                    if (buyPrice <= this._buyTargetPrice)
                    {
                        if (buyPrice < bestOffer.Price)
                        {
                            replaceOrder(webSocketConnection, symbol, OrderSide.BUY, buyPrice);
                        }
                        else
                        {
                            // avoid being a taker or receiving a reject when using ExecInst=6 but stay in the book with max price
                            ulong max_buy_price = bestOffer.Price - (ulong)(0.01 * 1e8);
                            var   own_order     = _tradeclient.miniOMS.GetOrderByClOrdID(_strategyBuyOrderClorid);
                            ulong availableQty  = calculateOrderQty(symbol, OrderSide.BUY, max_buy_price);
                            if (own_order == null || own_order.Price != max_buy_price || availableQty > own_order.OrderQty)
                            {
                                replaceOrder(webSocketConnection, symbol, OrderSide.BUY, max_buy_price);
                            }
                        }
                    }
                    else
                    {
                        // cannot fight for the first position thus try to find a visible position in the book
                        OrderBook orderBook            = _tradeclient.GetOrderBook(symbol);
                        List <OrderBook.Order> buyside = orderBook.GetBidOrders();
                        int i = buyside.BinarySearch(
                            new OrderBook.Order(OrderBook.OrdSide.BUY, _buyTargetPrice - (ulong)(0.01 * 1e8)),
                            new OrderBook.ReverseOrderPriceComparer()
                            );
                        int position = (i < 0 ? ~i : i);
                        Debug.Assert(position > 0);

                        if (this._priceType != PriceType.EXPLORE_BOOK_DEPTH)
                        {
                            // verificar se a profundidade vale a pena: (TODO: parameters for max_pos_depth and max_amount_depth)
                            if (position > 15 + 1 && orderBook.DoesAmountExceedsLimit(
                                    OrderBook.OrdSide.BUY,
                                    position - 1, (ulong)(20 * 1e8)))
                            {
                                _tradeclient.CancelOrderByClOrdID(webSocketConnection, _strategyBuyOrderClorid);
                                return;
                            }
                        }

                        var pivotOrder = buyside[position];
                        if (pivotOrder.UserId == _tradeclient.UserId)
                        {
                            // make sure the order is the same or from another client instance
                            MiniOMS.IOrder own_buy_order = _tradeclient.miniOMS.GetOrderByClOrdID(_strategyBuyOrderClorid);
                            if (buyside[position].OrderId == own_buy_order.OrderID)
                            {
                                // ordem ja e minha : pega + recursos disponiveis e cola no preco no vizinho se já nao estiver
                                ulong price_delta = buyside.Count > position + 1 ? pivotOrder.Price - buyside[position + 1].Price : 0;
                                ulong newBuyPrice = (price_delta > (ulong)(0.01 * 1e8) ?
                                                     pivotOrder.Price - price_delta + (ulong)(0.01 * 1e8) :
                                                     pivotOrder.Price);
                                ulong availableQty = calculateOrderQty(symbol, OrderSide.BUY, newBuyPrice);
                                if (newBuyPrice < pivotOrder.Price || availableQty > pivotOrder.Qty)
                                {
                                    replaceOrder(webSocketConnection, symbol, OrderSide.BUY, newBuyPrice, availableQty);
                                }
                            }
                        }
                        else
                        {
                            // estabelece preco de venda 1 centavo maior do que nesta posicao
                            ulong newbuyPrice = pivotOrder.Price + (ulong)(0.01 * 1e8);
                            replaceOrder(webSocketConnection, symbol, OrderSide.BUY, newbuyPrice);
                        }
                    }
                }
                else
                {
                    // make sure the order is the same or from another client instance
                    // check and replace order to get closer to the order in the second position and gather more avaible funds
                    List <OrderBook.Order> buyside       = _tradeclient.GetOrderBook(symbol).GetBidOrders();
                    MiniOMS.IOrder         own_buy_order = _tradeclient.miniOMS.GetOrderByClOrdID(_strategyBuyOrderClorid);
                    if (buyside[0].OrderId == own_buy_order.OrderID)
                    {
                        ulong price_delta = buyside.Count > 1 ? buyside[0].Price - buyside[1].Price : 0;
                        ulong newBuyPrice = (price_delta > (ulong)(0.01 * 1e8) ?
                                             bestBid.Price - price_delta + (ulong)(0.01 * 1e8) :
                                             bestBid.Price);
                        ulong availableQty = calculateOrderQty(symbol, OrderSide.BUY, newBuyPrice);
                        if (newBuyPrice < bestBid.Price || availableQty > bestBid.Qty)
                        {
                            replaceOrder(webSocketConnection, symbol, OrderSide.BUY, newBuyPrice, availableQty);
                        }
                    }
                }
            }
            else
            {
                // empty book scenario
                ulong availableQty = calculateOrderQty(symbol, OrderSide.BUY, _buyTargetPrice);
                Debug.Assert(_buyTargetPrice > 0);
                ulong buy_price = Math.Min(_buyTargetPrice, bestOffer != null ? bestOffer.Price - (ulong)(0.01 * 1e8) : ulong.MaxValue);
                sendOrder(webSocketConnection, symbol, OrderSide.BUY, availableQty, buy_price);
            }
        }
        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
                          );
            }
        }
        private void StartInitialRequestsAfterLogon(IWebSocketClientConnection connection)
        {
            // 1. cancel all user orders
            SendRequestToCancelAllOrders(connection); // not necessary if cancel on disconnect is active

            // 2. send the balance request
            JObject balance_request = new JObject();

            balance_request["MsgType"]      = "U2";
            balance_request["BalanceReqID"] = connection.NextOutgoingSeqNum();
            balance_request["FingerPrint"]  = connection.Device.FingerPrint;
            balance_request["STUNTIP"]      = connection.Device.Stuntip;
            connection.SendMessage(balance_request.ToString());

            // 3. send market data request
            JObject marketdata_request = new JObject();

            marketdata_request["MsgType"] = "V";
            marketdata_request["MDReqID"] = connection.NextOutgoingSeqNum();
            marketdata_request["SubscriptionRequestType"] = "1";
            marketdata_request["MarketDepth"]             = 0;
            marketdata_request["MDUpdateType"]            = "1";
            marketdata_request["MDEntryTypes"]            = new JArray("0", "1", "2"); // bid, offer, trade
            marketdata_request["Instruments"]             = new JArray(_tradingSymbol);
            marketdata_request["FingerPrint"]             = connection.Device.FingerPrint;
            marketdata_request["STUNTIP"] = connection.Device.Stuntip;
            connection.SendMessage(marketdata_request.ToString());

            // 4. send security status request
            JObject securitystatus_request = new JObject();

            securitystatus_request["MsgType"]                 = "e";
            securitystatus_request["SecurityStatusReqID"]     = connection.NextOutgoingSeqNum();
            securitystatus_request["SubscriptionRequestType"] = "1";
            JArray instruments = new JArray();

            instruments.Add("BLINK:BTCBRL");
            instruments.Add("BLINK:BTCUSD");
            instruments.Add("BLINK:BTCVND");
            instruments.Add("BLINK:BTCVEF");
            instruments.Add("BLINK:BTCPKR");
            instruments.Add("BLINK:BTCCLP");
            instruments.Add("BITSTAMP:BTCUSD");
            instruments.Add("ITBIT:BTCUSD");
            instruments.Add("BITFINEX:BTCUSD");
            instruments.Add("BTRADE:BTCUSD");
            instruments.Add("MBT:BTCBRL");
            instruments.Add("KRAKEN:BTCEUR");
            instruments.Add("COINFLOOR:BTCGBP");
            instruments.Add("UOL:USDBRL");
            instruments.Add("UOL:USDBRT");
            instruments.Add("OKCOIN:BTCCNY");
            securitystatus_request["Instruments"] = instruments;
            securitystatus_request["FingerPrint"] = connection.Device.FingerPrint;
            securitystatus_request["STUNTIP"]     = connection.Device.Stuntip;
            connection.SendMessage(securitystatus_request.ToString());

            // 5. send the trade history request
            JObject trades_request = new JObject();

            trades_request["MsgType"]           = "U32";
            trades_request["TradeHistoryReqID"] = connection.NextOutgoingSeqNum();
            //trades_request["Filter"] = new JArray("Symbol eq 'BTCBRL'"); // not working
            //trades_request["SymbolList"] = new JArray("BTCBRL"); // not working
            trades_request["FingerPrint"] = connection.Device.FingerPrint;
            trades_request["STUNTIP"]     = connection.Device.Stuntip;
            connection.SendMessage(trades_request.ToString());

            // 6. send request for all "open" orders
            SendRequestForOpenOrders(connection);
        }
Beispiel #22
0
        /*abstract*/ public void runStrategy(IWebSocketClientConnection webSocketConnection, string symbol)
        {
            // Run the strategy to try to have an order on at least one side of the book according to fixed price range
            // but never executing as a taker

            if (!_enabled)               // strategy cannot run when disabled
            {
                LogStatus(LogStatusType.WARN, "Strategy is disabled and will not run");
                return;
            }

            // ** temporary workaround to support market pegged sell order strategy without plugins**
            if (_priceType == PriceType.PEGGED && _strategySide == OrderSide.SELL)
            {
                // make the price float according to the MID Price

                /*
                 * // requires the Security List for the trading symbol
                 * SecurityStatus status = _tradeclient.GetSecurityStatus ("BLINK", symbol);
                 * if (status == null)
                 * {
                 *      LogStatus(
                 *              LogStatusType.WARN,
                 *              String.Format(
                 *                      "Waiting Security Status BLINK:{0} to run Pegged strategy",
                 *                      symbol)
                 *      );
                 *      return;
                 * }
                 */

                // check the remaining qty that can still be sold
                ulong theSoldAmount = _tradeclient.GetSoldAmount();
                if (theSoldAmount < _maxAmountToSell)
                {
                    ulong uAllowedAmountToSell = _maxAmountToSell - theSoldAmount;
                    _maxTradeSize = _maxTradeSize < uAllowedAmountToSell ? _maxTradeSize : uAllowedAmountToSell;
                    _maxTradeSize = _maxTradeSize > _minTradeSize ? _maxTradeSize : _minTradeSize;
                }
                else
                {
                    LogStatus(LogStatusType.WARN, String.Format("[runStrategy] Cannot exceed the allowed max amount to sell : {0} {1}", theSoldAmount, _maxAmountToSell));
                    _tradeclient.CancelOrderByClOrdID(webSocketConnection, _strategySellOrderClorid);
                    return;
                }

                // gather the data to calculate the midprice
                OrderBook orderBook = _tradeclient.GetOrderBook(symbol);

                // instead of bestAsk let's use the Price reached if one decides to buy 1 BTC
                ulong maxPriceToBuy1BTC = orderBook.MaxPriceForAmountWithoutSelfOrders(
                    OrderBook.OrdSide.SELL,
                    (ulong)(1 * 1e8),                     // TODO: make it a parameter
                    _tradeclient.UserId);


                // gather the magic element of the midprice (i.e. price to buy 10 BTC)
                ulong maxPriceToBuyXBTC = orderBook.MaxPriceForAmountWithoutSelfOrders(
                    OrderBook.OrdSide.SELL,
                    (ulong)(10 * 1e8),                                                                                             // TODO: make it a parameter
                    _tradeclient.UserId);



                // instead of the last price let's use the VWAP (short period tick based i.e last 30 min.)
                ulong vwap        = _tradeclient.CalculateVWAP();
                ulong lastPx      = _tradeclient.GetLastPrice();
                ulong marketPrice = vwap > lastPx ? vwap : lastPx;
                // calculate the mid price
                //ulong midprice = (ulong)((status.BestAsk + status.BestBid + status.LastPx + maxPriceToBuyXBTC) / 4);

                ulong midprice = (ulong)((orderBook.BestBid.Price + maxPriceToBuy1BTC + maxPriceToBuyXBTC + marketPrice) / 4);
                Debug.Assert(_pegOffsetValue > 0);
                _sellTargetPrice = midprice + _pegOffsetValue;

                // get the dollar price
                SecurityStatus usd_official_quote = _tradeclient.GetSecurityStatus("UOL", "USDBRL");                  // use USDBRT for the turism quote
                if (usd_official_quote == null || usd_official_quote.BestAsk == 0)
                {
                    LogStatus(LogStatusType.WARN, "UOL:USDBRL not available");
                }
                // get the BTC Price in dollar
                SecurityStatus bitfinex_btcusd_quote = _tradeclient.GetSecurityStatus("BITSTAMP", "BTCUSD");
                if (bitfinex_btcusd_quote == null || bitfinex_btcusd_quote.BestAsk == 0)
                {
                    LogStatus(LogStatusType.WARN, "BITSTAMP:BTCUSD not available");
                }
                // calculate the selling floor must be at least the price of the BTC in USD
                //ulong floor = (ulong)(1.01 * bitfinex_btcusd_quote.BestAsk * (float)(usd_official_quote.BestAsk / 1e8));

                //if (floor == 0) {
                ulong floor = (ulong)(8900 * 1e8);                 // TODO: make it an optional parameter or pegged to the dolar bitcoin
                //}

                //floor = (ulong)(5400 * 1e8);

                // check the selling FLOOR
                if (_sellTargetPrice < floor)
                {
                    _sellTargetPrice = floor;
                }
            }

            // run the strategy
            if (_maxTradeSize > 0)
            {
                webSocketConnection.EnableTestRequest = false;
                if (_strategySide == OrderSide.BUY || _strategySide == default(char)) // buy or both
                {
                    runBuyStrategy(webSocketConnection, symbol);
                }

                if (_strategySide == OrderSide.SELL || _strategySide == default(char)) // sell or both
                {
                    runSellStrategy(webSocketConnection, symbol);
                }
                webSocketConnection.EnableTestRequest = true;
            }
        }