예제 #1
0
        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(() => { });
        }