Criando Seu Próprio Conector

O mecanismo de mensagens é uma camada lógica interna da arquitetura StockSharp, que fornece a interação entre vários elementos da plataforma usando um protocolo padrão.

Existem duas classes principais:

Uma mensagem atua como um agente que transmite informações. As mensagens têm seu próprio tipo MessageTypes. Cada tipo de mensagem corresponde a uma classe específica. Por sua vez, todas as classes de mensagens herdam da classe abstrata Message, que dota os descendentes de propriedades como o tipo de mensagem Message.Type e Message.LocalTime - a hora local de criação/recebimento da mensagem.

As mensagens podem ser de entrada e de saída:

  • Mensagens de entrada - mensagens que são enviadas para um sistema externo. Geralmente, são comandos gerados pelo programa, por exemplo, a mensagem ConnectMessage - um comando que solicita uma conexão com o servidor.
  • Mensagens de saída - mensagens provenientes de um sistema externo. São mensagens que transmitem informações sobre dados de mercado, transações, portfólios, eventos de conexão, etc. Por exemplo, a mensagem QuoteChangeMessage transmite informações sobre mudanças no livro de ofertas.

O adaptador de mensagens desempenha o papel de intermediário entre o sistema de negociação e o programa. Para cada tipo de conector, existe uma classe adaptadora separada que herda da classe abstrata AsyncMessageAdapter.

O adaptador executa duas funções principais:

  1. Converte mensagens de entrada em comandos de um sistema de negociação específico.
  2. Converte informações recebidas do sistema de negociação (conexão, dados de mercado, transações, etc.) em mensagens de saída.

Abaixo está uma descrição do processo de criação de seu próprio adaptador para o Coinbase (todos os conectores com código-fonte estão disponíveis no repositório StockSharp e são fornecidos como um tutorial).

Exemplo de Criação de um Adaptador de Mensagens Coinbase

1. Criando uma Classe de Adaptador

Primeiro, criamos a classe adaptadora de mensagens CoinbaseMessageAdapter, herdada da classe abstrata AsyncMessageAdapter.

public partial class CoinbaseMessageAdapter : AsyncMessageAdapter
{
	private Authenticator _authenticator;
	private HttpClient _restClient;
	private SocketClient _socketClient;

	// Outros campos e propriedades do adaptador
}

2. Construtor do Adaptador

No construtor do adaptador, você precisa executar as seguintes ações:

  1. Passar o gerador de ID de transação que será usado para criar IDs de mensagem.

  2. Especificar os tipos de mensagem suportados usando os métodos:

  1. Especificar os tipos específicos de dados de mercado suportados pelo adaptador usando o método AddSupportedMarketDataType.
public CoinbaseMessageAdapter(IdGenerator transactionIdGenerator)
	: base(transactionIdGenerator)
{
	HeartbeatInterval = TimeSpan.FromSeconds(5);

	// Adicionar suporte a dados de mercado e transações
	this.AddMarketDataSupport();
	this.AddTransactionalSupport();

	// Remover tipos de mensagem não suportados
	this.RemoveSupportedMessage(MessageTypes.Portfolio);
	this.RemoveSupportedMessage(MessageTypes.OrderGroupCancel);

	// Adicionar tipos de dados de mercado suportados
	this.AddSupportedMarketDataType(DataType.Ticks);
	this.AddSupportedMarketDataType(DataType.MarketDepth);
	this.AddSupportedMarketDataType(DataType.Level1);
	this.AddSupportedMarketDataType(DataType.CandleTimeFrame);
}

3. Conectando e Desconectando o Adaptador

Para conectar o adaptador ao sistema de negociação, o método AsyncMessageAdapter.ConnectAsync é chamado. A ele é passada a mensagem de entrada ConnectMessage. Se a conexão for bem-sucedida, o adaptador envia uma mensagem de saída ConnectMessage.

public override async ValueTask ConnectAsync(ConnectMessage connectMsg, CancellationToken cancellationToken)
{
	// Verificar a presença de chaves para o modo transacional
	if (this.IsTransactional())
	{
		if (Key.IsEmpty())
			throw new InvalidOperationException(LocalizedStrings.KeyNotSpecified);

		if (Secret.IsEmpty())
			throw new InvalidOperationException(LocalizedStrings.SecretNotSpecified);
	}

	// Inicializar autenticador
	_authenticator = new(this.IsTransactional(), Key, Secret, Passphrase);

	// Verificar que os clientes ainda não foram criados
	if (_restClient != null)
		throw new InvalidOperationException(LocalizedStrings.NotDisconnectPrevTime);

	if (_socketClient != null)
		throw new InvalidOperationException(LocalizedStrings.NotDisconnectPrevTime);

	// Criar cliente REST
	_restClient = new(_authenticator) { Parent = this };

	// Criar e configurar cliente WebSocket
	_socketClient = new(_authenticator, ReConnectionSettings.ReAttemptCount) { Parent = this };
	SubscribePusherClient();

	// Conectar cliente WebSocket
	await _socketClient.Connect(cancellationToken);

	// Enviar mensagem de conexão bem-sucedida
	SendOutMessage(new ConnectMessage());
}

Para desconectar o adaptador do sistema de negociação, o método AsyncMessageAdapter.DisconnectAsync é chamado. Se a desconexão for bem-sucedida, o adaptador envia uma mensagem de saída DisconnectMessage.

public override ValueTask DisconnectAsync(DisconnectMessage disconnectMsg, CancellationToken cancellationToken)
{
	// Verificar que os clientes foram criados
	if (_restClient == null)
		throw new InvalidOperationException(LocalizedStrings.ConnectionNotOk);

	if (_socketClient == null)
		throw new InvalidOperationException(LocalizedStrings.ConnectionNotOk);

	// Liberar recursos do cliente REST
	_restClient.Dispose();
	_restClient = null;

	// Desconectar cliente WebSocket
	_socketClient.Disconnect();

	// Enviar mensagem de desconexão
	SendOutDisconnectMessage(true);
	return default;
}

Além disso, o adaptador fornece o método AsyncMessageAdapter.ResetAsync para redefinir o estado, que fecha a conexão e retorna o adaptador ao seu estado inicial.

public override ValueTask ResetAsync(ResetMessage resetMsg, CancellationToken cancellationToken)
{
	// Liberar recursos do cliente REST
	if (_restClient != null)
	{
		try
		{
			_restClient.Dispose();
		}
		catch (Exception ex)
		{
			SendOutError(ex);
		}

		_restClient = null;
	}

	// Desconectar e limpar cliente WebSocket
	if (_socketClient != null)
	{
		try
		{
			UnsubscribePusherClient();
			_socketClient.Disconnect();
		}
		catch (Exception ex)
		{
			SendOutError(ex);
		}

		_socketClient = null;
	}

	// Liberar recursos do autenticador
	if (_authenticator != null)
	{
		try
		{
			_authenticator.Dispose();
		}
		catch (Exception ex)
		{
			SendOutError(ex);
		}

		_authenticator = null;
	}

	// Limpar dados adicionais
	_candlesTransIds.Clear();

	// Enviar mensagem de redefinição
	SendOutMessage(new ResetMessage());
	return default;
}

Após a Conclusão do Desenvolvimento

Assim que o conector estiver implementado, existem duas opções para usá-lo:

  1. Publicá-lo na StockSharp Store como um produto pago ou gratuito. Nesse caso, os usuários instalam o conector automaticamente através do Installer.

  2. Para uso pessoal, copie o arquivo .dll do conector compilado para a pasta de sua aplicação (ou qualquer produto StockSharp). Na inicialização, a aplicação verifica o diretório atual em busca de adaptadores usando os seguintes critérios:

    • Somente arquivos com a extensão .dll cujos nomes comecem com StockSharp. são considerados.
    • Cada arquivo restante é verificado para garantir que seja um assembly .NET válido.
    • O assembly é carregado e todos os tipos que implementam IMessageAdapter são coletados.
    • Quaisquer erros encontrados durante a verificação ou o carregamento são gravados no log e não interrompem a busca. Se o carregamento falhar, abra a janela de log da aplicação ou o arquivo de log para ver os detalhes do erro.

Este documento descreve os princípios gerais de funcionamento do adaptador, sua criação e o gerenciamento da conexão com o sistema de negociação. Os documentos a seguir serão dedicados à implementação da funcionalidade do adaptador: