From d76dc40e94eddc02452f46b8922c772c9fd2b655 Mon Sep 17 00:00:00 2001 From: Vlad Dumitru Date: Tue, 31 Dec 2024 12:48:37 +0200 Subject: [PATCH] Fix balances methods --- .../API/Exchanges/MEXC/ExchangeMEXCAPI.cs | 47 ++++++++++++- .../Options/BalancesOption.cs | 70 +++++++++++++++++++ src/ExchangeSharpConsole/Program.cs | 1 + 3 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 src/ExchangeSharpConsole/Options/BalancesOption.cs diff --git a/src/ExchangeSharp/API/Exchanges/MEXC/ExchangeMEXCAPI.cs b/src/ExchangeSharp/API/Exchanges/MEXC/ExchangeMEXCAPI.cs index fb5fddf6..c7f6280b 100644 --- a/src/ExchangeSharp/API/Exchanges/MEXC/ExchangeMEXCAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/MEXC/ExchangeMEXCAPI.cs @@ -241,7 +241,6 @@ protected override async Task> OnGetAmountsAvailable { Currency = x["asset"].Value(), AvailableBalance = x["free"].Value() - + x["locked"].Value() } ) .ToDictionary(k => k.Currency, v => v.AvailableBalance); @@ -475,6 +474,51 @@ await _socket.SendMessageAsync(new WebSocketSubscription } ); } + + protected override Task ProcessRequestAsync( + IHttpWebRequest request, + Dictionary? payload + ) + { + if ( + CanMakeAuthenticatedRequest(payload) + || (payload == null && request.RequestUri.AbsoluteUri.Contains("userDataStream")) + ) + { + request.AddHeader("X-MEXC-APIKEY", PublicApiKey!.ToUnsecureString()); + } + return base.ProcessRequestAsync(request, payload); + } + + protected override Uri ProcessRequestUrl( + UriBuilder url, + Dictionary? payload, + string? method + ) + { + if (CanMakeAuthenticatedRequest(payload)) + { + // payload is ignored, except for the nonce which is added to the url query - bittrex puts all the "post" parameters in the url query instead of the request body + var query = (url.Query ?? string.Empty).Trim('?', '&'); + string newQuery = + "timestamp=" + + payload!["nonce"].ToStringInvariant() + + (query.Length != 0 ? "&" + query : string.Empty) + + ( + payload.Count > 1 + ? "&" + CryptoUtility.GetFormForPayload(payload, false) + : string.Empty + ); + string signature = CryptoUtility.SHA256Sign( + newQuery, + CryptoUtility.ToUnsecureBytesUTF8(PrivateApiKey) + ); + newQuery += "&signature=" + signature; + url.Query = newQuery; + return url.Uri; + } + return base.ProcessRequestUrl(url, payload, method); + } private async Task GetBalance() { @@ -515,6 +559,7 @@ private static ExchangeOrderResult ParseOrder(JToken token) Amount = token["origQty"].ConvertInvariant(), AmountFilled = token["executedQty"].ConvertInvariant(), Price = token["price"].ConvertInvariant(), + AveragePrice = token["price"].ConvertInvariant(), IsBuy = token["side"].ToStringInvariant() == "BUY", OrderDate = token["time"].ConvertInvariant().UnixTimeStampToDateTimeMilliseconds(), Result = ParseOrderStatus(token["status"].ToStringInvariant()) diff --git a/src/ExchangeSharpConsole/Options/BalancesOption.cs b/src/ExchangeSharpConsole/Options/BalancesOption.cs new file mode 100644 index 00000000..1b2001df --- /dev/null +++ b/src/ExchangeSharpConsole/Options/BalancesOption.cs @@ -0,0 +1,70 @@ +using CommandLine; +using ExchangeSharpConsole.Options.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExchangeSharpConsole.Options; + +[Verb( + "balances", + HelpText = "Displays the account balances for an exchange." +)] +public class BalancesOption : BaseOption, IOptionPerExchange, IOptionWithKey +{ + public override async Task RunCommand() + { + using var api = await GetExchangeInstanceAsync(ExchangeName); + + api.LoadAPIKeys(KeyPath); + + if (Mode.Equals("all", StringComparison.OrdinalIgnoreCase)) + { + Console.WriteLine("All Balances:"); + + var balances = await api.GetAmountsAsync(); + + foreach (var balance in balances) + { + Console.WriteLine($"{balance.Key}: {balance.Value}"); + } + + Console.WriteLine(); + } + else if (Mode.Equals("trade", StringComparison.OrdinalIgnoreCase)) + { + Console.WriteLine("Balances available for trading:"); + + var balances = await api.GetAmountsAvailableToTradeAsync(); + + foreach (var balance in balances) + { + Console.WriteLine($"{balance.Key}: {balance.Value}"); + } + + Console.WriteLine(); + } + else + { + throw new ArgumentException($"Invalid mode: {Mode}"); + } + } + + public string ExchangeName { get; set; } + + [Option( + 'm', + "mode", + Required = true, + HelpText = "Mode of execution." + + "\n\tPossible values are \"all\" or \"trade\"." + + "\n\t\tall: Displays all funds, regardless if they are locked or not." + + "\n\t\ttrade: Displays the funds that can be used, at the current moment, to trade." + )] + + public string Mode { get; set; } + + public string KeyPath { get; set; } +} diff --git a/src/ExchangeSharpConsole/Program.cs b/src/ExchangeSharpConsole/Program.cs index a5d2b600..eed61a2a 100644 --- a/src/ExchangeSharpConsole/Program.cs +++ b/src/ExchangeSharpConsole/Program.cs @@ -14,6 +14,7 @@ public partial class Program public Type[] CommandOptions { get; } = { + typeof(BalancesOption), typeof(BuyOption), typeof(CancelOrderOption), typeof(CandlesOption),