From 1bd0661559f7bea6a8c806bb44337f49c0b56f6d Mon Sep 17 00:00:00 2001
From: BZ-CO <30245815+BZ-CO@users.noreply.github.com>
Date: Tue, 21 Feb 2023 19:39:41 +0200
Subject: [PATCH 1/5] Add CryptoUtility.PrecisionToStepSize
Precision to step size convertor.
For example, precision of 5 would return a step size of 0.00001.
---
src/ExchangeSharp/Utility/CryptoUtility.cs | 23 ++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/src/ExchangeSharp/Utility/CryptoUtility.cs b/src/ExchangeSharp/Utility/CryptoUtility.cs
index 6b968352..ea8994ac 100644
--- a/src/ExchangeSharp/Utility/CryptoUtility.cs
+++ b/src/ExchangeSharp/Utility/CryptoUtility.cs
@@ -1363,6 +1363,29 @@ public static decimal CalculatePrecision(string numberWithDecimals)
return (decimal)Math.Pow(10, -1 * precision);
}
+
+ ///
+ /// Precision to step size.
+ /// For example, precision of 5 would return a step size of 0.00001
+ ///
+ public static decimal PrecisionToStepSize(decimal precision)
+ {
+ var sb = new StringBuilder();
+ sb.Append("0");
+ if (precision > 0) sb.Append(".");
+ if (precision == 1)
+ {
+ sb.Append("1");
+ return decimal.Parse(sb.ToStringInvariant());
+ }
+ for (var i = 0; i < precision; i++)
+ {
+ sb.Append(i + 1 == precision ? "1" : "0");
+ }
+
+ return decimal.Parse(sb.ToStringInvariant());
+ }
+
///
/// Make a task execute synchronously - do not call this from the UI thread or it will lock up the application
/// You should almos always use async / await instead
From 4f341c6dc2ad49b9d6024b765e4fdc2fb6265731 Mon Sep 17 00:00:00 2001
From: BZ-CO <30245815+BZ-CO@users.noreply.github.com>
Date: Tue, 21 Feb 2023 22:42:56 +0200
Subject: [PATCH 2/5] Add ExchangeAPIExtensions.ParseOrderBookFromJTokenArray
Common order book parsing method, some exchanges (Poloniex) use single array for the whole depth (price and qty) like "asks" : [ "24500.00", "0.024105", "24513.16", "0.611916", "24514.27", "0.001987"]
---
.../Exchanges/_Base/ExchangeAPIExtensions.cs | 40 +++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/src/ExchangeSharp/API/Exchanges/_Base/ExchangeAPIExtensions.cs b/src/ExchangeSharp/API/Exchanges/_Base/ExchangeAPIExtensions.cs
index ba9297d1..0ff4d961 100644
--- a/src/ExchangeSharp/API/Exchanges/_Base/ExchangeAPIExtensions.cs
+++ b/src/ExchangeSharp/API/Exchanges/_Base/ExchangeAPIExtensions.cs
@@ -351,6 +351,46 @@ public static async Task PlaceSafeMarketOrderAsync(this Exc
return result;
}
+ /// Common order book parsing method, some exchanges (like Poloniex) use single array for the whole depth (price and qty) like
+ /// "asks" : [ "24500.00", "0.024105", "24513.16", "0.611916", "24514.27", "0.001987"]
+ /// Token
+ /// Asks key
+ /// Bids key
+ /// Timestamp or sequence number
+ /// Order book
+ internal static ExchangeOrderBook ParseOrderBookFromJTokenArray
+ (
+ this JToken token,
+ string asks = "asks",
+ string bids = "bids",
+ string sequence = "ts"
+ )
+ {
+ var book = new ExchangeOrderBook { SequenceId = (token[sequence] ?? throw new InvalidOperationException()).ConvertInvariant() };
+ var asksCount = (token[asks] ?? throw new InvalidOperationException()).Count();
+ var bidsCount = (token[bids] ?? throw new InvalidOperationException()).Count();
+
+ for (var i = 0; i < asksCount; i++)
+ {
+ if (i % 2 != 0) continue;
+ var price = token[asks][i].Value();
+ var amount = token[asks][i+1].Value();
+ var depth = new ExchangeOrderPrice { Price = price, Amount = amount };
+ book.Asks[depth.Price] = depth;
+ }
+
+ for (var i = 0; i < bidsCount; i++)
+ {
+ if (i % 2 != 0) continue;
+ var price = token[bids][i].Value();
+ var amount = token[bids][i+1].Value();
+ var depth = new ExchangeOrderPrice { Price = price, Amount = amount };
+ book.Bids[depth.Price] = depth;
+ }
+
+ return book;
+ }
+
/// Common order book parsing method, most exchanges use "asks" and "bids" with
/// arrays of length 2 for price and amount (or amount and price)
/// Token
From c054525dfbc1903fa4e078362dfb44f2d8c01a09 Mon Sep 17 00:00:00 2001
From: BZ-CO <30245815+BZ-CO@users.noreply.github.com>
Date: Wed, 22 Feb 2023 23:00:05 +0200
Subject: [PATCH 3/5] Add CryptoUtility.SecondsToPeriodStringLongReverse
Convert seconds to a period string, i.e. SECOND_5, MINUTE_1, HOUR_2, DAY_3, WEEK_1week, MONTH_1, YEAR_1 etc.
---
src/ExchangeSharp/Utility/CryptoUtility.cs | 42 ++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/src/ExchangeSharp/Utility/CryptoUtility.cs b/src/ExchangeSharp/Utility/CryptoUtility.cs
index ea8994ac..2b93776d 100644
--- a/src/ExchangeSharp/Utility/CryptoUtility.cs
+++ b/src/ExchangeSharp/Utility/CryptoUtility.cs
@@ -1219,6 +1219,48 @@ public static string SecondsToPeriodStringLong(int seconds)
return seconds + "sec";
}
+ ///
+ /// Convert seconds to a period string, i.e. SECOND_5, MINUTE_1, HOUR_2, DAY_3, WEEK_1week, MONTH_1, YEAR_1 etc.
+ ///
+ /// Seconds. Use 60 for minute, 3600 for hour, 3600*24 for day, 3600*24*30 for month.
+ /// Period string
+ public static string SecondsToPeriodStringLongReverse(int seconds)
+ {
+ const int minuteThreshold = 60;
+ const int hourThreshold = 60 * 60;
+ const int dayThreshold = 60 * 60 * 24;
+ const int weekThreshold = dayThreshold * 7;
+ const int monthThreshold = dayThreshold * 30;
+ const int yearThreshold = monthThreshold * 12;
+
+ if (seconds >= yearThreshold)
+ {
+ return $"YEAR_{seconds / yearThreshold}";
+ }
+ if (seconds >= monthThreshold)
+ {
+ return $"MONTH_{seconds / monthThreshold}";
+ }
+ if (seconds >= weekThreshold)
+ {
+ return $"WEEK_{seconds / weekThreshold}";
+ }
+ if (seconds >= dayThreshold)
+ {
+ return $"DAY_{seconds / dayThreshold}";
+ }
+ if (seconds >= hourThreshold)
+ {
+ return $"HOUR_{seconds / hourThreshold}";
+ }
+ if (seconds >= minuteThreshold)
+ {
+ return $"MINUTE_{seconds / minuteThreshold}";
+ }
+
+ return $"SECOND_{seconds}";
+ }
+
///
/// Load protected data as strings from file. Call this function in your production environment, loading in a securely encrypted file which will stay encrypted in memory.
///
From 4586e339236f6481028cc917804b54dd55bb4b00 Mon Sep 17 00:00:00 2001
From: BZ-CO <30245815+BZ-CO@users.noreply.github.com>
Date: Wed, 22 Feb 2023 23:07:24 +0200
Subject: [PATCH 4/5] Migration to the new Poloniex API [Public endpoints only]
---
.../Exchanges/Poloniex/ExchangePoloniexAPI.cs | 334 ++++++++++--------
1 file changed, 180 insertions(+), 154 deletions(-)
diff --git a/src/ExchangeSharp/API/Exchanges/Poloniex/ExchangePoloniexAPI.cs b/src/ExchangeSharp/API/Exchanges/Poloniex/ExchangePoloniexAPI.cs
index f61b8be7..159f436e 100644
--- a/src/ExchangeSharp/API/Exchanges/Poloniex/ExchangePoloniexAPI.cs
+++ b/src/ExchangeSharp/API/Exchanges/Poloniex/ExchangePoloniexAPI.cs
@@ -19,14 +19,11 @@ namespace ExchangeSharp
using System.Collections.Generic;
using System.IO;
using System.Linq;
- using System.Net;
using System.Threading.Tasks;
- using Newtonsoft.Json;
-
public sealed partial class ExchangePoloniexAPI : ExchangeAPI
{
- public override string BaseUrl { get; set; } = "https://poloniex.com";
+ public override string BaseUrl { get; set; } = "https://api.poloniex.com";
public override string BaseUrlWebSocket { get; set; } = "wss://api2.poloniex.com";
private ExchangePoloniexAPI()
@@ -275,15 +272,34 @@ private async Task ParseTickerWebSocketAsync(string symbol, JTok
return await this.ParseTickerAsync(token, symbol, 2, 3, 1, 5, 6);
}
+ public override string PeriodSecondsToString(int seconds)
+ {
+ var allowedPeriods = new[]
+ {
+ "MINUTE_1", "MINUTE_5", "MINUTE_10", "MINUTE_15",
+ "MINUTE_30", "HOUR_1", "HOUR_2", "HOUR_4", "HOUR_6",
+ "HOUR_12", "DAY_1", "DAY_3", "WEEK_1", "MONTH_1"
+ };
+ var period = CryptoUtility.SecondsToPeriodStringLongReverse(seconds);
+ var periodIsvalid = allowedPeriods.Any(x => x == period);
+ if (!periodIsvalid) throw new ArgumentOutOfRangeException(nameof(period), $"{period} is not valid period on Poloniex");
+
+ return period;
+ }
+
protected override async Task ProcessRequestAsync(IHttpWebRequest request, Dictionary payload)
{
- if (CanMakeAuthenticatedRequest(payload))
- {
- string form = CryptoUtility.GetFormForPayload(payload);
- request.AddHeader("Key", PublicApiKey.ToUnsecureString());
- request.AddHeader("Sign", CryptoUtility.SHA512Sign(form, PrivateApiKey.ToUnsecureString()));
- request.Method = "POST";
- await CryptoUtility.WriteToRequestAsync(request, form);
+ if (CanMakeAuthenticatedRequest(payload))
+ {
+ payload["signTimestamp"] = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds();
+ var form = payload.GetFormForPayload();
+ var sig = $"{request.Method}\n" +
+ $"{request.RequestUri.PathAndQuery}\n" +
+ $"{form}";
+ request.AddHeader("key", PublicApiKey.ToUnsecureString());
+ request.AddHeader("signTimestamp", payload["signTimestamp"].ToStringInvariant());
+ request.AddHeader("signature", CryptoUtility.SHA256Sign(sig, PrivateApiKey.ToUnsecureString(), true));
+ await request.WriteToRequestAsync(form);
}
}
@@ -331,54 +347,50 @@ protected override async Task> OnGetMarketSymbolsAsync()
protected internal override async Task> OnGetMarketSymbolsMetadataAsync()
{
- //https://docs.poloniex.com/#returnticker
- /*
- {
- "BTC_BTS": {
- "id": 14,
- "last": "0.00000090",
- "lowestAsk": "0.00000091",
- "highestBid": "0.00000089",
- "percentChange": "-0.02173913",
- "baseVolume": "0.28698296",
- "quoteVolume": "328356.84081156",
- "isFrozen": "0",
- "postOnly": "0",
- "high24hr": "0.00000093",
- "low24hr": "0.00000087"
- },...
- */
+ //https://api.poloniex.com/markets
+ // [
+ // {
+ // "symbol": "BTC_USDT",
+ // "baseCurrencyName": "BTC",
+ // "quoteCurrencyName": "USDT",
+ // "displayName": "BTC/USDT",
+ // "state": "NORMAL",
+ // "visibleStartTime": 1659018819512,
+ // "tradableStartTime": 1659018819512,
+ // "symbolTradeLimit": {
+ // "symbol": "BTC_USDT",
+ // "priceScale": 2,
+ // "quantityScale": 6, - base
+ // "amountScale": 2, - quote
+ // "minQuantity": "0.000001" - base,
+ // "minAmount": "1", - quote
+ // "highestBid": "0",
+ // "lowestAsk": "0"
+ // },
+ // "crossMargin": {
+ // "supportCrossMargin": true,
+ // "maxLeverage": 3
+ // }
+ // ]
var markets = new List();
- Dictionary lookup = await MakeJsonRequestAsync>("/public?command=returnTicker");
- // StepSize is 8 decimal places for both price and amount on everything at Polo
- const decimal StepSize = 0.00000001m;
- const decimal minTradeSize = 0.0001m;
+ var symbols = await MakeJsonRequestAsync("/markets");
- foreach (var kvp in lookup)
+ foreach (var symbol in symbols)
{
- var market = new ExchangeMarket { MarketSymbol = kvp.Key, IsActive = false };
-
- string isFrozen = kvp.Value["isFrozen"].ToStringInvariant();
- string postOnly = kvp.Value["postOnly"].ToStringInvariant();
- if (string.Equals(isFrozen, "0") && string.Equals(postOnly, "0"))
- {
- market.IsActive = true;
- }
-
- string[] pairs = kvp.Key.Split('_');
- if (pairs.Length == 2)
- {
- market.MarketId = kvp.Value["id"].ToStringLowerInvariant();
- market.QuoteCurrency = pairs[0];
- market.BaseCurrency = pairs[1];
- market.PriceStepSize = StepSize;
- market.QuantityStepSize = StepSize;
- market.MinPrice = StepSize;
- market.MinTradeSize = minTradeSize;
- }
-
- markets.Add(market);
+ var market = new ExchangeMarket
+ {
+ MarketSymbol = symbol["symbol"].ToStringInvariant(),
+ IsActive = ParsePairState(symbol["state"].ToStringInvariant()),
+ BaseCurrency = symbol["baseCurrencyName"].ToStringInvariant(),
+ QuoteCurrency = symbol["quoteCurrencyName"].ToStringInvariant(),
+ MinTradeSize = symbol["symbolTradeLimit"]["minQuantity"].Value(),
+ MinTradeSizeInQuoteCurrency = symbol["symbolTradeLimit"]["minAmount"].Value(),
+ PriceStepSize = CryptoUtility.PrecisionToStepSize(symbol["symbolTradeLimit"]["priceScale"].Value()),
+ QuantityStepSize = CryptoUtility.PrecisionToStepSize(symbol["symbolTradeLimit"]["quantityScale"].Value()),
+ MarginEnabled = symbol["crossMargin"]["supportCrossMargin"].Value()
+ };
+ markets.Add(market);
}
return markets;
@@ -399,17 +411,38 @@ protected override async Task OnGetTickerAsync(string marketSymb
protected override async Task>> OnGetTickersAsync()
{
- // {"BTC_LTC":{"last":"0.0251","lowestAsk":"0.02589999","highestBid":"0.0251","percentChange":"0.02390438","baseVolume":"6.16485315","quoteVolume":"245.82513926"}
- List> tickers = new List>();
- JToken obj = await MakeJsonRequestAsync("/public?command=returnTicker");
- foreach (JProperty prop in obj.Children())
+ //https://api.poloniex.com/markets/ticker24h
+ // [ {
+ // "symbol" : "BTS_BTC",
+ // "open" : "0.0000005026",
+ // "low" : "0.0000004851",
+ // "high" : "0.0000005799",
+ // "close" : "0.0000004851",
+ // "quantity" : "34444",
+ // "amount" : "0.0179936481",
+ // "tradeCount" : 48,
+ // "startTime" : 1676918100000,
+ // "closeTime" : 1677004501011,
+ // "displayName" : "BTS/BTC",
+ // "dailyChange" : "-0.0348",
+ // "bid" : "0.0000004852",
+ // "bidQuantity" : "725",
+ // "ask" : "0.0000004962",
+ // "askQuantity" : "238",
+ // "ts" : 1677004503839,
+ // "markPrice" : "0.000000501"
+ // }]
+ var tickers = new List>();
+ var tickerResponse = await MakeJsonRequestAsync("/markets/ticker24h");
+ foreach (var instrument in tickerResponse)
{
- string marketSymbol = prop.Name;
- JToken values = prop.Value;
- //NOTE: Poloniex uses the term "caseVolume" when referring to the QuoteCurrencyVolume
- ExchangeTicker ticker = await this.ParseTickerAsync(values, marketSymbol, "lowestAsk", "highestBid", "last", "quoteVolume", "baseVolume", idKey: "id");
- tickers.Add(new KeyValuePair(marketSymbol, ticker));
+ var symbol = instrument["symbol"].ToStringInvariant();
+ var ticker = await this.ParseTickerAsync(
+ instrument, symbol, askKey: "ask", bidKey: "bid", baseVolumeKey: "quantity", lastKey: "close",
+ quoteVolumeKey: "amount", timestampKey: "ts", timestampType: TimestampType.UnixMilliseconds);
+ tickers.Add(new KeyValuePair(symbol, ticker));
}
+
return tickers;
}
@@ -602,108 +635,94 @@ protected override async Task OnGetDeltaOrderBookWebSocketAsync(Acti
protected override async Task OnGetOrderBookAsync(string marketSymbol, int maxCount = 100)
{
- // {"asks":[["0.01021997",22.83117932],["0.01022000",82.3204],["0.01022480",140],["0.01023054",241.06436945],["0.01023057",140]],"bids":[["0.01020233",164.195],["0.01020232",66.22565096],["0.01020200",5],["0.01020010",66.79296968],["0.01020000",490.19563761]],"isFrozen":"0","seq":147171861}
- JToken token = await MakeJsonRequestAsync("/public?command=returnOrderBook¤cyPair=" + marketSymbol + "&depth=" + maxCount);
- return ExchangeAPIExtensions.ParseOrderBookFromJTokenArrays(token);
+ //https://api.poloniex.com/markets/{symbol}/orderBook?scale={scale}&limit={limit}
+ // {
+ // "time" : 1677005825632,
+ // "scale" : "0.01",
+ // "asks" : [ "24702.89", "0.046082", "24702.90", "0.001681", "24703.09", "0.002037", "24710.10", "0.143572", "24712.18", "0.00118", "24713.68", "0.606951", "24724.80", "0.133", "24728.93", "0.7", "24728.94", "0.4", "24737.10", "0.135203" ],
+ // "bids" : [ "24700.03", "1.006472", "24700.02", "0.001208", "24698.71", "0.607319", "24697.99", "0.001973", "24688.50", "0.133", "24679.41", "0.4", "24679.40", "0.135", "24678.55", "0.3", "24667.00", "0.262", "24661.39", "0.14" ],
+ // "ts" : 1677005825637
+ // }
+ var response = await MakeJsonRequestAsync($"/markets/{marketSymbol}/orderBook?limit={maxCount}");
+ return response.ParseOrderBookFromJTokenArray();
}
- protected override async Task>> OnGetOrderBooksAsync(int maxCount = 100)
- {
- List> books = new List>();
- JToken obj = await MakeJsonRequestAsync("/public?command=returnOrderBook¤cyPair=all&depth=" + maxCount);
- foreach (JProperty token in obj.Children())
- {
- ExchangeOrderBook book = new ExchangeOrderBook();
- foreach (JArray array in token.First["asks"])
- {
- var depth = new ExchangeOrderPrice { Amount = array[1].ConvertInvariant(), Price = array[0].ConvertInvariant() };
- book.Asks[depth.Price] = depth;
- }
- foreach (JArray array in token.First["bids"])
- {
- var depth = new ExchangeOrderPrice { Amount = array[1].ConvertInvariant(), Price = array[0].ConvertInvariant() };
- book.Bids[depth.Price] = depth;
- }
- books.Add(new KeyValuePair(token.Name, book));
- }
- return books;
- }
-
- protected override async Task> OnGetRecentTradesAsync(string marketSymbol, int? limit = null)
+ protected override async Task> OnGetRecentTradesAsync(string marketSymbol, int? limit = null)
{
- List trades = new List();
- //https://docs.poloniex.com/#returnorderbook note poloniex limit = 1000
- int requestLimit = (limit == null || limit < 1 || limit > 1000) ? 1000 : (int)limit;
- string url = "/public?command=returnTradeHistory¤cyPair=" + marketSymbol + "&limit=" + requestLimit ;
-
- //JToken obj = await MakeJsonRequestAsync($"/aggTrades?symbol={marketSymbol}&limit={maxRequestLimit}");
- JToken obj = await MakeJsonRequestAsync(url);
-
- //JToken obj = await MakeJsonRequestAsync("/public/trades/" + marketSymbol + "?limit=" + maxRequestLimit + "?sort=DESC");
- if(obj.HasValues) { //
- foreach(JToken token in obj) {
- var trade = token.ParseTrade("amount", "rate", "type", "date", TimestampType.Iso8601UTC, "globalTradeID");
- trades.Add(trade);
- }
- }
+ //https://api.poloniex.com/markets/{symbol}/trades?limit={limit}
+ // Returns a list of recent trades, request param limit is optional, its default value is 500, and max value is 1000.
+ // [
+ // {
+ // "id": "194",
+ // "price": "1.9",
+ // "quantity": "110",
+ // "amount": "209.00",
+ // "takerSide": "SELL",
+ // "ts": 1648690080545,
+ // "createTime": 1648634905695
+ // }
+ // ]
+
+ limit = (limit == null || limit < 1 || limit > 1000) ? 1000 : limit;
+
+ var tradesResponse = await MakeJsonRequestAsync($"/markets/{marketSymbol}/trades?limit={limit}");
+
+ var trades = tradesResponse
+ .Select(t =>
+ t.ParseTrade(
+ amountKey: "amount", priceKey: "price", typeKey: "takerSide",
+ timestampKey: "ts", TimestampType.UnixMilliseconds, idKey: "id",
+ typeKeyIsBuyValue: "BUY")).ToList();
+
return trades;
}
- protected override async Task OnGetHistoricalTradesAsync(Func, bool> callback, string marketSymbol, DateTime? startDate = null, DateTime? endDate = null, int? limit = null)
- {
- // [{"globalTradeID":245321705,"tradeID":11501281,"date":"2017-10-20 17:39:17","type":"buy","rate":"0.01022188","amount":"0.00954454","total":"0.00009756"},...]
- ExchangeHistoricalTradeHelper state = new ExchangeHistoricalTradeHelper(this)
- {
- Callback = callback,
- EndDate = endDate,
- MillisecondGranularity = false,
- ParseFunction = (JToken token) => token.ParseTrade("amount", "rate", "type", "date", TimestampType.Iso8601UTC, "globalTradeID"),
- StartDate = startDate,
- MarketSymbol = marketSymbol,
- TimestampFunction = (DateTime dt) => ((long)CryptoUtility.UnixTimestampFromDateTimeSeconds(dt)).ToStringInvariant(),
- Url = "/public?command=returnTradeHistory¤cyPair=[marketSymbol]&start={0}&end={1}"
- };
- await state.ProcessHistoricalTrades();
- }
-
protected override async Task> OnGetCandlesAsync(string marketSymbol, int periodSeconds, DateTime? startDate = null, DateTime? endDate = null, int? limit = null)
{
- if (limit != null)
- {
- throw new APIException("Limit parameter not supported");
- }
-
- // https://poloniex.com/public?command=returnChartData¤cyPair=BTC_XMR&start=1405699200&end=9999999999&period=14400
- // [{"date":1405699200,"high":0.0045388,"low":0.00403001,"open":0.00404545,"close":0.00435873,"volume":44.34555992,"quoteVolume":10311.88079097,"weightedAverage":0.00430043}]
- string url = "/public?command=returnChartData¤cyPair=" + marketSymbol;
- if (startDate != null)
- {
- url += "&start=" + (long)startDate.Value.UnixTimestampFromDateTimeSeconds();
- }
- url += "&end=" + (endDate == null ? long.MaxValue.ToStringInvariant() : ((long)endDate.Value.UnixTimestampFromDateTimeSeconds()).ToStringInvariant());
- url += "&period=" + periodSeconds.ToStringInvariant();
- JToken token = await MakeJsonRequestAsync(url);
- List candles = new List();
- foreach (JToken candle in token)
- {
- candles.Add(this.ParseCandle(candle, marketSymbol, periodSeconds, "open", "high", "low", "close", "date", TimestampType.UnixSeconds, "quoteVolume", "volume", "weightedAverage"));
- }
- return candles;
+ //https://api.poloniex.com/markets/{symbol}/candles?interval={interval}&limit={limit}&startTime={startTime}&endTime={endTime}
+ // [
+ // [
+ // "45218",
+ // "47590.82",
+ // "47009.11",
+ // "45516.6",
+ // "13337805.8",
+ // "286.639111",
+ // "0",
+ // "0",
+ // 0,
+ // 0,
+ // "46531.7",
+ // "DAY_1",
+ // 1648684800000,
+ // 1648771199999
+ // ]
+ // ]
+ limit = (limit == null || limit < 1 || limit > 500) ? 500 : limit;
+ var period = PeriodSecondsToString(periodSeconds);
+ var url = $"/markets/{marketSymbol}/candles?interval={period}&limit={limit}";
+ if (startDate != null)
+ {
+ url = $"{url}&startTime={new DateTimeOffset(startDate.Value).ToUnixTimeMilliseconds()}";
+ }
+ if (endDate != null)
+ {
+ url = $"{url}&endTime={new DateTimeOffset(endDate.Value).ToUnixTimeMilliseconds()}";
+ }
+
+ var candleResponse = await MakeJsonRequestAsync(url);
+ return candleResponse
+ .Select(cr => this.ParseCandle(
+ cr, marketSymbol, periodSeconds, 2, 1, 0, 3, 12, TimestampType.UnixMilliseconds,
+ 5, 4, 10))
+ .ToList();
}
protected override async Task> OnGetAmountsAsync()
{
- Dictionary amounts = new Dictionary(StringComparer.OrdinalIgnoreCase);
- JToken result = await MakePrivateAPIRequestAsync("returnCompleteBalances");
- foreach (JProperty child in result.Children())
- {
- decimal amount = child.Value["available"].ConvertInvariant();
- if (amount > 0m)
- {
- amounts[child.Name] = amount;
- }
- }
- return amounts;
+ Dictionary payload = await GetNoncePayloadAsync();
+ var response = await MakeJsonRequestAsync("/accounts/balances", payload: payload);
+ return null;
}
protected override async Task> OnGetAmountsAvailableToTradeAsync()
@@ -1070,6 +1089,13 @@ private static bool TryPopulateAddressAndTag(string currency, IReadOnlyDictionar
}
+ private static bool ParsePairState(string state)
+ {
+ if (string.IsNullOrWhiteSpace(state)) return false;
+
+ return state == "NORMAL";
+ }
+
///
/// Create a deposit address
///
From cbb28af17413beb367f798869bfc8b0555ae555e Mon Sep 17 00:00:00 2001
From: BZ-CO <30245815+BZ-CO@users.noreply.github.com>
Date: Thu, 23 Feb 2023 00:23:44 +0200
Subject: [PATCH 5/5] Add incomplete request and signature for Poloniex
Can't come up with a working solution.
---
.../API/Exchanges/Poloniex/ExchangePoloniexAPI.cs | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/ExchangeSharp/API/Exchanges/Poloniex/ExchangePoloniexAPI.cs b/src/ExchangeSharp/API/Exchanges/Poloniex/ExchangePoloniexAPI.cs
index 159f436e..dc060b77 100644
--- a/src/ExchangeSharp/API/Exchanges/Poloniex/ExchangePoloniexAPI.cs
+++ b/src/ExchangeSharp/API/Exchanges/Poloniex/ExchangePoloniexAPI.cs
@@ -11,6 +11,7 @@ The above copyright notice and this permission notice shall be included in all c
*/
using System.Diagnostics;
+using System.Web;
namespace ExchangeSharp
{
@@ -294,11 +295,11 @@ protected override async Task ProcessRequestAsync(IHttpWebRequest request, Dicti
payload["signTimestamp"] = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds();
var form = payload.GetFormForPayload();
var sig = $"{request.Method}\n" +
- $"{request.RequestUri.PathAndQuery}\n" +
- $"{form}";
+ $"{request.RequestUri.PathAndQuery}\n" +
+ $"{HttpUtility.UrlEncode(form)}";
request.AddHeader("key", PublicApiKey.ToUnsecureString());
+ request.AddHeader("signature", CryptoUtility.SHA256Sign(sig, PrivateApiKey.ToUnsecureString()));
request.AddHeader("signTimestamp", payload["signTimestamp"].ToStringInvariant());
- request.AddHeader("signature", CryptoUtility.SHA256Sign(sig, PrivateApiKey.ToUnsecureString(), true));
await request.WriteToRequestAsync(form);
}
}
@@ -720,7 +721,8 @@ protected override async Task> OnGetCandlesAsync(strin
protected override async Task> OnGetAmountsAsync()
{
- Dictionary payload = await GetNoncePayloadAsync();
+ // Dictionary payload = await GetNoncePayloadAsync();
+ Dictionary payload = new Dictionary();
var response = await MakeJsonRequestAsync("/accounts/balances", payload: payload);
return null;
}