1
0
mirror of https://github.com/JKorf/CryptoExchange.Net synced 2026-04-13 00:22:22 +00:00
This commit is contained in:
Jkorf 2026-02-12 16:19:33 +01:00
parent 63d6701a8d
commit fe31568100
16 changed files with 238 additions and 40 deletions

View File

@ -4,6 +4,7 @@ using CryptoExchange.Net.SharedApis;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Threading;
@ -329,6 +330,109 @@ namespace CryptoExchange.Net
}
}
public static IEnumerable<T> ApplyFilter<T>(
IEnumerable<T> data,
Func<T, DateTime> timeSelector,
DateTime? startTime,
DateTime? endTime,
PageDirection direction)
{
if (direction == PageDirection.Ascending)
data = data.OrderBy(timeSelector);
else
data = data.OrderByDescending(timeSelector);
if (startTime != null)
data = data.Where(x => timeSelector(x) >= startTime.Value);
if (endTime != null)
data = data.Where(x => timeSelector(x) < endTime.Value);
return data;
}
public static bool CheckForNextPage(
int resultCount,
IEnumerable<DateTime> timestamps,
DateTime? startTime,
DateTime? endTime,
int limit,
PageDirection paginationDirection)
{
if (resultCount < limit)
return false;
if (paginationDirection == PageDirection.Ascending)
{
if (timestamps.Max() >= endTime)
return false;
return true;
}
else
{
if (timestamps.Min() < startTime)
return false;
return true;
}
}
public enum PaginationFilterType
{
FromId,
Time,
}
public static (DateTime? startTime, DateTime? endTime, long? fromId) ApplyPaginationRequestFilters(
PaginationFilterType? ascendingFilterType,
PaginationFilterType? descendingFilterType,
PageDirection direction,
DateTime? userFilterStartTime,
DateTime? userFilterEndTime,
DateTime? paginationStartTime,
DateTime? paginationEndTime,
TimeSpan? maxTimePeriod,
string? paginationFromId)
{
// TODO this only is applicable for Ascending FromId / Descending EndTime filtering
// Can we apply logic per direction?
var filterType = direction == PageDirection.Ascending ? ascendingFilterType : descendingFilterType;
if (filterType == PaginationFilterType.FromId)
{
long? fromId = ApplyFromId(paginationFromId);
return (fromId == null ? userFilterStartTime : null, fromId == null ? userFilterEndTime : null, fromId);
}
else // Time
{
DateTime? startTime = direction == PageDirection.Descending ? null : ApplyTime(userFilterStartTime, paginationStartTime);
DateTime? endTime = (direction == PageDirection.Ascending && paginationStartTime != null) ? null : ApplyTime(userFilterEndTime, paginationEndTime);
if (maxTimePeriod.HasValue && endTime - startTime > maxTimePeriod.Value)
{
if (direction == PageDirection.Ascending)
endTime = startTime.Value.Add(maxTimePeriod.Value);
else
startTime = endTime.Value.Add(-maxTimePeriod.Value);
}
return (startTime, endTime, null);
}
}
private static DateTime? ApplyTime(DateTime? userFilterTime, DateTime? paginationTime)
{
return paginationTime ?? userFilterTime;
}
//private static DateTime? ApplyTimeIfFromIdNull(string? fromId, DateTime? userFilterTime, DateTime? paginationTime)
//{
// return fromId != null ? null: paginationTime ?? userFilterTime;
//}
private static long? ApplyFromId(string? paginationFromId)
{
return paginationFromId == null ? null : long.Parse(paginationFromId);
}
/// <summary>
/// Apply the rules (price and quantity step size and decimals precision, min/max quantity) from the symbol to the quantity and price
/// </summary>

View File

