コード例 #1
0
        public void Create_Dynamically_Distributed_Limit_Orders_By_Levels_With_Buy_Smart_Markup()
        {
            // arrange

            int priceAccuracy  = 3;
            int volumeAccuracy = 8;

            var quote1 = new Quote("BTCUSD", DateTime.UtcNow, 6000, 5950, "none");
            var quote2 = new Quote("BTCUSD", DateTime.UtcNow, 6050, 5900, "none");

            decimal baseAssetBalance   = 1;
            decimal quoteAssetBalance  = -46.5m;
            int     timeSinceLastTrade = 1;
            int     halfLifePeriod     = 30;

            var levels = new[]
            {
                new InstrumentLevel {
                    Number = 1, Volume = 0.01m, Markup = 0.01m
                },
                new InstrumentLevel {
                    Number = 2, Volume = 0.02m, Markup = 0.02m
                },
                new InstrumentLevel {
                    Number = 3, Volume = 0.03m, Markup = 0.03m
                },
                new InstrumentLevel {
                    Number = 4, Volume = 0.04m, Markup = 0.04m
                },
                new InstrumentLevel {
                    Number = 5, Volume = 0.05m, Markup = 0.05m
                }
            };

            var expectedLimitOrders = new List <LimitOrder>
            {
                LimitOrder.CreateSell(6390.000m, 0.05m),
                LimitOrder.CreateSell(6295.715m, 0.04m),
                LimitOrder.CreateSell(6209.429m, 0.03m),
                LimitOrder.CreateSell(6130.929m, 0.02m),
                LimitOrder.CreateSell(6060.000m, 0.01m),
                LimitOrder.CreateBuy(5866.741m, 0.01m),
                LimitOrder.CreateBuy(5820.499m, 0.02m),
                LimitOrder.CreateBuy(5743.785m, 0.03m),
                LimitOrder.CreateBuy(5660.571m, 0.04m),
                LimitOrder.CreateBuy(5571.071m, 0.05m)
            };

            // act

            IReadOnlyCollection <LimitOrder> actualLimitOrders = Calculator.CalculateLimitOrders(quote1, quote2, levels,
                                                                                                 baseAssetBalance, quoteAssetBalance, timeSinceLastTrade, halfLifePeriod, true, 0, 0, 0, 0, priceAccuracy,
                                                                                                 volumeAccuracy);

            // assert

            Assert.IsTrue(AreEqual(expectedLimitOrders, actualLimitOrders));
        }
コード例 #2
0
        public void Create_Dynamically_Distributed_Limit_Orders_By_Levels_With_Sell_Smart_Markup_With_One_Quote()
        {
            // arrange

            int priceAccuracy  = 3;
            int volumeAccuracy = 8;

            var quote1 = new Quote("BTCUSD", DateTime.UtcNow, 6000, 5950, "none");
            var quote2 = new Quote("BTCUSD", DateTime.UtcNow, 6000, 5950, "none");

            decimal baseAssetBalance   = -10;
            decimal quoteAssetBalance  = -10;
            int     timeSinceLastTrade = 1;
            int     halfLifePeriod     = 30;

            var levels = new[]
            {
                new InstrumentLevel {
                    Number = 1, Volume = 0.01m, Markup = 0.01m
                },
                new InstrumentLevel {
                    Number = 2, Volume = 0.02m, Markup = 0.02m
                },
                new InstrumentLevel {
                    Number = 3, Volume = 0.03m, Markup = 0.03m
                },
                new InstrumentLevel {
                    Number = 4, Volume = 0.04m, Markup = 0.04m
                },
                new InstrumentLevel {
                    Number = 5, Volume = 0.05m, Markup = 0.05m
                }
            };

            var expectedLimitOrders = new List <LimitOrder>
            {
                LimitOrder.CreateSell(6300.000m, 0.05m),
                LimitOrder.CreateSell(6240.000m, 0.04m),
                LimitOrder.CreateSell(6180.000m, 0.03m),
                LimitOrder.CreateSell(6120.000m, 0.02m),
                LimitOrder.CreateSell(6083.958m, 0.01m),
                LimitOrder.CreateBuy(5890.500m, 0.01m),
                LimitOrder.CreateBuy(5831.000m, 0.02m),
                LimitOrder.CreateBuy(5771.500m, 0.03m),
                LimitOrder.CreateBuy(5712.000m, 0.04m),
                LimitOrder.CreateBuy(5652.500m, 0.05m)
            };

            // act

            IReadOnlyCollection <LimitOrder> actualLimitOrders = Calculator.CalculateLimitOrders(quote1, quote2, levels,
                                                                                                 baseAssetBalance, quoteAssetBalance, timeSinceLastTrade, halfLifePeriod, true, 0, 0, 0, 0, priceAccuracy,
                                                                                                 volumeAccuracy, new OrderBookUpdateReport(DateTime.UtcNow));

            // assert

            Assert.IsTrue(AreEqual(expectedLimitOrders, actualLimitOrders));
        }
コード例 #3
0
 /// <summary>
 /// Calculates cross limit orders by original limit orders using cross price.
 /// </summary>
 /// <param name="quote">Best prices of cross instrument.</param>
 /// <param name="limitOrders">The collection of original limit orders.</param>
 /// <param name="inverse">Indicates that the quote is inverse.</param>
 /// <param name="priceAccuracy">The accuracy of price.</param>
 /// <param name="volumeAccuracy">The accuracy of volume.</param>
 /// <returns>A collection of limit orders.</returns>
 public static IReadOnlyCollection <LimitOrder> CalculateCrossLimitOrders(Quote quote,
                                                                          IReadOnlyCollection <LimitOrder> limitOrders, bool inverse, int priceAccuracy, int volumeAccuracy)
 {
     return(limitOrders
            .Select(o => o.Type == LimitOrderType.Sell
             ? LimitOrder.CreateSell(CalculateCrossSellPrice(quote, o.Price, inverse)
                                     .TruncateDecimalPlaces(priceAccuracy, true), Math.Round(o.Volume, volumeAccuracy))
             : LimitOrder.CreateBuy(CalculateCrossBuyPrice(quote, o.Price, inverse)
                                    .TruncateDecimalPlaces(priceAccuracy), Math.Round(o.Volume, volumeAccuracy)))
            .ToArray());
 }
コード例 #4
0
        public void Create_Dynamically_Distributed_Limit_Orders_By_Levels()
        {
            // arrange

            int priceAccuracy  = 3;
            int volumeAccuracy = 8;

            var quote1 = new Quote("BTCUSD", DateTime.UtcNow, 6000, 5950, "none");
            var quote2 = new Quote("BTCUSD", DateTime.UtcNow, 6050, 5900, "none");

            var levels = new[]
            {
                new InstrumentLevel {
                    Number = 1, Volume = 0.01m, Markup = 0.01m
                },
                new InstrumentLevel {
                    Number = 2, Volume = 0.02m, Markup = 0.02m
                },
                new InstrumentLevel {
                    Number = 3, Volume = 0.03m, Markup = 0.03m
                },
                new InstrumentLevel {
                    Number = 4, Volume = 0.04m, Markup = 0.04m
                },
                new InstrumentLevel {
                    Number = 5, Volume = 0.05m, Markup = 0.05m
                }
            };

            var expectedLimitOrders = new List <LimitOrder>
            {
                LimitOrder.CreateSell(6390.000m, 0.05m),
                LimitOrder.CreateSell(6295.715m, 0.04m),
                LimitOrder.CreateSell(6209.429m, 0.03m),
                LimitOrder.CreateSell(6130.929m, 0.02m),
                LimitOrder.CreateSell(6060.000m, 0.01m),
                LimitOrder.CreateBuy(5890.500m, 0.01m),
                LimitOrder.CreateBuy(5820.499m, 0.02m),
                LimitOrder.CreateBuy(5743.785m, 0.03m),
                LimitOrder.CreateBuy(5660.571m, 0.04m),
                LimitOrder.CreateBuy(5571.071m, 0.05m)
            };

            // act

            IReadOnlyCollection <LimitOrder> actualLimitOrders =
                Calculator.CalculateLimitOrders(quote1, quote2, levels, 0, 0, 0, 0, false, 0, 0, 0, 0, priceAccuracy,
                                                volumeAccuracy, new OrderBookUpdateReport(DateTime.UtcNow));

            // assert

            Assert.IsTrue(AreEqual(expectedLimitOrders, actualLimitOrders));
        }
