Esempio n. 1
0
        public Task <IEnumerable <SynthOrderBook> > CalculateSynthOrderBooksAsync()
        {
            var watch = Stopwatch.StartNew();

            var newActualSynthOrderBooks = new List <SynthOrderBook>();
            var orderBooks = GetWantedActualOrderBooks().ToList();
            var settings   = Settings();

            foreach (var @base in settings.BaseAssets)
            {
                var target = new AssetPair(@base, settings.QuoteAsset, 8, 8);
                var newActualSynthsFromAll = SynthOrderBook.GetSynthsFromAll(target, orderBooks, orderBooks);

                newActualSynthOrderBooks.AddRange(newActualSynthsFromAll);
            }

            foreach (var newSynthOrderBook in newActualSynthOrderBooks)
            {
                _synthOrderBooks[new AssetPairSource(newSynthOrderBook.Source, newSynthOrderBook.AssetPair)] = newSynthOrderBook;
            }

            watch.Stop();
            if (watch.ElapsedMilliseconds > 500)
            {
                _log.Info($"{watch.ElapsedMilliseconds} ms, {_synthOrderBooks.Count} synthetic order books, {orderBooks.Count} order books.");
            }

            return(Task.FromResult(_synthOrderBooks.Select(x => x.Value)));
        }
