Ejemplo n.º 1
0
        private static Arbitrage DetectArbitrage(Trade trade, Decimal quantity, SortedOrderBook[] orderBooks)
        {
            try
            {
                DateTime  referenceTime = orderBooks.Min(x => x.UpdateTime);
                Arbitrage arbitrage     = new Arbitrage(referenceTime, trade);

                foreach ((Int32 offset, TriangulationStep x, TriangulationStep y) in arbitrage.GetSequence())
                {
                    if (x.Position == Position.Buy)
                    {
                        SortedOrderBook orderBook      = orderBooks[offset];
                        Decimal         dustedQuantity = OrderBookConversion(false, quantity, x.ReferenceAsset, y.ReferenceAsset, orderBook);

                        y.In  = CalculateDustless(dustedQuantity, x.DustDecimals);
                        x.Out = OrderBookConversion(true, y.In, y.ReferenceAsset, x.ReferenceAsset, orderBook);
                    }
                    else
                    {
                        x.Out = CalculateDustless(quantity, x.DustDecimals);
                        y.In  = OrderBookConversion(false, x.Out, x.ReferenceAsset, y.ReferenceAsset, orderBooks[offset]);
                    }

                    quantity = y.In;
                }

                return(arbitrage);
            }
            catch
            {
                return(null);
            }
        }
Ejemplo n.º 2
0
        private Execution ExecuteArbitrageSequential(Arbitrage arbitrage)
        {
            Decimal?  recalculatedQuantity = null;
            Execution execution            = new Execution(arbitrage);

            foreach ((Int32 offset, TriangulationStep x, TriangulationStep y) in execution.GetSequence())
            {
                Decimal quantity = recalculatedQuantity ?? arbitrage.GetQuantity(offset);

                if (!Config.TradingEnabled)
                {
                    m_ExchangeEngine.TestOrder(x.Position, x.Symbol, quantity);
                    continue;
                }

                OrderResult result = m_ExchangeEngine.PlaceOrder(x.Position, x.Symbol, quantity);

                if (x.Position == Position.Buy)
                {
                    x.Out = result.OtherQuantity;
                    y.In  = result.ExecutedQuantity;

                    SortedOrderBook orderBook = m_ExchangeEngine.GetOrderBook(y.Symbol);
                    recalculatedQuantity = CalculateDustless(OrderBookConversion(false, y.In, y.QuoteAsset, y.BaseAsset, orderBook), y.DustDecimals);
                }
                else
                {
                    x.Out = result.ExecutedQuantity;
                    y.In  = result.OtherQuantity;

                    recalculatedQuantity = CalculateDustless(y.In, y.DustDecimals);
                }

                execution.Fees += result.Fees;
            }

            return(execution);
        }
Ejemplo n.º 3
0
        private static Decimal OrderBookConversion(Boolean reversed, Decimal quantityFrom, String assetFrom, String assetTo, SortedOrderBook orderBook)
        {
            if (quantityFrom == 0M)
            {
                return(0M);
            }

            ReadOnlyCollection <SortedOrderBookElement> asks = orderBook.Asks;

            if (asks.Count == 0)
            {
                throw new InvalidOperationException(Resources.NoAvailableAsks);
            }

            ReadOnlyCollection <SortedOrderBookElement> bids = orderBook.Bids;

            if (bids.Count == 0)
            {
                throw new Exception(Resources.NoAvailableBids);
            }

            Decimal quantityTo = 0M;

            if (bids[0].Price > asks[0].Price)
            {
                throw new Exception(Resources.NoBidAskSpread);
            }

            if (reversed)
            {
                if ((assetFrom + assetTo) == orderBook.Symbol)
                {
                    foreach (SortedOrderBookElement ask in asks)
                    {
                        Decimal price                = ask.Price;
                        Decimal quantity             = ask.Quantity;
                        Decimal quantityExchangeable = price * quantity;

                        if (quantity >= quantityFrom)
                        {
                            return(quantityTo + (quantityFrom * price));
                        }

                        quantityFrom -= quantity;
                        quantityTo   += quantityExchangeable;
                    }
                }
                else
                {
                    foreach (SortedOrderBookElement bid in bids)
                    {
                        Decimal price                = bid.Price;
                        Decimal quantity             = bid.Quantity;
                        Decimal quantityExchangeable = price * quantity;

                        if (quantityExchangeable >= quantityFrom)
                        {
                            return(quantityTo + (quantityFrom / price));
                        }

                        quantityFrom -= quantityExchangeable;
                        quantityTo   += quantity;
                    }
                }
            }
            else
            {
                if ((assetFrom + assetTo) == orderBook.Symbol)
                {
                    foreach (SortedOrderBookElement bid in bids)
                    {
                        Decimal price                = bid.Price;
                        Decimal quantity             = bid.Quantity;
                        Decimal quantityExchangeable = price * quantity;

                        if (quantity >= quantityFrom)
                        {
                            return(quantityTo + (quantityFrom * price));
                        }

                        quantityFrom -= quantity;
                        quantityTo   += quantityExchangeable;
                    }
                }
                else
                {
                    foreach (SortedOrderBookElement ask in asks)
                    {
                        Decimal price                = ask.Price;
                        Decimal quantity             = ask.Quantity;
                        Decimal quantityExchangeable = price * quantity;

                        if (quantityExchangeable >= quantityFrom)
                        {
                            return(quantityTo + (quantityFrom / price));
                        }

                        quantityFrom -= quantityExchangeable;
                        quantityTo   += quantity;
                    }
                }
            }

            throw new Exception(Resources.DepthsTooShallow);
        }
Ejemplo n.º 4
0
        private void DetectArbitrages()
        {
            if (m_Trading)
            {
                m_MessagePump.Signal(Utilities.FormatMessage(Resources.CycleSkipped, Config.TradingCyclesDelay));
                return;
            }

            m_Trading = true;

            DateTime            start  = DateTime.Now;
            ICollection <Trade> trades = m_ExchangeEngine.GetTrades();
            IDictionary <String, SortedOrderBook> orderBooks = m_ExchangeEngine.GetOrderBooks();

            Parallel.ForEach(trades, trade =>
            {
                SortedOrderBook orderBook1 = orderBooks[trade.Leg1.Symbol];

                if (orderBook1.Invalid)
                {
                    return;
                }

                SortedOrderBook orderBook2 = orderBooks[trade.Leg2.Symbol];

                if (orderBook2.Invalid)
                {
                    return;
                }

                SortedOrderBook orderBook3 = orderBooks[trade.Leg3.Symbol];

                if (orderBook3.Invalid)
                {
                    return;
                }

                Arbitrage bestArbitrage           = null;
                SortedOrderBook[] orderBooksArray = { orderBook1, orderBook2, orderBook3 };

                for (Decimal quantity = Config.InvestmentMinimum; quantity <= Config.InvestmentMaximum; quantity += Config.InvestmentStep)
                {
                    Arbitrage arbitrage = DetectArbitrage(trade, quantity, orderBooksArray);

                    if ((arbitrage == null) || (arbitrage.Profit <= 0M))
                    {
                        continue;
                    }

                    if ((bestArbitrage == null) || (arbitrage.Profit > bestArbitrage.Profit))
                    {
                        bestArbitrage = arbitrage;
                    }
                }

                if (bestArbitrage != null)
                {
                    m_Arbitrages.Add(bestArbitrage);
                }
            });

            Int32 duration = (DateTime.Now - start).Milliseconds;

            if (m_CycleDurations.Count == 300)
            {
                m_CycleDurations.RemoveAt(0);
            }

            m_CycleDurations.Add(duration);

            m_MessagePump.Signal(Utilities.FormatMessage(Resources.CycleCompleted, duration, $"{(Int32)Math.Round(m_CycleDurations.Average(), 0)}"));

            m_Trading = false;
        }
