cTrader FIX API【メッセージの送受信】

概要

本記事では、C# の例を使用して、FIX メッセージを作成し、サーバーに送信し、応答を受信する方法を詳しく説明します。

この例は、複雑なアプリケーションではなく、プログラマーが FIX API メッセージを使用する概念を簡単に理解できるように、できるだけシンプルに記述してあります。

サーバーとの適切な通信を確立・維持し、レスポンスを適切に処理するためには、追加機能が必要ですが、それらは単純明快にするために省略されています。レスポンスの適切な処理については、今後の記事で扱う予定です。

コードサンプル

この記事で取り上げたコードサンプルは、こちらのGitHubリポジトリでご覧いただけます: https://github.com/spotware/FIX-API-Sample

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メッセージに見られる繰り返しパターンは次のとおりです:

タグ=値|タグ=値|タグ=値|…

各メッセージの目的に応じて、毎回異なるタグと値のセットが必要になります。各メッセージに必要なタグと値は、cTrader FIX Engine Rules of Engagementに詳しく記載されています。(最新のルールについては、常にこちらをご確認ください)

同様に、サーバーからレスポンスが送り返されます。以下は、上記のメッセージに対するサーバーからの応答です。

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 Library プロジェクトの中にあります。

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

  1. ホスト(*): cServerが置かれているアドレス。
  2. ユーザー名(*): アカウント番号
  3. パスワード(*): パスワード
  4. SenderCompID(*): cTrader の FIX API 形式で提供されます。
  5. SenderSubID: SenderCompID の 2 番目の部分。
  6. TargetCompID(*): cTrader の FIX API 形式で提供される。通常は cServer です。

この情報は、cTrader FIX API フォームに記載されています。

MessageConstructor を初期化したら、FIX API メッセージを作成する準備は完了です。

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

1public string LogonMessage(SessionQualifier qualifier, int messageSequenceNumber,
2
3  int heartBeatSeconds, bool resetSeqNum)
4
5  {
6
7    var body = new StringBuilder ();
8
9    //Defines a message encryption scheme.Currently, only transportlevel security
10
11    //is supported.Valid value is “0”(zero) = NONE_OTHER (encryption is not used).
12
13    body.Append( “98=0|”);
14
15    //Heartbeat interval in seconds.
16
17    //Value is set in the ‘config.properties’ file (client side) as
18
19    // ‘SERVER.POLLING.INTERVAL’ .
20
21    //30 seconds is default interval value. If HeartBtInt is set to 0,
22
23    no heartbeat message
24
25    //is required.
26
27    body.Append( “108=” + heartBeatSeconds + “|”);
28
29    // All sides of FIX session should have
30
31    //sequence numbers reset. Valid value
32
33    //is “Y” = Yes(reset).
34
35    if (resetSeqNum)
36
37    body.Append( “141=Y|”);
38
39    //The numeric User ID. User is linked to SenderCompID (#49) value (the
40
41    //user’s organization).
42
43    body.Append( “553=” + _username + “|”);
44
45    //USer Password
46
47    body.Append( “554=” + _password + “|”);
48
49    var header = ConstructHeader(qualifier,
50
51    SessionMessageCode( SessionMessageType.Logon), messageSequenceNumber,
52
53    body.ToString());
54
55    var headerAndBody = header + body;
56
57    var trailer = ConstructTrailer(headerAndBody);
58
59    var headerAndMessageAndTrailer = header + body + trailer;
60
61    return headerAndMessageAndTrailer.Replace(“|”, “\u0001”);
62
63  }

まずボディ部分を作成し、次にそれをヘッダー関数に渡し、最後に両方の部分をトレーラー関数に渡しているのがお分かりいただけると思います。この3つの部分については、次の段落で詳しく説明します。

メッセージの作成プロセスは、必要なタグ、値、セパレータを文字列に追加するだけです。

ボディ

まず、メッセージの本文を作る必要があるので、本文の作り方から説明します。

上の例(ログオン・メッセージの作成)を見てください。

まず、StringBuilder クラスを初期化し、関数の入力に基づいてタグを 1 つずつ追加していきます。メッセージ・タイプに応じて、本文は異なるタグ・セットで構成されなければなりません。

各メッセージの構造は、私たちの規約(/FIX)に記載されています。

次に、ログオン・メッセージのヘッダーを作成し、それにメッセージ本文を追加する。最後に、headerAndBody文字列を使って、トレーラを生成します。

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

  1. BeginString:: 開始文字列は FIX プロトコルのバージョンを定義します。
  2. BodyLength: 本文の長さは、BeginString、BodyLength、Trailer フィールドを除いたメッセージの長さを文字数で表す。
  3. MsgType: このフィールドでは、メッセージ・タイプを定義する。
  4. SenderCompID: ここでは SenderCompID を設定する。
  5. TargetCompID: これはメッセージのターゲットである。この場合、常に CSERVER となる。
  6. SenderSubID: トレーダーのログイン名。
  7. MsgSeqNum: メッセージのシーケンス番号: メッセージのシーケンス番号。同じセッションでメッセージを送信するたびに増加させる必要がある。
  8. Sending Time: メッセージ送信時刻。

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

