コンテンツにスキップ

メッセージの送受信

はじめに

この記事は、FIX APIを使用してSpotware cServerと対話することに興味のある方向けに、FIX APIの使用方法を紹介するものです。

この記事では、C#の例を用いて、FIXメッセージの構築方法、サーバーへの送信方法、応答の受信方法の原理を詳細に説明します。 この例は決して完璧なアプリケーションではなく、プログラマーがFIX APIメッセージの使用概念を容易に理解できるように、できるだけシンプルに保たれています。

サーバーとの適切な通信を確立・維持し、応答を適切に処理するためには、追加の機能が必要ですが、簡潔さと明確さを重視するため、ここでは省略しています。これらの主題については、今後の記事で扱う予定です。

コードサンプル

この記事で説明されているコードサンプルは、GitHubリポジトリで見つけることができます。

FIX通信の概要

FIXメッセージは、縦棒(|)で区切られた数値タグと値のセットで構成される単なる文字列です。各タグは異なるフィールドを表し、特定の値のセットが許可されています。以下に、サーバーに認証を要求するサンプルFIXメッセージを示します。

8=FIX.4.4|9=126|35=A|49=theBroker.12345|56=CSERVER|34=1|52=20170117- 08:03:04|57=TRADE|50=any_string|98=0|108=30|141=Y|553=12345|554=passw0rd!|10=131|

ご覧のように、各FIXメッセージで見られる繰り返しパターンは次のとおりです:

Tag=Value|Tag=Value|Tag=Value|...

各メッセージの目的に応じて、毎回異なるタグと値のセットが必要になります。各メッセージに必要なタグと値は、cTrader FIXエンジンの利用規約(常に最新の利用規約を確認してください)に詳細に記載されています。

同様に、サーバーからの応答も送り返されます。以下に、上記メッセージに対するサーバーの応答を示します。

8=FIX.4.4|9=106|35=A|34=1|49=CSERVER|50=TRADE|52=20170117- 08:03:04.509|56=theBroker.12345|57=any_string|98=0|108=30|141=Y|10=066|

FIXサーバーとの通信プロセスに含まれるステップは以下の通りです:

  1. FIXメッセージの構築

  2. FIXメッセージの送信

  3. FIXメッセージの受信

  4. FIXメッセージの解析

生のFIXメッセージは、理解しやすさよりも効率性を重視して設計されているため、非常に読みにくい形式です。 したがって、各ソフトウェアアプリケーションでは、提供された情報を対応するFIXメッセージに変換するプロセスが常に必要になります。

我々のC#サンプルアプリケーションでは、メッセージ構築を処理するクラスと、関連情報に基づいてFIXメッセージを作成する関数を作成しました。

メッセージが構築された後、それらはインターネットを介してネットワークソケットを通じてサーバーとクライアント間で送信されます。メッセージを受信した後、読みやすい形式で表示するために解析する必要があります。

この記事では、構築、送信、応答の受信のプロセスをカバーします。解析については将来の記事で扱います。

FIXメッセージの構築

メッセージ構造

サンプルアプリケーションでは、FIXメッセージを作成する責任を持つクラスを作成しました。このクラスはMessageConstructorと呼ばれ、FIX APIライブラリプロジェクトにあります。

MessageConstructorは以下のパラメータで初期化されます:

  1. Host(*) – cServerが配置されているアドレス。

  2. Username(*) – アカウント番号

  3. Password(*) – パスワード

  4. SenderCompID(*) – cTraderのFIX APIフォームで提供されます。フォーマットは

  5. SenderSubID* – SenderCompIDの2番目の部分

  6. TargetCompID(*) – cTraderのFIX APIフォームで提供されます(通常はcServer)

この情報はcTraderのFIX APIフォームで見つけることができます。

MessageConstructorを初期化した後、FIX APIメッセージを構築する準備が整います。

