Table of Contents

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:

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:

  1. Converts incoming messages into commands of a specific trading system.
  2. 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:

  1. Pass the transaction ID generator that will be used to create message IDs.

  2. Specify the supported message types using the methods:

  1. Specify the specific types of market data supported by the adapter using the AddSupportedMarketDataType method.

  2. 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: