Exemplo n.º 1
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;
            }
        }
Exemplo n.º 2
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);
            }
        }
Exemplo n.º 3
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;
            }
        }