すべてのメッセージは同様の方法で構築されます。以下にログオンメッセージを構築するコードサンプルがあります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public  string LogonMessage(SessionQualifier qualifier, int messageSequenceNumber,

            int  heartBeatSeconds,  bool  resetSeqNum)

       {

            var  body = new  StringBuilder ();

        //Defines a message encryption scheme.Currently, only transportlevel security

       //is supported.Valid value is "0"(zero) = NONE_OTHER (encryption is not used).

           body.Append( "98=0|");

            //Heartbeat interval in seconds. 

           //Value is set in the 'config.properties' file (client side) as 

         // 'SERVER.POLLING.INTERVAL' . 

           //30 seconds is default interval value. If HeartBtInt is set to 0, 

         no heartbeat message  

           //is required.

           body.Append( "108="  + heartBeatSeconds +  "|");

           // All sides of FIX session should have

           //sequence numbers reset. Valid value

            //is "Y" = Yes(reset). 

            if  (resetSeqNum)

               body.Append( "141=Y|");

            //The numeric User ID.  User is linked to SenderCompID (#49) value (the

            //user's organization). 

           body.Append( "553=" + _username + "|");

           //User Password

           body.Append( "554=" + _password + "|");

            var  header = ConstructHeader(qualifier, 

         SessionMessageCode( SessionMessageType.Logon), messageSequenceNumber,

          body.ToString());

            var  headerAndBody = header + body;            

            var  trailer = ConstructTrailer(headerAndBody);

            var  headerAndMessageAndTrailer = header + body + trailer;

            return  headerAndMessageAndTrailer.Replace("|", "\u0001");

       }

ご覧のように、まず本文部分を構築し、それをヘッダー関数に渡し、最後に両方の部分をトレーラー関数に渡します。これら3つの部分について以下で詳しく説明します。

メッセージ構築プロセスは、必要なタグ、値、区切り文字を文字列に追加するだけです。

本文

まず、本文の構築について説明します。本文は最初に作成する必要があるからです。上記の例(ログオンメッセージの作成)を参照してください。

StringBuilderクラスを初期化することから始め、関数の入力に基づいてタグを1つずつ追加していきます。メッセージタイプに基づいて、本文は異なるタグのセットで構成される必要があり、一部は必須で、他はオプションです。

各メッセージの構造は、利用規約(/FIX)で確認できます。

次に、ログオンメッセージのヘッダーを作成し、メッセージの本文を追加します。最後に、headerAndBody文字列を使用してトレーラーを生成します。以下では、ヘッダーとトレーラーの構築方法を見ていきます。

ヘッダー

ヘッダーはFIXメッセージの最初の部分で、以下のフィールドで構成されています(すべてのメッセージで同じ):

  1. BeginString – 開始文字列はFIXプロトコルのバージョンを定義し、我々の場合はFIX4.4に固定されています。
  2. BodyLength – 本文の長さは、BeginStringBodyLength、トレーラーフィールドを除く、メッセージの文字数を示します。
  3. MsgType – このフィールドでメッセージタイプを定義し、受信者が本文をどのように解析するかを知らせます。
  4. SenderCompID – ここでSenderCompIDを設定します。
  5. TargetCompID – これは我々のメッセージの対象です。我々の場合、常にcServerになります。
  6. SenderSubID – トレーダーのログイン。
  7. MsgSeqNum – これはメッセージのシーケンス番号です。同じセッションで送信される各メッセージに対して増加させる必要があります。
  8. Sending Time – メッセージ送信時刻。

以下に、ヘッダーの構築を担当するConstructHeader関数を示します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
private  string ConstructHeader(SessionQualifier qualifier, string type,

         int  messageSequenceNumber, string bodyMessage)

    {

         var  header = new  StringBuilder ();

        // Protocol version. FIX.4.4 (Always unencrypted, must be first field 

        // in message.

        header.Append( "8=FIX.4.4|");

         var  message = new  StringBuilder ();

        // Message type. Always unencrypted, must be third field in message.

        message.Append( "35=" + type + "|");

        // ID of the trading party in following format: <BrokerUID>.<Trader Login> 

        // where BrokerUID is provided by cTrader and Trader Login is numeric 

        // identifier of the trader account.

        message.Append( "49="  + _senderCompID +  "|");  

        // Message target. Valid value is "CSERVER"

        message.Append( "56="  + _targetCompID +  "|");  

        // Additional session qualifier. Possible values are: "QUOTE", "TRADE".

        message.Append( "57="  + qualifier.ToString() +  "|");  

        // Assigned value used to identify specific message originator.

        message.Append( "50="  + _senderSubID +  "|");

        // Message Sequence Number

        message.Append( "34="  + messageSequenceNumber +  "|");

         // Time of message transmission (always expressed in UTC(Universal Time  

        // Coordinated, also known as 'GMT').

        message.Append("52=" + DateTime.UtcNow.ToString("yyyyMMdd-HH:mm:ss") + "|");

         var  length = message.Length + bodyMessage.Length;

        // Message body length. Always unencrypted, must be second field in message.

        header.Append( "9=" + length + "|");

        header.Append(message);      

         return  header.ToString();

    }

トレーラー