コード例 #5
0
        //[TestMethod]
        public async Task Calculate_Limit_Orders()
        {
            // arrange

            IReadOnlyCollection <LimitOrder> actualLimitOrders = null;

            var expectedLimitOrders = new[]
            {
                LimitOrder.CreateSell(6600.35m, 10),
                LimitOrder.CreateSell(6522.917m, 6),
                LimitOrder.CreateSell(6464, 1),
                LimitOrder.CreateBuy(6237, 1),
                LimitOrder.CreateBuy(6179.25m, 6),
                LimitOrder.CreateBuy(5935.05m, 10)
            };

            _instruments.Add(new Instrument
            {
                AssetPairId = "BTCUSD",
                Mode        = InstrumentMode.Active,
                Levels      = new[]
                {
                    new InstrumentLevel {
                        Number = 1, Volume = 1, Markup = .01m
                    },
                    new InstrumentLevel {
                        Number = 2, Volume = 6, Markup = .03m
                    },
                    new InstrumentLevel {
                        Number = 3, Volume = 10, Markup = .05m
                    }
                }
            });

            _lykkeExchangeServiceMock
            .Setup(o => o.ApplyAsync(It.IsAny <string>(), It.IsAny <IReadOnlyCollection <LimitOrder> >()))
            .Returns((string assetPairId, IReadOnlyCollection <LimitOrder> limitOrders) => Task.CompletedTask)
            .Callback((string assetPairId, IReadOnlyCollection <LimitOrder> limitOrders) =>
                      actualLimitOrders = limitOrders);

            _orderBookServiceMock.Setup(o => o.UpdateAsync(It.IsAny <OrderBook>()))
            .Returns((OrderBook orderBook) => Task.CompletedTask);

            // act

            await _service.UpdateOrderBooksAsync();

            // assert

            Assert.IsTrue(AreEqual(expectedLimitOrders, actualLimitOrders));
        }
コード例 #6
0
        /// <summary>
        /// Creates limit orders using price levels of an instrument.
        /// </summary>
        /// <param name="quote">Best prices of instrument.</param>
        /// <param name="levels">A collection of price levels.</param>
        /// <param name="priceAccuracy">The accuracy of price.</param>
        /// <param name="volumeAccuracy">The accuracy of volume.</param>
        /// <returns>A collection of limit orders.</returns>
        public static IReadOnlyCollection <LimitOrder> CalculateLimitOrders(Quote quote,
                                                                            IReadOnlyCollection <InstrumentLevel> levels, int priceAccuracy, int volumeAccuracy)
        {
            var limitOrders = new List <LimitOrder>();

            foreach (InstrumentLevel instrumentLevel in levels.OrderBy(o => o.Number))
            {
                decimal sellPrice = (quote.Ask * (1 + instrumentLevel.Markup))
                                    .TruncateDecimalPlaces(priceAccuracy, true);
                decimal buyPrice = (quote.Bid * (1 - instrumentLevel.Markup))
                                   .TruncateDecimalPlaces(priceAccuracy);

                decimal volume = Math.Round(instrumentLevel.Volume, volumeAccuracy);

                limitOrders.Add(LimitOrder.CreateSell(sellPrice, volume));
                limitOrders.Add(LimitOrder.CreateBuy(buyPrice, volume));
            }

            return(limitOrders);
        }