Ejemplo n.º 5
0
        public override Boolean Initialize()
        {
            m_MessagePump.Signal(Resources.ExchangeInitialization);

            try
            {
                BinanceStatus status = m_Api.GetSystemStatusAsync().Result;

                if (status == BinanceStatus.Maintenance)
                {
                    throw new Exception(Resources.ServerMaintenance);
                }

                TimeSpan delta = TimeSpan.Zero;

                for (Int32 i = 0; i < 5; ++i)
                {
                    DateTime now    = DateTime.Now;
                    Boolean  result = m_Api.PingAsync().Result;

                    if (!result)
                    {
                        throw new Exception(Resources.ServerPing);
                    }

                    delta += DateTime.Now - now;
                }

                m_MessagePump.Signal(Utilities.FormatMessage(Resources.MessageConnectivityOK, delta.Milliseconds / 5));
            }
            catch (Exception e)
            {
                m_MessagePump.Signal(Utilities.FormatMessage(Resources.ConnectivityKO, Utilities.GetExceptionMessage(e)));
                return(false);
            }

            try
            {
                m_User = new BinanceApiUser(Config.KeyApi, Config.KeySecret);
                m_MessagePump.Signal(Resources.LoginOK);
            }
            catch (Exception e)
            {
                m_MessagePump.Signal(Utilities.FormatMessage(Resources.LoginKO, Utilities.GetExceptionMessage(e)));
                return(false);
            }

            try
            {
                UpdateBalances(m_Api.GetAccountInfoAsync(m_User).Result);

                m_UserDataWebSocket = new UserDataWebSocketManager();
                m_UserDataWebSocket.SubscribeAsync <AccountUpdateEventArgs>(m_User, x => UpdateBalances(x.AccountInfo)).Wait();
                m_UserDataWebSocket.WaitUntilWebSocketOpenAsync().Wait();

                if (m_Balances.Count == 0)
                {
                    m_MessagePump.Signal(Resources.BalancesOK0);
                }
                else
                {
                    m_MessagePump.Signal(Utilities.FormatMessage(Resources.BalancesKO, m_Balances.Count, (m_Balances.Count == 1 ? String.Empty : "s")));

                    foreach (KeyValuePair <String, Decimal> balance in m_Balances.OrderBy(x => x.Key))
                    {
                        m_MessagePump.Signal($" - {balance.Value:F8} {balance.Key}");
                    }
                }
            }
            catch (Exception e)
            {
                m_MessagePump.Signal(Utilities.FormatMessage(Resources.BalancesKO, Utilities.GetExceptionMessage(e)));
                return(false);
            }

            try
            {
                Dictionary <String, Int32> symbolsData = new Dictionary <String, Int32>();
                HashSet <String>           assets      = new HashSet <String>();

                foreach (Symbol symbol in m_Api.GetSymbolsAsync().Result)
                {
                    if (symbol.Status != SymbolStatus.Trading)
                    {
                        continue;
                    }

                    String baseAsset  = symbol.BaseAsset;
                    String quoteAsset = symbol.BaseAsset;
                    String name       = symbol.ToString();

                    if ((Config.TradingWhitelist.Count > 0) && (!Config.TradingWhitelist.Contains(baseAsset) || !Config.TradingWhitelist.Contains(quoteAsset)))
                    {
                        continue;
                    }

                    String mininumQuantity = symbol.Quantity.Minimum.ToString(CultureInfo.InvariantCulture);
                    Int32  dustDecimals    = Math.Max(0, mininumQuantity.IndexOf("1", StringComparison.OrdinalIgnoreCase) - 1);

                    assets.Add(baseAsset);
                    assets.Add(quoteAsset);
                    symbolsData[name] = dustDecimals;
                }

                ConcurrentBag <Trade> trades = new ConcurrentBag <Trade>();

                Parallel.ForEach(assets.SelectMany(x => assets, (a1, a2) => new { Asset1 = a1, Asset2 = a2 }), pair =>
                {
                    if (pair.Asset1 == pair.Asset2)
                    {
                        return;
                    }

                    TradeLeg leg1 = BuildTradeLeg(symbolsData, Config.InvestmentBaseAsset, pair.Asset1);

                    if ((leg1 == null) || (leg1.Position != Config.TradingPositions[0]))
                    {
                        return;
                    }

                    TradeLeg leg2 = BuildTradeLeg(symbolsData, pair.Asset1, pair.Asset2);

                    if ((leg2 == null) || (leg2.Position != Config.TradingPositions[1]))
                    {
                        return;
                    }

                    TradeLeg leg3 = BuildTradeLeg(symbolsData, pair.Asset2, Config.InvestmentBaseAsset);

                    if ((leg3 == null) || (leg3.Position != Config.TradingPositions[2]))
                    {
                        return;
                    }

                    trades.Add(new Trade(leg1, leg2, leg3));
                });

                if (trades.Count == 0)
                {
                    throw new Exception(Resources.NoTriangularRelationships);
                }

                m_Trades.AddRange(trades);

                m_MessagePump.Signal(Utilities.FormatMessage(Resources.TradesOK, m_Trades.Count, (m_Trades.Count == 1 ? String.Empty : "s")));
            }
            catch (Exception e)
            {
                m_MessagePump.Signal(Utilities.FormatMessage(Resources.TradesKO, Utilities.GetExceptionMessage(e)));
                return(false);
            }

            try
            {
                List <String> symbols = m_Trades.Select(x => x.Leg1.Symbol)
                                        .Union(m_Trades.Select(x => x.Leg2.Symbol))
                                        .Union(m_Trades.Select(x => x.Leg3.Symbol))
                                        .Distinct().OrderBy(x => x).ToList();

                foreach (String symbol in symbols)
                {
                    m_OrderBooks[symbol] = new SortedOrderBook(m_Api.GetOrderBookAsync(symbol, Config.DataSize).Result);

                    m_OrderBooksCache[symbol]            = new DepthWebSocketCache();
                    m_OrderBooksCache[symbol].Error     += (sender, e) => m_OrderBooks[symbol].Invalid = true;
                    m_OrderBooksCache[symbol].OutOfSync += (sender, e) => m_OrderBooks[symbol].Invalid = true;
                    m_OrderBooksCache[symbol].Subscribe(symbol, e => m_OrderBooks[symbol] = new SortedOrderBook(e.OrderBook));

                    Thread.Sleep(Config.DataStagger);
                }

                m_MessagePump.Signal(Utilities.FormatMessage(Resources.OrderBooksOK, symbols.Count, (symbols.Count == 1 ? String.Empty : "s")));
                m_MessagePump.Signal(String.Empty);

                m_Initialized = true;

                return(true);
            }
            catch (Exception e)
            {
                m_MessagePump.Signal(Utilities.FormatMessage(Resources.OrderBooksKO, Utilities.GetExceptionMessage(e)));
                return(false);
            }
        }