Click or drag to resize

Adapters

The message mechanism allows creating own connections to any external trading system. To do that, it is necessary to create an own class of the message adapter, inherited from the abstract class MessageAdapter.

The following task shall be solved at development of own message adapter:

  1. Write the code, converting outgoing messages S# into external system commands.

  2. Write the code, converting information from the external system into incoming messages.

  3. Perform conversion of encoded information from the external system (codes of instruments and boards, listings etc.) into types of S#.

  4. Perform additional adjustments, related with the external system features.

Prior to starting description of the own adapter development, let's consider the process of creation and processing of incoming and outgoing messages in the S# by the example of the message ConnectMessage. Supposing, that the program called the Connect method, then the following will happen in the base class Connector:

  1. The protected method ConnectorOnConnect, is called, in which the message is created and transmitted to the method ConnectorSendInMessage(Message).

    C#
    protected virtual void OnConnect()
    {
        SendInMessage(new ConnectMessage());
    }
  2. In the method ConnectorSendInMessage(Message) the message is sent to the adapter method with the same name.

    C#
    public void SendInMessage(Message message)
    {
        _inAdapter.SendInMessage(message);
    }
  3. In the adapter method MessageAdapterSendInMessage(Message) additional checks are performed. If everything is OK, then the message is sent to the method MessageAdapterOnSendInMessage(Message) (see below). If error was generated, a new incoming message of the similar type is created, the exception object is transferred into the message's Error feature. This new message is sent to the SendOutMessage(Message) method, in which event will be generated for occurrence of new incoming message NewOutMessage, signaling on error.

Incoming messages are created using the MessageAdapterSendOutMessage(Message) method, where the message object is sent to. In this method the event of new incoming message NewOutMessage is generated. Next, this event is processed in the base class of the connecter on the protected method ConnectorOnProcessMessage(Message) where, depending on situation, the message is converted into the corresponding S# type, and connector event is generated, also, additional outgoing messages can be created.

Below see description of own adapter creation process for Interactive Brokers.

