From 19c2726437d465fce9990555bb82cc87f8983ff1 Mon Sep 17 00:00:00 2001 From: Tomas Bezouska Date: Sun, 23 Jan 2022 10:44:10 +0100 Subject: [PATCH 1/6] Coinmate public methods --- .../Exchanges/Coinmate/ExchangeCoinmateAPI.cs | 81 +++++++++++++++++++ .../Coinmate/Models/CoinmateOrderBook.cs | 14 ++++ .../Coinmate/Models/CoinmateResponse.cs | 9 +++ .../Coinmate/Models/CoinmateSymbol.cs | 9 +++ .../Coinmate/Models/CoinmateTradingPair.cs | 15 ++++ 5 files changed, 128 insertions(+) create mode 100644 src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs create mode 100644 src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateOrderBook.cs create mode 100644 src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateResponse.cs create mode 100644 src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateSymbol.cs create mode 100644 src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateTradingPair.cs diff --git a/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs b/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs new file mode 100644 index 00000000..1bb8c6e8 --- /dev/null +++ b/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs @@ -0,0 +1,81 @@ +using ExchangeSharp.API.Exchanges.Coinmate.Models; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ExchangeSharp +{ + public class ExchangeCoinmateAPI : ExchangeAPI + { + public override string BaseUrl { get; set; } = "https://coinmate.io/api"; + + public ExchangeCoinmateAPI() + { + MarketSymbolSeparator = "_"; + } + + public override string Name => "Coinmate"; + + protected override async Task OnGetTickerAsync(string marketSymbol) + { + var response = await MakeCoinmateRequest($"/ticker?currencyPair={marketSymbol}"); + return await this.ParseTickerAsync(response, marketSymbol, "ask", "bid", "last", "amount", null, "timestamp", TimestampType.UnixSeconds); + } + + protected override async Task> OnGetMarketSymbolsAsync() + { + var response = await MakeCoinmateRequest("/products"); + return response.Select(x => $"{x.FromSymbol}{MarketSymbolSeparator}{x.ToSymbol}").ToArray(); + } + + protected internal override async Task> OnGetMarketSymbolsMetadataAsync() + { + var response = await MakeCoinmateRequest("/tradingPairs"); + return response.Select(x => new ExchangeMarket + { + IsActive = true, + BaseCurrency = x.FirstCurrency, + QuoteCurrency = x.SecondCurrency, + MarketSymbol = x.Name, + MinTradeSize = x.MinAmount, + PriceStepSize = 1 / (decimal)(Math.Pow(10, x.PriceDecimals)), + QuantityStepSize = 1 / (decimal)(Math.Pow(10, x.LotDecimals)) + }).ToArray(); + } + + protected override async Task OnGetOrderBookAsync(string marketSymbol, int maxCount = 100) + { + var book = await MakeCoinmateRequest("/orderBook?&groupByPriceLimit=False¤cyPair=" + marketSymbol); + var result = new ExchangeOrderBook + { + MarketSymbol = marketSymbol, + }; + + book.Asks + .GroupBy(x => x.price) + .ToList() + .ForEach(x => result.Asks.Add(x.Key, new ExchangeOrderPrice { Amount = x.Sum(x => x.amount), Price = x.Key })); + + book.Bids + .GroupBy(x => x.price) + .ToList() + .ForEach(x => result.Bids.Add(x.Key, new ExchangeOrderPrice { Amount = x.Sum(x => x.amount), Price = x.Key })); + + return result; + } + + private async Task MakeCoinmateRequest(string url) + { + var response = await MakeJsonRequestAsync>(url); + + if (response.Error) + { + throw new APIException(response.ErrorMessage); + } + + return response.Data; + } + } +} diff --git a/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateOrderBook.cs b/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateOrderBook.cs new file mode 100644 index 00000000..78872a79 --- /dev/null +++ b/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateOrderBook.cs @@ -0,0 +1,14 @@ +namespace ExchangeSharp.API.Exchanges.Coinmate.Models +{ + public class CoinmateOrderBook + { + public AskBid[] Asks { get; set; } + public AskBid[] Bids { get; set; } + + public class AskBid + { + public decimal price { get; set; } + public decimal amount { get; set; } + } + } +} diff --git a/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateResponse.cs b/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateResponse.cs new file mode 100644 index 00000000..1f1a28dc --- /dev/null +++ b/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateResponse.cs @@ -0,0 +1,9 @@ +namespace ExchangeSharp.API.Exchanges.Coinmate.Models +{ + public class CoinmateResponse + { + public bool Error { get; set; } + public string ErrorMessage { get; set; } + public T Data { get; set; } + } +} diff --git a/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateSymbol.cs b/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateSymbol.cs new file mode 100644 index 00000000..57443474 --- /dev/null +++ b/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateSymbol.cs @@ -0,0 +1,9 @@ +namespace ExchangeSharp.API.Exchanges.Coinmate.Models +{ + public class CoinmateSymbol + { + public string Id { get; set; } + public string FromSymbol { get; set; } + public string ToSymbol { get; set; } + } +} diff --git a/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateTradingPair.cs b/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateTradingPair.cs new file mode 100644 index 00000000..4bbe57a9 --- /dev/null +++ b/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateTradingPair.cs @@ -0,0 +1,15 @@ +namespace ExchangeSharp.API.Exchanges.Coinmate.Models +{ + public class CoinmateTradingPair + { + public string Name { get; set; } + public string FirstCurrency { get; set; } + public string SecondCurrency { get; set; } + public int PriceDecimals { get; set; } + public int LotDecimals { get; set; } + public decimal MinAmount { get; set; } + public string TradesWebSocketChannelId { get; set; } + public string OrderBookWebSocketChannelId { get; set; } + public string TradeStatisticsWebSocketChannelId { get; set; } + } +} From 9994f9923c92bde07a86fd0a2f2c533c5fd2530d Mon Sep 17 00:00:00 2001 From: Tomas Bezouska Date: Mon, 24 Jan 2022 11:01:10 +0100 Subject: [PATCH 2/6] Coinmate auth + OnGetAmountsAsync --- .../Exchanges/Coinmate/ExchangeCoinmateAPI.cs | 57 ++++++++++++++++++- .../Coinmate/Models/CoinmateBalance.cs | 10 ++++ .../Coinmate/Models/CoinmateTransaction.cs | 12 ++++ 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateBalance.cs create mode 100644 src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateTransaction.cs diff --git a/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs b/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs index 1bb8c6e8..fca6f2da 100644 --- a/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs @@ -13,11 +13,22 @@ public class ExchangeCoinmateAPI : ExchangeAPI public ExchangeCoinmateAPI() { + RequestContentType = "application/x-www-form-urlencoded"; MarketSymbolSeparator = "_"; + NonceStyle = NonceStyle.UnixMilliseconds; } public override string Name => "Coinmate"; + /// + /// Coinmate private API requires a client id. Internally this is secured in the PassPhrase property. + /// + public string ClientId + { + get { return Passphrase.ToUnsecureString(); } + set { Passphrase = value.ToSecureString(); } + } + protected override async Task OnGetTickerAsync(string marketSymbol) { var response = await MakeCoinmateRequest($"/ticker?currencyPair={marketSymbol}"); @@ -66,9 +77,51 @@ protected override async Task OnGetOrderBookAsync(string mark return result; } - private async Task MakeCoinmateRequest(string url) + protected override async Task> OnGetRecentTradesAsync(string marketSymbol, int? limit = null) + { + var txs = await MakeCoinmateRequest("/transactions?minutesIntoHistory=1440¤cyPair=" + marketSymbol); + return txs.Select(x => new ExchangeTrade + { + Amount = x.Amount, + Id = x.TransactionId, + IsBuy = x.TradeType == "BUY", + Price = x.Price, + Timestamp = CryptoUtility.ParseTimestamp(x.Timestamp, TimestampType.UnixMilliseconds) + }) + .Take(limit ?? int.MaxValue) + .ToArray(); + } + + protected override async Task> OnGetAmountsAsync() + { + var payload = await GetNoncePayloadAsync(); + var balances = await MakeCoinmateRequest>("/balances", payload, "POST"); + + return balances.ToDictionary(x => x.Key, x => x.Value.Balance); + } + + protected override async Task ProcessRequestAsync(IHttpWebRequest request, Dictionary payload) + { + if (CanMakeAuthenticatedRequest(payload)) + { + if (string.IsNullOrWhiteSpace(ClientId)) + { + throw new APIException("Client ID is not set for Coinmate"); + } + + var apiKey = PublicApiKey.ToUnsecureString(); + var messageToSign = payload["nonce"].ToStringInvariant() + ClientId + apiKey; + var signature = CryptoUtility.SHA256Sign(messageToSign, PrivateApiKey.ToUnsecureString()).ToUpperInvariant(); + payload["signature"] = signature; + payload["clientId"] = ClientId; + payload["publicKey"] = apiKey; + await CryptoUtility.WritePayloadFormToRequestAsync(request, payload); + } + } + + private async Task MakeCoinmateRequest(string url, Dictionary payload = null, string method = null) { - var response = await MakeJsonRequestAsync>(url); + var response = await MakeJsonRequestAsync>(url, null, payload, method); if (response.Error) { diff --git a/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateBalance.cs b/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateBalance.cs new file mode 100644 index 00000000..630607ad --- /dev/null +++ b/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateBalance.cs @@ -0,0 +1,10 @@ +namespace ExchangeSharp.API.Exchanges.Coinmate.Models +{ + public class CoinmateBalance + { + public string Currency { get; set; } + public decimal Balance { get; set; } + public decimal Reserved { get; set; } + public decimal Available { get; set; } + } +} diff --git a/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateTransaction.cs b/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateTransaction.cs new file mode 100644 index 00000000..90a19573 --- /dev/null +++ b/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateTransaction.cs @@ -0,0 +1,12 @@ +namespace ExchangeSharp.API.Exchanges.Coinmate.Models +{ + public class CoinmateTransaction + { + public long Timestamp { get; set; } + public string TransactionId { get; set; } + public decimal Price { get; set; } + public decimal Amount { get; set; } + public string CurrencyPair { get; set; } + public string TradeType { get; set; } + } +} From 766beea01d2d9dec8fdcc2654829fc5fa72d9c63 Mon Sep 17 00:00:00 2001 From: Tomas Bezouska Date: Mon, 24 Jan 2022 16:45:05 +0100 Subject: [PATCH 3/6] Coinmate buy, sell, order --- .../Exchanges/Coinmate/ExchangeCoinmateAPI.cs | 94 +++++++++++++++++++ .../Coinmate/Models/CoinmateOrder.cs | 19 ++++ 2 files changed, 113 insertions(+) create mode 100644 src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateOrder.cs diff --git a/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs b/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs index fca6f2da..e4b7b84e 100644 --- a/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs @@ -100,6 +100,100 @@ protected override async Task> OnGetAmountsAsync() return balances.ToDictionary(x => x.Key, x => x.Value.Balance); } + protected override async Task> OnGetAmountsAvailableToTradeAsync() + { + var payload = await GetNoncePayloadAsync(); + var balances = await MakeCoinmateRequest>("/balances", payload, "POST"); + + return balances.ToDictionary(x => x.Key, x => x.Value.Available); + } + + protected override async Task OnGetOrderDetailsAsync(string orderId, string marketSymbol = null, bool isClientOrderId = false) + { + var payload = await GetNoncePayloadAsync(); + + CoinmateOrder o; + + if (isClientOrderId) + { + payload["clientOrderId"] = orderId; + var orders = await MakeCoinmateRequest("/order", payload, "POST"); + o = orders.OrderByDescending(x => x.Timestamp).FirstOrDefault(); + } + else + { + payload["orderId"] = orderId; + o = await MakeCoinmateRequest("/orderById", payload, "POST"); + } + + if (o == null) return null; + + return new ExchangeOrderResult + { + Amount = o.OriginalAmount, + AmountFilled = o.OriginalAmount - o.RemainingAmount, + AveragePrice = o.AvgPrice, + ClientOrderId = isClientOrderId ? orderId : null, + OrderId = o.Id.ToString(), + Price = o.Price, + IsBuy = o.Type == "BUY", + OrderDate = CryptoUtility.ParseTimestamp(o.Timestamp, TimestampType.UnixMilliseconds), + ResultCode = o.Status, + Result = o.Status switch + { + "CANCELLED" => ExchangeAPIOrderResult.Canceled, + "FILLED" => ExchangeAPIOrderResult.Filled, + "PARTIALLY_FILLED" => ExchangeAPIOrderResult.FilledPartially, + "OPEN" => ExchangeAPIOrderResult.Open, + _ => ExchangeAPIOrderResult.Unknown + }, + MarketSymbol = marketSymbol + }; + } + + protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null) + { + var payload = await GetNoncePayloadAsync(); + payload["orderId"] = orderId; + + await MakeCoinmateRequest("/cancelOrder", payload, "POST"); + } + + protected override async Task OnPlaceOrderAsync(ExchangeOrderRequest order) + { + var payload = await GetNoncePayloadAsync(); + + if (order.OrderType != OrderType.Limit && order.OrderType != OrderType.Stop) + { + throw new NotImplementedException("This type of order is currently not supported."); + } + + payload["amount"] = order.Amount; + payload["price"] = order.Price; + payload["currencyPair"] = order.MarketSymbol; + payload["postOnly"] = order.IsPostOnly.GetValueOrDefault() ? 1 : 0; + + if (order.OrderType == OrderType.Stop) + { + payload["stopPrice"] = order.StopPrice; + } + + if (order.ClientOrderId != null) + { + if (!long.TryParse(order.ClientOrderId, out var clientOrderId)) + { + throw new InvalidOperationException("ClientId must be numerical for Coinmate"); + } + + payload["clientOrderId"] = clientOrderId; + } + + var url = order.IsBuy ? "/buyLimit" : "/sellLimit"; + var id = await MakeCoinmateRequest(url, payload, "POST"); + + return await GetOrderDetailsAsync(id?.ToString(), marketSymbol: order.MarketSymbol); + } + protected override async Task ProcessRequestAsync(IHttpWebRequest request, Dictionary payload) { if (CanMakeAuthenticatedRequest(payload)) diff --git a/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateOrder.cs b/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateOrder.cs new file mode 100644 index 00000000..c64ce7c6 --- /dev/null +++ b/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateOrder.cs @@ -0,0 +1,19 @@ +namespace ExchangeSharp.API.Exchanges.Coinmate.Models +{ + public class CoinmateOrder + { + public int Id { get; set; } + public long Timestamp { get; set; } + public string Type { get; set; } + public decimal? Price { get; set; } + public decimal? RemainingAmount { get; set; } + public decimal OriginalAmount { get; set; } + public decimal? StopPrice { get; set; } + public string Status { get; set; } + public string OrderTradeType { get; set; } + public decimal? AvgPrice { get; set; } + public bool Trailing { get; set; } + public string StopLossOrderId { get; set; } + public string OriginalOrderId { get; set; } + } +} From 981d78bab49c1b2f7fa636e6fb51c9bf9896eaa9 Mon Sep 17 00:00:00 2001 From: Tomas Bezouska Date: Mon, 24 Jan 2022 17:00:44 +0100 Subject: [PATCH 4/6] Coinmate OnGetOpenOrderDetailsAsync --- .../Exchanges/Coinmate/ExchangeCoinmateAPI.cs | 28 ++++++++++++++++--- .../Coinmate/Models/CoinmateOpenOrder.cs | 19 +++++++++++++ .../Coinmate/Models/CoinmateOrderBook.cs | 4 +-- 3 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateOpenOrder.cs diff --git a/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs b/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs index e4b7b84e..a0c6ee92 100644 --- a/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs @@ -65,14 +65,14 @@ protected override async Task OnGetOrderBookAsync(string mark }; book.Asks - .GroupBy(x => x.price) + .GroupBy(x => x.Price) .ToList() - .ForEach(x => result.Asks.Add(x.Key, new ExchangeOrderPrice { Amount = x.Sum(x => x.amount), Price = x.Key })); + .ForEach(x => result.Asks.Add(x.Key, new ExchangeOrderPrice { Amount = x.Sum(x => x.Amount), Price = x.Key })); book.Bids - .GroupBy(x => x.price) + .GroupBy(x => x.Price) .ToList() - .ForEach(x => result.Bids.Add(x.Key, new ExchangeOrderPrice { Amount = x.Sum(x => x.amount), Price = x.Key })); + .ForEach(x => result.Bids.Add(x.Key, new ExchangeOrderPrice { Amount = x.Sum(x => x.Amount), Price = x.Key })); return result; } @@ -194,6 +194,26 @@ protected override async Task OnPlaceOrderAsync(ExchangeOrd return await GetOrderDetailsAsync(id?.ToString(), marketSymbol: order.MarketSymbol); } + protected override async Task> OnGetOpenOrderDetailsAsync(string marketSymbol = null) + { + var payload = await GetNoncePayloadAsync(); + payload["currencyPair"] = marketSymbol; + + var orders = await MakeCoinmateRequest("/openOrders", payload, "POST"); + + return orders.Select(x => new ExchangeOrderResult + { + Amount = x.Amount, + ClientOrderId = x.ClientOrderId?.ToString(), + IsBuy = x.Type == "BUY", + MarketSymbol = x.CurrencyPair, + OrderDate = CryptoUtility.ParseTimestamp(x.Timestamp, TimestampType.UnixMilliseconds), + OrderId = x.Id.ToString(), + Price = x.Price, + + }).ToArray(); + } + protected override async Task ProcessRequestAsync(IHttpWebRequest request, Dictionary payload) { if (CanMakeAuthenticatedRequest(payload)) diff --git a/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateOpenOrder.cs b/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateOpenOrder.cs new file mode 100644 index 00000000..00b10b7f --- /dev/null +++ b/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateOpenOrder.cs @@ -0,0 +1,19 @@ +namespace ExchangeSharp.API.Exchanges.Coinmate.Models +{ + public class CoinmateOpenOrder + { + public int Id { get; set; } + public long Timestamp { get; set; } + public string Type { get; set; } + public string CurrencyPair { get; set; } + public decimal Price { get; set; } + public decimal Amount { get; set; } + public decimal? StopPrice { get; set; } + public string OrderTradeType { get; set; } + public bool Hidden { get; set; } + public bool Trailing { get; set; } + public long? StopLossOrderId { get; set; } + + public long? ClientOrderId { get; set; } + } +} diff --git a/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateOrderBook.cs b/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateOrderBook.cs index 78872a79..b0c61eb7 100644 --- a/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateOrderBook.cs +++ b/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateOrderBook.cs @@ -7,8 +7,8 @@ public class CoinmateOrderBook public class AskBid { - public decimal price { get; set; } - public decimal amount { get; set; } + public decimal Price { get; set; } + public decimal Amount { get; set; } } } } From 0d6c44bdd8be33ec2950059248c0ae3d5a711208 Mon Sep 17 00:00:00 2001 From: Tomas Bezouska Date: Mon, 24 Jan 2022 22:42:59 +0100 Subject: [PATCH 5/6] Coinmate OnGetDepositAddressAsync, OnWithdrawAsync --- .../Exchanges/Coinmate/ExchangeCoinmateAPI.cs | 55 ++++++++++++++++++- .../Coinmate/Models/CoinmateOpenOrder.cs | 1 - 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs b/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs index a0c6ee92..ac4c0713 100644 --- a/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs @@ -191,7 +191,14 @@ protected override async Task OnPlaceOrderAsync(ExchangeOrd var url = order.IsBuy ? "/buyLimit" : "/sellLimit"; var id = await MakeCoinmateRequest(url, payload, "POST"); - return await GetOrderDetailsAsync(id?.ToString(), marketSymbol: order.MarketSymbol); + try + { + return await GetOrderDetailsAsync(id?.ToString(), marketSymbol: order.MarketSymbol); + } + catch + { + return new ExchangeOrderResult { OrderId = id?.ToString() }; + } } protected override async Task> OnGetOpenOrderDetailsAsync(string marketSymbol = null) @@ -214,6 +221,37 @@ protected override async Task> OnGetOpenOrderDe }).ToArray(); } + protected override async Task OnGetDepositAddressAsync(string currency, bool forceRegenerate = false) + { + var payload = await GetNoncePayloadAsync(); + var currencyName = GetCurrencyName(currency); + var addresses = await MakeCoinmateRequest($"/{currencyName}DepositAddresses", payload, "POST"); + + return new ExchangeDepositDetails + { + Address = addresses.FirstOrDefault(), + Currency = currency, + }; + } + + protected override async Task OnWithdrawAsync(ExchangeWithdrawalRequest withdrawalRequest) + { + var payload = await GetNoncePayloadAsync(); + var currencyName = GetCurrencyName(withdrawalRequest.Currency); + + payload["amount"] = withdrawalRequest.Amount; + payload["address"] = withdrawalRequest.Address; + payload["amountType"] = withdrawalRequest.TakeFeeFromAmount ? "NET" : "GROSS"; + + var id = await MakeCoinmateRequest($"/{currencyName}Withdrawal", payload, "POST"); + + return new ExchangeWithdrawalResponse + { + Id = id?.ToString(), + Success = id != null + }; + } + protected override async Task ProcessRequestAsync(IHttpWebRequest request, Dictionary payload) { if (CanMakeAuthenticatedRequest(payload)) @@ -244,5 +282,20 @@ private async Task MakeCoinmateRequest(string url, Dictionary "bitcoin", + "LTC" => "litecoin", + "BCH" => "bitcoinCash", + "ETH" => "ethereum", + "XRP" => "ripple", + "DASH" => "dash", + "DAI" => "dai", + _ => throw new NotImplementedException("Unsupported currency") + }; + } } } diff --git a/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateOpenOrder.cs b/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateOpenOrder.cs index 00b10b7f..53af4fa8 100644 --- a/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateOpenOrder.cs +++ b/src/ExchangeSharp/API/Exchanges/Coinmate/Models/CoinmateOpenOrder.cs @@ -13,7 +13,6 @@ public class CoinmateOpenOrder public bool Hidden { get; set; } public bool Trailing { get; set; } public long? StopLossOrderId { get; set; } - public long? ClientOrderId { get; set; } } } From cf227b46a773c1ef7c65bd541a355bf80270f245 Mon Sep 17 00:00:00 2001 From: Tomas Bezouska Date: Tue, 25 Jan 2022 09:12:22 +0100 Subject: [PATCH 6/6] Coinmate readme + ExchangeName --- README.md | 1 + src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/README.md b/README.md index f0df15f5..8ba0de41 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ The following cryptocurrency exchanges are supported: | BTSE | x | x | | | Bybit | x | x | R | Has public method for Websocket Positions | Coinbase | x | x | T R U | +| Coinmate | x | x | | | Digifinex | x | x | R B | | FTX | x | x | T | | gate.io | x | x | | diff --git a/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs b/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs index ac4c0713..0d571572 100644 --- a/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Coinmate/ExchangeCoinmateAPI.cs @@ -297,5 +297,7 @@ private string GetCurrencyName(string currency) _ => throw new NotImplementedException("Unsupported currency") }; } + + public partial class ExchangeName { public const string Coinmate = "Coinmate"; } } }