diff --git a/ExchangeSharp.sln b/ExchangeSharp.sln index 4e2fdb19..0ecdd3f7 100644 --- a/ExchangeSharp.sln +++ b/ExchangeSharp.sln @@ -5,34 +5,34 @@ VisualStudioVersion = 16.0.29318.209 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{1AA29282-C764-47CA-83AB-A6017E941D76}" ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + .gitignore = .gitignore + azure-pipelines.yml = azure-pipelines.yml CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md CONTRIBUTING.md = CONTRIBUTING.md + global.json = global.json icon.png = icon.png ISSUE_TEMPLATE.md = ISSUE_TEMPLATE.md LICENSE.txt = LICENSE.txt logo.png = logo.png README.md = README.md - .gitignore = .gitignore - .editorconfig = .editorconfig - azure-pipelines.yml = azure-pipelines.yml - global.json = global.json EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{51222217-63AE-4881-9972-E0966299D416}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeSharpConsole", "src\ExchangeSharpConsole\ExchangeSharpConsole.csproj", "{2CC066B2-715E-43F2-B50F-4BCD80164B7D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExchangeSharpConsole", "src\ExchangeSharpConsole\ExchangeSharpConsole.csproj", "{2CC066B2-715E-43F2-B50F-4BCD80164B7D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeSharp", "src\ExchangeSharp\ExchangeSharp.csproj", "{E9321D38-DBCF-4387-9547-47EABF60423F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExchangeSharp", "src\ExchangeSharp\ExchangeSharp.csproj", "{E9321D38-DBCF-4387-9547-47EABF60423F}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{83784E6A-9582-4001-9491-C70BBA71334F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeSharpTests", "tests\ExchangeSharpTests\ExchangeSharpTests.csproj", "{DF09A8D9-9BC2-4B50-BE45-A83C9D29B994}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExchangeSharpTests", "tests\ExchangeSharpTests\ExchangeSharpTests.csproj", "{DF09A8D9-9BC2-4B50-BE45-A83C9D29B994}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{9E02A8EA-70EE-4C89-80C6-BD2EC51854CE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeSharpWinForms", "examples\ExchangeSharpWinForms\ExchangeSharpWinForms.csproj", "{571623F9-1652-4669-8E17-A6FAD1426181}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExchangeSharpWinForms", "examples\ExchangeSharpWinForms\ExchangeSharpWinForms.csproj", "{571623F9-1652-4669-8E17-A6FAD1426181}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeSharp.Forms", "src\ExchangeSharp.Forms\ExchangeSharp.Forms.csproj", "{0EA8DE09-C8D4-410E-A345-D172A72EC77C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExchangeSharp.Forms", "src\ExchangeSharp.Forms\ExchangeSharp.Forms.csproj", "{0EA8DE09-C8D4-410E-A345-D172A72EC77C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -64,9 +64,6 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {46DCEF62-6B50-4260-8C47-4368B05CFAB4} - EndGlobalSection GlobalSection(NestedProjects) = preSolution {2CC066B2-715E-43F2-B50F-4BCD80164B7D} = {51222217-63AE-4881-9972-E0966299D416} {E9321D38-DBCF-4387-9547-47EABF60423F} = {51222217-63AE-4881-9972-E0966299D416} @@ -74,4 +71,7 @@ Global {571623F9-1652-4669-8E17-A6FAD1426181} = {9E02A8EA-70EE-4C89-80C6-BD2EC51854CE} {0EA8DE09-C8D4-410E-A345-D172A72EC77C} = {51222217-63AE-4881-9972-E0966299D416} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {46DCEF62-6B50-4260-8C47-4368B05CFAB4} + EndGlobalSection EndGlobal diff --git a/src/ExchangeSharp/API/Exchanges/BinanceGroup/BinanceGroupCommon.cs b/src/ExchangeSharp/API/Exchanges/BinanceGroup/BinanceGroupCommon.cs index 0d959ac0..489bb3d7 100644 --- a/src/ExchangeSharp/API/Exchanges/BinanceGroup/BinanceGroupCommon.cs +++ b/src/ExchangeSharp/API/Exchanges/BinanceGroup/BinanceGroupCommon.cs @@ -88,19 +88,17 @@ public async Task> GetMyTradesAsync(string? mar protected override async Task> OnGetCurrenciesAsync() { var result = await MakeJsonRequestAsync>("/capital/config/getall", BaseUrlSApi); - - return result.ToDictionary(x => x.Coin.ToUpper(), x => { - var network = x.NetworkList.FirstOrDefault(x => x.IsDefault); - return new ExchangeCurrency - { - Name = x.Coin, - FullName = x.Name, - DepositEnabled = network?.DepositEnable ?? x.DepositAllEnable, - WithdrawalEnabled = network?.WithdrawEnable ?? x.WithdrawAllEnable, - MinConfirmations = network?.MinConfirm ?? 0, - MinWithdrawalSize = decimal.Parse(network?.WithdrawMin ?? "0"), - TxFee = decimal.Parse(network?.WithdrawFee ?? "0") - }; + + return result.ToDictionary(x => x.AssetCode, x => new ExchangeCurrency + { + Name = x.AssetCode, + FullName = x.AssetName, + DepositEnabled = x.EnableCharge ?? false, + WithdrawalEnabled = x.EnableWithdraw.GetValueOrDefault(false), + MinConfirmations = x.ConfirmTimes.GetValueOrDefault(0), + MinWithdrawalSize = x.MinProductWithdraw.GetValueOrDefault(decimal.Zero), + TxFee = x.FeeRate.GetValueOrDefault(decimal.Zero), + CoinType = x.ParentCode }); } diff --git a/src/ExchangeSharp/API/Exchanges/BinanceGroup/Models/Currency.cs b/src/ExchangeSharp/API/Exchanges/BinanceGroup/Models/Currency.cs index 5c36e73b..ec69c6cd 100644 --- a/src/ExchangeSharp/API/Exchanges/BinanceGroup/Models/Currency.cs +++ b/src/ExchangeSharp/API/Exchanges/BinanceGroup/Models/Currency.cs @@ -1,49 +1,149 @@ +using System; using Newtonsoft.Json; namespace ExchangeSharp.BinanceGroup { public class Currency { - [JsonProperty("coin")] - public string Coin { get; set; } + [JsonProperty("id")] + public long? Id { get; set; } - [JsonProperty("depositAllEnable")] - public bool DepositAllEnable { get; set; } + [JsonProperty("assetCode")] + public string AssetCode { get; set; } - [JsonProperty("free")] - public string Free { get; set; } + [JsonProperty("assetName")] + public string AssetName { get; set; } - [JsonProperty("freeze")] - public string Freeze { get; set; } + [JsonProperty("unit")] + public string Unit { get; set; } - [JsonProperty("ipoable")] - public string Ipoable { get; set; } + [JsonProperty("transactionFee")] + public double? TransactionFee { get; set; } - [JsonProperty("ipoing")] - public string Ipoing { get; set; } + [JsonProperty("commissionRate")] + public long? CommissionRate { get; set; } + + [JsonProperty("freeAuditWithdrawAmt")] + public long? FreeAuditWithdrawAmt { get; set; } + + [JsonProperty("freeUserChargeAmount")] + public long? FreeUserChargeAmount { get; set; } + + [JsonProperty("minProductWithdraw")] + public decimal? MinProductWithdraw { get; set; } + + [JsonProperty("withdrawIntegerMultiple")] + public float? WithdrawIntegerMultiple { get; set; } + + [JsonProperty("confirmTimes")] + public int? ConfirmTimes { get; set; } + + [JsonProperty("chargeLockConfirmTimes")] + public int? ChargeLockConfirmTimes { get; set; } + + [JsonProperty("createTime")] + public object CreateTime { get; set; } + + [JsonProperty("test")] + public long? Test { get; set; } + + [JsonProperty("url")] + public Uri Url { get; set; } + + [JsonProperty("addressUrl")] + public Uri AddressUrl { get; set; } + + [JsonProperty("blockUrl")] + public string BlockUrl { get; set; } + + [JsonProperty("enableCharge")] + public bool? EnableCharge { get; set; } + + [JsonProperty("enableWithdraw")] + public bool? EnableWithdraw { get; set; } + + [JsonProperty("regEx")] + public string RegEx { get; set; } + + [JsonProperty("regExTag")] + public string RegExTag { get; set; } + + [JsonProperty("gas")] + public long? Gas { get; set; } + + [JsonProperty("parentCode")] + public string ParentCode { get; set; } [JsonProperty("isLegalMoney")] - public bool IsLegalMoney { get; set; } + public bool? IsLegalMoney { get; set; } + + [JsonProperty("reconciliationAmount")] + public long? ReconciliationAmount { get; set; } + + [JsonProperty("seqNum")] + public long? SeqNum { get; set; } + + [JsonProperty("chineseName")] + public string ChineseName { get; set; } + + [JsonProperty("cnLink")] + public Uri CnLink { get; set; } + + [JsonProperty("enLink")] + public Uri EnLink { get; set; } + + [JsonProperty("logoUrl")] + public string LogoUrl { get; set; } + + [JsonProperty("fullLogoUrl")] + public Uri FullLogoUrl { get; set; } + + [JsonProperty("forceStatus")] + public bool? ForceStatus { get; set; } + + [JsonProperty("resetAddressStatus")] + public bool? ResetAddressStatus { get; set; } + + [JsonProperty("chargeDescCn")] + public object ChargeDescCn { get; set; } + + [JsonProperty("chargeDescEn")] + public object ChargeDescEn { get; set; } + + [JsonProperty("assetLabel")] + public object AssetLabel { get; set; } + + [JsonProperty("sameAddress")] + public bool? SameAddress { get; set; } + + [JsonProperty("depositTipStatus")] + public bool? DepositTipStatus { get; set; } + + [JsonProperty("dynamicFeeStatus")] + public bool? DynamicFeeStatus { get; set; } + + [JsonProperty("depositTipEn")] + public object DepositTipEn { get; set; } - [JsonProperty("locked")] - public string Locked { get; set; } + [JsonProperty("depositTipCn")] + public object DepositTipCn { get; set; } - [JsonProperty("name")] - public string Name { get; set; } + [JsonProperty("assetLabelEn")] + public object AssetLabelEn { get; set; } - [JsonProperty("storage")] - public string Storage { get; set; } + [JsonProperty("supportMarket")] + public object SupportMarket { get; set; } - [JsonProperty("trading")] - public bool Trading { get; set; } + [JsonProperty("feeReferenceAsset")] + public string FeeReferenceAsset { get; set; } - [JsonProperty("withdrawAllEnable")] - public bool WithdrawAllEnable { get; set; } + [JsonProperty("feeRate")] + public decimal? FeeRate { get; set; } - [JsonProperty("withdrawing")] - public string Withdrawing { get; set; } + [JsonProperty("feeDigit")] + public long? FeeDigit { get; set; } - [JsonProperty("networkList")] - public CurrencyNetwork[] NetworkList { get; set; } + [JsonProperty("legalMoney")] + public bool? LegalMoney { get; set; } } } diff --git a/src/ExchangeSharp/API/Exchanges/Poloniex/ExchangePoloniexAPI.cs b/src/ExchangeSharp/API/Exchanges/Poloniex/ExchangePoloniexAPI.cs index 16be4aef..d0e7316a 100644 --- a/src/ExchangeSharp/API/Exchanges/Poloniex/ExchangePoloniexAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Poloniex/ExchangePoloniexAPI.cs @@ -166,11 +166,16 @@ public void ParseOrderTrades(IEnumerable trades, ExchangeOrderResult ord decimal tradeAmt = trade["amount"].ConvertInvariant(); decimal tradeRate = trade["rate"].ConvertInvariant(); - order.AveragePrice = (order.AveragePrice.GetValueOrDefault(decimal.Zero) * order.AmountFilled + tradeAmt * tradeRate) / (order.AmountFilled + tradeAmt); + order.AveragePrice = + (order.AveragePrice.GetValueOrDefault(decimal.Zero) * + order.AmountFilled.GetValueOrDefault(decimal.Zero) + tradeAmt * tradeRate) / + (order.AmountFilled.GetValueOrDefault(decimal.Zero) + tradeAmt); if (order.Amount == 0m) { order.Amount += tradeAmt; } + + order.AmountFilled = order.AmountFilled.GetValueOrDefault(decimal.Zero); order.AmountFilled += tradeAmt; if (order.OrderDate == DateTime.MinValue) @@ -179,10 +184,11 @@ public void ParseOrderTrades(IEnumerable trades, ExchangeOrderResult ord } // fee is a percentage taken from the traded amount rounded to 8 decimals + order.Fees = order.Fees.GetValueOrDefault(decimal.Zero); order.Fees += CalculateFees(tradeAmt, tradeRate, order.IsBuy, trade["fee"].ConvertInvariant()); } - if (order.AmountFilled >= order.Amount) + if (order.AmountFilled.GetValueOrDefault(decimal.Zero) >= order.Amount) { order.Result = ExchangeAPIOrderResult.Filled; } @@ -191,6 +197,7 @@ public void ParseOrderTrades(IEnumerable trades, ExchangeOrderResult ord order.Result = ExchangeAPIOrderResult.FilledPartially; } // Poloniex does not provide a way to get the original price + order.AveragePrice = order.AveragePrice?.Normalize(); order.Price = order.AveragePrice; } @@ -246,7 +253,7 @@ private void ParseCompletedOrderDetails(List orders, JToken IEnumerable tradesForOrder = trades.Where(x => x["orderNumber"].ToStringInvariant() == orderNum); ExchangeOrderResult order = new ExchangeOrderResult { OrderId = orderNum, MarketSymbol = marketSymbol }; ParseOrderTrades(tradesForOrder, order); - order.Price = order.AveragePrice; + //order.Price = order.AveragePrice; order.Result = ExchangeAPIOrderResult.Filled; orders.Add(order); } diff --git a/src/ExchangeSharp/Utility/DecimalExtensions.cs b/src/ExchangeSharp/Utility/DecimalExtensions.cs new file mode 100644 index 00000000..a4ac569f --- /dev/null +++ b/src/ExchangeSharp/Utility/DecimalExtensions.cs @@ -0,0 +1,15 @@ +namespace ExchangeSharp.Utility +{ + public static class DecimalExtensions + { + /// + /// Remove trailing zeros. + /// + /// The decimal value to normalize. + /// + public static decimal Normalize(this decimal value) + { + return value / 1.0000000000000000000000000000m; + } + } +} diff --git a/tests/ExchangeSharpTests/ExchangeBinanceAPITests.cs b/tests/ExchangeSharpTests/ExchangeBinanceAPITests.cs index 2d7b62cd..999d5357 100644 --- a/tests/ExchangeSharpTests/ExchangeBinanceAPITests.cs +++ b/tests/ExchangeSharpTests/ExchangeBinanceAPITests.cs @@ -110,7 +110,7 @@ public async Task CurrenciesParsedCorrectly() bnb.MinConfirmations.Should().Be(30); bnb.FullName.Should().Be("Binance Coin"); bnb.Name.Should().Be("BNB"); - bnb.TxFee.Should().Be(0.23m); + bnb.TxFee.Should().Be(0.006m); bnb.CoinType.Should().Be("ETH"); bnb.BaseAddress.Should().BeNullOrEmpty("api does not provide this info");