Example of Interactive Brokers message adapter creating

  1. Creating an adapter class.

    First, we create the InteractiveBrokersAdapter message adapter class, inherited from the abstract MessageAdapter. class:

    C#
    public class InteractiveBrokersAdapter : MessageAdapter 
    {
    }
  2. Adapter constructor.

    C#
    public InteractiveBrokersAdapter(IdGenerator transactionIdGenerator)
        : base(transactionIdGenerator)
    {
        this.AddSupportedMessage(MessageTypes.MarketData);
        this.AddSupportedMessage(MessageTypes.SecurityLookup);
    
        this.AddSupportedMessage(MessageTypes.OrderCancel);
        this.AddSupportedMessage(MessageTypes.OrderGroupCancel);
        this.AddSupportedMessage(MessageTypes.OrderPairReplace);
        this.AddSupportedMessage(MessageTypes.OrderRegister);
        this.AddSupportedMessage(MessageTypes.OrderReplace);
        // this.AddSupportedMessage(MessageTypes.OrderStatus);
        this.AddSupportedMessage(MessageTypes.Portfolio);
        this.AddSupportedMessage(MessageTypes.PortfolioLookup);
        this.AddSupportedMessage(MessageTypes.Position);
    }
  3. Adapter properties.

    We override the IsSupportNativePortfolioLookup and IsSupportNativeSecurityLookup properties. Because Interactive Brokers supports searching for portfolios and instruments, we make the properties return ‘true’. Also add properties for the login, password, address and port of the Interactive Brokers server.

    C#
    protected override bool IsSupportNativePortfolioLookup
    {
        get { return true; }
    }
    
    protected override bool IsSupportNativeSecurityLookup
    {
        get { return true; }
    }
  4. The OnSendInMessage(Message) method.

    Next, you need to override the OnSendInMessage(Message). method. As mentioned above, all outgoing messages are passed to this method and for each type of message you need to write code that converts messages into Interactive Brokers commands.

    When the MessageTypesReset message is received it is necessary to perform a "zeroing" of the state and free up resources. Upon completion of these operations, you need to send outgoing message ResetMessage.

    When the MessageTypesConnect message arrives, we initialize the _ibSocket variable, subscribe to Interactive Brokers events and establish a connection using the Connect method of the native API. In case of successful connection, the IBSocket.Connected event should occur where reverse MessageTypesConnect instance should be generated.

    C#
    protected override void OnSendInMessage(Message message)
    {
        switch (message.Type)
        {
             case MessageTypes.Reset:
             {
                  _lookupSecuritiesId = 0;
                  _lookupPortfoliosId = 0;
    
                  if (_ibSocket != null)
                  {
                      try
                      {
                          DisposeSocket();
                      }
                      catch (Exception ex)
                      {
                          SendOutError(ex);
                      }
    
                      _ibSocket = null;
                  }
    
                  SendOutMessage(new ResetMessage());
    
                  break;
            }
    
            case MessageTypes.Connect:
            {
                if (_ibSocket != null)
                    throw new InvalidOperationException("_ibSocket != null");
    
                _ibSocket = new IBSocket();
    
                _ibSocket.Connect(Address, Port);
    
                break;
            }
    
            case MessageTypes.Disconnect:
            {
                if (_ibSocket == null)
                    throw new InvalidOperationException(LocalizedStrings.Str1856);
    
                _ibSocket.disconnect();
    
                break;
            }
    
            case MessageTypes.OrderRegister:
                // TODO
                break;
    
            case MessageTypes.OrderCancel:
                // TODO
                break;
    
            case MessageTypes.OrderGroupCancel:
                 // TODO
                 break;
    
            case MessageTypes.OrderReplace:
                 // TODO
                 break;
    
            case MessageTypes.Portfolio:
                 // TODO
                 break;
    
            case MessageTypes.PortfolioLookup:
                 ProcessPortolioLookupMessage((PortfolioLookupMessage)message);
                 break;
    
            case MessageTypes.MarketData:
                 ProcessMarketDataMessage((MarketDataMessage)message);
                 break;
    
            case MessageTypes.SecurityLookup:
                 ProcessSecurityLookupMessage((SecurityLookupMessage)message);
                 break;
        }
    }
  5. The events.

    In the native API connection event handler, you must send the outgoing ConnectMessage. message. When processing this message in the code of the base Connector class, the values of the Boolean properties will be checked:

    If ‘true’, then the corresponding message will be sent. By default, the values of these properties depend on the availability of the corresponding message type in the list of supported messages. In our example (see Adapter constructor) the MessageTypesSecurityLookup and MessageTypesPortfolioLookup, types were added to this list, so you should expect to receive outbound messages SecurityLookupMessage and PortfolioLookupMessage.

    C#
    private void IBSocket_Connected()
    {
        SendOutMessage(new ConnectMessage());
    }
  6. Outgoing messages PortfolioLookupMessage and SecurityLookupMessage.

    When you receive these messages, you should call Interactive Brokers, functions that request portfolios and instruments, respectively. Calling these functions will trigger IBSocket.NewPortfolio (portfolios) and IBSocket.NewSymbol (instruments) events.

    C#
    private void ProcessPortolioLookupMessage(PortfolioLookupMessage pfMsg)
    {
        if (_lookupPortfoliosId == 0)
        {
            _lookupPortfoliosId = pfMsg.TransactionId;
            _ibSocket.GetPrortfolioList(); 
        }
        else
            SendOutError(LocalizedStrings.Str1868);
    }
    
    private void ProcessSecurityLookupMessage(SecurityLookupMessage message)
    {
        if (_lookupSecuritiesId == 0)
        {
            _lookupSecuritiesId = message.TransactionId;
            _ibSocket.GetSymbols();
        }
        else
            SendOutError(LocalizedStrings.Str1854);
    }
  7. The IBSocket.NewPortfolio event.

    In the IBSocket.NewPortfolio event handler, the received portfolio information must be converted to the incoming PortfolioMessage. message. And after receiving all the portfolios, you need to send the incoming PortfolioLookupResultMessage message. Note that in the latter case, the OriginalTransactionId property must be set to the TransactionId value of the corresponding outgoing message.

    C#
    private void IBSocket_NewPortfolio(int row, int nrows, string portfolioName, string portfolioExch)
    {
        SendOutMessage(new PortfolioMessage
        {
            PortfolioName = portfolioName,
            BoardCode = PortfolioBoardCodes.TryGetValue(portfolioExch),
            ExtensionInfo = new Dictionary<object, object>
         });
    
         if ((row + 1) < nrows)
              return;
    
         SendOutMessage(new PortfolioLookupResultMessage { OriginalTransactionId = _lookupPortfoliosId });
         _lookupPortfoliosId = 0;
    }
  8. The IBSocket.NewSymbol event.

    In the IBSocket.NewSymbol event handler, the received information about the instrument must be converted to the incoming SecurityMessage message. And after receiving all the instruments, you need to send the incoming message SecurityLookupResultMessage. Note that the OriginalTransactionId property of both incoming messages must be set to the TransactionId value of the corresponding outgoing message.

    C#
    private void IBSocket_NewSymbol(int row, int rowCount, long contractId, string name, string secCode, string secClass, int decimals, int lotSize, double stepPrice, double priceStep,
                string isin, string board, System.DateTime expiryDate, double daysBeforeExpiry, double strike)
    {
        if (secCode.IsEmpty())
            secCode = contractId.To<string>();
    
        var securityId = new SecurityId
        {
            SecurityCode = secCode,
            BoardCode = board,
            NativeAsInt = contractId,
            Isin = isin
        };
    
        if (secClass.IsEmpty())
            secClass = board;
    
        DateTime? expDate = DateTime.FromOADate(0) == expiryDate ? (DateTime?)null : expiryDate;
    
        var secMsg = new SecurityMessage
        {
            PriceStep = priceStep.ToDecimal(),
            Decimals = decimals,
            Multiplier = lotSize,
            Name = name,
            ShortName = name,
            ExpiryDate = expDate == null ? (DateTimeOffset?)null : expDate.Value.ApplyTimeZone(TimeHelper.EST),
            ExtensionInfo = new Dictionary<object, object>
            {
                { "Class", secClass }
            },
            OriginalTransactionId = _lookupSecuritiesId
         };
    
         SendOutMessage(secMsg);
    }
  9. Registration/cancellation of registration of trades.

    When you call the RegisterTrades(Security) or UnRegisterTrades(Security) methods, the outgoing message MarketDataMessage MarketDataMessage will be received.

    When processing this message, you must call the Interactive Brokers methods registering/canceling the registration of trades receiving.

    Since the message is used to work with all types of market data, you need to use the DataType property to select a specific type. For trades, the value of this property is MarketDataTypesTrades.

    After calling the IBSocket.ListenTicks method, the trades will be received in the IBSocket.NewTick event.

    C#
    private void ProcessMarketDataMessage(MarketDataMessage mdMsg)
    {
        var contractId = mdMsg.SecurityId.NativeAsInt;
    
        switch (mdMsg.DataType)
        {
            case MarketDataTypes.Level1:
            {
                 //TODO
                 break;
            }
            case MarketDataTypes.MarketDepth:
            {
                 //TODO
                 break;
            }
            case MarketDataTypes.Trades:
            {
                if (mdMsg.From == null)
                {
                    if (mdMsg.IsSubscribe)
                        _ibSocket.ListenTicks(contractId);
                    else
                        _ibSocket.CancelTicks(contractId);
               }
               else
               {
                    //TODO
               }
    
               break;
            }
            case MarketDataTypes.CandleTimeFrame:
            {
                 //TODO
                 break;
            }
            default:
            {
                SendOutMarketDataNotSupported(mdMsg.TransactionId);
                return;
         }
    }
  10. The IBSocket.NewTick event.

    In the IBSocket.NewTick event handler, the received trade information must be converted to the incoming ExecutionMessage. message. Note that ExecutionMessage messages are used for both trades and orders. Therefore, the message states that it refers to the trade - ExecutionType = ExecutionTypesTick.

    C#
    private void IBSocket_NewTick(string contractId, System.DateTime time, double price, double volume, string tradeId)
    {
        SendOutMessage(CreateTrade(contractId, time, price.ToDecimal(), volume.ToDecimal(), tradeId.To<long>(), action));
    }
    
    private static ExecutionMessage CreateTrade(long contractId, DateTime time, decimal? price, decimal? volume, long tradeId)
    {
        return new ExecutionMessage
        {
             SecurityId = new SecurityId { NativeAsInt = contractId },
             TradeId = tradeId,
             TradePrice = price,
             Volume = volume,
             ServerTime = time.ApplyTimeZone(TimeHelper.EST),
             ExecutionType = ExecutionTypes.Tick
       };
    }