トレーラーは、メッセージの残りの部分のチェックサムを含むタグだけです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
private  string ConstructTrailer(string message)

       {

            //Three byte, simple checksum.  Always last field in message; i.e. serves,

           //with the trailing<SOH>, 

           //as the end - of - message delimiter. Always defined as three characters

           //(and always unencrypted).

            var  trailer = "10=" + CalculateChecksum(message.Replace("|", "\u0001").ToString()).ToString().PadLeft(3, '0') + "|";

            return  trailer;

       }

システムメッセージ

我々のサンプルには、以下のシステムメッセージを返す関数が含まれています:

  • ハートビート: MessageConstructor.HeartbeatMessage()
  • テストリクエスト: MessageConstructor.TestRequestMessage()
  • ログオン: MessageConstructor.LogonMessage()
  • ログアウト: MessageConstructor.LogoutMessage()
  • 再送リクエスト: MessageConstructor.ResendMessage()
  • 拒否: MessageConstructor.RejectMessage()
  • シーケンスリセット: MessageConstructor.SequenceResetMessage()

アプリケーションメッセージ

我々のサンプルには、以下のシステムメッセージを返す関数が含まれています:

  • マーケットデータリクエスト: MessageConstructor.HeartbeatMessage()
  • マーケットデータスナップショット/フルリフレッシュ: MessageConstructor.MarketDataSnapshotMessage()
  • マーケットデータインクリメンタルリフレッシュ: MessageConstructor.MarketDataIncrementalRefreshMessage()
  • 新規注文シングル: MessageConstructor.NewOrderSingleMessage()
  • 注文ステータスリクエスト: MessageConstructor.OrderStatusRequest()
  • 執行レポート: MessageConstructor.ExecutionReport()
  • ビジネスメッセージ拒否: MessageConstructor.BusinessMessageReject()
  • ポジションリクエスト: MessageConstructor.RequestForPositions()
  • ポジションレポート: MessageConstructor.PositionReport()

メッセージの送信と応答の受信

cServerにFIXメッセージを送信するには、まずサーバーとの接続を確立する必要があります。これはTcpClientを作成することで行えます。我々の場合、価格見積メッセージと取引メッセージはサーバー上の異なるポートで処理されるため、2つのクライアントを作成します。

次に、メッセージが送信される2つのストリームを取得する必要があります。このプロセスは、以下に示すようにフォームのコンストラクタで行われます:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public  frmFIXAPISample()
{

   InitializeComponent();

    _priceClient = new  TcpClient( _host, _pricePort);           

   _priceStream = _ priceClient.GetStream ();            

   _tradeClient =  new  TcpClient ( _host, _tradePort);

   _tradeStream = _ tradeClient.GetStream ();

   _messageConstructor = new  MessageConstructor( _host, _username,

       _password, _senderCompID, _senderSubID, _targetCompID);

}

コンストラクタでは、メッセージ生成に使用されるMessageConstructorクラスも初期化します。

次に、メッセージを送信するために、SendPriceMessage()SendTradeMessage()という2つの異なる関数を作成しました。各関数はFIXメッセージを入力として受け取り、メッセージと対応するストリームを入力としてSendMessage()関数を呼び出します。

SendMessage()関数は以下のように動作します:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private  string SendMessage(string message,  NetworkStream  stream)
{
    var  byteArray =  Encoding.ASCII.GetBytes (message);

    stream.Write(byteArray, 0, byteArray.Length);

    var  buffer = new byte[1024];

    int  i = 0;

    while  (!stream.DataAvailable && i < 100)
    {
         Thread.Sleep ( 100);
         i++;
    }

    if( stream.DataAvailable )
        stream.Read(buffer, 0, 1024);

    _messageSequenceNumber++;

    var  returnMessage = Encoding.ASCII.GetString(buffer);

    return  returnMessage;
}

詳細な手順は以下の通りです:

  1. メッセージをバイト配列にエンコードします。
  2. バイト配列をストリームに書き込みます。
  3. ストリームから応答を読み取ります。
  4. メッセージシーケンス番号を増加させます。
  5. メッセージを文字列にエンコードします。

この関数は、サーバーから送信されたFIXメッセージを返す必要があります。

想像の通り、生のFIXメッセージをユーザーに表示することはできないので、受信したメッセージを解析する追加のステップを開発する必要があります。

結論

このアプリケーションは、FIXメッセージを使用してcServerと通信する方法の簡単なデモンストレーションです。これはFIXプロトコルの概念を説明する例に過ぎず、決して完全なFIXエンジンではありません。独自のFIXエンジンの構築を避けたい場合は、利用可能なサードパーティのFIXエンジンの使用を検討することをお勧めします。

注意

この記事は2017年2月3日時点で最新であり、cTrader FIXエンジン、利用規約v2.9.1を考慮して開発されています。