@ -538,6 +538,20 @@ namespace CryptoExchange.Net.Objects
return new ExchangeWebResult<K>(exchange, tradeMode, As<K>(data), nextPageToken);
}
/// <summary>
/// Copy the WebCallResult to an ExchangeWebResult of a new data type
/// </summary>
/// <typeparam name="K">The new type</typeparam>
/// <param name="exchange">The exchange</param>
/// <param name="tradeMode">Trade mode the result applies to</param>
/// <param name="data">Data</param>
/// <param name="nextPageToken">Next page token</param>
/// <returns></returns>
public ExchangeWebResult<K> AsExchangeResult<K>(string exchange, TradingMode tradeMode, [AllowNull] K data, PageRequest? nextPageRequest)
{
return new ExchangeWebResult<K>(exchange, tradeMode, As<K>(data), nextPageRequest);
}
/// <summary>
/// Copy the WebCallResult to an ExchangeWebResult of a new data type
/// </summary>

View File

@ -78,7 +78,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public FromIdToken(int fromToken)
public FromIdToken(long fromToken)
{
FromToken = fromToken.ToString();
}

View File

@ -20,6 +20,6 @@ namespace CryptoExchange.Net.SharedApis
/// <param name="nextPageToken">The pagination token from the previous request to continue pagination</param>
/// <param name="ct">Cancellation token</param>
/// <returns></returns>
Task<ExchangeWebResult<SharedTrade[]>> GetTradeHistoryAsync(GetTradeHistoryRequest request, INextPageToken? nextPageToken = null, CancellationToken ct = default);
Task<ExchangeWebResult<SharedTrade[]>> GetTradeHistoryAsync(GetTradeHistoryRequest request, PageRequest? nextPageToken = null, CancellationToken ct = default);
}
}

View File

@ -28,6 +28,11 @@ namespace CryptoExchange.Net.SharedApis
/// </summary>
public INextPageToken? NextPageToken { get; }
/// <summary>
///
/// </summary>
public PageRequest? NextPageRequest { get; }
/// <summary>
/// ctor
/// </summary>
@ -95,6 +100,62 @@ namespace CryptoExchange.Net.SharedApis
NextPageToken = nextPageToken;
}
/// <summary>
/// ctor
/// </summary>
public ExchangeWebResult(
string exchange,
TradingMode dataTradeMode,
WebCallResult<T> result,
PageRequest? nextPageToken) :
base(result.ResponseStatusCode,
result.HttpVersion,
result.ResponseHeaders,
result.ResponseTime,
result.ResponseLength,
result.OriginalData,
result.RequestId,
result.RequestUrl,
result.RequestBody,
result.RequestMethod,
result.RequestHeaders,
result.DataSource,
result.Data,
result.Error)
{
DataTradeMode = new[] { dataTradeMode };
Exchange = exchange;
NextPageRequest = nextPageToken;
}
/// <summary>
/// ctor
/// </summary>
public ExchangeWebResult(
string exchange,
TradingMode[]? dataTradeModes,
WebCallResult<T> result,
PageRequest? nextPageRequest) :
base(result.ResponseStatusCode,
result.HttpVersion,
result.ResponseHeaders,
result.ResponseTime,
result.ResponseLength,
result.OriginalData,
result.RequestId,
result.RequestUrl,
result.RequestBody,
result.RequestMethod,
result.RequestHeaders,
result.DataSource,
result.Data,
result.Error)
{
DataTradeMode = dataTradeModes;
Exchange = exchange;
NextPageRequest = nextPageRequest;
}
/// <summary>
/// Create a new result
/// </summary>

View File

@ -16,7 +16,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public GetClosedOrdersOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit) : base(paginationType, timeFilterSupported, maxLimit, true)
public GetClosedOrdersOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit) : base(timeFilterSupported, maxLimit, true)
{
TimeFilterSupported = timeFilterSupported;
}

View File

@ -16,7 +16,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public GetDepositsOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit) : base(paginationType, timeFilterSupported, maxLimit, true)
public GetDepositsOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit) : base(timeFilterSupported, maxLimit, true)
{
TimeFilterSupported = timeFilterSupported;
}