Esempio n. 2
0
        private IEnumerable <Arbitrage> GetArbitrages(IReadOnlyCollection <OrderBook> orderBooks)
        {
            orderBooks = orderBooks.Where(x => x.BestBid != null || x.BestAsk != null)
                         .OrderBy(x => x.AssetPair.Name).ToList();

            var result = new List <Arbitrage>();

            var watch = Stopwatch.StartNew();

            var synthsCount = 0;

            // O( (n^2)/2 )
            // TODO: cache should be implemented to avoid iterating over all asset pairs every time
            for (var i = 0; i < orderBooks.Count; i++)
            {
                if (i == orderBooks.Count - 1)
                {
                    break;
                }

                var target = orderBooks.ElementAt(i);

                for (var j = i + 1; j < orderBooks.Count; j++)
                {
                    var source = orderBooks.ElementAt(j);

                    if (target.ToString() == source.ToString())
                    {
                        continue;
                    }

                    // Calculate all synthetic order books between source order book and target order book
                    var synthOrderBooks = SynthOrderBook.GetSynthsFromAll(target.AssetPair, source, orderBooks);
                    synthsCount += synthOrderBooks.Count;

                    // Compare each synthetic with target
                    foreach (var synthOrderBook in synthOrderBooks)
                    {
                        decimal spread     = 0;
                        decimal volume     = 0;
                        decimal pnL        = 0;
                        string  targetSide = null;
                        IReadOnlyList <string> marketMakers = new List <string>();

                        if (target.BestBid?.Price > synthOrderBook.BestAsk?.Price)
                        {
                            spread = Arbitrage.GetSpread(target.BestBid.Price, synthOrderBook.BestAsk.Price);
                            var volumePnL = Arbitrage.GetArbitrageVolumeAndPnL(target.Bids, synthOrderBook.Asks);
                            Debug.Assert(volumePnL?.Volume != null);
                            Debug.Assert(volumePnL?.PnL != null);
                            targetSide   = Bid;
                            marketMakers = GetMarketMakers(target.BestBid, synthOrderBook.GetLimitOrdersOfBestAsk());
                            volume       = volumePnL.Value.Volume;
                            pnL          = volumePnL.Value.PnL;
                        }

                        if (synthOrderBook.BestBid?.Price > target.BestAsk?.Price)
                        {
                            spread = Arbitrage.GetSpread(synthOrderBook.BestBid.Price, target.BestAsk.Price);
                            var volumePnL = Arbitrage.GetArbitrageVolumeAndPnL(synthOrderBook.Bids, target.Asks);
                            Debug.Assert(volumePnL?.Volume != null);
                            Debug.Assert(volumePnL?.PnL != null);
                            targetSide   = Ask;
                            marketMakers = GetMarketMakers(target.BestAsk, synthOrderBook.GetLimitOrdersOfBestBid());
                            volume       = volumePnL.Value.Volume;
                            pnL          = volumePnL.Value.PnL;
                        }

                        if (targetSide == null) // no arbitrages
                        {
                            continue;
                        }

                        var volumeInUsd = _orderBooksService.ConvertToUsd(target.AssetPair.Base.Id, volume);
                        var pnLInUsd    = _orderBooksService.ConvertToUsd(target.AssetPair.Quote.Id, pnL);

                        var lykkeArbitrage = new Arbitrage(
                            target.AssetPair,
                            source.AssetPair,
                            spread,
                            targetSide,
                            synthOrderBook.ConversionPath,
                            volume,
                            volumeInUsd,
                            pnL,
                            pnLInUsd,
                            target.BestBid?.Price,
                            target.BestAsk?.Price,
                            synthOrderBook.BestBid?.Price,
                            synthOrderBook.BestAsk?.Price,
                            marketMakers,
                            DateTime.UtcNow
                            );
                        result.Add(lykkeArbitrage);
                    }
                }
            }

            watch.Stop();
            if (watch.ElapsedMilliseconds > 1000)
            {
                _log.Info($"Performance issue - {watch.ElapsedMilliseconds} ms, {result.Count} arbitrages, {orderBooks.Count} order books," +
                          $"{synthsCount} synthetic order books created.");
            }

            return(result.ToList());
        }
        private Task <IReadOnlyList <LykkeArbitrageRow> > GetArbitragesAsync(IReadOnlyList <OrderBook> orderBooks)
        {
            orderBooks = orderBooks.Where(x => x.BestBid.HasValue || x.BestAsk.HasValue).OrderBy(x => x.AssetPair.Name).ToList();

            var result = new List <LykkeArbitrageRow>();

            var watch = Stopwatch.StartNew();

            var synthsCount = 0;

            // O( (n^2)/2 )
            for (var i = 0; i < orderBooks.Count; i++)
            {
                if (i == orderBooks.Count - 1)
                {
                    break;
                }

                var target = orderBooks.ElementAt(i);

                for (var j = i + 1; j < orderBooks.Count; j++)
                {
                    var source = orderBooks.ElementAt(j);

                    if (target.ToString() == source.ToString())
                    {
                        continue;
                    }

                    // Calculate all synthetic order books between a source order book and a target order book
                    var synthOrderBooks = SynthOrderBook.GetSynthsFromAll(target.AssetPair, source, orderBooks);
                    synthsCount += synthOrderBooks.Count;

                    // Compare each synthetic with current target asset pair
                    foreach (var synthOrderBook in synthOrderBooks)
                    {
                        decimal spread     = 0;
                        decimal volume     = 0;
                        decimal pnL        = 0;
                        string  targetSide = null;

                        // Bid side
                        if (target.BestBid?.Price > synthOrderBook.BestAsk?.Price)
                        {
                            spread = Arbitrage.GetSpread(target.BestBid.Value.Price, synthOrderBook.BestAsk.Value.Price);
                            var volumePnL = Arbitrage.GetArbitrageVolumePnL(target.Bids, synthOrderBook.Asks);
                            Debug.Assert(volumePnL?.Volume != null);
                            Debug.Assert(volumePnL?.PnL != null);
                            targetSide = "Bid";
                            volume     = volumePnL.Value.Volume;
                            pnL        = volumePnL.Value.PnL;
                        }

                        // Ask side
                        if (synthOrderBook.BestBid?.Price > target.BestAsk?.Price)
                        {
                            spread = Arbitrage.GetSpread(synthOrderBook.BestBid.Value.Price, target.BestAsk.Value.Price);
                            var volumePnL = Arbitrage.GetArbitrageVolumePnL(synthOrderBook.Bids, target.Asks);
                            Debug.Assert(volumePnL?.Volume != null);
                            Debug.Assert(volumePnL?.PnL != null);
                            targetSide = "Ask";
                            volume     = volumePnL.Value.Volume;
                            pnL        = volumePnL.Value.PnL;
                        }

                        if (string.IsNullOrWhiteSpace(targetSide)) // no arbitrage
                        {
                            continue;
                        }

                        var baseToUsdRate  = Convert(target.AssetPair.Base, Usd, orderBooks);
                        var quoteToUsdRate = Convert(target.AssetPair.Quote, Usd, orderBooks);
                        var volumeInUsd    = volume * baseToUsdRate;
                        volumeInUsd = volumeInUsd.HasValue ? Math.Round(volumeInUsd.Value) : (decimal?)null;
                        var pnLInUsd = pnL * quoteToUsdRate;
                        pnLInUsd = pnLInUsd.HasValue ? Math.Round(pnLInUsd.Value) : (decimal?)null;

                        var lykkeArbitrage = new LykkeArbitrageRow(target.AssetPair, source.AssetPair, spread, targetSide, synthOrderBook.ConversionPath,
                                                                   volume, target.BestBid?.Price, target.BestAsk?.Price, synthOrderBook.BestBid?.Price, synthOrderBook.BestAsk?.Price, volumeInUsd,
                                                                   pnL, pnLInUsd);
                        result.Add(lykkeArbitrage);
                    }
                }
            }

            watch.Stop();
            if (watch.ElapsedMilliseconds > 1000)
            {
                _log.Info($"{watch.ElapsedMilliseconds} ms, {result.Count} arbitrages, {orderBooks.Count} order books, {synthsCount} synthetic order books.");
            }

            return(Task.FromResult(result.OrderBy(x => x.Target).ThenBy(x => x.Source).ToList() as IReadOnlyList <LykkeArbitrageRow>));
        }