public async Task HandleTradeMsg(BinanceTradeOrderData TradeMsg, string[] ID) { // EXECUTION SWITCH switch (TradeMsg.ExecutionType) { case ExecutionType.New: // UPDATE UNCONFIRMED BUY/SELL OrderInfo in openOrders LIST: var orderInfo = new OrderInfo(TradeMsg); if (ID[1] == "Buy") { P2_BuyOrders[P2_BuyOrders.IndexOf(P2_BuyOrders.First(o => o.ClientID == TradeMsg.NewClientOrderId))] = orderInfo; } else if (ID[1] == "Sell") { P2_SellOrders[P2_SellOrders.IndexOf(P2_SellOrders.First(o => o.ClientID == TradeMsg.NewClientOrderId))] = orderInfo; } break; case ExecutionType.Trade: if (TradeMsg.OrderStatus == OrderStatus.Filled) { // UPDATE ORDERS StratStatus in OpenDATAorders: var order = new OrderInfo(TradeMsg); if (ID[1] == "Buy") { // HANDLE RAPID TP-ORDER HERE if (order.StratState == StrategyState.OPEN_FILLED) { // place TP order, update to TP_ENTERED var tpPrice = Math.Round(order.Price * (1 + SETTINGS.TakeProfitMargin), PriceRoundDigits); var tpQty = order.Qty; var tpOrd = new CreateOrderRequest() { Side = OrderSide.Sell, Symbol = order.Symbol, Price = tpPrice, Quantity = tpQty, NewClientOrderId = order.ClientID }; var resp = await BinaREST.CreateLimitOrder(tpOrd); if (resp != null) { Log.Debug($">>> [BUY] Take-Profit submitted"); } // update order info var initPrice = order.Price; order = new OrderInfo(tpOrd, StrategyState.TP_UNCONFIRMED); order.InitialPrice = initPrice; } else if (order.StratState == StrategyState.TP_FILLED) { BUYProfitCyclesP2++; } P2_BuyOrders[P2_BuyOrders.IndexOf(P2_BuyOrders.First(o => o.ClientID == TradeMsg.NewClientOrderId))] = order; } else if (ID[1] == "Sell") { // HANDLE RAPID TP-ORDER HERE if (order.StratState == StrategyState.OPEN_FILLED) { // place TP order, update to TP_ENTERED var tpPrice = Math.Round(order.Price * (1 - SETTINGS.TakeProfitMargin), PriceRoundDigits); var tpQty = Math.Round(Convert.ToDecimal(order.BaseCurrReturn) / (tpPrice * (1 + SETTINGS.FeePercentage)), QtyRoundDigits); var ord = new CreateOrderRequest() { Side = OrderSide.Buy, Symbol = order.Symbol, Price = tpPrice, Quantity = tpQty, NewClientOrderId = order.ClientID }; var resp = await BinaREST.CreateLimitOrder(ord); if (resp != null) { Log.Debug($">>> [SELL] Take-Profit submitted"); } // update order info var initPrice = order.Price; order = new OrderInfo(ord, StrategyState.TP_UNCONFIRMED); order.InitialPrice = initPrice; } else if (order.StratState == StrategyState.TP_FILLED) { SELLProfitCyclesP2++; } P2_SellOrders[P2_SellOrders.IndexOf(P2_SellOrders.First(o => o.ClientID == TradeMsg.NewClientOrderId))] = order; } } else if (TradeMsg.OrderStatus == OrderStatus.PartiallyFilled) { // HANDLE PARTIAL FILLS (ANCHOR UNTIL FILLED) var order = new OrderInfo(TradeMsg); if (order.StratState == StrategyState.PARTIAL_FILL) { if (ID[1] == "Buy") { P2_BuyOrders[P2_BuyOrders.IndexOf(P2_BuyOrders.First(o => o.ClientID == TradeMsg.NewClientOrderId))] = order; } else if (ID[1] == "Sell") { P2_SellOrders[P2_SellOrders.IndexOf(P2_SellOrders.First(o => o.ClientID == TradeMsg.NewClientOrderId))] = order; } } } break; case ExecutionType.Cancelled: break; case ExecutionType.Rejected: LogOrderMessage(TradeMsg); break; default: LogOrderMessage(TradeMsg); break; } }
public async Task Activate(decimal middle, decimal top, decimal bottom) { // populate grid values if necessary PopulateGridVals(bottom); // snap existing P2 TakeProfit orders to an initial grid price SetInitialPrices(); // narrow down grid_values to only possible order placements var middleRound = Math.Round(middle, PriceRoundDigits); var possibleBuyVals = grid_values.Where(x => x >= middle * (1 - (SETTINGS.grid_interval * SETTINGS.MaxOrdsPerSide)) && x <middleRound && // Within range, and not impeding the phase1 TP boundary x> bottom && x * (1 + SETTINGS.TakeProfitMargin) < top).ToList(); var possibleSellVals = grid_values.Where(x => x <= middle * (1 + (SETTINGS.grid_interval * SETTINGS.MaxOrdsPerSide)) && x > middleRound && x <top && x * (1 - SETTINGS.TakeProfitMargin)> bottom).ToList(); // further filter possibleVals and cancel any OPEN orders that fall outside: var canceledOrds = new List <OrderInfo>(); foreach (var order in P2_BuyOrders) { // remove already placed orders from possibleVals if (order.StratState == StrategyState.OPEN || order.StratState == StrategyState.OPEN_UNCONFIRMED || order.StratState == StrategyState.OPEN_FILLED || order.StratState == StrategyState.PARTIAL_FILL) { possibleBuyVals.Remove(order.Price); } // cancel any OPEN status orders outside the possibleVals else if (order.StratState == StrategyState.OPEN && !possibleBuyVals.Any(x => x == order.Price)) { var cancelResp = await BinaREST.CancelOrder(order.Symbol, order.ClientID); if (cancelResp != null) { Log.Debug($">>> [P2_BUY] CANCELED - Price: {order.Price}"); } canceledOrds.Add(order); } // and remove possibleVals that already have corresponding TakeProfit.InitPrice open else if (order.StratState == StrategyState.TP_ENTERED || order.StratState == StrategyState.TP_UNCONFIRMED || order.StratState == StrategyState.TP_FILLED) { possibleBuyVals.Remove(order.InitialPrice); } } P2_BuyOrders = P2_BuyOrders.Except(canceledOrds).ToList(); // Same as above for Sell side: canceledOrds.Clear(); foreach (var order in P2_SellOrders) { if (order.StratState == StrategyState.OPEN || order.StratState == StrategyState.OPEN_UNCONFIRMED || order.StratState == StrategyState.OPEN_FILLED || order.StratState == StrategyState.PARTIAL_FILL) { possibleSellVals.Remove(order.Price); } else if (order.StratState == StrategyState.OPEN && !possibleSellVals.Any(x => x == order.Price)) { var cancelResp = await BinaREST.CancelOrder(order.Symbol, order.ClientID); if (cancelResp != null) { Log.Debug($">>> [P2_SELL] CANCELED - Price: {order.Price}"); } canceledOrds.Add(order); } else if (order.StratState == StrategyState.TP_ENTERED || order.StratState == StrategyState.TP_UNCONFIRMED || order.StratState == StrategyState.TP_FILLED) { possibleSellVals.Remove(order.InitialPrice); } } P2_SellOrders = P2_SellOrders.Except(canceledOrds).ToList(); possibleBuyVals = possibleBuyVals.Distinct().ToList(); possibleSellVals = possibleSellVals.Distinct().ToList(); // place orders on remaining values, add to orderInfo Lists possibleBuyVals.OrderByDescending(v => v); foreach (var p in possibleBuyVals) { // FIRST REPLACE ANY 'TP_FILLED' WITH COMPOUNDED GAINS var filledTP = P2_BuyOrders.FirstOrDefault(o => o.StratState == StrategyState.TP_FILLED); if (filledTP != null) { var buyQty = Math.Round(Convert.ToDecimal(filledTP.BaseCurrReturn) / (p * (1 + SETTINGS.FeePercentage)), QtyRoundDigits); var order = new CreateOrderRequest() { Side = OrderSide.Buy, Symbol = MarketSymbol, Price = p, Quantity = buyQty, NewClientOrderId = filledTP.ClientID }; var response = await BinaREST.CreateLimitOrder(order); if (response != null) { Log.Debug($">>> [BUY] (RE)ORDER PLACED - Price: {p}"); } // update order info var newOrd = new OrderInfo(order, StrategyState.OPEN_UNCONFIRMED); newOrd.InitialPrice = p; P2_BuyOrders[P2_BuyOrders.IndexOf(P2_BuyOrders.First(o => o.ClientID == filledTP.ClientID))] = newOrd; } //Place remaining available OPEN order slots else if (P2_BuyOrders.Count() < SETTINGS.MaxOrdsPerSide) { var buyQty = Math.Round(SETTINGS.PrimaryWager / (p * (1 + SETTINGS.FeePercentage)), QtyRoundDigits); var order = new CreateOrderRequest() { Side = OrderSide.Buy, Symbol = MarketSymbol, Price = p, Quantity = buyQty, NewClientOrderId = "bin2_Buy_" + UniqueString() }; var response = await BinaREST.CreateLimitOrder(order); if (response != null) { Log.Debug($">>> [P2_BUY] ORDER PLACED - Price: {p}"); } // add order status to prevent repeat order var newOrd = new OrderInfo(order, StrategyState.OPEN_UNCONFIRMED); newOrd.InitialPrice = p; P2_BuyOrders.Add(newOrd); } // MOVE UP LOWER BUY ORDERS else if (P2_BuyOrders.Any(o => o.StratState == StrategyState.OPEN && o.Price < p)) { // Cancel lowest buy order var minPrice = P2_BuyOrders.Where(o => o.StratState == StrategyState.OPEN).Min(o => o.Price); var oldOrder = P2_BuyOrders.First(o => o.Price == minPrice); var baseReturn = 0M; if (oldOrder.BaseCurrReturn != null) { baseReturn = Convert.ToDecimal(oldOrder.BaseCurrReturn); } var cancelResp = await BinaREST.CancelOrder(oldOrder.Symbol, oldOrder.ClientID); if (cancelResp != null) { Log.Debug($">>> [P2_SELL] CANCELED - Price: {oldOrder.Price}"); } P2_BuyOrders.Remove(oldOrder); // Place order at correct price // If baseCurr != 0, use this to calc QTY, else primary wager var buyQty = Math.Round(SETTINGS.PrimaryWager / (p * (1 + SETTINGS.FeePercentage)), QtyRoundDigits); if (baseReturn != 0) { buyQty = Math.Round(Convert.ToDecimal(baseReturn) / (p * (1 + SETTINGS.FeePercentage)), QtyRoundDigits); } var order = new CreateOrderRequest() { Side = OrderSide.Buy, Symbol = MarketSymbol, Price = p, Quantity = buyQty, NewClientOrderId = "bin2_Buy_" + UniqueString() }; var response = await BinaREST.CreateLimitOrder(order); if (response != null) { Log.Debug($">>> [P2_BUY] ORDER PLACED - Price: {p}"); } // add order status to prevent repeat order var newOrd = new OrderInfo(order, StrategyState.OPEN_UNCONFIRMED); newOrd.InitialPrice = p; P2_BuyOrders.Add(newOrd); } } // Same as above for sell side: possibleSellVals.OrderBy(v => v); foreach (var p in possibleSellVals) { // FIRST REPLACE ANY 'TP_FILLED' WITH COMPOUNDED GAINS var filledTP = P2_SellOrders.FirstOrDefault(o => o.StratState == StrategyState.TP_FILLED); if (filledTP != null) { var sellQty = filledTP.Qty; var order = new CreateOrderRequest() { Side = OrderSide.Sell, Symbol = MarketSymbol, Price = p, Quantity = sellQty, NewClientOrderId = filledTP.ClientID }; var response = await BinaREST.CreateLimitOrder(order); if (response != null) { Log.Debug($">>> [SELL] ORDER (RE)PLACED - Price: {p}"); } // update order info var newOrd = new OrderInfo(order, StrategyState.OPEN_UNCONFIRMED); newOrd.InitialPrice = p; P2_SellOrders[P2_SellOrders.IndexOf(P2_SellOrders.First(o => o.ClientID == filledTP.ClientID))] = newOrd; } //Place remaining available OPEN order slots else if (P2_SellOrders.Count() < SETTINGS.MaxOrdsPerSide) { var sellQty = Math.Round(SETTINGS.PrimaryWager / (p * (1 - SETTINGS.FeePercentage)), QtyRoundDigits); var order = new CreateOrderRequest() { Side = OrderSide.Sell, Symbol = MarketSymbol, Price = p, Quantity = sellQty, NewClientOrderId = "bin2_Sell_" + UniqueString() }; var response = await BinaREST.CreateLimitOrder(order); if (response != null) { Log.Debug($">>> [P2_SELL] ORDER PLACED - Price: {p}"); } // add order status to prevent repeat order var newOrd = new OrderInfo(order, StrategyState.OPEN_UNCONFIRMED); newOrd.InitialPrice = p; P2_SellOrders.Add(newOrd); } // MOVE DOWN ANY HIGHER SELL ORDERS else if (P2_SellOrders.Any(o => o.StratState == StrategyState.OPEN && o.Price > p)) { // Cancel order var maxPrice = P2_SellOrders.Where(o => o.StratState == StrategyState.OPEN).Max(o => o.Price); var oldOrder = P2_SellOrders.First(o => o.Price == maxPrice); var baseCurr = (oldOrder.Price * (1 - SETTINGS.FeePercentage)) * oldOrder.Qty; var cancelResp = await BinaREST.CancelOrder(oldOrder.Symbol, oldOrder.ClientID); if (cancelResp != null) { Log.Debug($">>> [P2_SELL] CANCELED - Price: {oldOrder.Price}"); } P2_SellOrders.Remove(oldOrder); // Place order at correct price // use same BaseCurr wager amount var sellQty = Math.Round(baseCurr / (p * (1 - SETTINGS.FeePercentage)), QtyRoundDigits); var order = new CreateOrderRequest() { Side = OrderSide.Sell, Symbol = MarketSymbol, Price = p, Quantity = sellQty, NewClientOrderId = "bin2_Sell_" + UniqueString() }; var response = await BinaREST.CreateLimitOrder(order); if (response != null) { Log.Debug($">>> [P2_SELL] ORDER PLACED - Price: {p}"); } // add order status to prevent repeat order var newOrd = new OrderInfo(order, StrategyState.OPEN_UNCONFIRMED); newOrd.InitialPrice = p; P2_SellOrders.Add(newOrd); } } P2_Active = true; }