private async Task CancelOrder(string currentOrderId) { await _trader.CancelOrder(currentOrderId); _levelsByOrder.Remove(currentOrderId); }
public async Task PlaceToMarketAsync() { var level = _levels.FirstOrDefault(l => l.Status == MarketLevelStatus.Empty); while (level != null) { var side = (level.Side == MarketSide.Long && level.Type == MarketLevelType.Direct) || (level.Side == MarketSide.Short && level.Type == MarketLevelType.Compensate) ? MarketSide.Long : MarketSide.Short; var price = level.Type == MarketLevelType.Direct ? level.Price : level.PriceCompensate; var scope = _levels.Where(l => l.Status == MarketLevelStatus.Placed && l.OrderSide != side); var crossLevel = side == MarketSide.Long ? scope.Where(l => l.Price <= price).OrderBy(l => l.Price).FirstOrDefault() : scope.Where(l => l.Price >= price).OrderByDescending(l => l.Price).FirstOrDefault(); if (crossLevel != null) { crossLevel.CurrentOrderId = string.Empty; crossLevel.Status = MarketLevelStatus.Empty; crossLevel.Type = crossLevel.Type == MarketLevelType.Direct ? MarketLevelType.Compensate : MarketLevelType.Direct; if (crossLevel.Status == MarketLevelStatus.Placed) { await _trader.CancelOrder(crossLevel.CurrentOrderId); _levelsByOrder.Remove(crossLevel.CurrentOrderId); } Console.WriteLine($"flip level. Price: {level.Price}, Side: {level.Side}, Type: {level.Type}, Id: {level.CurrentOrderId}"); level.CurrentOrderId = string.Empty; level.Status = MarketLevelStatus.Empty; level.Type = level.Type == MarketLevelType.Direct ? MarketLevelType.Compensate : MarketLevelType.Direct; Console.WriteLine($"flip level. Price: {level.Price}, Side: {level.Side}, Type: {level.Type}"); } else { var orderId = await _trader.PlaceOrderAsync( _symbol, price, level.OriginalSize, side); if (!string.IsNullOrEmpty(orderId)) { level.CurrentOrderId = orderId; level.Status = MarketLevelStatus.Placed; level.OrderSide = side; Console.WriteLine( $"Placed. Price: {level.Price}, Type: {level.Side}/{level.Type}, O-Price: {price}, O-Side: {side}, O-Id: {level.CurrentOrderId}"); _levelsByOrder[orderId] = level; } } level = _levels.FirstOrDefault(l => l.Status == MarketLevelStatus.Empty); } }
private async void HandleNonFilledOrder(ExchangeOrderResult orderResult, Candle candle) { ExchangeOrderResult result = null; Ticker ticker; switch (orderResult.Result) { case ExchangeAPIOrderResult.FilledPartially: Log($"Partially filled order quantity filled {orderResult.AmountFilled} of {orderResult.Amount}"); /* * * PARTIALLY FILLED * * 1. Save the initial order and what was filled * 2. Check in thrity seconds for any more filling of it * 3. Add a new order against this position * 4. If not filled after two more queries, cancel it * */ // 1. Save SavePosition(SaveOrder(orderResult, GetTimeStamp(candle.Timestamp))); // 2. Check in 30 seconds twice int counter = 0; while (counter < 3) { Log("Waiting 30 seconds.."); Pause(30); ++counter; result = await CheckOrderStatus(orderResult.OrderId); if (result.Result == ExchangeAPIOrderResult.Filled) // if status now is now filled great { Log($"Remaining quanity of order filled"); SaveOrder(orderResult, GetTimeStamp(candle.Timestamp)); UpdatePositionQuantity(); return; } else { if (counter == 2) // this is the last time & it wasn't filled { Log($"Remaining quanity of order not filled after waiting 90 seconds, cancelling the order, exchange order id {orderResult.OrderId}, message: {orderResult.Message}"); _trader.CancelOrder(orderResult.OrderId); // TODO: Could look at putting in another order for the outstanding quantity // Get the minimum quantity for the currency // if the outstanding quantity is more than the minimum quantity // get the current price // create a new order for the remaining quantity // update the position with the new quantity received } } } break; case ExchangeAPIOrderResult.Canceled: /* * * CANCELLED by exchange * * 1. Wait 10 seconds * 3. Check price now * 4. Issue a new Buy * */ Log($"Order cancelled by exchange for orderId {orderResult.OrderId}, waiting 10 seconds and retrying, message: {orderResult.Message}"); Log("Waiting 10 seconds"); Pause(10); ticker = _trader.GetLatestTicker(_inMemoryBot.BaseCoin, _inMemoryBot.Coin); if (orderResult.IsBuy) { Buy(new Candle() { Timestamp = DateTime.Now, ClosePrice = ticker.Ask }); } else { Sell(new Candle() { Timestamp = DateTime.Now, ClosePrice = ticker.Bid }); } break; case ExchangeAPIOrderResult.Error: /* * * ERROR * * 1. Wait 10 seconds and check again * 2. If still error, cancel it * 3. Check price now * 4. Issue a new Buy * */ Log($"Error reported on exchange for orderId {orderResult.OrderId}, message: {orderResult.Message}"); Log("Waiting 10 seconds to check again"); Pause(10); result = await CheckOrderStatus(orderResult.OrderId); Log($"Result.. {result.Result}"); // 2. If not filled, cancel it if (result.Result != ExchangeAPIOrderResult.Filled) { Log($"Not filled, cancelling order {orderResult.OrderId}"); _trader.CancelOrder(orderResult.OrderId); // 3. Check the price now ticker = _trader.GetLatestTicker(_inMemoryBot.BaseCoin, _inMemoryBot.Coin); Log($"Going to order again, latest ticker {JsonConvert.SerializeObject(ticker)} "); if (orderResult.IsBuy) { Buy(new Candle() { Timestamp = DateTime.Now, ClosePrice = ticker.Ask }); } else { Sell(new Candle() { Timestamp = DateTime.Now, ClosePrice = ticker.Bid }); } } break; case ExchangeAPIOrderResult.Pending: /* * * PENDING * * 1. Wait 30 seconds and check again * 2. If still pending, cancel it * 3. Check price now * 4. Issue a new Buy * */ Log($"Pending reported on exchange for orderId {orderResult.OrderId}, message: {orderResult.Message}"); // 1. Wait thirty seconds & check Pause(30); result = await CheckOrderStatus(orderResult.OrderId); // 2. If not filled, cancel it if (result.Result == ExchangeAPIOrderResult.Pending) { _trader.CancelOrder(orderResult.OrderId); // 3. Check the price now ticker = _trader.GetLatestTicker(_inMemoryBot.BaseCoin, _inMemoryBot.Coin); Log($"Order still pending after 30 seconds for orderId {orderResult.OrderId}, cancelling and re-ordering at new price {ticker.Ask}"); if (orderResult.IsBuy) { Buy(new Candle() { Timestamp = DateTime.Now, ClosePrice = ticker.Ask }); } else { Sell(new Candle() { Timestamp = DateTime.Now, ClosePrice = ticker.Bid }); } } break; case ExchangeAPIOrderResult.Unknown: /* * * UNKNOWN * * 1. Wait thirty seconds and check again * 2. If still unknow, cancel it * 3. Check price now * 4. Issue a new Buy * */ Log($"UNKNOWN reported by exchange for orderId {orderResult.OrderId}, message: {orderResult.Message}. Waiting 30 seconds to try again"); // 1. Wait thirty seconds & check Pause(30); result = await CheckOrderStatus(orderResult.OrderId); // 2. If not filled, cancel it if (result.Result == ExchangeAPIOrderResult.Unknown) { _trader.CancelOrder(orderResult.OrderId); // 3. Check the price now ticker = _trader.GetLatestTicker(_inMemoryBot.BaseCoin, _inMemoryBot.Coin); Log($"Order still UNKNOWN after 30 seconds for orderId {orderResult.OrderId}, cancelling and re-ordering at new price {ticker.Ask}"); if (orderResult.IsBuy) { Buy(new Candle() { Timestamp = DateTime.Now, ClosePrice = ticker.Ask }); } else { Sell(new Candle() { Timestamp = DateTime.Now, ClosePrice = ticker.Bid }); } } break; } }