Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ applyTo: "**/*"

Bitstamp.Net is a CryptoExchange.Net-based client for the Bitstamp REST and websocket APIs. Use this guide when generating code or documentation for this repository.

For multi-exchange code, use `CryptoExchange.Net.SharedApis` through the `.SharedClient` properties on the `ExchangeApi` surfaces. Use `.SharedClient.Discover()` to inspect supported shared features at runtime.

## Package And Client Shape

- NuGet package id: `Bitstamp.Net`
Expand Down Expand Up @@ -159,7 +161,7 @@ Always check `subscription.Success` before using `subscription.Data`. Unsubscrib

## Result Handling

REST calls return `WebCallResult<T>` and socket subscriptions return `CallResult<UpdateSubscription>`.
REST calls return `HttpResult<T>` and socket subscriptions return `WebSocketResult<UpdateSubscription>`. Shared non-I/O symbol/cache helpers return `ExchangeCallResult<T>`.

```csharp
var result = await client.ExchangeApi.ExchangeData.GetTickerAsync("ETH/USD");
Expand Down
5 changes: 3 additions & 2 deletions Bitstamp.Net.UnitTests/BitstampRestClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using System.Net.Http;
using Bitstamp.Net;
using Bitstamp.Net.Clients;
using System.Data.Common;
using CryptoExchange.Net.Objects;

namespace Bitstamp.Net.UnitTests
{
Expand All @@ -28,12 +30,11 @@ public void CheckSignatureExample1()
return headers["X-Auth-Signature"].ToString();
},
"6DCA01A0817446CBC44CD792EF743262A7342A15C2DF805BC89D30DBE099EEFA",
new Dictionary<string, object>
new Parameters(BitstampExchange._parameterSerializationSettings)
{
{ "side", 0 },
},
DateTimeConverter.ParseFromDouble(1499827319559),
true,
false);
}

Expand Down
2 changes: 1 addition & 1 deletion Bitstamp.Net.UnitTests/RestRequestTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public async Task ValidateTradingCalls()
await tester.ValidateAsync(client => client.ExchangeApi.Trading.GetOrderHistoryAsync(OrderSource.Orderbook, "123"), "GetOrderHistory", ignoreProperties: ["id_str", "datetime", "amount_str", "price_str"]);
}

private bool IsAuthenticated(WebCallResult result)
private bool IsAuthenticated(IHttpResult result)
{
return result.RequestHeaders.SingleOrDefault(x => x.Key == "X-Auth-Signature").Key != null;
}
Expand Down
2 changes: 1 addition & 1 deletion Bitstamp.Net.UnitTests/SocketSubscriptionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public async Task ValidateConcurrentSubscriptions()

}), logger);

var tester = new SocketSubscriptionValidator<BitstampSocketClient>(client, "Subscriptions/ExchangeApi", "wss://ws.bitstamp.com");
var tester = new SocketSubscriptionValidator<BitstampSocketClient>(client, "Subscriptions/ExchangeApi", "wss://ws.bitstamp.net");
await tester.ValidateConcurrentAsync<BitstampTradeUpdate>(
(client, handler) => client.ExchangeApi.SubscribeToTradeUpdatesAsync("ETH/USD", handler),
(client, handler) => client.ExchangeApi.SubscribeToTradeUpdatesAsync("BTC/USD", handler),
Expand Down
2 changes: 1 addition & 1 deletion Bitstamp.Net/Bitstamp.Net.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="CryptoExchange.Net" Version="11.2.2" />
<PackageReference Include="CryptoExchange.Net" Version="12.0.1" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="10.0.101">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
10 changes: 5 additions & 5 deletions Bitstamp.Net/BitstampAuthenticationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ public BitstampAuthenticationProvider(BitstampCredentials credentials, string? n
#region Methods
public override void ProcessRequest(RestApiClient apiClient, RestRequestConfiguration requestConfig)
{
if (!requestConfig.Authenticated)
if (!requestConfig.RequestDefinition.Authenticated)
return;

var key = "BITSTAMP " + Credential.Key;
var nonce = _nonce ?? Guid.NewGuid().ToString();
var timestamp = GetMillisecondTimestamp(apiClient);

string bodyContent = "";
if (requestConfig.BodyParameters?.Any() == true)
if (requestConfig.BodyParameters != null && !requestConfig.BodyParameters.Empty)
{
if (requestConfig.BodyFormat is RequestBodyFormat.Json)
bodyContent = _serializer.Serialize(requestConfig.BodyParameters);
Expand All @@ -56,13 +56,13 @@ public override void ProcessRequest(RestApiClient apiClient, RestRequestConfigur
contentType = Constants.FormContentHeader;
}

var pathAndQuery = requestConfig.Path + requestConfig.GetQueryString(true);
var pathAndQuery = requestConfig.RequestDefinition.Path + requestConfig.GetQueryString(true);
if (!pathAndQuery.EndsWith("/"))
pathAndQuery += "/";

var signatureText = key +
requestConfig.Method +
new Uri(requestConfig.BaseAddress).Host +
requestConfig.RequestDefinition.Method +
new Uri(requestConfig.RequestDefinition.BaseAddress).Host +
pathAndQuery +
contentType +
nonce +
Expand Down
21 changes: 17 additions & 4 deletions Bitstamp.Net/BitstampExchange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ namespace Bitstamp.Net
public static class BitstampExchange
{
internal static JsonSerializerContext _serializerContext = JsonSerializerContextCache.GetOrCreate<BitstampSourceGenerationContext>();
internal static ParameterSerializationSettings _parameterSerializationSettings = new ParameterSerializationSettings
{
Decimal = DecimalSerialization.Number,
Bool = BoolSerialization.String,
DateTimes = DateTimeSerialization.MillisecondsNumber
};

/// <summary>
/// Platform metadata
Expand All @@ -27,7 +33,8 @@ public static class BitstampExchange
"https://www.bitstamp.com",
["https://www.bitstamp.net/api/"],
PlatformType.CryptoCurrencyExchange,
CentralizationType.Centralized
CentralizationType.Centralized,
BitstampEnvironment.All
);

/// <summary>
Expand Down Expand Up @@ -99,7 +106,7 @@ public static string FormatSymbol(string baseAsset, string quoteAsset, TradingMo
/// <summary>
/// Rate limiter configuration for the Bitstamp API
/// </summary>
public static BitstampRateLimiters RateLimiter { get; } = new BitstampRateLimiters();
public static BitstampRateLimiters RateLimiter { get; set; } = new BitstampRateLimiters();
}

/// <summary>
Expand All @@ -118,13 +125,19 @@ public class BitstampRateLimiters
public event Action<RateLimitUpdateEvent> RateLimitUpdated;

#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
internal BitstampRateLimiters()
/// <summary>
/// ctor
/// </summary>
public BitstampRateLimiters()
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
{
Initialize();
}

private void Initialize()
/// <summary>
/// Initialize the rate limits
/// </summary>
protected virtual void Initialize()
{
Rest = new RateLimitGate("Rest")
.AddGuard(new RateLimitGuard(RateLimitGuard.PerHost, new PathStartFilter("api/"), 400, TimeSpan.FromSeconds(1), RateLimitWindowType.Fixed))
Expand Down
2 changes: 0 additions & 2 deletions Bitstamp.Net/BitstampUserDataTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ public BitstampUserSpotDataTracker(
SpotUserDataTrackerConfig? config) : base(
logger,
restClient.ExchangeApi.SharedClient,
null,
restClient.ExchangeApi.SharedClient,
null,
restClient.ExchangeApi.SharedClient,
Expand All @@ -45,7 +44,6 @@ public BitstampUserFuturesDataTracker(
string? userIdentifier,
FuturesUserDataTrackerConfig? config) : base(logger,
restClient.ExchangeApi.SharedClient,
null,
restClient.ExchangeApi.SharedClient,
null,
restClient.ExchangeApi.SharedClient,
Expand Down
2 changes: 1 addition & 1 deletion Bitstamp.Net/Clients/BitstampRestClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public BitstampRestClient(HttpClient? httpClient, ILoggerFactory? loggerFactory,
{
Initialize(options.Value);

ExchangeApi = AddApiClient(new BitstampRestClientExchangeApi(_logger, httpClient, options.Value));
ExchangeApi = AddApiClient(new BitstampRestClientExchangeApi(loggerFactory, httpClient, options.Value));
}
#endregion

Expand Down
2 changes: 1 addition & 1 deletion Bitstamp.Net/Clients/BitstampSocketClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public BitstampSocketClient(IOptions<BitstampSocketOptions> options, ILoggerFact
})));
_keyGenerator = new BitstampSocketKeyGenerator(_restClient);

ExchangeApi = AddApiClient(new BitstampSocketClientExchangeApi(_logger, options.Value, _keyGenerator));
ExchangeApi = AddApiClient(new BitstampSocketClientExchangeApi(loggerFactory, options.Value, _keyGenerator));
}
#endregion

Expand Down
112 changes: 15 additions & 97 deletions Bitstamp.Net/Clients/BitstampUserClientProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@
using Bitstamp.Net.Interfaces.Clients;
using Bitstamp.Net.Objects.Options;
using CryptoExchange.Net.Authentication;
using CryptoExchange.Net.Clients;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Bitstamp.Net.Clients
{
/// <inheritdoc />
public class BitstampUserClientProvider : IBitstampUserClientProvider
public class BitstampUserClientProvider : UserClientProvider<
IBitstampRestClient,
IBitstampSocketClient,
BitstampRestOptions,
BitstampSocketOptions,
BitstampCredentials,
BitstampEnvironment
>, IBitstampUserClientProvider
{
private ConcurrentDictionary<string, IBitstampRestClient> _restClients = new ConcurrentDictionary<string, IBitstampRestClient>();
private ConcurrentDictionary<string, IBitstampSocketClient> _socketClients = new ConcurrentDictionary<string, IBitstampSocketClient>();

private readonly IOptions<BitstampRestOptions> _restOptions;
private readonly IOptions<BitstampSocketOptions> _socketOptions;
private readonly HttpClient _httpClient;
private readonly ILoggerFactory? _loggerFactory;

/// <inheritdoc />
public string ExchangeName => BitstampExchange.ExchangeName;
public override string ExchangeName => BitstampExchange.ExchangeName;

/// <summary>
/// ctor
Expand All @@ -38,97 +38,15 @@ public BitstampUserClientProvider(
ILoggerFactory? loggerFactory,
IOptions<BitstampRestOptions> restOptions,
IOptions<BitstampSocketOptions> socketOptions)
: base(httpClient, loggerFactory, restOptions, socketOptions)
{
_httpClient = httpClient ?? new HttpClient();
_httpClient.Timeout = restOptions.Value.RequestTimeout;
_loggerFactory = loggerFactory;
_restOptions = restOptions;
_socketOptions = socketOptions;
}

/// <inheritdoc />
public void InitializeUserClient(string userIdentifier, BitstampCredentials credentials, BitstampEnvironment? environment = null)
{
CreateRestClient(userIdentifier, credentials, environment);
CreateSocketClient(userIdentifier, credentials, environment);
}

/// <inheritdoc />
public void ClearUserClients(string userIdentifier)
{
_restClients.TryRemove(userIdentifier, out _);
_socketClients.TryRemove(userIdentifier, out _);
}

protected override IBitstampRestClient ConstructRestClient(HttpClient client, ILoggerFactory? loggerFactory, IOptions<BitstampRestOptions> options)
=> new BitstampRestClient(client, loggerFactory, options);
/// <inheritdoc />
public IBitstampRestClient GetRestClient(string userIdentifier, BitstampCredentials? credentials = null, BitstampEnvironment? environment = null)
{
if (!_restClients.TryGetValue(userIdentifier, out var client) || client.Disposed)
client = CreateRestClient(userIdentifier, credentials, environment);

return client;
}

/// <inheritdoc />
public IBitstampSocketClient GetSocketClient(string userIdentifier, BitstampCredentials? credentials = null, BitstampEnvironment? environment = null)
{
if (!_socketClients.TryGetValue(userIdentifier, out var client) || client.Disposed)
client = CreateSocketClient(userIdentifier, credentials, environment);

return client;
}

private IBitstampRestClient CreateRestClient(string userIdentifier, BitstampCredentials? credentials, BitstampEnvironment? environment)
{
var clientRestOptions = SetRestEnvironment(environment);
var client = new BitstampRestClient(_httpClient, _loggerFactory, clientRestOptions);
if (credentials != null)
{
client.SetApiCredentials(credentials);
_restClients[userIdentifier] = client;
}
return client;
}

private IBitstampSocketClient CreateSocketClient(string userIdentifier, BitstampCredentials? credentials, BitstampEnvironment? environment)
{
var clientSocketOptions = SetSocketEnvironment(environment);
var client = new BitstampSocketClient(clientSocketOptions!, _loggerFactory);
if (credentials != null)
{
client.SetApiCredentials(credentials);
_socketClients[userIdentifier] = client;
}
return client;
}

private IOptions<BitstampRestOptions> SetRestEnvironment(BitstampEnvironment? environment)
{
if (environment == null)
return _restOptions;

var newRestClientOptions = new BitstampRestOptions();
var restOptions = _restOptions.Value.Set(newRestClientOptions);
newRestClientOptions.Environment = environment;
return Options.Create(newRestClientOptions);
}

private IOptions<BitstampSocketOptions> SetSocketEnvironment(BitstampEnvironment? environment)
{
if (environment == null)
return _socketOptions;

var newSocketClientOptions = new BitstampSocketOptions();
var restOptions = _socketOptions.Value.Set(newSocketClientOptions);
newSocketClientOptions.Environment = environment;
return Options.Create(newSocketClientOptions);
}

private static T ApplyOptionsDelegate<T>(Action<T>? del) where T : new()
{
var opts = new T();
del?.Invoke(opts);
return opts;
}
protected override IBitstampSocketClient ConstructSocketClient(ILoggerFactory? loggerFactory, IOptions<BitstampSocketOptions> options)
=> new BitstampSocketClient(options, loggerFactory);
}
}
19 changes: 6 additions & 13 deletions Bitstamp.Net/Clients/ExchangeApi/BitstampRestClientExchangeApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ internal partial class BitstampRestClientExchangeApi : RestApiClient<BitstampEnv
/// <inheritdoc />
public IBitstampRestClientExchangeApiTrading Trading { get; }

public BitstampRestClientExchangeApi(ILogger logger, HttpClient? httpClient, BitstampRestOptions options) :
base(logger, httpClient, options.Environment.RestBaseAddress, options, options.ApiOptions)
public BitstampRestClientExchangeApi(ILoggerFactory? loggerFactory, HttpClient? httpClient, BitstampRestOptions options) :
base(loggerFactory, BitstampExchange.Metadata.Id, httpClient, options.Environment.RestBaseAddress, options, options.ApiOptions)
{
RequestBodyFormat = RequestBodyFormat.FormData;
RequestBodyEmptyContent = "";
Expand All @@ -62,21 +62,14 @@ public override string FormatSymbol(string baseAsset, string quoteAsset, Trading
/// <inheritdoc />
public IBitstampRestClientExchangeApiShared SharedClient => this;

internal Task<WebCallResult<T>> SendAsync<T>(RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null) where T : class
=> SendToAddressAsync<T>(BaseAddress, definition, parameters, cancellationToken, weight);

internal async Task<WebCallResult<T>> SendToAddressAsync<T>(string baseAddress, RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null) where T : class
internal async Task<HttpResult<T>> SendAsync<T>(RequestDefinition definition, Parameters? parameters, CancellationToken cancellationToken, int? weight = null) where T : class
{
return await base.SendAsync<T>(baseAddress, definition, parameters, cancellationToken, null, weight).ConfigureAwait(false);
return await base.SendAsync<T>(definition, parameters, cancellationToken, null, weight).ConfigureAwait(false);
}


internal Task<WebCallResult> SendAsync(RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null)
=> SendToAddressAsync(BaseAddress, definition, parameters, cancellationToken, weight);

internal async Task<WebCallResult> SendToAddressAsync(string baseAddress, RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null)
internal async Task<HttpResult> SendAsync(RequestDefinition definition, Parameters? parameters, CancellationToken cancellationToken, int? weight = null)
{
return await base.SendAsync(baseAddress, definition, parameters, cancellationToken, null, weight).ConfigureAwait(false);
return await base.SendAsync<Unit>(definition, parameters, cancellationToken, null, weight).ConfigureAwait(false);
}
}
}
Loading
Loading