Creating Your Own Connector
The messaging mechanism is an internal logical layer of the StockSharp architecture, which provides interaction between various platform elements using a standard protocol.
There are two main classes:
- Message - a message carrying information.
- AsyncMessageAdapter - a message adapter (=converter).
A message acts as an agent that transmits information. Messages have their own type MessageTypes. Each message type corresponds to a specific class. In turn, all message classes inherit from the abstract class Message, which endows descendants with properties such as message type Message.Type and Message.LocalTime - the local time of message creation/receipt.
Messages can be incoming and outgoing:
- Incoming messages - messages that are sent to an external system. Usually, these are commands generated by the program, for example, the ConnectMessage message - a command requesting a connection to the server.
- Outgoing messages - messages coming from an external system. These are messages that transmit information about market data, transactions, portfolios, connection events, etc. For example, the QuoteChangeMessage message transmits information about changes in the order book.
The message adapter plays the role of an intermediary between the trading system and the program. For each type of connector, there is a separate adapter class that inherits from the abstract class AsyncMessageAdapter.
The adapter performs two main functions:
- Converts incoming messages into commands of a specific trading system.
- Converts information received from the trading system (connection, market data, transactions, etc.) into outgoing messages.
Below is a description of the process of creating your own adapter for Coinbase (all connectors with source code are available in the StockSharp repository and are provided as a tutorial).
Example of Creating a Coinbase Message Adapter
1. Creating an Adapter Class
First, we create the CoinbaseMessageAdapter message adapter class, inherited from the abstract class AsyncMessageAdapter.
public partial class CoinbaseMessageAdapter : AsyncMessageAdapter
{
private Authenticator _authenticator;
private HttpClient _restClient;
private SocketClient _socketClient;
// Other adapter fields and properties
}
2. Adapter Constructor
In the adapter constructor, you need to perform the following actions:
Pass the transaction ID generator that will be used to create message IDs.
Specify the supported message types using the methods:
- AddMarketDataSupport - support for messages to subscribe to market data.
- AddTransactionalSupport - support for transactional messages.
Specify the specific types of market data supported by the adapter using the AddSupportedMarketDataType method.
Specify the types of resulting messages that will be supported by the adapter using the AddSupportedResultMessage method. Message types such as SecurityLookupMessage, PortfolioLookupMessage, OrderStatusMessage, etc. request information from the connector and expect corresponding response messages.
public CoinbaseMessageAdapter(IdGenerator transactionIdGenerator)
: base(transactionIdGenerator)
{
HeartbeatInterval = TimeSpan.FromSeconds(5);
// Add support for market data and transactions
this.AddMarketDataSupport();
this.AddTransactionalSupport();
// Remove unsupported message types
this.RemoveSupportedMessage(MessageTypes.Portfolio);
this.RemoveSupportedMessage(MessageTypes.OrderGroupCancel);
// Add supported market data types
this.AddSupportedMarketDataType(DataType.Ticks);
this.AddSupportedMarketDataType(DataType.MarketDepth);
this.AddSupportedMarketDataType(DataType.Level1);
this.AddSupportedMarketDataType(DataType.CandleTimeFrame);
// Add supported resulting messages
this.AddSupportedResultMessage(MessageTypes.SecurityLookup);
this.AddSupportedResultMessage(MessageTypes.PortfolioLookup);
this.AddSupportedResultMessage(MessageTypes.OrderStatus);
}
3. Connecting and Disconnecting the Adapter
To connect the adapter to the trading system, the AsyncMessageAdapter.ConnectAsync method is called. It is passed the incoming ConnectMessage message. If the connection is successful, the adapter sends an outgoing ConnectMessage message.
public override async ValueTask ConnectAsync(ConnectMessage connectMsg, CancellationToken cancellationToken)
{
// Check the presence of keys for transactional mode
if (this.IsTransactional())
{
if (Key.IsEmpty())
throw new InvalidOperationException(LocalizedStrings.KeyNotSpecified);
if (Secret.IsEmpty())
throw new InvalidOperationException(LocalizedStrings.SecretNotSpecified);
}
// Initialize the authenticator
_authenticator = new(this.IsTransactional(), Key, Secret, Passphrase);
// Check that clients are not yet created
if (_restClient != null)
throw new InvalidOperationException(LocalizedStrings.NotDisconnectPrevTime);
if (_socketClient != null)
throw new InvalidOperationException(LocalizedStrings.NotDisconnectPrevTime);
// Create REST client
_restClient = new(_authenticator) { Parent = this };
// Create and configure WebSocket client
_socketClient = new(_authenticator, ReConnectionSettings.ReAttemptCount) { Parent = this };
SubscribePusherClient();
// Connect WebSocket client
await _socketClient.Connect(cancellationToken);
// Send successful connection message
SendOutMessage(new ConnectMessage());
}
To disconnect the adapter from the trading system, the AsyncMessageAdapter.DisconnectAsync method is called. If the disconnection is successful, the adapter sends an outgoing DisconnectMessage message.
public override ValueTask DisconnectAsync(DisconnectMessage disconnectMsg, CancellationToken cancellationToken)
{
// Check that clients are created
if (_restClient == null)
throw new InvalidOperationException(LocalizedStrings.ConnectionNotOk);
if (_socketClient == null)
throw new InvalidOperationException(LocalizedStrings.ConnectionNotOk);
// Free REST client resources
_restClient.Dispose();
_restClient = null;
// Disconnect WebSocket client
_socketClient.Disconnect();
// Send disconnection message
SendOutDisconnectMessage(true);
return default;
}
In addition, the adapter provides the AsyncMessageAdapter.ResetAsync method to reset the state, which closes the connection and returns the adapter to its initial state.
public override ValueTask ResetAsync(ResetMessage resetMsg, CancellationToken cancellationToken)
{
// Free REST client resources
if (_restClient != null)
{
try
{
_restClient.Dispose();
}
catch (Exception ex)
{
SendOutError(ex);
}
_restClient = null;
}
// Disconnect and clear WebSocket client
if (_socketClient != null)
{
try
{
UnsubscribePusherClient();
_socketClient.Disconnect();
}
catch (Exception ex)
{
SendOutError(ex);
}
_socketClient = null;
}
// Free authenticator resources
if (_authenticator != null)
{
try
{
_authenticator.Dispose();
}
catch (Exception ex)
{
SendOutError(ex);
}
_authenticator = null;
}
// Clear additional data
_candlesTransIds.Clear();
// Send reset message
SendOutMessage(new ResetMessage());
return default;
}
This document describes the general principles of the adapter's operation, its creation, and management of the connection with the trading system. The following documents will be devoted to the implementation of the adapter's functionality: