-
Notifications
You must be signed in to change notification settings - Fork 75
Architecture
The purpose of this document is to describe the core separation of JSON producers and consumers and how they form the layered architecture of this library as well as to explain the is-a and has-a relationships of the interfaces and implementations of this library.
This is not intended to be a 'how-to' guide. Currently, the sample applications still offer the best demonstrations and example use of this library. For those looking for the simplest application-level development, consider using the web socket clients and caches or the composite IBinanceWebSocketClientManager
.

This fundamental interface defines a JSON message producer. This can be anything that produces JSON messages (in Binance format or otherwise) such as a Web Socket, HTTP client, or database API (for back-testing/simulation).
public interface IJsonProducer
{
event EventHandler<JsonMessageEventArgs> Message;
}
NOTE: Application-level development will typically not use this 'low-level' event for anything other than logging or troubleshooting. Also, since some JSON data can contain sensitive information, it is no longer logged automatically (but, can still be logged at the application-level using this event).
The JSON message event arguments include the message data as a JSON object or array and the message Subject
, which defines the context of the message. For instance, the IBinanceHttpClient
and IWebSocketClient
implementations use their respective URI.AbsoluteUri
as the message subject, while IJsonStream
implementations use the stream name.
public sealed class JsonMessageEventArgs
{
public string Subject { get; }
public string Json { get; }
}
NOTE: When using combined streams, the IWebSocketClient
message is the raw
JSON before being separated into stream name and data.
This is the IJsonProducer
interface to the Binance REST API using a single HttpClient
that returns 'raw' JSON. The specific methods are implemented as extensions in BinanceHttpClientExtensions
. New capabilities can be added as extensions of IBinanceHttpClient
in a similar fashion (at the application level) utilizing the shared, singleton, BinanceHttpClient
.
The top-level IBinanceAPI
interface deserializes the JSON data from IBinanceHttpClient
into convenient domain value-objects. All of the deserializers used in BinanceApi
are available in the Binance.Serialization
namespace for application-level deserialization of 'raw' Binance JSON data (for instance, if stored for back-testing/simulation).
This defines an abstract, streaming, JSON producer. Streaming is controlled by calling the StreamAsync
method and by providing a required CancellationToken
to abort the streaming operation. The stream is 'closed' when the Task
has completed (and IsStreaming
is false
). There is no Error
event because exceptions can and should be caught with try/catch (when using async
) or with the AggregateException
via the returned Task
.
NOTE: Users of this library are expected to be familiar with TAP.
NOTE: The IJsonStream
implementations handle subscribe/unsubscribe of streams whether streaming is active or not. However (depending on the implementation), if changes are made while streaming, the flow of data may be interrupted (this is the case with BinanceWebSocketStream
since the web socket client must be aborted and another connection established with a new URI).
public interface IJsonStream : IJsonProducer
{
bool IsStreaming { get; }
Task StreamAsync(CancellationToken token);
}
This IJsonProducer
decorator buffers data from another JSON producer.
public interface IBufferedJsonProducer<TProducer> : IJsonProducer
where TProducer : IJsonProducer
{
TProducer JsonProducer { get; }
}
This interface defines a specific implementation of IJsonStream
that uses a 'low-level' IWebSocketClient
to provide JSON data, which can be accessed via the WebSocket
property. The extending IBinanceWebSocketStream
interface provides an additional IsCombined
property.
NOTE: An IWebSocketStream
is not an IWebSocketClient
. The IWebSocketStream
is a specific form of IJsonStream
that uses an IWebSocketClient
. The IWebSocketClient.StreamAsync()
method combines connect with receive operations and returns a Task
, in-part, controlled by the IJsonStream
. The Open
event is raised after a connection is established. The Close
event is raised just before the Task
completes. Stream controllers need only manage the StreamAsync()
returned Task
and not concern themselves with the implementation details of the IJsonStream
(i.e. the Open
and Close
events).
public interface IWebSocketClient : IJsonProvider
{
event EventHandler<EventArgs> Open;
event EventHandler<EventArgs> Close;
Task StreamAsync(Uri uri, CancellationToken token);
}
public interface IWebSocketStream : IJsonStream
{
IWebSocketClient WebSocket { get; }
}
public interface IBinanceWebSocketStream : IWebSocketStream
{
bool IsCombined { get; }
}
This defines a JSON publisher. New message notification is sent to subscribed observers or Message
event handlers, where the message subject is the stream name. When subscribing to a stream, the subscriber is optional (if using an event handler instead). The PublishedStreams
property lists the unique stream names that are currently subscribed.
NOTE: The IJsonStream
implementations handle subscribe/unsubscribe of streams whether streaming is active or not. However (depending on the implementation), if changes are made while streaming, the flow of data may be interrupted (this is the case with BinanceWebSocketStream
since the web socket client must be aborted and another connection established with a new URI).
public interface IJsonPublisher : IJsonProducer
{
IEnumerable<string> PublishedStreams { get; }
IJsonPublisher Subscribe(IJsonSubscriber subscriber, params string[] streamNames);
IJsonPublisher Unsubscribe(IJsonSubscriber subscriber, params string[] streamNames);
This interface provides a means of identifying classes as observers of a IJsonStream
as passed to the subscribe/unsubscribe methods as well as providing the ability to handle message events. Any class can handle the IJsonProducer.Message
event, but that handler will receive all 'raw' JSON messages (depending on context of the event).
public interface IJsonSubscriber
{
IEnumerable<string> SubscribedStreams { get; }
IJsonSubscriber Unsubscribe();
void HandleMessage(string stream, string json);
}
These JSON stream subscribers have the capability of deserializing JSON into higher-level objects and providing notification via subscribed callbacks and/or non-specific events. Each Binance implementation is responsible for interpreting JSON from one or more of the official web socket endpoints.
public interface IJsonClient : IJsonSubscriber
{ }
NOTE: As JSON subscribers, the clients, can be used to process JSON from any compatible IJsonProducer
implementation.
Processes JSON data from Aggregate Trade streams. When subscribing or unsubscribing, the callback is optional (extension methods are available).
public interface IAggregateTradeClient : IJsonClient
{
event EventHandler<AggregateTradeEventArgs> AggregateTrade;
IAggregateTradeClient Subscribe(string symbol, Action<AggregateTradeEventArgs> callback);
IAggregateTradeClient Unsubscribe(string symbol, Action<AggregateTradeEventArgs> callback);
}
Processes JSON data from Trade streams. When subscribing or unsubscribing, the callback is optional (extension methods are available).
public interface ITradeClient : IJsonClient
{
event EventHandler<TradeEventArgs> Trade;
ITradeClient Subscribe(string symbol, Action<TradeEventArgs> callback);
ITradeClient Unsubscribe(string symbol, Action<TradeEventArgs> callback);
}
Processes JSON data from Candlestick streams. When subscribing or unsubscribing, the callback is optional (extension methods are available).
public interface ICandlestickClient : IJsonClient
{
event EventHandler<CandlestickEventArgs> Candlestick;
ICandlestickClient Subscribe(string symbol, CandlestickInterval interval, Action<CandlestickEventArgs> callback);
ICandlestickClient Unsubscribe(string symbol, CandlestickInterval interval, Action<CandlestickEventArgs> callback);
}
Processes JSON data from Symbol Ticker streams and All Market Tickers stream. When subscribing or unsubscribing, the callback is optional (extension methods are available).
public interface ISymbolStatisticsClient : IJsonClient
{
event EventHandler<SymbolStatisticsEventArgs> StatisticsUpdate;
ISymbolStatisticsClient Subscribe(Action<SymbolStatisticsEventArgs> callback, params string[] symbols);
ISymbolStatisticsClient Unsubscribe(Action<SymbolStatisticsEventArgs> callback, params string[] symbols);
}
Processes JSON data from Partial Book streams and Diff. Depth streams. The partial depth stream is used if a limit is specified. Currently, the only valid values for the partial book depth stream are: 5, 10, or 20. When subscribing or unsubscribing, the callback is optional (extension methods are available).
public interface IDepthClient : IJsonClient
{
event EventHandler<DepthUpdateEventArgs> DepthUpdate;
IDepthClient Subscribe(string symbol, int limit, Action<DepthUpdateEventArgs> callback);
IDepthClient Unsubscribe(string symbol, int limit, Action<DepthUpdateEventArgs> callback);
}
Processes JSON data from User Data streams. When subscribing or unsubscribing, the callback is optional (extension methods are available).
public interface IUserDataClient : IJsonClient
{
event EventHandler<AccountUpdateEventArgs> AccountUpdate;
event EventHandler<OrderUpdateEventArgs> OrderUpdate;
event EventHandler<AccountTradeUpdateEventArgs> TradeUpdate;`
IUserDataClient Subscribe<TEventArgs>(string listenKey, IBinanceApiUser user, Action<TEventArgs> callback)
where TEventArgs : UserDataEventArgs;
IUserDataClient Unsubscribe<TEventArgs>(string listenKey, Action<TEventArgs> callback)
where TEventArgs : UserDataEventArgs;
void HandleListenKeyChange(string oldListenKey, string newListenKey);
These classes add an in-memory, thread-safe, cache extending the IJsonClient
interface. After an event is received from the IJsonClient
the cache is updated and a translated Update
event is fired (and callbacks are notified).
NOTE: IJsonClientCache
has an IJsonClient
and is an IJsonClient
. The Client
can be set and, if previously subscribed, the cache will automatically unsubscribe (its context) from the existing client and subscribe (the cache context) to the new client.
NOTE: These classes do not include a JSON stream or stream controller.
public interface IJsonClientCache<TClient, TEventArgs> : IJsonClient
where TClient : IJsonClient
where TEventArgs : CacheEventArgs
{
event EventHandler<TEventArgs> Update;
TClient Client { get; set; }
}
public abstract class CacheEventArgs : EventArgs
{ }
An IJsonClientCache
that keeps an in-memory copy of the latest account info updated by an IUserDataClient
.
public interface IAccountInfoCache
: IJsonClientCache<IUserDataWebSocketManager, AccountInfoCacheEventArgs>
{
AccountInfo AccountInfo { get; }
void Subscribe(string listenKey, IBinanceApiUser user, Action<AccountInfoCacheEventArgs> callback);
}
An IJsonClientCache
that loads the latest aggregate trades using IBinanceApi
and keeps an in-memory list of the latest aggregate trades updated by an IAggregateTradeClient
. When a new aggregate trade is received the oldest is removed from the in-memory list.
public interface IAggregateTradeCache
: IJsonClientCache<IAggregateTradeClient, AggregateTradeCacheEventArgs>
{
event EventHandler<EventArgs> OutOfSync;
IEnumerable<AggregateTrade> Trades { get; }
void Subscribe(string symbol, int limit, Action<AggregateTradeCacheEventArgs> callback);
An IJsonClientCache
that loads the latest candlesticks using IBinanceApi
and keeps an in-memory list of the latest candlesticks updated by an ICandlestickClient
. When the latest candlestick is closed and a new candlestick is received the oldest candlestick is removed from the in-memory list.
public interface ICandlestickCache
: IJsonClientCache<ICandlestickClient, CandlestickCacheEventArgs>
{
IEnumerable<Candlestick> Candlesticks { get; }
void Subscribe(
string symbol, CandlestickInterval interval, int limit,
Action<CandlestickCacheEventArgs> callback);
}
An IJsonClientCache
that loads the latest order book using IBinanceApi
and keeps an in-memory OrderBook
updated by an IDepthClient
.
public interface IOrderBookCache
: IJsonClientCache<IDepthClient, OrderBookCacheEventArgs>
{
event EventHandler<EventArgs> OutOfSync;
OrderBook OrderBook { get; }
void Subscribe(string symbol, int limit, Action<OrderBookCacheEventArgs> callback);
}
An IJsonClientCache
that loads the latest 24-hour statistics using IBinanceApi
for a symbol or symbols and keeps an in-memory array updated by an ISymbolStatisticsClient
.
public interface ISymbolStatisticsCache
: IJsonClientCache<ISymbolStatisticsClient, SymbolStatisticsCacheEventArgs>
{
IEnumerable<SymbolStatistics> Statistics { get; }
SymbolStatistics GetStatistics(string symbol);
IEnumerable<SymbolStatistics> GetStatistics(params string[] symbols);
void Subscribe(Action<SymbolStatisticsCacheEventArgs> callback);
void Subscribe(Action<SymbolStatisticsCacheEventArgs> callback, params string[] symbols);
}
An IJsonClientCache
that loads the latest trades using IBinanceApi
and keeps an in-memory list of the latest trades updated by an ITradeClient
. When a new trade is received the oldest is removed from the in-memory list.
public interface ITradeCache
: IJsonClientCache<ITradeClient, TradeCacheEventArgs>
{
event EventHandler<EventArgs> OutOfSync;
IEnumerable<Trade> Trades { get; }
void Subscribe(string symbol, int limit, Action<TradeCacheEventArgs> callback);
}
This is the first interface to combine the behavior of a JSON producer and consumer. However, as with IJsonClient
, these classes do not provide a stream controller; an external controller is required to call Stream.StreamAsync()
and manage the returned Task
. The TaskController
and RetryTaskController
utility classes are available for this purpose, but are not required.
public interface IJsonPublisherClient<out TPublisher> : IJsonClient
where TPublisher : IJsonPublisher
{
TPublisher Publisher { get; }
}
This interface extends IJsonPublisherClient
and uses a web socket stream to provide JSON data. Implementations are responsible for translating application-level arguments into a Binance stream name and coordinating the subscribing of that stream name between the JSON stream (producer) and client (consumer).
The specific implementations are used like their JSON client counterparts, but include automatic stream/controller/task management. A watchdog timer is added to abort streaming if data has not been received for a configurable time interval (default 1 hour). If a timeout occurs and streaming is aborted, streaming will be restarted by the IRetryTaskController
after a configurable delay. Internal exceptions are accessible from the Error
event.
public interface IWebSocketPublisherClient : IJsonPublisherClient<IAutoJsonStreamPublisher<IWebSocketStream>>, IError
{ }
An IAggregateTradeClient
that uses a IBinanceWebSocketStream
.
public interface IAggregateTradeWebSocketClient
: IAggregateTradeClient, IBinanceWebSocketClient
{ }
An ITradeClient
that uses a IBinanceWebSocketStream
.
public interface ITradeWebSocketClient
: ITradeClient, IBinanceWebSocketClient
{ }
An ICandlestickWebSocketClient
that uses a IBinanceWebSocketStream
.
public interface ICandlestickWebSocketClient
: ICandlestickClient, IBinanceWebSocketClient
{ }
An ISymbolStatisticsWebSocketClient
that uses a IBinanceWebSocketStream
.
public interface ISymbolStatisticsWebSocketClient
: ISymbolStatisticsClient, IBinanceWebSocketClient
{ }
An IDepthClient
that uses a IBinanceWebSocketStream
.
public interface IDepthWebSocketClient
: IDepthClient, IBinanceWebSocketClient
{ }
An IUserDataClient
that uses a IBinanceWebSocketStream
.
public interface IUserDataWebSocketClient
: IUserDataClient, IBinanceWebSocketClient
{ }
A manager that uses a IBinanceWebSocketStream
. This interface does not extend IUserDataClient
(future implementations might). The implementation also encapsulates user listen key management with an IUserDataWebSocketStreamControl
.
public interface IUserDataWebSocketManager : IControllerManager<IWebSocketStream>
{
event EventHandler<AccountUpdateEventArgs> AccountUpdate;
event EventHandler<OrderUpdateEventArgs> OrderUpdate;
event EventHandler<AccountTradeUpdateEventArgs> TradeUpdate;
Task SubscribeAsync<TEventArgs>(IBinanceApiUser user, Action<TEventArgs> callback,
CancellationToken token = default)
where TEventArgs : UserDataEventArgs;
Task UnsubscribeAsync<TEventArgs>(IBinanceApiUser user, Action<TEventArgs> callback,
CancellationToken token = default)
where TEventArgs : UserDataEventArgs;
Task UnsubscribeAllAsync(CancellationToken token = default);
This interface handles user data web socket open, keep-alive, and close API calls and management of user listen keys. When a user is added, a listen key is queried and returned. An internal timer is used to keep-alive each user data stream with a ping every 30 minutes (default). If the ping fails, a new key is queried and listeners are notified via the ListenKeyUpdate
event.
public interface IUserDataWebSocketStreamControl : IDisposable
{
event EventHandler<UserDataListenKeyUpdateEventArgs> ListenKeyUpdate;
IEnumerable<IBinanceApiUser> Users { get; }
TimeSpan KeepAliveTimerPeriod { get; set; }
Task<string> GetStreamNameAsync(IBinanceApiUser user, CancellationToken token = default);
Task<string> OpenStreamAsync(IBinanceApiUser user, CancellationToken token = default);
Task CloseStreamAsync(IBinanceApiUser user, CancellationToken token = default);
Task CloseAllStreamsAsync(CancellationToken token = default);
}
- Verify connection to the Binance server (minimal examples).
- Get the market depth (order book) for a symbol.
- Maintain a real-time order book cache for a symbol.
- Get the aggregate trades for a symbol.
- Maintain a real-time trade history cache for a symbol.
- Get the candlesticks for a symbol.
- Maintain a real-time price chart cache for a symbol.
- Get the 24-hour statistics for a symbol.
- Get current prices for all symbols for a price ticker.
- Get best price and quantity on the order book for all symbols.
- Get a list of all current symbols.
- Place a LIMIT order.
- Place a MARKET order.
- Place a TEST order to verify client order properties.
- Look-up an existing order to check status.
- Cancel an open order.
- Get all open orders for a symbol.
- Get all orders for a symbol.
- Get current account information.
- Get account trades for a symbol.
- Submit a withdraw request.
- Get deposit history.
- Get withdraw history.
- Donate BTC to the creator of this library.