View File

@ -8,7 +8,7 @@
/// <summary>
/// ctor
/// </summary>
public GetFundingRateHistoryOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit, bool needsAuthentication) : base(paginationType, timeFilterSupported, maxLimit, needsAuthentication)
public GetFundingRateHistoryOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit, bool needsAuthentication) : base(timeFilterSupported, maxLimit, needsAuthentication)
{
}
}

View File

@ -26,7 +26,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public GetKlinesOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit, bool needsAuthentication) : base(paginationType, timeFilterSupported, maxLimit, needsAuthentication)
public GetKlinesOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit, bool needsAuthentication) : base(timeFilterSupported, maxLimit, needsAuthentication)
{
SupportIntervals = new[]
{
@ -50,7 +50,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public GetKlinesOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit, bool needsAuthentication, params SharedKlineInterval[] intervals) : base(paginationType, timeFilterSupported, maxLimit, needsAuthentication)
public GetKlinesOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit, bool needsAuthentication, params SharedKlineInterval[] intervals) : base(timeFilterSupported, maxLimit, needsAuthentication)
{
SupportIntervals = intervals;
}

View File

@ -8,7 +8,7 @@
/// <summary>
/// ctor
/// </summary>
public GetPositionHistoryOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit) : base(paginationType, timeFilterSupported, maxLimit, true)
public GetPositionHistoryOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit) : base(timeFilterSupported, maxLimit, true)
{
}
}

View File

@ -17,7 +17,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public GetTradeHistoryOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit, bool needsAuthentication) : base(paginationType, timeFilterSupported, maxLimit, needsAuthentication)
public GetTradeHistoryOptions(bool timeFilterSupported, int maxLimit, bool needsAuthentication) : base(timeFilterSupported, maxLimit, needsAuthentication)
{
}

View File

@ -16,7 +16,7 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public GetWithdrawalsOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit) : base(paginationType, timeFilterSupported, maxLimit, true)
public GetWithdrawalsOptions(SharedPaginationSupport paginationType, bool timeFilterSupported, int maxLimit) : base(timeFilterSupported, maxLimit, true)
{
TimeFilterSupported = timeFilterSupported;
}

View File

