Пример #1
0
        private bool CheckIfSpreadChangedOrItsParts(MarketDepthPair bestPair)
        {
            var result = bestPair.Bid.Price != _lastQuoterSpreadBuyPart ||
                         bestPair.Ask.Price != _lastQuoterSpreadSellPart;

            if (result)
            {
                _lastQuoterSpreadBuyPart  = bestPair.Bid.Price;
                _lastQuoterSpreadSellPart = bestPair.Ask.Price;
            }

            return(result);
        }
        /// <summary>
        /// Process new best <see cref="MarketDepthPair"/>
        /// </summary>
        /// <param name="marketPair">Best ask-bid pair</param>
        /// <returns>Recommended price-volume pair or <see langword="null"/></returns>
        public Quote Process(MarketDepthPair marketPair)
        {
            if (marketPair == null)
            {
                throw new ArgumentNullException(nameof(marketPair));
            }
            if (!marketPair.IsFull)
            {
                return(null);
            }


            Quote quote = null;

            _logger.Info($"Best ask / bid: {marketPair.Ask.Price} / {marketPair.Bid.Price}. Update Id: {marketPair.UpdateTime}.");

            // get price spreads (in percent)
            decimal actualSpread   = marketPair.PriceSpread !.Value / marketPair.MediumPrice !.Value * 100; // spread_relative = spread_absolute/price * 100
            decimal expectedSpread = _marketStrategyConfig.TradeWhenSpreadGreaterThan;

            _logger.Info($"Spread absolute (relative): {marketPair.PriceSpread} ({actualSpread/100:P}). Update Id: {marketPair.UpdateTime}.");


            if (actualSpread >= expectedSpread)
            {
                // compute new order price
                decimal extra      = marketPair.MediumPrice.Value * (actualSpread - expectedSpread) / 100; // extra = medium_price * (spread_actual - spread_expected)
                decimal orderPrice = marketPair.Bid.Price + extra;                                         // new_price = best_bid + extra

                // compute order volume
                decimal volumeSpread = marketPair.VolumeSpread !.Value;
                decimal orderVolume  = volumeSpread > _marketStrategyConfig.MaxOrderVolume ?
                                       _marketStrategyConfig.MaxOrderVolume :                                                                       // max volume restriction
                                       (volumeSpread < _marketStrategyConfig.MinOrderVolume ? _marketStrategyConfig.MinOrderVolume : volumeSpread); // min volume restriction

                // return new price-volume pair
                quote = new Quote(orderPrice, orderVolume, OrderSide.Buy);
            }


            return(quote);
        }
Пример #3
0
        /// <summary>
        /// Update market depth
        /// </summary>
        /// <remarks>
        /// How to manage a local order book correctly [1]:
        ///    1. Open a stream to wss://stream.binance.com:9443/ws/bnbbtc@depth
        ///    2. Buffer the events you receive from the stream
        ///    3. Get a depth snapshot from https://www.binance.com/api/v1/depth?symbol=BNBBTC&amp;limit=1000
        /// -> 4. Drop any event where u is less or equal lastUpdateId in the snapshot
        ///    5. The first processed should have U less or equal lastUpdateId+1 AND u equal or greater lastUpdateId+1
        /// -> 6. While listening to the stream, each new event's U should be equal to the previous event's u+1
        /// -> 7. The data in each event is the absolute quantity for a price level
        /// -> 8. If the quantity is 0, remove the price level
        ///    9. Receiving an event that removes a price level that is not in your local order book can happen and is normal.
        /// Reference:
        ///     1. https://github.com/binance/binance-spot-api-docs/blob/master/web-socket-streams.md#how-to-manage-a-local-order-book-correctly
        /// </remarks>
        public void UpdateDepth(IEnumerable <BinanceOrderBookEntry> asks, IEnumerable <BinanceOrderBookEntry> bids, long updateTime)
        {
            if (updateTime <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(updateTime));
            }

            // if nothing was changed then return
            if (updateTime <= LastUpdateTime)
            {
                return;
            }
            if (asks == null && bids == null)
            {
                return;
            }


            void UpdateOrderBook(IEnumerable <BinanceOrderBookEntry> updates, IDictionary <decimal, decimal> orders)
            {
                if (orders == null)
                {
                    throw new ArgumentNullException(nameof(orders));
                }
                if (updates == null)
                {
                    return;
                }

                // WARN: clean orders in cases when connector received orderbook snapshots instead of orderbook updates
                // orders.Clear();

                // update order book
                foreach (BinanceOrderBookEntry t in updates)
                {
                    if (t.Quantity > IgnoreVolumeValue)
                    {
                        orders[t.Price] = t.Quantity;
                    }
                    else
                    if (orders.ContainsKey(t.Price))
                    {
                        orders.Remove(t.Price);
                    }
                }
            }

            // save prev BestPair to OnMarketBestPairChanged raise event
            MarketDepthPair prevBestPair = BestPair;

            // update asks market depth
            UpdateOrderBook(asks, _asks);
            UpdateOrderBook(bids, _bids);
            // set new update time
            LastUpdateTime = updateTime;

            // raise events
            OnMarketDepthChanged(new MarketDepthChangedEventArgs(Asks, Bids, LastUpdateTime.Value));
            if (!BestPair.Equals(prevBestPair))
            {
                OnMarketBestPairChanged(new MarketBestPairChangedEventArgs(BestPair));
            }
        }