コード例 #7
0
        private IReadOnlyCollection <LimitOrder> CreateLimitOrders(IndexSettings indexSettings,
                                                                   AssetPairSettings assetPairSettings, decimal sellPrice, decimal buyPrice)
        {
            var limitOrders = new List <LimitOrder>();

            string walletId = _settingsService.GetWalletId();

            decimal sellVolume = Math.Round(indexSettings.SellVolume / indexSettings.SellLimitOrdersCount,
                                            assetPairSettings.VolumeAccuracy);

            if (sellVolume >= assetPairSettings.MinVolume)
            {
                for (int i = 0; i < indexSettings.SellLimitOrdersCount; i++)
                {
                    limitOrders.Add(LimitOrder.CreateSell(walletId, sellPrice, sellVolume));
                }
            }
            else
            {
                limitOrders.Add(LimitOrder.CreateSell(walletId, sellPrice,
                                                      Math.Round(indexSettings.SellVolume, assetPairSettings.VolumeAccuracy)));
            }

            decimal buyVolume = Math.Round(indexSettings.BuyVolume / indexSettings.BuyLimitOrdersCount,
                                           assetPairSettings.VolumeAccuracy);

            if (buyVolume >= assetPairSettings.MinVolume)
            {
                for (int i = 0; i < indexSettings.BuyLimitOrdersCount; i++)
                {
                    limitOrders.Add(LimitOrder.CreateBuy(walletId, buyPrice, buyVolume));
                }
            }
            else
            {
                limitOrders.Add(LimitOrder.CreateBuy(walletId, buyPrice,
                                                     Math.Round(indexSettings.BuyVolume, assetPairSettings.VolumeAccuracy)));
            }

            return(limitOrders);
        }
