/// <inheritdoc /> protected override ResponseObject <BacktestingCandle[]> GetCandles(TradingPair pair, int limit) { var client = _communications.Client; var result = new BacktestingCandle[limit]; int chunkSize = Configuration.Instance.BinanceClientSettings.CandleRequestSize; var width = Configuration.Instance.CandleWidth; var tasks = new (int, Task <CallResult <BinanceKline[]> >)[(limit / chunkSize) + 1];
public void LowestLowTwoCandles() { var input = new BacktestingCandle[] { new BacktestingCandle( openTimestamp: 0L, open: 10M, close: 15M, high: 20M, low: 5, volume: 100, tradingPair: "EOSETH"), new BacktestingCandle( openTimestamp: 0L, open: 10M, close: 12M, high: 14M, low: 3, volume: 100, tradingPair: "EOSETH"), }; var result = _buildLowestLowBuffer(input, 1); Assert.Equal(2, result.Length); Assert.Equal(5M, result[0]); Assert.Equal(3M, result[1]); }
public void HighestHighTwoCandlesTogetherSmaller() { var input = new BacktestingCandle[] { new BacktestingCandle( openTimestamp: 0L, open: 10M, close: 15M, high: 20M, low: 5, volume: 100, tradingPair: "EOSETH"), new BacktestingCandle( openTimestamp: 0L, open: 10M, close: 12M, high: 12M, low: 5, volume: 100, tradingPair: "EOSETH"), }; var result = _buildHighestHighBuffer(input, 2); Assert.Equal(2, result.Length); Assert.Equal(20M, result[0]); Assert.Equal(20M, result[1]); }
public void HighestHighHugeCase() { var random = new Random(); var candles = new BacktestingCandle[1451]; for (int i = 0; i < candles.Length; i++) { var high = (decimal)(random.NextDouble() * 30) + 1; var low = Math.Min((decimal)(random.NextDouble() * 30) + 1, high); var open = Math.Max(Math.Min((decimal)(random.NextDouble() * 30) + 1, high), low); var close = Math.Max(Math.Min((decimal)(random.NextDouble() * 30) + 1, high), low); var volume = (decimal)(random.NextDouble() * 420); candles[i] = new BacktestingCandle( openTimestamp: i * 60000, open: open, close: close, high: high, low: low, volume: volume, tradingPair: "EOSETH"); } var result = _buildHighestHighBuffer(candles, 400); for (int i = 399; i < candles.Length; i++) { Assert.Equal(candles.Skip(i - 399).Take(400).Max(x => x.High), result[i]); } }
protected override ResponseObject <BacktestingCandle[]> GetCandles(TradingPair pair, int limit) { var random = new Random("$pread$hare".GetHashCode(StringComparison.InvariantCulture)); var result = new BacktestingCandle[limit]; for (int i = 0; i < limit; i++) { var high = (decimal)(random.NextDouble() * 30) + 1; var low = Math.Min((decimal)(random.NextDouble() * 30) + 1, high); var open = Math.Max(Math.Min((decimal)(random.NextDouble() * 30) + 1, high), low); var close = Math.Max(Math.Min((decimal)(random.NextDouble() * 30) + 1, high), low); var volume = (decimal)(random.NextDouble() * 420); result[i] = new BacktestingCandle( openTimestamp: i * 60000, open: open, close: close, high: high, low: low, volume: volume, tradingPair: "EOSETH"); } return(new ResponseObject <BacktestingCandle[]>(result)); }
public void GetFilledOrderNullOrder() { var candle = new BacktestingCandle(0, 1, 1, 1, 1, 1, "EOSETH"); var filled = GetFilledOrder(null, candle, 0); Assert.False(filled); }
public void GetFilledBuyStoplossOrderUnMutated() { var order = new OrderUpdate( orderId: 9, tradeId: 8, orderStatus: OrderUpdate.OrderStatus.New, orderType: OrderTypes.StopLoss, createdTimestamp: 302, setPrice: 2.1M, side: OrderSide.Buy, pair: TradingPair.Parse("TRXETH"), setQuantity: 18.6M) { StopPrice = 1.9M, }; var candle = new BacktestingCandle(0, 1, 1, 2, 1, 0, "EOSETH"); GetFilledOrder(order, candle, 434424233); Assert.Equal(OrderStatus.Filled, order.Status); Assert.Equal(OrderTypes.StopLoss, order.OrderType); Assert.Equal(1.9M, order.AverageFilledPrice); Assert.Equal(1.9M, order.LastFillPrice); Assert.Equal(18.6M, order.FilledQuantity); Assert.Equal(18.6M, order.LastFillIncrement); Assert.Equal(434424233, order.FilledTimestamp); }
public void GetFilledBuyLimitOrderUsesMax() { var order = new OrderUpdate( orderId: 0, tradeId: 0, orderStatus: OrderUpdate.OrderStatus.New, orderType: OrderTypes.Limit, createdTimestamp: 0, setPrice: 6.6M, side: OrderSide.Buy, pair: TradingPair.Parse("EOSETH"), setQuantity: 0); var candle = new BacktestingCandle(0, 2, 5, 6.6M, 1.8M, 0, "EOSETH"); var filled = GetFilledOrder(order, candle, 0); Assert.True(filled); }
public void GetFilledSellLimitOrderIsNotFilled() { var order = new OrderUpdate( orderId: 0, tradeId: 0, orderStatus: OrderUpdate.OrderStatus.New, orderType: OrderTypes.Limit, createdTimestamp: 0, setPrice: 3.4M, side: OrderSide.Sell, pair: TradingPair.Parse("EOSETH"), setQuantity: 0); var candle = new BacktestingCandle(0, 1, 2, 3.2M, 0.8M, 0, "EOSETH"); var filled = GetFilledOrder(order, candle, 0); Assert.False(filled); }
public void GetFilledOrderMarketThrows() { var order = GetSomeOrder(OrderTypes.Market); var candle = new BacktestingCandle(1, 1, 1, 1, 1, 1, "EOSETH"); Assert.Throws <UnexpectedOrderTypeException>(() => { try { GetFilledOrder(order, candle, 0); } catch (TargetInvocationException e) { throw e.InnerException; } }); }
public void HighestHighOneCandle() { var input = new BacktestingCandle[] { new BacktestingCandle( openTimestamp: 0L, open: 10M, close: 15M, high: 20M, low: 5, volume: 100, tradingPair: "EOSETH"), }; var result = _buildHighestHighBuffer(input, 1); Assert.Single(result); Assert.Equal(20M, result[0]); }
private static bool GetFilledLimitOrder(OrderUpdate order, BacktestingCandle currentCandle, long currentTime) { bool filled = order.Side == OrderSide.Buy ? currentCandle.Low <= order.SetPrice : currentCandle.High >= order.SetPrice; if (filled) { order.StopPrice = order.StopPrice; order.FilledQuantity = order.SetQuantity; order.AverageFilledPrice = order.SetPrice; order.LastFillIncrement = order.SetQuantity; order.LastFillPrice = order.SetPrice; order.Status = OrderUpdate.OrderStatus.Filled; order.FilledTimestamp = currentTime; return(true); } return(false); }
public void GetFilledSellStoplossOrderIsNotFilled() { var order = new OrderUpdate( orderId: 0, tradeId: 0, orderStatus: OrderUpdate.OrderStatus.New, orderType: OrderTypes.StopLoss, createdTimestamp: 0, setPrice: 0M, side: OrderSide.Sell, pair: TradingPair.Parse("EOSETH"), setQuantity: 0) { StopPrice = 8.5M, }; var candle = new BacktestingCandle(0, 10.1M, 11.2M, 12.4M, 10.1M, 0, "EOSETH"); var filled = GetFilledOrder(order, candle, 0); Assert.False(filled); }
public void GetFilledBuyStoplossOrderIsFilled() { var order = new OrderUpdate( orderId: 0, tradeId: 0, orderStatus: OrderUpdate.OrderStatus.New, orderType: OrderTypes.StopLoss, createdTimestamp: 0, setPrice: 0M, side: OrderSide.Buy, pair: TradingPair.Parse("EOSETH"), setQuantity: 0) { StopPrice = 3.2M, }; var candle = new BacktestingCandle(0, 3, 5, 6.6M, 3M, 0, "EOSETH"); var filled = GetFilledOrder(order, candle, 0); Assert.True(filled); }
/// <summary> /// Returns a bool indicating whether or not the order was filled, and transforms the order into a filled state if so. /// </summary> /// <param name="order">The order to check as filled.</param> /// <param name="currentCandle">The candle to check the order against.</param> /// <param name="currentTime">The current time (will be used as time of fill).</param> /// <returns>Whether the order was filled.</returns> /// <exception cref="UnexpectedOrderTypeException">The order type is not supported by the method.</exception> private static bool EvaluateFilledOrder(OrderUpdate order, BacktestingCandle currentCandle, long currentTime) { Guard.Argument(currentCandle).NotNull(); if (order == null) { return(false); } switch (order.OrderType) { case OrderUpdate.OrderTypes.Limit: return(GetFilledLimitOrder(order, currentCandle, currentTime)); case OrderUpdate.OrderTypes.StopLoss: return(GetFilledStoplossOrder(order, currentCandle, currentTime)); default: throw new UnexpectedOrderTypeException( $"Backtest watchlist should not contain order of type {order.OrderType}"); } }
public void GetFilledSellStoplossOrderUsesMin() { var order = new OrderUpdate( orderId: 0, tradeId: 0, orderStatus: OrderUpdate.OrderStatus.New, orderType: OrderTypes.StopLoss, createdTimestamp: 0, setPrice: 0, side: OrderSide.Sell, pair: TradingPair.Parse("EOSETH"), setQuantity: 0) { StopPrice = 3.4M, }; var candle = new BacktestingCandle(0, 5, 4, 6.6M, 3M, 0, "EOSETH"); var filled = GetFilledOrder(order, candle, 0); Assert.True(filled); Assert.Equal(3.4M, order.AverageFilledPrice); }
public void GetFilledBuyLimitOrderUnMutated() { var order = new OrderUpdate( orderId: 9, tradeId: 8, orderStatus: OrderUpdate.OrderStatus.New, orderType: OrderTypes.Limit, createdTimestamp: 302, setPrice: 2.1M, side: OrderSide.Buy, pair: TradingPair.Parse("TRXETH"), setQuantity: 15.6M); var candle = new BacktestingCandle(0, 5, 6, 100M, 0M, 0, "EOSETH"); GetFilledOrder(order, candle, 2394923); Assert.Equal(OrderStatus.Filled, order.Status); Assert.Equal(OrderTypes.Limit, order.OrderType); Assert.Equal(2.1M, order.AverageFilledPrice); Assert.Equal(2.1M, order.LastFillPrice); Assert.Equal(15.6M, order.FilledQuantity); Assert.Equal(15.6M, order.LastFillIncrement); Assert.Equal(2394923, order.FilledTimestamp); }
/// <summary> /// Compress a list of candles given a certain ratio. /// </summary> /// <param name="input">The collection of candles to compress.</param> /// <param name="compressionRatio">The reducing ratio. (e.g. 2 -> 10 candles become 5 candles.</param> /// <returns>An array of candles.</returns> public static BacktestingCandle[] CompressCandles( BacktestingCandle[] input, int compressionRatio) { Guard.Argument(compressionRatio) .NotZero() .NotNegative(); Guard.Argument(input) .NotNull(nameof(input)) .NotEmpty() .Require <ArgumentException>( x => x.Length % compressionRatio == 0, x => $"{nameof(x)} has length {x.Length} which is not divisible by {nameof(compressionRatio)}, namely {compressionRatio}"); // number of candles used to create new candles var result = new BacktestingCandle[input.Length / compressionRatio]; Parallel.For(0, result.Length, index => { var subset = input.Skip(index * compressionRatio).Take(compressionRatio).ToArray(); var first = subset[0]; var last = subset[subset.Length - 1]; result[index] = new BacktestingCandle( openTimestamp: first.OpenTimestamp, open: first.Open, close: last.Close, high: subset.Max(x => x.High), low: subset.Min(x => x.Low), volume: subset.Sum(x => x.Volume), tradingPair: first.TradingPair); }); return(result); }