public void DOMMidprice() { // Arrange const decimal BUY_PRICE = 6004.5m; const decimal SELL_PRICE = 6016.0m; const decimal EXPECTED_MIDPRICE = 6010.25m; const decimal LOTS = 100m; var bids = new List <Tuple <decimal, decimal> >(); bids.Add(new Tuple <decimal, decimal>(BUY_PRICE - 10m, LOTS)); bids.Add(new Tuple <decimal, decimal>(BUY_PRICE, LOTS)); bids.Add(new Tuple <decimal, decimal>(BUY_PRICE - 100m, LOTS)); var asks = new List <Tuple <decimal, decimal> >(); asks.Add(new Tuple <decimal, decimal>(SELL_PRICE + 45m, LOTS)); asks.Add(new Tuple <decimal, decimal>(SELL_PRICE, LOTS)); asks.Add(new Tuple <decimal, decimal>(SELL_PRICE + 167m, LOTS)); var dom = new SimpleDOM(bids, asks); // Act var d = new DOMBalance(Instrument.XBTUSD(), dom, 0.1, 100m); // Asert Assert.AreEqual(EXPECTED_MIDPRICE, d.MidPrice, $"Incorrect mid-price for spread {BUY_PRICE}..{SELL_PRICE}"); }
public void FilterOutFarOrders() { // Arrange const decimal EXPECTED_MIDPRICE = 4510.25m; const double RADIUS_FROM_MIDPRICE_PERCENT = 0.5; // 50% from mid price const decimal BUY_PRICE = EXPECTED_MIDPRICE - EXPECTED_MIDPRICE * (decimal)(RADIUS_FROM_MIDPRICE_PERCENT)-0.5m; // 0.5 ticks below lower border const decimal SELL_PRICE = EXPECTED_MIDPRICE + EXPECTED_MIDPRICE * (decimal)(RADIUS_FROM_MIDPRICE_PERCENT)+0.5m; // 0.5 ticks over upper border const decimal EXPECTED_LIQUIDITY = 47m; const decimal LOTS = 100m; var bids = new List <Tuple <decimal, decimal> >(); bids.Add(new Tuple <decimal, decimal>(BUY_PRICE, LOTS)); var asks = new List <Tuple <decimal, decimal> >(); asks.Add(new Tuple <decimal, decimal>(SELL_PRICE, LOTS)); var dom = new SimpleDOM(bids, asks); // Act var d = new DOMBalance(Instrument.XBTUSD(), dom, RADIUS_FROM_MIDPRICE_PERCENT, EXPECTED_LIQUIDITY); // Asert Assert.AreEqual(EXPECTED_MIDPRICE, d.MidPrice, $"Incorrect mid-price for spread {BUY_PRICE}..{SELL_PRICE}"); // Since all far orders are filtered out, so we need to create buy and sell orders to make liquidity Assert.AreEqual(0m, d.SellAmount, "Sell not filtered out"); Assert.AreEqual(0m, d.BuyAmount, "Buy not filtered out"); Assert.AreEqual(EXPECTED_LIQUIDITY / 2.0m, d.BuyDisbalance, "Expected to buy"); Assert.AreEqual(EXPECTED_LIQUIDITY / 2.0m, d.SellDisbalance, "Expected to sell"); }
public void SingleSellMidprice() { // Arrange const decimal PRICE = 1.2345m; const decimal LOTS = 100m; var bids = new List <Tuple <decimal, decimal> >(); var asks = new List <Tuple <decimal, decimal> >(); asks.Add(new Tuple <decimal, decimal>(PRICE, LOTS)); var dom = new SimpleDOM(bids, asks); // Act var d = new DOMBalance(Instrument.XBTUSD(), dom, 0.1, 100m); // Asert Assert.AreEqual(PRICE, d.MidPrice, "Incorrect mid-price"); }
public void OppositeOrderToSingleSell() { // Arrange const decimal PRICE = 6056.0m; const int LOT_SIZE = 2; const decimal REQUIRED_LIQUIDITY_IN_DOM = PRICE * LOT_SIZE * 2; // 2 orders with the same price*vol var bids = new List <Tuple <decimal, decimal> >(); var asks = new List <Tuple <decimal, decimal> >(); asks.Add(new Tuple <decimal, decimal>(PRICE, LOT_SIZE)); var dom = new SimpleDOM(bids, asks); // Act var d = new DOMBalance(Instrument.XBTUSD(), dom, 1, REQUIRED_LIQUIDITY_IN_DOM); // Asert Assert.AreEqual(PRICE, d.MidPrice, "Incorrect mid-price"); Assert.AreEqual(0m, d.BuyAmount, "Incorrect buy amount"); Assert.AreEqual(0m, d.SellDisbalance, "Not expected to sell"); Assert.AreEqual(PRICE * LOT_SIZE, d.SellAmount, "Incorrect sell amount"); Assert.AreEqual(PRICE * LOT_SIZE, d.BuyDisbalance, "Expected to buy"); }
public void SingleLargeSell() { // Arrange const decimal PRICE = 6056.0m; const int LOTS = 2; const decimal REQUIRED_LIQUIDITY_IN_DOM = PRICE * LOTS * 0.8m; var bids = new List <Tuple <decimal, decimal> >(); var asks = new List <Tuple <decimal, decimal> >(); asks.Add(new Tuple <decimal, decimal>(PRICE, LOTS)); var dom = new SimpleDOM(bids, asks); // Act var d = new DOMBalance(Instrument.XBTUSD(), dom, 1, REQUIRED_LIQUIDITY_IN_DOM); // Asert Assert.AreEqual(PRICE, d.MidPrice, "Incorrect mid-price"); Assert.AreEqual(0m, d.BuyAmount, "Incorrect buy amount"); Assert.Less(d.SellDisbalance, 0m, "Sell disbalance shall be negative (need close/eat some sell LMTs)"); Assert.AreEqual(PRICE * LOTS, d.SellAmount, "Incorrect sell amount"); Assert.AreEqual(REQUIRED_LIQUIDITY_IN_DOM / 2.0m, d.BuyDisbalance, "Expected to buy"); }
/// <summary> /// Calcs balance and mid-price for the book. Provided bids and asks shall not contain own order. /// </summary> /// <param name="instr">Instrument</param> /// <param name="radiusFromMidpricePercent">Percent around mid-price to filter asks and bids</param> /// <param name="requiredLiquidity">Required liquidity to have in DOM within radiusFromMidpricePercent</param> public DOMBalance(Instrument instr, SimpleDOM dom, double radiusFromMidpricePercent, decimal requiredLiquidity) { if (dom == null) { throw new ArgumentNullException(nameof(dom)); } IList <Tuple <decimal, decimal> > bids = dom.Bids; IList <Tuple <decimal, decimal> > asks = dom.Asks; if (requiredLiquidity < 0) { throw new ArgumentOutOfRangeException(); } if (radiusFromMidpricePercent <= 0) { throw new ArgumentOutOfRangeException(); } if (bids == null || asks == null) { throw new ArgumentNullException("Please provide empty list instead of null"); } Instrument = instr; // BBA var bestBid = bids.OrderBy(b => b.Item1).LastOrDefault(); decimal?bidPrice = null; if (bestBid != null) { bidPrice = bestBid.Item1; } var bestAsk = asks.OrderBy(b => b.Item1).FirstOrDefault(); decimal?askPrice = null; if (bestAsk != null) { askPrice = bestAsk.Item1; } // Mid price if (bidPrice == null && askPrice != null) { MidPrice = askPrice; } if (bidPrice != null && askPrice == null) { MidPrice = bidPrice; } if (bidPrice != null && askPrice != null) { MidPrice = (askPrice + bidPrice) / 2.0m; } if (!MidPrice.HasValue) { return; } foreach (var b in bids) { if (b.Item1 >= MidPrice - MidPrice * (decimal)radiusFromMidpricePercent) { BuyAmount += b.Item1 * b.Item2; } } foreach (var a in asks) { if (a.Item1 <= MidPrice + MidPrice * (decimal)radiusFromMidpricePercent) { SellAmount += a.Item1 * a.Item2; } } BuyDisbalance = requiredLiquidity / 2.0m - BuyAmount; SellDisbalance = requiredLiquidity / 2.0m - SellAmount; }