public async Task <BinanceFuturesPlacedOrderResponse> PlaceOrder(BinanceFuturesPlacedOrder order) { using (var httpClient = new HttpClient()) { long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); long recvWindow = (long)(order.RecvWindow ?? 5000); List <String> queryParts = new List <string>(); if (!string.IsNullOrEmpty(order.Symbol)) { queryParts.Add($"symbol={order.Symbol}"); } if (!string.IsNullOrEmpty(order.Side)) { queryParts.Add($"side={order.Side}"); } if (!string.IsNullOrEmpty(order.PositionSide)) { queryParts.Add($"positionSide={order.PositionSide}"); } if (!string.IsNullOrEmpty(order.Type)) { queryParts.Add($"type={order.Type}"); } if (!string.IsNullOrEmpty(order.TimeInForce)) { queryParts.Add($"timeInForce={order.TimeInForce}"); } if (!string.IsNullOrEmpty(order.NewClientOrderId)) { queryParts.Add($"newClientOrderId={order.TimeInForce}"); } if (order.Price != null && order.Price > 0) { queryParts.Add($"price={order.Price?.ToString(CultureInfo.InvariantCulture)}"); } if (order.StopPrice != null && order.StopPrice > 0) { queryParts.Add($"stopPrice={order.StopPrice?.ToString(CultureInfo.InvariantCulture)}"); } if (order.Quantity != null && order.Quantity > 0) { queryParts.Add($"quantity={order.Quantity?.ToString(CultureInfo.InvariantCulture)}"); } if (order.CallbackRate != null && order.CallbackRate > 0) { queryParts.Add($"callbackRate={order.CallbackRate?.ToString(CultureInfo.InvariantCulture)}"); } if (order.ActivationPrice != null && order.ActivationPrice != 0) { queryParts.Add($"activationPrice={order.ActivationPrice?.ToString(CultureInfo.InvariantCulture)}"); } queryParts.Add($"timestamp={timestamp}"); queryParts.Add($"recvWindow={recvWindow}"); string queryWithoutSignature = string.Join("&", queryParts); var hmacSha256 = new HMACSHA256(System.Text.Encoding.UTF8.GetBytes(_placingOrderPrivateApiKey)); string signature = Convert.ToHexString(hmacSha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(queryWithoutSignature))); string url = @"https://fapi.binance.com/fapi/v1/order?" + queryWithoutSignature + @"&signature=" + signature; HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url); request.Headers.Add("X-MBX-APIKEY", _placingOrderPublicApiKey); HttpResponseMessage response = await httpClient.SendAsync(request); string responseContent = await response.Content.ReadAsStringAsync(); if (response.IsSuccessStatusCode) { Debug.WriteLine($"[{DateTime.UtcNow.ToString("dd - MM - yyyy hh: mm:ss")}] Reponse OK dans placeOrder: {responseContent}"); var responseObj = JsonConvert.DeserializeObject <BinanceFuturesPlacedOrderResponse>(responseContent); responseObj.IsSuccess = true; return(responseObj); } Debug.WriteLine($"[{DateTime.UtcNow.ToString("dd - MM - yyyy hh: mm:ss")}] Reponse incorrecte dans placeOrder: {responseContent}"); return(new BinanceFuturesPlacedOrderResponse() { Message = responseContent }); } }
private async Task _binanceWsClient_UserStreamUpdateReceived(object sender, BinanceUserStreamUpdateEventArgs args) { var update = args.Update; if (update.EventType == "ACCOUNT_UPDATE" && update.BalancePosition?.Balances != null) { Debug.WriteLine($"[{DateTime.UtcNow.ToString("dd - MM - yyyy hh: mm:ss")}] User stream update received: {JsonConvert.SerializeObject(update)}"); var usdtBalance = update.BalancePosition.Balances.FirstOrDefault(b => b.Asset == "USDT"); if (usdtBalance != null) { PortfolioValues.Add(usdtBalance.WalletBalance ?? 0.0m); decimal currMarketMeanPrice = 0; var lastPrices = _binanceWsClient.WholeMarketKlines.Select(k => k.Value.LastOrDefault()?.Kline?.ClosePrice ?? 0).ToList(); currMarketMeanPrice = (decimal)lastPrices.Average(); MarketMeanPrices.Add(currMarketMeanPrice); if (MarketMeanPrices.Count >= 2) { CumulatedMarketPerf += MarketPerfs.Last(); CumulatedPotfolioPerf += PortfolioPerfs.Last(); } if (MarketMeanPrices.Count > MAX_PERFS_HIST) { MarketMeanPrices.RemoveAt(0); PortfolioValues.RemoveAt(0); } decimal returnWithfees = CumulatedPotfolioPerf * (InitPortfolioValue / _parameters.FixedInvestedQuoteAssetQty); decimal fees = (-1) * NbTradeCycles * DOUBLE_FEES_RATE; Debug.WriteLine($"[{DateTime.UtcNow.ToString("dd-MM-yyyy HH:mm:ss")}] Cumul. portfolio return (ren. by invest. size) = {returnWithfees} (cumul. gross: {returnWithfees - fees} -- cumul. fees = {fees}) -- cumul. market return = {CumulatedMarketPerf} "); } //if (_parameters.DoPlaceMarketOrders) //{ // IEnumerable<string> liquidatedSymbols = CurrentHoldBaseAssetPos.Where(p => p.Value.Side != 0).Select(p => p.Key) // .Except(update.BalancePosition?.Positions.Select(p => p.Symbol.ToLower())); // foreach(var symbol in liquidatedSymbols) // { // await _binanceWsClient.CancelAllOpenOrders(symbol); // await Task.Delay(DELAY_BETWEEN_ORDERS_IN_MILLISECS); // } //} } if (update.EventType == "ORDER_TRADE_UPDATE") { Debug.WriteLine($"[{DateTime.UtcNow.ToString("dd - MM - yyyy hh: mm:ss")}] Ordre trade update: {JsonConvert.SerializeObject(update)}"); if (update.OrderUpdate != null && _parameters.DoPlaceMarketOrders && update.OrderUpdate.OrderType == "MARKET" && (update.OrderUpdate.Side == "BUY" || update.OrderUpdate.Side == "SELL") && CurrentHoldBaseAssetPos[update.OrderUpdate.Symbol.ToLower()].Side == (update.OrderUpdate.Side == "BUY" ? 1 : update.OrderUpdate.Side == "SELL" ? -1 : 0) && (update.OrderUpdate.OrderStatus == "FILLED" || update.OrderUpdate.OrderStatus == "PARTIALLY_FILLED")) { string symbol = update.OrderUpdate.Symbol.ToLower(); decimal entryPrice = (decimal)update.OrderUpdate.LastFilledPrice; string side = update.OrderUpdate.Side; decimal qty = (decimal)update.OrderUpdate.OrderLastFilledQuantity; var stopLoss = new BinanceFuturesPlacedOrder { Symbol = symbol, Side = side == "BUY" ? "SELL" : "BUY", Quantity = qty, Type = "STOP_MARKET", StopPrice = side == "BUY" ? Math.Round(entryPrice * (1 - _parameters.RiskThreshold), ExchangeInfo.Symbols.First(s => s.Symbol.ToLower() == symbol).PricePrecision, MidpointRounding.ToZero) : Math.Round(entryPrice * (1 + _parameters.RiskThreshold), ExchangeInfo.Symbols.First(s => s.Symbol.ToLower() == symbol).PricePrecision, MidpointRounding.AwayFromZero) }; Debug.WriteLine($"[{DateTime.UtcNow.ToString("dd-MM-yyyy HH:mm:ss")}] Avant envoi StopLoss : {JsonConvert.SerializeObject(stopLoss, new JsonSerializerSettings { Formatting = Formatting.None })}"); var trailingTakeProfit = new BinanceFuturesPlacedOrder { Symbol = symbol, Side = side == "BUY" ? "SELL" : "BUY", Quantity = qty, Type = "TRAILING_STOP_MARKET", CallbackRate = Math.Min(0.1m, _parameters.RewardThrIncreaseRate * 100), ActivationPrice = Math.Round(side == "BUY" ? entryPrice * (1 + _parameters.RewardThreshold) : entryPrice * (1 - _parameters.RewardThreshold), ExchangeInfo.Symbols.First(s => s.Symbol.ToLower() == symbol).PricePrecision, MidpointRounding.AwayFromZero) }; Debug.WriteLine($"[{DateTime.UtcNow.ToString("dd-MM-yyyy HH:mm:ss")}] Avant envoi TakeProfit: {JsonConvert.SerializeObject(trailingTakeProfit, new JsonSerializerSettings { Formatting = Formatting.None })}"); var responseStopLoss = await _binanceWsClient.PlaceOrder(stopLoss); CurrentHoldBaseAssetPos[symbol].CurrentStopLosses.Add(responseStopLoss); if (responseStopLoss.IsSuccess && responseStopLoss.Code == null) { Debug.WriteLine($"[{DateTime.UtcNow.ToString("dd-MM-yyyy HH:mm:ss")}] Vente/achat envoyé: {JsonConvert.SerializeObject(responseStopLoss, new JsonSerializerSettings { Formatting = Formatting.None })}"); } else { Debug.WriteLine($"[{DateTime.UtcNow.ToString("dd-MM-yyyy HH:mm:ss")}]Vente/achat non-envoyé: {JsonConvert.SerializeObject(responseStopLoss, new JsonSerializerSettings { Formatting = Formatting.None })}"); } var responseTakeProfit = await _binanceWsClient.PlaceOrder(trailingTakeProfit); CurrentHoldBaseAssetPos[symbol].CurrentTakeProfits.Add(responseTakeProfit); if (responseTakeProfit.IsSuccess && responseTakeProfit.Code == null) { Debug.WriteLine($"[{DateTime.UtcNow.ToString("dd-MM-yyyy HH:mm:ss")}] Vente/achat envoyé: {JsonConvert.SerializeObject(responseTakeProfit, new JsonSerializerSettings { Formatting = Formatting.None })}"); } else { Debug.WriteLine($"[{DateTime.UtcNow.ToString("dd-MM-yyyy HH:mm:ss")}]Vente/achat non-envoyé: {JsonConvert.SerializeObject(responseTakeProfit, new JsonSerializerSettings { Formatting = Formatting.None })}"); } } if (update.OrderUpdate != null && _parameters.DoPlaceMarketOrders && update.OrderUpdate.OrderType == "MARKET" && (update.OrderUpdate.Side == "BUY" || update.OrderUpdate.Side == "SELL") && CurrentHoldBaseAssetPos[update.OrderUpdate.Symbol.ToLower()].Side == (update.OrderUpdate.Side == "BUY" ? -1 : update.OrderUpdate.Side == "SELL" ? 1 : 0) && (update.OrderUpdate.OrderStatus == "FILLED" || update.OrderUpdate.OrderStatus == "PARTIALLY_FILLED")) { string symbol = update.OrderUpdate.Symbol.ToLower(); //Cas de l'execution d'un stop loss/trailing take profit bool isStopLoss = CurrentHoldBaseAssetPos[symbol].CurrentStopLosses.Select(x => x.OrderId).Contains(update.OrderUpdate.OrderId); bool isTakeProfit = CurrentHoldBaseAssetPos[symbol].CurrentTakeProfits.Select(x => x.OrderId).Contains(update.OrderUpdate.OrderId); if (isStopLoss || isTakeProfit) { CurrentHoldBaseAssetPos[symbol].Qty -= update.OrderUpdate.OrderLastFilledQuantity ?? 0; if (CurrentHoldBaseAssetPos[symbol].Qty == 0) { CurrentHoldBaseAssetPos[symbol].Side = 0; CurrentHoldBaseAssetPos[symbol].IsBeingLiquidated = false; CurrentHoldBaseAssetPos[symbol].AvgEntryPrice = 0; CurrentHoldBaseAssetPos[symbol].CurrentStopLosses.Clear(); CurrentHoldBaseAssetPos[symbol].CurrentTakeProfits.Clear(); await _binanceWsClient.CancelAllOpenOrders(symbol); MinutesSinceLastPosition = 0; } else if (update.OrderUpdate.OrderStatus == "FILLED") { long expiredOrderId = update.OrderUpdate.OrderId; if (isTakeProfit) { var removedTakeProfit = CurrentHoldBaseAssetPos[symbol].CurrentTakeProfits.FirstOrDefault(tp => tp.OrderId == expiredOrderId); if (removedTakeProfit != null) { int removedTPIndex = CurrentHoldBaseAssetPos[symbol].CurrentTakeProfits.IndexOf(removedTakeProfit); long mirrorOrderId = CurrentHoldBaseAssetPos[symbol].CurrentStopLosses[removedTPIndex].OrderId; await _binanceWsClient.CancelOrder(symbol, mirrorOrderId); CurrentHoldBaseAssetPos[symbol].CurrentTakeProfits.RemoveAt(removedTPIndex); CurrentHoldBaseAssetPos[symbol].CurrentStopLosses.RemoveAt(removedTPIndex); } } else //if (isStopLoss) { var removedStopLoss = CurrentHoldBaseAssetPos[symbol].CurrentStopLosses.FirstOrDefault(tp => tp.OrderId == expiredOrderId); if (removedStopLoss != null) { int removedStopLossIndex = CurrentHoldBaseAssetPos[symbol].CurrentStopLosses.IndexOf(removedStopLoss); long mirrorOrderId = CurrentHoldBaseAssetPos[symbol].CurrentTakeProfits[removedStopLossIndex].OrderId; await _binanceWsClient.CancelOrder(symbol, mirrorOrderId); CurrentHoldBaseAssetPos[symbol].CurrentTakeProfits.RemoveAt(removedStopLossIndex); CurrentHoldBaseAssetPos[symbol].CurrentStopLosses.RemoveAt(removedStopLossIndex); } } } if (isTakeProfit) { Debug.WriteLine($"[{DateTime.UtcNow.ToString("dd - MM - yyyy hh: mm:ss")}] Diminution/liquidation position sur {symbol} (execution du trailing take profit)"); } else { Debug.WriteLine($"[{DateTime.UtcNow.ToString("dd - MM - yyyy hh: mm:ss")}] Diminution/liquidation position sur {symbol} (execution du stop loss)"); } return; } } } await Task.Run(() => { }); }