@ -13,10 +13,13 @@ namespace CryptoExchange.Net.SharedApis
public class PaginatedEndpointOptions<T> : EndpointOptions<T> where T : SharedRequest
#endif
{
/// <summary>
/// Type of pagination supported
/// </summary>
public SharedPaginationSupport PaginationSupport { get; }
///// <summary>
///// Type of pagination supported
///// </summary>
//public SharedPaginationSupport PaginationSupport { get; }
public bool SupportsAscendingPagination { get; set; }
public bool SupportsDescendingPagination { get; set; }
/// <summary>
/// Whether filtering based on start/end time is supported
@ -31,9 +34,8 @@ namespace CryptoExchange.Net.SharedApis
/// <summary>
/// ctor
/// </summary>
public PaginatedEndpointOptions(SharedPaginationSupport paginationType, bool timePeriodSupport, int maxLimit, bool needsAuthentication) : base(needsAuthentication)
public PaginatedEndpointOptions(bool timePeriodSupport, int maxLimit, bool needsAuthentication) : base(needsAuthentication)
{
PaginationSupport = paginationType;
TimePeriodFilterSupport = timePeriodSupport;
MaxLimit = maxLimit;
}
@ -42,7 +44,8 @@ namespace CryptoExchange.Net.SharedApis
public override string ToString(string exchange)
{
var sb = new StringBuilder(base.ToString(exchange));
sb.AppendLine($"Pagination type: {PaginationSupport}");
sb.AppendLine($"Pagination ASC supported: {SupportsAscendingPagination}");
sb.AppendLine($"Pagination DESC supported: {SupportsDescendingPagination}");
sb.AppendLine($"Time period filter support: {TimePeriodFilterSupport}");
sb.AppendLine($"Max limit: {MaxLimit}");
return sb.ToString();

View File

@ -1,23 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CryptoExchange.Net.SharedApis.Models
namespace CryptoExchange.Net.SharedApis
{
public interface PageRequest
{
public enum PageDirection
{
Ascending, // Old to new
Descending // New to old
}
public record PageParameters
public class PageRequest
{
public string? Cursor { get; set; }
public string? FromId { get; set; }
public int? Page { get; set; }
public int? Offset { get; set; }
public DateTime? StartTime { get; set; }
public DateTime? EndTime { get; set; }
public string? NextCursor { get; set; }
public int? NextPage { get; set; }
public int? NextOffset { get; set; }
public string? NextFromId { get; set; }
public DateTime? NextStartTime { get; set; }
public DateTime? NextEndTime { get; set; }
public SharedPaginationSupport? Direction { get; set; }
public static PageRequest FromNextCursor(string nextCursor) => new PageRequest { NextCursor = nextCursor };
public static PageRequest FromNextPage(int nextPage) => new PageRequest { NextPage = nextPage };
public static PageRequest FromNextOffset(int nextOffset) => new PageRequest { NextOffset = nextOffset };
public static PageRequest FromNextFromIdAsc(IEnumerable<long> idSelector) => new PageRequest { NextFromId = (idSelector.Max() + 1).ToString() };
public static PageRequest FromNextFromIdDesc(IEnumerable<long> idSelector) => new PageRequest { NextFromId = (idSelector.Min() - 1).ToString() };
public static PageRequest FromNextStartTimeAsc(IEnumerable<DateTime> timestampSelector) => new PageRequest { NextStartTime = timestampSelector.Max().AddMilliseconds(1) };
public static PageRequest FromNextStartTimeDesc(IEnumerable<DateTime> timestampSelector) => new PageRequest { NextStartTime = timestampSelector.Min().AddMilliseconds(-1) };
public static PageRequest FromNextEndTimeAsc(IEnumerable<DateTime> timestampSelector) => new PageRequest { NextEndTime = timestampSelector.Max().AddMilliseconds(1) };
public static PageRequest FromNextEndTimeDesc(IEnumerable<DateTime> timestampSelector) => new PageRequest { NextEndTime = timestampSelector.Min().AddMilliseconds(-1) };
}
}

View File

@ -20,6 +20,8 @@ namespace CryptoExchange.Net.SharedApis
/// </summary>
public int? Limit { get; set; }
public PageDirection? Direction { get; set; }
/// <summary>
/// ctor
/// </summary>

View File

@ -256,21 +256,21 @@ namespace CryptoExchange.Net.Trackers.Trades
if (_historyRestClient != null)
{
var startTime = Period == null ? DateTime.UtcNow.AddMinutes(-5) : DateTime.UtcNow.Add(-Period.Value);
var request = new GetTradeHistoryRequest(Symbol, startTime, DateTime.UtcNow);
var data = new List<SharedTrade>();
await foreach(var result in ExchangeHelpers.ExecutePages(_historyRestClient.GetTradeHistoryAsync, request).ConfigureAwait(false))
{
if (!result)
return result;
//var startTime = Period == null ? DateTime.UtcNow.AddMinutes(-5) : DateTime.UtcNow.Add(-Period.Value);
//var request = new GetTradeHistoryRequest(Symbol, startTime, DateTime.UtcNow);
//var data = new List<SharedTrade>();
//await foreach(var result in ExchangeHelpers.ExecutePages(_historyRestClient.GetTradeHistoryAsync, request).ConfigureAwait(false))
//{
// if (!result)
// return result;
if (Limit != null && data.Count > Limit)
break;
// if (Limit != null && data.Count > Limit)
// break;
data.AddRange(result.Data);
}
// data.AddRange(result.Data);
//}
SetInitialData(data);
//SetInitialData(data);
}
else if (_recentRestClient != null)
{