コード例 #8
0
        /// <summary>
        /// Creates dynamically distributed limit orders using price levels of an instrument.
        /// </summary>
        /// <param name="quote1">Best prices of instrument on first level.</param>
        /// <param name="quote2">Best prices of instrument on second level.</param>
        /// <param name="levels">A collection of price levels.</param>
        /// <param name="baseAssetBalance">The amount of the base asset on a hedge exchange.</param>
        /// <param name="quoteAssetBalance">The amount of the quote asset on a hedge exchange.</param>
        /// <param name="timeSinceLastTrade">The total seconds from last trade in order book.</param>
        /// <param name="halfLifePeriod">The half life period in seconds.</param>
        /// <param name="allowSmartMarkup">If <c>true</c> then smart markup for first level will be applied; otherwise default markup.</param>
        /// <param name="globalMarkup">Global markup.</param>
        /// <param name="pnLStopLossMarkup">Total pnl stop loss markup.</param>
        /// <param name="fiatEquityStopLossMarkup">Fiat equity stop loss markup.</param>
        /// <param name="noFreshQuotesStopLossMarkup">No fresh quotes stop loss markup.</param>
        /// <param name="priceAccuracy">The accuracy of price.</param>
        /// <param name="volumeAccuracy">The accuracy of volume.</param>
        /// <returns>A collection of limit orders.</returns>
        public static IReadOnlyCollection <LimitOrder> CalculateLimitOrders(
            Quote quote1,
            Quote quote2,
            InstrumentLevel[] levels,
            decimal baseAssetBalance,
            decimal quoteAssetBalance,
            int timeSinceLastTrade,
            int halfLifePeriod,
            bool allowSmartMarkup,
            decimal globalMarkup,
            decimal pnLStopLossMarkup,
            decimal fiatEquityStopLossMarkup,
            decimal noFreshQuotesStopLossMarkup,
            int priceAccuracy,
            int volumeAccuracy)
        {
            var limitOrders = new List <LimitOrder>();

            if (levels.Length == 0)
            {
                return(limitOrders);
            }

            decimal sumSideVolume = levels.Sum(o => o.Volume);

            decimal deltaVolume = sumSideVolume - levels[0].Volume;

            decimal sellDeltaPrice = (quote2.Ask - quote1.Ask) / quote1.Ask;

            decimal buyDeltaPrice = (quote1.Bid - quote2.Bid) / quote1.Bid;

            decimal sellMarketPrice = quote1.Ask;

            decimal sellRawPrice = quote1.Ask;

            decimal buyMarketPrice = quote1.Bid;

            decimal buyRawPrice = quote1.Bid;

            decimal sumVolume = levels[0].Volume;

            decimal sellFirstLevelMarkup = levels[0].Markup;

            decimal buyFirstLevelMarkup = levels[0].Markup;

            if (allowSmartMarkup)
            {
                double alpha = Math.Log(2) / halfLifePeriod;

                decimal relativeSpread = (quote1.Ask - quote1.Bid) / quote1.Mid;

                decimal smartMarkup = levels[0].Markup - relativeSpread / 2m +
                                      relativeSpread * (decimal)Math.Exp(-alpha * timeSinceLastTrade);

                if (-baseAssetBalance * quote1.Mid >= quoteAssetBalance)
                {
                    sellFirstLevelMarkup = smartMarkup;
                }
                else
                {
                    buyFirstLevelMarkup = smartMarkup;
                }
            }

            decimal commonMarkup = globalMarkup + pnLStopLossMarkup + noFreshQuotesStopLossMarkup;

            decimal sellFirstLevelPrice =
                (sellRawPrice * (1 + sellFirstLevelMarkup + commonMarkup + fiatEquityStopLossMarkup))
                .TruncateDecimalPlaces(priceAccuracy, true);

            if (fiatEquityStopLossMarkup != decimal.MinusOne) // don't create sell orders if 'FiatEquityThresholdTo' exceeded
            {
                limitOrders.Add(LimitOrder.CreateSell(sellFirstLevelPrice, Math.Round(levels[0].Volume, volumeAccuracy)));
            }

            decimal buyFirstLevelPrice =
                (buyRawPrice * (1 - (buyFirstLevelMarkup + commonMarkup)))
                .TruncateDecimalPlaces(priceAccuracy);

            limitOrders.Add(LimitOrder.CreateBuy(buyFirstLevelPrice, Math.Round(levels[0].Volume, volumeAccuracy)));

            for (int i = 1; i < levels.Length; i++)
            {
                decimal prevSellMarketPrice = sellMarketPrice;

                decimal prevBuyMarketPrice = buyMarketPrice;

                decimal prevSumVolume = sumVolume;

                sumVolume += levels[i].Volume;

                sellMarketPrice = (1 + (sumVolume - levels[0].Volume) / deltaVolume * sellDeltaPrice) * quote1.Ask;

                buyMarketPrice = (1 - (sumVolume - levels[0].Volume) / deltaVolume * buyDeltaPrice) * quote1.Bid;

                sellRawPrice = (sellMarketPrice * sumVolume - prevSellMarketPrice * prevSumVolume) / levels[i].Volume;

                buyRawPrice = (buyMarketPrice * sumVolume - prevBuyMarketPrice * prevSumVolume) / levels[i].Volume;

                decimal buyPrice = (buyRawPrice * (1 - (levels[i].Markup + commonMarkup)))
                                   .TruncateDecimalPlaces(priceAccuracy);

                limitOrders.Add(LimitOrder.CreateBuy(Math.Min(buyFirstLevelPrice, buyPrice),
                                                     Math.Round(levels[i].Volume, volumeAccuracy)));

                if (fiatEquityStopLossMarkup == decimal.MinusOne) // don't create sell orders if 'FiatEquityThresholdTo' exceeded
                {
                    continue;
                }

                decimal sellPrice = (sellRawPrice * (1 + levels[i].Markup + commonMarkup + fiatEquityStopLossMarkup))
                                    .TruncateDecimalPlaces(priceAccuracy, true);

                limitOrders.Add(LimitOrder.CreateSell(Math.Max(sellFirstLevelPrice, sellPrice),
                                                      Math.Round(levels[i].Volume, volumeAccuracy)));
            }

            return(limitOrders);
        }