public async Task BuildDepthCache(string symbol) { // Code example of building out a Dictionary local cache for a symbol using deltas from the WebSocket var localDepthCache = new Dictionary <string, DepthCacheObject> { { symbol, new DepthCacheObject() { Asks = new Dictionary <decimal, decimal>(), Bids = new Dictionary <decimal, decimal>(), } } }; var marketDepthCache = localDepthCache[symbol]; // Get Order Book, and use Cache var depthResults = await BinaREST.GetOrderBook(symbol, 100); // Populate our depth cache depthResults.Asks.ForEach(a => { if (a.Quantity != 0.00000000M) { marketDepthCache.Asks.Add(a.Price, a.Quantity); } }); depthResults.Bids.ForEach(a => { if (a.Quantity != 0.00000000M) { marketDepthCache.Bids.Add(a.Price, a.Quantity); } }); UpdateIDs[symbol] = depthResults.LastUpdateId; OrderBooks[symbol] = localDepthCache[symbol]; }
public async Task LoadOpenP2Order(OrderResponse order, string[] ID) { // Place OrderP2 object into dictionary var ordInfo = new OrderInfo(order); if (ordInfo.Status == OrderStatus.New) { if (ordInfo.StratState == StrategyState.OPEN) { var cancelResp = await BinaREST.CancelOrder(ordInfo.Symbol, ordInfo.ClientID); if (cancelResp != null) { Log.Debug($">>> [P2_BUY] CANCELED - Price: {ordInfo.Price}"); } } else if (ID[1] == "Buy") { P2_BuyOrders.Add(ordInfo); } else if (ID[1] == "Sell") { P2_SellOrders.Add(ordInfo); } } else if (ordInfo.Status == OrderStatus.PartiallyFilled) { if (ordInfo.StratState == StrategyState.OPEN) { ordInfo.StratState = StrategyState.PARTIAL_FILL; } if (ID[1] == "Buy") { P2_BuyOrders.Add(ordInfo); } else if (ID[1] == "Sell") { P2_SellOrders.Add(ordInfo); } } }
public static async Task Main(string[] args) { Console.WriteLine("--------------------------"); Console.WriteLine(" BinaTrader"); Console.WriteLine("--------------------------"); // Provide your configuration and keys here, this allows the client to function as expected. string apiKey = ConfigurationManager.AppSettings["API_KEY"]; string secretKey = ConfigurationManager.AppSettings["SECRET_KEY"]; // Initialize logging: var Log = LogManager.GetLogger(typeof(Program)); Log.Debug("\n\nRUN START - " + DateTime.Now); // Initialise the general client client with config var client = new BinanceClient(new ClientConfiguration() { ApiKey = apiKey, SecretKey = secretKey, Logger = Log, //EnableRateLimiting = true }); BinaREST.SetClient(client); // Initialze strategy markets, then execute strategy: strategy = new Strategy_Phase1(); await strategy.Initialize(); PrintOrderBook(Strategy_Phase1.SETTINGS.SubSpecificSymbols[0], 35); await strategy.Execute(); Console.WriteLine("\n\n\n\n\t*** END OF PROGRAM ***\n\n\t Press enter 3 times to exit."); Console.ReadKey(); Console.ReadKey(); Console.ReadKey(); }
public async Task Deactivate() { if (!P2_Active) { return; } // Cancel all OPEN orders, leave TakeProfits open var canceledOrds = new List <OrderInfo>(); foreach (var ord in P2_BuyOrders.Where(o => o.StratState == StrategyState.OPEN)) { var cancelResp = await BinaREST.CancelOrder(ord.Symbol, ord.ClientID); if (cancelResp != null) { Log.Debug($">>> [P2_BUY] CANCELED - Price: {ord.Price}"); } canceledOrds.Add(ord); } P2_BuyOrders = P2_BuyOrders.Except(canceledOrds).ToList(); canceledOrds.Clear(); foreach (var ord in P2_SellOrders.Where(o => o.StratState == StrategyState.OPEN)) { var cancelResp = await BinaREST.CancelOrder(ord.Symbol, ord.ClientID); if (cancelResp != null) { Log.Debug($">>> [P2_BUY] CANCELED - Price: {ord.Price}"); } canceledOrds.Add(ord); } P2_SellOrders = P2_SellOrders.Except(canceledOrds).ToList(); P2_Active = false; }
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; }
private async Task LoadOpenOrders() { // CALL ALL ORDERS FOR MARKET, SORT THROUGH ALREADY PLACED ORDERS var orders = await BinaREST.GetOpenOrders(SETTINGS.SubSpecificSymbols[0]); foreach (var ord in orders) { var clientID = ord.ClientOrderId.Split('_'); if (clientID[0] == "bin2") { await StrategyP2.LoadOpenP2Order(ord, clientID); continue; } else if (clientID[0] != "bina") { continue; } var ordInfo = new OrderInfo(ord); if (ordInfo.Status == OrderStatus.New) { if (clientID[1] == "Buy") { OpenBUYorders.Add(ordInfo); } else if (clientID[1] == "Sell") { OpenSELLorders.Add(ordInfo); } continue; } else if (ordInfo.Status == OrderStatus.PartiallyFilled) { if (clientID[1] == "Buy") { if (ordInfo.Side == OrderSide.Buy) { ordInfo.StratState = StrategyState.PARTIAL_FILL; OpenBUYorders.Add(ordInfo); } else { OpenBUYorders.Add(ordInfo); } } else if (clientID[1] == "Sell") { if (ordInfo.Side == OrderSide.Sell) { { ordInfo.StratState = StrategyState.PARTIAL_FILL; OpenSELLorders.Add(ordInfo); } } else { OpenSELLorders.Add(ordInfo); } } continue; } } }
private async Task <decimal> ReplaceSpreadOrders(string symbol, decimal oldMiddle) { // CHECK UserData Websocket STABILITY await CheckUserDataWS(); // SORT LISTS W/ OPEN TAKE-PROFITS AT END if (OpenBUYorders.Any()) { OpenBUYorders = SortOrderList(OpenBUYorders); } if (OpenSELLorders.Any()) { OpenSELLorders = SortOrderList(OpenSELLorders); } //COPY LIST FOR REVISIONS var newBUYorders = new List <OrderInfo>(OpenBUYorders); var newSELLorders = new List <OrderInfo>(OpenSELLorders); var newMiddle = depthDATA.GetSpreadMiddle(symbol); // PLACE/REPLACE ORDERS: for (int t = 0; t < SETTINGS.Tiers; t++) { // BUYS: //if (OpenBUYorders[t] == null) if (OpenBUYorders.ElementAtOrDefault(t) == null) { // no order found on this tier - place order, add new data var buyPrice = Math.Round(newMiddle * (1 - (SETTINGS.PrimaryMargin * SETTINGS.TierMultipliers[t])), PriceRoundDigits); var buyQty = Math.Round(SETTINGS.PrimaryWager / (buyPrice * (1 + SETTINGS.FeePercentage)), QtyRoundDigits); var order = new CreateOrderRequest() { Side = OrderSide.Buy, Symbol = symbol, Price = buyPrice, Quantity = buyQty, NewClientOrderId = "bina_Buy_" + UniqueString() }; var response = await BinaREST.CreateLimitOrder(order); if (response != null) { Log.Debug($">>> [BUY] INITIAL ORDER PLACED: T{t+1}"); } // add order status to prevent repeat order newBUYorders.Add(new OrderInfo(order, StrategyState.OPEN_UNCONFIRMED)); } else { switch (OpenBUYorders[t].StratState) { case StrategyState.OPEN: if (newMiddle > oldMiddle * (1 + SETTINGS.MoveOrderThresh) || newMiddle < oldMiddle * (1 - SETTINGS.MoveOrderThresh)) { // cancel and replace order at new price var cancelResp = await BinaREST.CancelOrder(SETTINGS.SubSpecificSymbols[0], OpenBUYorders[t].ClientID); if (cancelResp != null) { Log.Debug($">>> [BUY] CANCELED: T{t + 1}"); } var newPrice = Math.Round(newMiddle * (1 - (SETTINGS.PrimaryMargin * SETTINGS.TierMultipliers[t])), PriceRoundDigits); var newQty = Math.Round((OpenBUYorders[t].Qty * (OpenBUYorders[t].Price * (1 + SETTINGS.FeePercentage))) / (newPrice * (1 + SETTINGS.FeePercentage)), QtyRoundDigits); var o = new CreateOrderRequest() { Side = OrderSide.Buy, Symbol = symbol, Price = newPrice, Quantity = newQty, NewClientOrderId = OpenBUYorders[t].ClientID }; var r = await BinaREST.CreateLimitOrder(o); if (r != null) { Log.Debug($">>> [BUY] MOVED: T{t + 1}"); } // update order info newBUYorders[t] = new OrderInfo(o, StrategyState.OPEN_UNCONFIRMED); } break; case StrategyState.TP_FILLED: // place new order on starting side, carry over qty for compounding var buyPrice = Math.Round(newMiddle * (1 - (SETTINGS.PrimaryMargin * SETTINGS.TierMultipliers[t])), PriceRoundDigits); var buyQty = Math.Round(Convert.ToDecimal(OpenBUYorders[t].BaseCurrReturn) / (buyPrice * (1 + SETTINGS.FeePercentage)), QtyRoundDigits); var order = new CreateOrderRequest() { Side = OrderSide.Buy, Symbol = symbol, Price = buyPrice, Quantity = buyQty, NewClientOrderId = OpenBUYorders[t].ClientID }; var response = await BinaREST.CreateLimitOrder(order); if (response != null) { Log.Debug($">>> [BUY] ORDER PLACED: T{t + 1}"); } // update order info newBUYorders[t] = new OrderInfo(order, StrategyState.OPEN_UNCONFIRMED); BUYProfitCycles++; break; } } // SELLS: if (OpenSELLorders.ElementAtOrDefault(t) == null) { // no order found on this tier - place order, add new data var sellPrice = Math.Round(newMiddle * (1 + (SETTINGS.PrimaryMargin * SETTINGS.TierMultipliers[t])), PriceRoundDigits); var sellQty = Math.Round(SETTINGS.PrimaryWager / (sellPrice * (1 - SETTINGS.FeePercentage)), QtyRoundDigits); var order = new CreateOrderRequest() { Side = OrderSide.Sell, Symbol = symbol, Price = sellPrice, Quantity = sellQty, NewClientOrderId = "bina_Sell_" + UniqueString() }; var response = await BinaREST.CreateLimitOrder(order); if (response != null) { Log.Debug($">>> [SELL] INITIAL ORDER PLACED: T{t + 1}"); } // add order status to prevent repeat order newSELLorders.Add(new OrderInfo(order, StrategyState.OPEN_UNCONFIRMED)); } else { switch (OpenSELLorders[t].StratState) { case StrategyState.OPEN: if (newMiddle > oldMiddle * (1 + SETTINGS.MoveOrderThresh) || newMiddle < oldMiddle * (1 - SETTINGS.MoveOrderThresh)) { // cancel and replace order at new price var cancelResp = await BinaREST.CancelOrder(SETTINGS.SubSpecificSymbols[0], OpenSELLorders[t].ClientID); if (cancelResp != null) { Log.Debug($">>> [SELL] CANCELED: T{t + 1}"); } var newPrice = Math.Round(newMiddle * (1 + (SETTINGS.PrimaryMargin * SETTINGS.TierMultipliers[t])), PriceRoundDigits); var newQty = Math.Round((OpenSELLorders[t].Qty * (OpenSELLorders[t].Price * (1 - SETTINGS.FeePercentage))) / (newPrice * (1 - SETTINGS.FeePercentage)), QtyRoundDigits); var o = new CreateOrderRequest() { Side = OrderSide.Sell, Symbol = symbol, Price = newPrice, Quantity = newQty, NewClientOrderId = OpenSELLorders[t].ClientID }; var r = await BinaREST.CreateLimitOrder(o); if (r != null) { Log.Debug($">>> [SELL] MOVED: T{t + 1}"); } // update order info newSELLorders[t] = new OrderInfo(o, StrategyState.OPEN_UNCONFIRMED); } break; case StrategyState.TP_FILLED: // place new order on starting side, carry over qty for compounding var sellPrice = Math.Round(newMiddle * (1 + (SETTINGS.PrimaryMargin * SETTINGS.TierMultipliers[t])), PriceRoundDigits); var sellQty = OpenSELLorders[t].Qty; var order = new CreateOrderRequest() { Side = OrderSide.Sell, Symbol = symbol, Price = sellPrice, Quantity = sellQty, NewClientOrderId = OpenSELLorders[t].ClientID }; var response = await BinaREST.CreateLimitOrder(order); if (response != null) { Log.Debug($">>> [SELL] ORDER PLACED: T{t + 1}"); } // update order info newSELLorders[t] = new OrderInfo(order, StrategyState.OPEN_UNCONFIRMED); SELLProfitCycles++; break; } } } // UPDATE ORDER STATUS: OpenBUYorders = new List <OrderInfo>(newBUYorders); OpenSELLorders = new List <OrderInfo>(newSELLorders); if (newMiddle > oldMiddle * (1 + SETTINGS.MoveOrderThresh) || newMiddle < oldMiddle * (1 - SETTINGS.MoveOrderThresh)) { return(newMiddle); } else { return(oldMiddle); } }
public async Task Execute() { var spreadMiddle = 0M; var lastUpdate = new DateTime(); // BEGIN EXECUTION LOOP: while (true) { // Place new/Replace tiered orders, then pause if (DateTime.Now - lastUpdate > SETTINGS.OrderExecDelay) { spreadMiddle = await ReplaceSpreadOrders("XLMUSDT", spreadMiddle); lastUpdate = DateTime.Now; } else if (userDATA.UserUpdateQueue.IsEmpty) { Thread.Sleep(100); } // PROCESS ORDER UPDATES, MAKE DECISIONS BASED ON ExecutionType: while (!userDATA.UserUpdateQueue.IsEmpty) { // DeQueue next order update: bool tryDQ = false; var userMsg = new UserDataUpdate(); do { tryDQ = userDATA.UserUpdateQueue.TryDequeue(out userMsg); } while (!tryDQ); if (userMsg.TradeOrderData != null) // (!= balance update msg) { var clientID = userMsg.TradeOrderData.NewClientOrderId.Split('_'); if (clientID[0] == "bin2") { await StrategyP2.HandleTradeMsg(userMsg.TradeOrderData, clientID); continue; } else if (clientID[0] != "bina") { continue; } switch (userMsg.TradeOrderData.ExecutionType) { case ExecutionType.New: // UPDATE UNCONFIRMED BUY/SELL OrderInfo in openOrders LIST: var orderInfo = new OrderInfo(userMsg.TradeOrderData); if (clientID[1] == "Buy") { OpenBUYorders[OpenBUYorders.IndexOf(OpenBUYorders.First(o => o.ClientID == userMsg.TradeOrderData.NewClientOrderId))] = orderInfo; } else if (clientID[1] == "Sell") { OpenSELLorders[OpenSELLorders.IndexOf(OpenSELLorders.First(o => o.ClientID == userMsg.TradeOrderData.NewClientOrderId))] = orderInfo; } break; case ExecutionType.Trade: if (userMsg.TradeOrderData.OrderStatus == OrderStatus.Filled) { // UPDATE ORDERS StratStatus in OpenDATAorders: var order = new OrderInfo(userMsg.TradeOrderData); if (clientID[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 ord = new CreateOrderRequest() { Side = OrderSide.Sell, Symbol = order.Symbol, Price = tpPrice, Quantity = tpQty, NewClientOrderId = order.ClientID }; var resp = await BinaREST.CreateLimitOrder(ord); if (resp != null) { Log.Debug($">>> [BUY] Take-Profit submitted"); } // update order info order = new OrderInfo(ord, StrategyState.TP_UNCONFIRMED); } OpenBUYorders[OpenBUYorders.IndexOf(OpenBUYorders.First(o => o.ClientID == userMsg.TradeOrderData.NewClientOrderId))] = order; } else if (clientID[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 order = new OrderInfo(ord, StrategyState.TP_UNCONFIRMED); } OpenSELLorders[OpenSELLorders.IndexOf(OpenSELLorders.First(o => o.ClientID == userMsg.TradeOrderData.NewClientOrderId))] = order; } } else if (userMsg.TradeOrderData.OrderStatus == OrderStatus.PartiallyFilled) { // HANDLE PARTIAL FILLS (ANCHOR UNTIL FILLED) var order = new OrderInfo(userMsg.TradeOrderData); if (order.StratState == StrategyState.PARTIAL_FILL) { if (clientID[1] == "Buy") { OpenBUYorders[OpenBUYorders.IndexOf(OpenBUYorders.First(o => o.ClientID == userMsg.TradeOrderData.NewClientOrderId))] = order; } else if (clientID[1] == "Sell") { OpenSELLorders[OpenSELLorders.IndexOf(OpenSELLorders.First(o => o.ClientID == userMsg.TradeOrderData.NewClientOrderId))] = order; } } } break; case ExecutionType.Cancelled: break; case ExecutionType.Rejected: LogOrderMessage(userMsg.TradeOrderData); break; default: LogOrderMessage(userMsg.TradeOrderData); break; } } } if (OpenBUYorders.Where(o => o.StratState == StrategyState.TP_ENTERED).Count() == SETTINGS.Tiers && OpenSELLorders.Where(o => o.StratState == StrategyState.TP_ENTERED).Count() == SETTINGS.Tiers && !SETTINGS.MutePhase2) { var window_top = OpenBUYorders.Min(tp => tp.Price); var window_bottom = OpenSELLorders.Max(tp => tp.Price); await StrategyP2.Activate(depthDATA.GetSpreadMiddle(SETTINGS.SubSpecificSymbols[0]), window_top, window_bottom); } else { await StrategyP2.Deactivate(); } } }