1private string ConstructHeader(SessionQualifier qualifier, string type,
2
3  int messageSequenceNumber, string bodyMessage)
4
5  {
6
7    var header = new StringBuilder ();
8
9    // Protocol version. FIX.4.4 (Always unencrypted, must be first field
10
11    // in message.
12
13    header.Append( “8=FIX.4.4|”);
14
15    var message = new StringBuilder ();
16
17    // Message type. Always unencrypted, must be third field in message.
18
19    message.Append( “35=” + type + “|”);
20
21    // ID of the trading party in following format: <BrokerUID>.<Trader Login>
22
23    // where BrokerUID is provided by cTrader and Trader Login is numeric
24
25    // identifier of the trader account.
26
27    message.Append( “49=” + _senderCompID + “|”);
28
29    // Message target. Valid value is “CSERVER”
30
31    message.Append( “56=” + _targetCompID + “|”);
32
33    // Additional session qualifier. Possible values are: “QUOTE”, “TRADE”.
34
35    message.Append( “57=” + qualifier.ToString() + “|”);
36
37    // Assigned value used to identify specific message originator.
38
39    message.Append( “50=” + _senderSubID + “|”);
40
41    // Message Sequence Number
42
43    message.Append( “34=” + messageSequenceNumber + “|”);
44
45    // Time of message transmission (always expressed in UTC(Universal Time
46
47    // Coordinated, also known as ‘GMT’).
48
49    message.Append(“52=” + DateTime.UtcNow.ToString(“yyyyMMdd-HH:mm:ss”) + “|”);
50
51    var length = message.Length + bodyMessage.Length;
52
53    // Message body length. Always unencrypted, must be second field in message.
54
55    header.Append( “9=” + length + “|”);
56
57    header.Append(message);
58
59    return header.ToString();
60
61  }

トレーラー

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

1private string ConstructTrailer(string message)
2
3{
4
5  //Three byte, simple checksum. Always last field in message; i.e. serves,
6
7  //with the trailing<SOH>,
8
9  //as the end – of – message delimiter. Always defined as three characters
10
11  //(and always unencrypted).
12
13  var trailer = “10=” + CalculateChecksum(message.Replace(“|”, “\u0001”).ToString()).ToString().PadLeft(3, ‘0’) + “|”;
14
15  return trailer;
16
17}

システムメッセージ

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

  • ハートビート 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()
  • RequestForPositions 位置の要求: MessageConstructor.RequestForPositions()
  • ポジションレポート MessageConstructor.PositionReport()

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

cServerにFIXメッセージを送信するには、まずサーバーとの接続を確立する必要があります。そのためには TcpClient を作成します。

なお、価格提示メッセージと取引メッセージはサーバー上の異なるポートで処理されます。

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

1public frmFIXAPISample()
2{
3
4  InitializeComponent();
5
6  _priceClient = new TcpClient( _host, _pricePort);
7
8  _priceStream = _ priceClient.GetStream ();
9
10  _tradeClient = new TcpClient ( _host, _tradePort);
11
12  _tradeStream = _ tradeClient.GetStream ();
13
14  _messageConstructor = new MessageConstructor( _host, _username,
15
16  _password, _senderCompID, _senderSubID, _targetCompID);
17
18}

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

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

SendMessage() 関数の動作は以下のとおりです:

1private string SendMessage(string message, NetworkStream stream)
2{
3  var byteArray = Encoding.ASCII.GetBytes (message);
4
5  stream.Write(byteArray, 0, byteArray.Length);
6
7  var buffer = new byte[1024];
8
9  int i = 0;
10
11  while (!stream.DataAvailable && i < 100)
12  {
13    Thread.Sleep ( 100);
14    i++;
15  }
16
17  if( stream.DataAvailable )
18  stream.Read(buffer, 0, 1024);
19
20  _messageSequenceNumber++;
21
22  var returnMessage = Encoding.ASCII.GetString(buffer);
23
24  return returnMessage;
25}

具体的な手順は以下のようになります

  1. メッセージをバイト配列にエンコードする。
  2. バイト配列をストリームに書き込む。
  3. ストリームから応答を読み出す。
  4. メッセージのシーケンス番号を増やす。
  5. メッセージを文字列にエンコードする。

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

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

まとめ

このアプリケーションは、FIXメッセージを使用してcServerと通信する方法についての簡単なデモンストレーションです。本記事での解説は FIX プロトコルの概念を示す単なる例であり、決して完全な FIX エンジンではありません。

独自開発を効率化するには、サードパーティの FIX エンジンの使用をご検討ください。

本記事はcTrader FIX Engine, Rules of Engagement v2.9.1を念頭に置いて作成されています。