1
0
mirror of https://github.com/JKorf/CryptoExchange.Net.git synced 2026-10-13 22:51:39 +00:00
This commit is contained in:
Jkorf
2026-09-11 13:22:48 +02:00
parent 911e09be61
commit 8419af278d
11 changed files with 431 additions and 31 deletions
@@ -1,5 +1,7 @@
using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.SharedApis;
using Microsoft.Extensions.DependencyInjection;
using NUnit.Framework; using NUnit.Framework;
using System.Linq;
namespace CryptoExchange.Net.UnitTests namespace CryptoExchange.Net.UnitTests
{ {
@@ -16,7 +18,7 @@ namespace CryptoExchange.Net.UnitTests
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(client.GetCapability<ITestCapability>(TradingMode.Spot), Is.SameAs(sharedApi)); Assert.That(client.GetCapability<ITestCapability>(TradingMode.Spot)?.Capability, Is.SameAs(sharedApi));
Assert.That(client.GetCapability<ITestCapability>(TradingMode.PerpetualLinear), Is.Null); Assert.That(client.GetCapability<ITestCapability>(TradingMode.PerpetualLinear), Is.Null);
Assert.That(client.GetCapabilities<ITestCapability>(TradingMode.Spot), Has.Count.EqualTo(1)); Assert.That(client.GetCapabilities<ITestCapability>(TradingMode.Spot), Has.Count.EqualTo(1));
Assert.That(client.GetCapabilities<ITestCapability>(TradingMode.PerpetualLinear), Is.Empty); Assert.That(client.GetCapabilities<ITestCapability>(TradingMode.PerpetualLinear), Is.Empty);
@@ -33,8 +35,73 @@ namespace CryptoExchange.Net.UnitTests
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(client.GetCapability<ITestCapability>(TradingMode.Spot), Is.SameAs(sharedApi)); Assert.That(client.GetCapability<ITestCapability>(TradingMode.Spot)?.Capability, Is.SameAs(sharedApi));
Assert.That(client.GetCapability<ITestCapability>(TradingMode.PerpetualLinear), Is.SameAs(sharedApi)); Assert.That(client.GetCapability<ITestCapability>(TradingMode.PerpetualLinear)?.Capability, Is.SameAs(sharedApi));
});
}
[Test]
public void GetCapabilities_WithReference_ShouldReturnMatchingCapabilities()
{
var sharedApi = new TestSharedApi(
[TradingMode.Spot],
[TradingMode.Spot]);
var client = new TestSharedApiClient(sharedApi);
var result = client.GetCapabilities(
new SharedCapabilityReference<ITestCapability>(),
TradingMode.Spot);
Assert.That(result, Has.Count.EqualTo(1));
Assert.That(result[0].Capability, Is.SameAs(sharedApi));
Assert.That(result[0].Options, Is.SameAs(sharedApi.CapabilityOptions));
}
[Test]
public void Discover_ShouldReturnAllSharedApiInformation()
{
var restApi = new TestRestSharedApi();
var socketApi = new TestSocketSharedApi();
var client = new TestDiSharedApiClient(
SharedTransport.Socket,
restApi,
socketApi);
var result = client.Discover();
Assert.Multiple(() =>
{
Assert.That(result.PreferredTransport, Is.EqualTo(SharedTransport.Socket));
Assert.That(result.SharedApis, Has.Length.EqualTo(2));
Assert.That(result.SharedApis[0].Transport, Is.EqualTo(SharedTransport.Rest));
Assert.That(result.SharedApis[1].Transport, Is.EqualTo(SharedTransport.Socket));
Assert.That(result.SharedApis[0].SupportedTradingModes, Is.EqualTo(new[] { TradingMode.Spot }));
Assert.That(result.SharedApis[0].Capabilities, Has.Length.EqualTo(1));
Assert.That(result.SharedApis[0].Authenticated, Is.False);
});
}
[Test]
public void RegisterSharedApiClientCapabilities_ShouldResolvePreferredTransport()
{
var restApi = new TestRestSharedApi();
var socketApi = new TestSocketSharedApi();
var services = new ServiceCollection();
services.RegisterSharedApi<ITestRestSharedApi>(_ => restApi);
services.RegisterSharedApi<ITestSocketSharedApi>(_ => socketApi);
services.AddTransient<ITestSharedApiClient>(_ =>
new TestDiSharedApiClient(SharedTransport.Socket, restApi, socketApi));
services.RegisterSharedApiClientCapabilities<ITestSharedApiClient>();
using var provider = services.BuildServiceProvider();
Assert.Multiple(() =>
{
Assert.That(provider.GetRequiredService<ITestCapability>(), Is.SameAs(socketApi));
Assert.That(provider.GetServices<ITestCapability>().ToArray(), Is.EqualTo(new[] { socketApi }));
Assert.That(provider.GetRequiredService<ITestRestCapability>(), Is.SameAs(restApi));
Assert.That(provider.GetRequiredService<ITestSocketCapability>(), Is.SameAs(socketApi));
}); });
} }
@@ -101,6 +168,28 @@ namespace CryptoExchange.Net.UnitTests
{ {
} }
private interface ITestRestCapability : ITestCapability, ISharedRest
{
}
private interface ITestSocketCapability : ITestCapability, ISharedSocket
{
}
private interface ITestRestSharedApi : ITestRestCapability
{
}
private interface ITestSocketSharedApi : ITestSocketCapability
{
}
private interface ITestSharedApiClient : ISharedApiClientBase
{
ITestRestSharedApi Rest { get; }
ITestSocketSharedApi Socket { get; }
}
private sealed class TestCapabilityOptions : CapabilityOptions<SharedRequest, ITestCapability> private sealed class TestCapabilityOptions : CapabilityOptions<SharedRequest, ITestCapability>
{ {
public override string Description => "Test capability"; public override string Description => "Test capability";
@@ -159,10 +248,64 @@ namespace CryptoExchange.Net.UnitTests
public override SharedClientInfo Discover() => new(); public override SharedClientInfo Discover() => new();
} }
private abstract class TestTransportSharedApi : SharedApiBase
{
protected TestTransportSharedApi(SharedTransport transport)
: base(
transport,
"TestExchange",
[TradingMode.Spot],
() => false,
(baseAsset, quoteAsset, tradingMode, deliverDate) => $"{baseAsset}{quoteAsset}")
{
SetCapabilities(new TestCapabilityOptions([TradingMode.Spot]));
}
public override SharedClientInfo Discover() => new()
{
Exchange = this.Exchange,
TypeName = GetType().Name,
SupportedTradingModes = this.SupportedTradingModes,
Transport = this.Transport,
Authenticated = this.Authenticated,
Capabilities = ((ISharedApi)this).Capabilities.ToArray()
};
}
private sealed class TestRestSharedApi : TestTransportSharedApi, ITestRestSharedApi
{
public TestRestSharedApi() : base(SharedTransport.Rest)
{
}
}
private sealed class TestSocketSharedApi : TestTransportSharedApi, ITestSocketSharedApi
{
public TestSocketSharedApi() : base(SharedTransport.Socket)
{
}
}
private sealed class TestDiSharedApiClient : SharedApiClientBase, ITestSharedApiClient
{
public ITestRestSharedApi Rest { get; }
public ITestSocketSharedApi Socket { get; }
public TestDiSharedApiClient(
SharedTransport preferredTransport,
ITestRestSharedApi rest,
ITestSocketSharedApi socket)
: base(preferredTransport, rest, socket)
{
Rest = rest;
Socket = socket;
}
}
private sealed class TestSharedApiClient : SharedApiClientBase private sealed class TestSharedApiClient : SharedApiClientBase
{ {
public TestSharedApiClient(params ISharedApiCapability[] sharedApis) public TestSharedApiClient(params ISharedApiCapability[] sharedApis)
: base([SharedTransport.Rest, SharedTransport.Socket], sharedApis) : base(SharedTransport.Rest, sharedApis)
{ {
} }
} }
+84 -11
View File
@@ -488,19 +488,77 @@ namespace CryptoExchange.Net
{ {
services.AddTransient<TSharedApi>(factory); services.AddTransient<TSharedApi>(factory);
var markerInterfaces = new[] var allInterfaces = typeof(TSharedApi).GetInterfaces();
{
typeof(ISharedApiCapability),
typeof(ISharedRest),
typeof(ISharedSocket),
typeof(ISharedSubscription)
};
var capabilityInterfaces = typeof(TSharedApi) var transportCapabilityInterfaces = allInterfaces
.GetInterfaces()
.Where(x => .Where(x =>
typeof(ISharedApiCapability).IsAssignableFrom(x) typeof(ISharedApiCapability).IsAssignableFrom(x)
&& !markerInterfaces.Contains(x)) && (typeof(ISharedRest).IsAssignableFrom(x)
|| typeof(ISharedSocket).IsAssignableFrom(x))
&& x != typeof(ISharedRest)
&& x != typeof(ISharedSocket)
&& x != typeof(ISharedSubscription))
.Distinct()
.ToArray();
var baseCapabilityInterfaces = allInterfaces
.Where(x =>
x != typeof(ISharedApiCapability)
&& typeof(ISharedApiCapability).IsAssignableFrom(x)
&& !typeof(ISharedRest).IsAssignableFrom(x)
&& !typeof(ISharedSocket).IsAssignableFrom(x))
.Distinct()
.ToArray();
services.AddSingleton(new SharedApiRegistrationInfo(
typeof(TSharedApi),
baseCapabilityInterfaces));
foreach (var capabilityInterface in transportCapabilityInterfaces)
{
services.AddTransient(
capabilityInterface,
serviceProvider =>
serviceProvider.GetRequiredService<TSharedApi>());
}
return services;
}
/// <summary>
/// Registers transport-agnostic Shared API capabilities through the
/// exchange-specific Shared API aggregate client.
/// </summary>
public static IServiceCollection RegisterSharedApiClientCapabilities<
#if NET5_0_OR_GREATER
[DynamicallyAccessedMembers(
DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors)]
#endif
TSharedApiClient>(
this IServiceCollection services)
where TSharedApiClient : class, ISharedApiClientBase
{
services.AddTransient<ISharedApiClientBase>(serviceProvider =>
serviceProvider.GetRequiredService<TSharedApiClient>());
var sharedApiTypes = new HashSet<Type>(
typeof(TSharedApiClient)
.GetProperties()
.Where(property =>
typeof(ISharedApi).IsAssignableFrom(
property.PropertyType))
.Select(property => property.PropertyType));
var capabilityInterfaces = services
.Where(descriptor =>
descriptor.ImplementationInstance
is SharedApiRegistrationInfo)
.Select(descriptor =>
(SharedApiRegistrationInfo)descriptor.ImplementationInstance!)
.Where(registration =>
sharedApiTypes.Contains(registration.SharedApiType))
.SelectMany(registration =>
registration.BaseCapabilityTypes)
.Distinct() .Distinct()
.ToArray(); .ToArray();
@@ -509,7 +567,22 @@ namespace CryptoExchange.Net
services.AddTransient( services.AddTransient(
capabilityInterface, capabilityInterface,
serviceProvider => serviceProvider =>
serviceProvider.GetRequiredService<TSharedApi>()); {
var sharedApiClient = serviceProvider
.GetRequiredService<TSharedApiClient>();
if (sharedApiClient is not ISharedApiClientResolver resolver)
{
throw new InvalidOperationException(
$"{typeof(TSharedApiClient).Name} should derive from " +
$"{nameof(SharedApiClientBase)}");
}
return resolver.GetCapability(capabilityInterface)
?? throw new InvalidOperationException(
$"No implementation of " +
$"{capabilityInterface.Name} is available");
});
} }
return services; return services;
@@ -11,6 +11,16 @@ namespace CryptoExchange.Net.SharedApis
public class SharedCapabilityResolution<T> public class SharedCapabilityResolution<T>
where T : ISharedApiCapability where T : ISharedApiCapability
{ {
/// <summary>
/// The exchange name
/// </summary>
public string Exchange => Capability.Exchange;
/// <summary>
/// The transport type
/// </summary>
public SharedTransport Transport => Capability.Transport;
/// <summary> /// <summary>
/// The capability /// The capability
/// </summary> /// </summary>
@@ -7,15 +7,15 @@ namespace CryptoExchange.Net.SharedApis
/// <summary> /// <summary>
/// The type of transport used for the shared API /// The type of transport used for the shared API
/// </summary> /// </summary>
public record SharedTransport(string Name) public enum SharedTransport
{ {
/// <summary> /// <summary>
/// REST transport /// REST transport
/// </summary> /// </summary>
public static SharedTransport Rest { get; } = new("REST"); Rest,
/// <summary> /// <summary>
/// WebSocket transport /// WebSocket transport
/// </summary> /// </summary>
public static SharedTransport Socket { get; } = new("WebSocket"); Socket
} }
} }
@@ -0,0 +1,49 @@
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Discovery information for a Shared API client.
/// </summary>
public class SharedApiClientInfo
{
/// <summary>
/// Preferred transport when multiple implementations of a capability are available.
/// </summary>
public SharedTransport PreferredTransport { get; }
/// <summary>
/// Shared APIs available on the client.
/// </summary>
public SharedClientInfo[] SharedApis { get; }
internal SharedApiClientInfo(
SharedTransport preferredTransport,
SharedClientInfo[] sharedApis)
{
PreferredTransport = preferredTransport;
SharedApis = sharedApis;
}
/// <inheritdoc />
public override string ToString() => ToString(false);
/// <summary>
/// Create a string representation of the available Shared APIs.
/// </summary>
/// <param name="detailed">Whether to include detailed capability information.</param>
public string ToString(bool detailed)
{
var builder = new StringBuilder();
builder.AppendLine($"Preferred transport: {PreferredTransport}");
foreach (var sharedApi in SharedApis)
{
builder.AppendLine();
builder.Append(sharedApi.ToString(detailed));
}
return builder.ToString();
}
}
}
@@ -35,6 +35,14 @@ namespace CryptoExchange.Net.SharedApis
/// </summary> /// </summary>
public CentralizationType CentralizationType { get; set; } public CentralizationType CentralizationType { get; set; }
/// <summary> /// <summary>
/// The underlying transport type used by this client
/// </summary>
public SharedTransport Transport { get; init; }
/// <summary>
/// Whether API credentials have been configured for this client. Does not check whether the credentials are valid.
/// </summary>
public bool Authenticated { get; init; }
/// <summary>
/// Use Capabilities instead /// Use Capabilities instead
/// </summary> /// </summary>
#pragma warning disable CS0618 // Type or member is obsolete #pragma warning disable CS0618 // Type or member is obsolete
@@ -74,6 +82,8 @@ namespace CryptoExchange.Net.SharedApis
sb.AppendLine($"Supported environments: {string.Join(", ", SupportedEnvironments)}"); sb.AppendLine($"Supported environments: {string.Join(", ", SupportedEnvironments)}");
sb.AppendLine($"Supported trading modes: {string.Join(", ", SupportedTradingModes)}"); sb.AppendLine($"Supported trading modes: {string.Join(", ", SupportedTradingModes)}");
sb.AppendLine($"Centralization type: {CentralizationType}"); sb.AppendLine($"Centralization type: {CentralizationType}");
sb.AppendLine($"Transport: {Transport}");
sb.AppendLine($"Authenticated: {Authenticated}");
sb.AppendLine($"Capabilities:"); sb.AppendLine($"Capabilities:");
foreach (var capability in Capabilities) foreach (var capability in Capabilities)
{ {
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for Shared API client instances
/// </summary>
public class SharedApiOptions
{
/// <summary>
/// Whether to use XPerps as perpetual linear contracts when using the Shared API's
/// </summary>
public bool EuropeUseXPerps { get; set; }
/// <summary>
/// The preferred transport type when requesting transport agnostic capabilities
/// </summary>
public SharedTransport PreferredTransport { get; set; }
= SharedTransport.Rest;
}
}
@@ -65,7 +65,7 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc /> /// <inheritdoc />
public override string ToString() public override string ToString()
{ {
return $"[{ValueType.Name}] \"{Name}\" {string.Join(" / ", Aliases.Select(x => $"\"{x}\""))}: {Description}"; return $"[{(Nullable.GetUnderlyingType(ValueType) ?? ValueType).Name}] \"{Name}\" {string.Join(" / ", Aliases.Select(x => $"\"{x}\""))}: {Description}";
} }
} }
@@ -135,7 +135,7 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc /> /// <inheritdoc />
public override string ToString() public override string ToString()
{ {
return $"[{Support}] [{ValueType.Name}] \"{Name}\" {string.Join(" / ", Aliases.Select(x => $"\"{x}\""))}: {Description}"; return $"[{Support}] [{(Nullable.GetUnderlyingType(ValueType) ?? ValueType).Name}] \"{Name}\" {string.Join(" / ", Aliases.Select(x => $"\"{x}\""))}: {Description}";
} }
} }
@@ -167,7 +167,7 @@ namespace CryptoExchange.Net.SharedApis
/// <inheritdoc /> /// <inheritdoc />
public override string ToString() public override string ToString()
{ {
return $"[{Requirement}] [{ValueType.Name}] \"{Name}\" {string.Join(" / ", Aliases.Select(x => $"\"{x}\""))}: {Description}"; return $"[{Requirement}] [{(Nullable.GetUnderlyingType(ValueType) ?? ValueType).Name}] \"{Name}\" {string.Join(" / ", Aliases.Select(x => $"\"{x}\""))}: {Description}";
} }
} }
} }
@@ -1,10 +1,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
namespace CryptoExchange.Net.SharedApis namespace CryptoExchange.Net.SharedApis
{ {
internal interface ISharedApiClientResolver
{
object? GetCapability(Type capabilityType);
}
/// <summary> /// <summary>
/// Base interface for Shared API clients /// Base interface for Shared API clients
/// </summary> /// </summary>
@@ -14,6 +19,19 @@ namespace CryptoExchange.Net.SharedApis
/// Shared API's available on this client /// Shared API's available on this client
/// </summary> /// </summary>
IReadOnlyList<ISharedApi> SharedApis { get; } IReadOnlyList<ISharedApi> SharedApis { get; }
/// <summary>
/// Preferred transport selection when multiple transport options are available
/// </summary>
SharedTransport PreferredTransport { get; }
/// <summary>
/// Get discovery information for the Shared APIs available on this client.
/// </summary>
SharedApiClientInfo Discover();
/// <summary>
/// The exchange name of this client
/// </summary>
string Exchange { get; }
/// <summary> /// <summary>
/// Get all capabilities matching the provided capability type. /// Get all capabilities matching the provided capability type.
@@ -26,6 +44,17 @@ namespace CryptoExchange.Net.SharedApis
IReadOnlyList<SharedCapabilityResolution<T>> GetCapabilities<T>(TradingMode? tradingMode = null, SharedTransport? transport = null) IReadOnlyList<SharedCapabilityResolution<T>> GetCapabilities<T>(TradingMode? tradingMode = null, SharedTransport? transport = null)
where T : ISharedApiCapability; where T : ISharedApiCapability;
/// <summary>
/// Get all capabilities matching the provided capability reference.
/// </summary>
/// <typeparam name="T">The capability type.</typeparam>
/// <param name="capability">The capability reference.</param>
/// <param name="tradingMode">Filter by supported trading mode.</param>
IReadOnlyList<SharedCapabilityResolution<T>> GetCapabilities<T>(
SharedCapabilityReference<T> capability,
TradingMode? tradingMode = null)
where T : ISharedApiCapability;
/// <summary> /// <summary>
/// Get the requested capability type. If more than one implementation is available the first one is returned. /// Get the requested capability type. If more than one implementation is available the first one is returned.
/// In this case prefer using <see cref="GetCapability{T}(TradingMode, SharedTransport?)"/> to select a specific trading mode. /// In this case prefer using <see cref="GetCapability{T}(TradingMode, SharedTransport?)"/> to select a specific trading mode.
@@ -74,30 +103,43 @@ namespace CryptoExchange.Net.SharedApis
/// <returns>The requested capability type or null if not found</returns> /// <returns>The requested capability type or null if not found</returns>
SharedCapabilityResolution<T>? GetCapability<T>(SharedCapabilityReference<T> capability, TradingMode tradingMode) SharedCapabilityResolution<T>? GetCapability<T>(SharedCapabilityReference<T> capability, TradingMode tradingMode)
where T : ISharedApiCapability; where T : ISharedApiCapability;
} }
/// <summary> /// <summary>
/// Base client for Shared API clients /// Base client for Shared API clients
/// </summary> /// </summary>
public abstract class SharedApiClientBase : ISharedApiClientBase public abstract class SharedApiClientBase : ISharedApiClientBase, ISharedApiClientResolver
{ {
private readonly ISharedApi[] _sharedApis; private readonly ISharedApi[] _sharedApis;
private readonly SharedTransport[] _preferredTransports; private readonly SharedTransport _preferredTransport;
/// <inheritdoc /> /// <inheritdoc />
public IReadOnlyList<ISharedApi> SharedApis => _sharedApis; public IReadOnlyList<ISharedApi> SharedApis => _sharedApis;
/// <inheritdoc />
public SharedTransport PreferredTransport => _preferredTransport;
/// <inheritdoc />
public string Exchange => _sharedApis[0].Exchange;
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public SharedApiClientBase( public SharedApiClientBase(
IEnumerable<SharedTransport> transportPreference, SharedTransport transportPreference,
params ISharedApi[] sharedApis) params ISharedApi[] sharedApis)
{ {
_preferredTransports = transportPreference.Distinct().ToArray(); _preferredTransport = transportPreference;
_sharedApis = sharedApis.Distinct().ToArray(); _sharedApis = sharedApis.Distinct().ToArray();
} }
/// <inheritdoc />
public SharedApiClientInfo Discover()
{
return new SharedApiClientInfo(
PreferredTransport,
_sharedApis.Select(x => x.Discover()).ToArray());
}
/// <inheritdoc /> /// <inheritdoc />
public SharedCapabilityResolution<T>? GetCapability<T>(SharedTransport? transport = null) public SharedCapabilityResolution<T>? GetCapability<T>(SharedTransport? transport = null)
where T : ISharedApiCapability where T : ISharedApiCapability
@@ -126,6 +168,7 @@ namespace CryptoExchange.Net.SharedApis
return GetCapabilityCore<T>(tradingMode, null); return GetCapabilityCore<T>(tradingMode, null);
} }
/// <inheritdoc /> /// <inheritdoc />
public IReadOnlyList<SharedCapabilityResolution<T>> GetCapabilities<T>( public IReadOnlyList<SharedCapabilityResolution<T>> GetCapabilities<T>(
TradingMode? tradingMode = null, TradingMode? tradingMode = null,
@@ -157,6 +200,29 @@ namespace CryptoExchange.Net.SharedApis
.ToArray(); .ToArray();
} }
/// <inheritdoc />
public IReadOnlyList<SharedCapabilityResolution<T>> GetCapabilities<T>(
SharedCapabilityReference<T> capability,
TradingMode? tradingMode = null)
where T : ISharedApiCapability
{
return GetCapabilities<T>(tradingMode);
}
object? ISharedApiClientResolver.GetCapability(Type capabilityType)
{
return _sharedApis
.Where(sharedApi =>
capabilityType.IsInstanceOfType(sharedApi)
&& GetMatchingOptions(
sharedApi,
capabilityType,
null) != null)
.OrderBy(sharedApi =>
GetTransportPriority(sharedApi.Transport))
.FirstOrDefault();
}
private SharedCapabilityResolution<T>? GetCapabilityCore<T>( private SharedCapabilityResolution<T>? GetCapabilityCore<T>(
TradingMode? tradingMode, TradingMode? tradingMode,
SharedTransport? selectedTransport = null) SharedTransport? selectedTransport = null)
@@ -171,8 +237,17 @@ namespace CryptoExchange.Net.SharedApis
TradingMode? tradingMode) TradingMode? tradingMode)
where T : ISharedApiCapability where T : ISharedApiCapability
{ {
var requestedType = typeof(T); return GetMatchingOptions(
sharedApi,
typeof(T),
tradingMode);
}
private static CapabilityOptions? GetMatchingOptions(
ISharedApi sharedApi,
Type requestedType,
TradingMode? tradingMode)
{
return sharedApi.Capabilities return sharedApi.Capabilities
.Where(options => .Where(options =>
options.Supported options.Supported
@@ -182,7 +257,6 @@ namespace CryptoExchange.Net.SharedApis
&& (tradingMode == null && (tradingMode == null
|| options.SupportedTradingModes.Contains( || options.SupportedTradingModes.Contains(
tradingMode.Value))) tradingMode.Value)))
// Prefer options declared for the exact requested interface.
.OrderByDescending(options => .OrderByDescending(options =>
options.CapabilityType == requestedType) options.CapabilityType == requestedType)
.FirstOrDefault(); .FirstOrDefault();
@@ -198,8 +272,7 @@ namespace CryptoExchange.Net.SharedApis
private int GetTransportPriority(SharedTransport transport) private int GetTransportPriority(SharedTransport transport)
{ {
var index = Array.IndexOf(_preferredTransports, transport); return transport == _preferredTransport ? 0 : 1;
return index == -1 ? int.MaxValue : index;
} }
} }
} }
@@ -0,0 +1,18 @@
using System;
namespace CryptoExchange.Net.SharedApis
{
internal sealed class SharedApiRegistrationInfo
{
public Type SharedApiType { get; }
public Type[] BaseCapabilityTypes { get; }
public SharedApiRegistrationInfo(
Type sharedApiType,
Type[] baseCapabilityTypes)
{
SharedApiType = sharedApiType;
BaseCapabilityTypes = baseCapabilityTypes;
}
}
}
@@ -21,6 +21,8 @@ namespace CryptoExchange.Net.SharedApis
SupportedEnvironments = platformInfo.SupportedEnvironments, SupportedEnvironments = platformInfo.SupportedEnvironments,
SupportedTradingModes = client.SupportedTradingModes, SupportedTradingModes = client.SupportedTradingModes,
CentralizationType = platformInfo.CentralizationType, CentralizationType = platformInfo.CentralizationType,
Transport = client.Transport,
Authenticated = client.Authenticated,
Capabilities = client.Capabilities.Where(x => x.Supported).ToArray() Capabilities = client.Capabilities.Where(x => x.Supported).ToArray()
}; };
} }