Bỏ qua

Gửi và nhận tin nhắn

Giới thiệu

Bài viết này đóng vai trò như một phần giới thiệu về việc sử dụng FIX API cho bất kỳ ai quan tâm đến việc tương tác với Spotware cServer bằng FIX.

Trong bài viết này, chúng tôi sẽ sử dụng một ví dụ bằng C# để mô tả chi tiết các nguyên tắc về cách xây dựng một tin nhắn FIX, gửi nó đến máy chủ và nhận phản hồi. Ví dụ này không phải là một ứng dụng hoàn hảo và được giữ đơn giản nhất có thể để cho phép các lập trình viên dễ dàng hiểu khái niệm sử dụng tin nhắn FIX API.

Để thiết lập và duy trì giao tiếp đúng với máy chủ và xử lý phản hồi đúng cách, cần có thêm chức năng, điều này đã được bỏ qua vì mục đích đơn giản và rõ ràng. Chúng tôi sẽ đề cập đến những chủ đề này trong các bài viết trong tương lai.

Mẫu mã

Bạn có thể tìm thấy mẫu mã được thảo luận trong bài viết này trên kho lưu trữ GitHub của chúng tôi

Tổng quan về giao tiếp FIX

Một tin nhắn FIX chỉ là một chuỗi bao gồm các bộ thẻ số và giá trị được phân tách bằng dấu gạch đứng (|). Mỗi thẻ đại diện cho một trường khác nhau mà một tập hợp giá trị nhất định được cho phép. Dưới đây bạn có thể thấy một mẫu tin nhắn FIX yêu cầu xác thực từ máy chủ.

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|

Như bạn có thể thấy, mẫu lặp lại được tìm thấy trong mỗi tin nhắn FIX là:

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

Tùy thuộc vào mục đích của mỗi tin nhắn, một bộ thẻ và giá trị khác nhau được yêu cầu mỗi lần. Các thẻ và giá trị cần thiết cho mỗi tin nhắn được mô tả chi tiết trong Quy tắc Tham gia của cTrader FIX engine (luôn kiểm tra quy tắc tham gia mới nhất).

Tương tự, các phản hồi được gửi lại từ máy chủ. Dưới đây bạn có thể thấy phản hồi của máy chủ cho tin nhắn trên.

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|

Các bước liên quan đến quá trình giao tiếp với máy chủ FIX như sau:

  1. Xây dựng một tin nhắn FIX

  2. Truyền một tin nhắn FIX

  3. Nhận một tin nhắn FIX

  4. Phân tích cú pháp một tin nhắn FIX

Một tin nhắn FIX thô không phải là một định dạng dễ đọc vì nó được thiết kế với mục đích hiệu quả hơn là dễ hiểu. Do đó, đối với mỗi ứng dụng phần mềm sẽ luôn có một quá trình dịch thông tin được cung cấp thành tin nhắn FIX tương ứng.

Trong ứng dụng mẫu C# của chúng tôi, chúng tôi đã tạo một lớp để xử lý việc xây dựng tin nhắn cũng như các hàm để tạo tin nhắn FIX dựa trên thông tin liên quan.

Sau khi tin nhắn đã được xây dựng, chúng được truyền giữa máy chủ và máy khách qua internet thông qua các socket mạng. Khi tin nhắn được nhận, chúng cần được phân tích cú pháp để được trình bày ở định dạng có thể đọc được.

Trong bài viết này, chúng tôi sẽ đề cập đến quá trình xây dựng, truyền và nhận phản hồi. Chúng tôi sẽ đề cập đến việc phân tích cú pháp trong một bài viết trong tương lai.

Xây dựng một tin nhắn FIX

Cấu trúc tin nhắn

Trong ứng dụng mẫu của chúng tôi, chúng tôi đã tạo một lớp chịu trách nhiệm tạo tin nhắn FIX. Lớp này được gọi là MessageConstructor và có thể được tìm thấy trong dự án FIX API Library.

MessageConstructor được khởi tạo với các tham số sau:

  1. Host(*) – địa chỉ nơi cServer của chúng tôi được đặt.

  2. Username(*) – số tài khoản

  3. Password(*) – mật khẩu

  4. SenderCompID(*) – được cung cấp trong biểu mẫu FIX API của cTrader. Nó có định dạng

  5. SenderSubID* – là phần thứ hai của SenderCompID

  6. TargetCompID(*) – được cung cấp trong biểu mẫu FIX API của cTrader (thường là cServer)

Bạn có thể tìm thấy thông tin này trong biểu mẫu FIX API cTrader của bạn.

Sau khi chúng ta đã khởi tạo một MessageConstructor thì chúng ta đã sẵn sàng để xây dựng tin nhắn FIX API.

Tất cả các tin nhắn đều được xây dựng theo cách tương tự. Dưới đây là một mẫu mã xây dựng tin nhắn Đăng nhập.

 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");

       }

Bạn có thể thấy rằng trước tiên chúng ta xây dựng phần thân, sau đó chúng ta truyền nó vào hàm tiêu đề và cuối cùng chúng ta truyền cả hai phần vào hàm đuôi. Ba phần này được mô tả chi tiết dưới đây.

Quá trình xây dựng tin nhắn chỉ là việc thêm các thẻ, giá trị và dấu phân cách cần thiết vào một chuỗi.

Thân

Đầu tiên chúng ta sẽ bắt đầu bằng cách mô tả việc xây dựng thân, vì thân của tin nhắn cần được tạo trước. Chúng ta có thể thấy một ví dụ ở trên (tạo tin nhắn Đăng nhập).

Chúng ta bắt đầu bằng cách khởi tạo một lớp StringBuilder và chúng ta thêm các thẻ từng cái một dựa trên đầu vào của hàm. Dựa trên loại tin nhắn, thân phải được cấu tạo từ các bộ thẻ khác nhau, một số là bắt buộc và một số khác là tùy chọn.

Bạn có thể tìm thấy cấu trúc của từng tin nhắn trong Quy tắc Tham gia của chúng tôi (/FIX).

Sau đó chúng ta tạo một tiêu đề cho tin nhắn Đăng nhập và thêm vào đó thân của tin nhắn. Cuối cùng, sử dụng chuỗi headerAndBody chúng ta tạo ra đuôi. Tiếp theo, chúng ta sẽ xem cách chúng ta xây dựng một tiêu đề và một đuôi.

Tiêu đề

Tiêu đề là phần đầu tiên của tin nhắn FIX và nó bao gồm các trường sau (giống nhau cho tất cả các tin nhắn):

  1. BeginString – chuỗi bắt đầu xác định phiên bản giao thức FIX và trong trường hợp của chúng ta được cố định là FIX4.4.
  2. BodyLength – độ dài thân nêu rõ độ dài của tin nhắn tính bằng ký tự, không bao gồm các trường BeginString, BodyLength và đuôi.
  3. MsgType – trong trường này chúng ta xác định loại tin nhắn, để người nhận biết cách phân tích cú pháp thân.
  4. SenderCompID – ở đây chúng ta đặt SenderCompID.
  5. TargetCompID – đây là đích của tin nhắn của chúng ta. Trong trường hợp của chúng ta, nó sẽ luôn là cServer.
  6. SenderSubID – đăng nhập của nhà giao dịch.
  7. MsgSeqNum – đây là số thứ tự của tin nhắn. Nó cần được tăng lên cho mỗi tin nhắn được gửi trong cùng một phiên.
  8. Sending Time – thời gian truyền tin nhắn.

Dưới đây bạn có thể thấy hàm ConstructHeader, chịu trách nhiệm xây dựng các tiêu đề.

 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();

    }

Đuôi

Đuôi chỉ là một thẻ chứa tổng kiểm tra của phần còn lại của tin nhắn.

 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;

       }

Tin nhắn hệ thống

Mẫu của chúng tôi chứa các hàm trả về các tin nhắn hệ thống sau:

  • Nhịp tim: MessageConstructor.HeartbeatMessage()
  • Yêu cầu kiểm tra: MessageConstructor.TestRequestMessage()
  • Đăng nhập: MessageConstructor.LogonMessage()
  • Đăng xuất: MessageConstructor.LogoutMessage()
  • Yêu cầu gửi lại: MessageConstructor.ResendMessage()
  • Từ chối: MessageConstructor.RejectMessage()
  • Đặt lại chuỗi: MessageConstructor.SequenceResetMessage()

Tin nhắn ứng dụng

Mẫu của chúng tôi chứa các hàm trả về các tin nhắn hệ thống sau:

  • Yêu cầu dữ liệu thị trường: MessageConstructor.HeartbeatMessage()
  • Tổng quan thị trường/làm mới toàn bộ: MessageConstructor.MarketDataSnapshotMessage()
  • Làm mới dữ liệu thị trường tăng dần: MessageConstructor.MarketDataIncrementalRefreshMessage()
  • Lệnh đơn mới: MessageConstructor.NewOrderSingleMessage()
  • Yêu cầu trạng thái lệnh: MessageConstructor.OrderStatusRequest()
  • Báo cáo thực hiện: MessageConstructor.ExecutionReport()
  • Từ chối thông điệp kinh doanh: MessageConstructor.BusinessMessageReject()
  • Yêu cầu vị thế: MessageConstructor.RequestForPositions()
  • Báo cáo vị thế: MessageConstructor.PositionReport()

Gửi tin nhắn và nhận phản hồi

Để gửi một tin nhắn FIX đến cServer, trước tiên bạn cần thiết lập kết nối với máy chủ. Bạn có thể làm điều này bằng cách tạo một TcpClient. Trong trường hợp của chúng tôi, chúng tôi tạo hai máy khách, vì tin nhắn báo giá và tin nhắn giao dịch được xử lý bởi các cổng khác nhau trên máy chủ.

Sau đó, chúng ta cần lấy hai luồng mà tin nhắn sẽ được gửi. Quá trình này diễn ra trong hàm tạo của biểu mẫu như được hiển thị dưới đây:

 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);

}

Trong hàm tạo, chúng tôi cũng khởi tạo một lớp MessageConstructor sẽ được sử dụng để tạo tin nhắn.

Tiếp theo, để gửi tin nhắn, chúng tôi đã tạo hai hàm khác nhau, SendPriceMessage()SendTradeMessage(). Mỗi hàm lấy tin nhắn FIX làm đầu vào và sau đó gọi hàm SendMessage() với tin nhắn và luồng tương ứng làm đầu vào.

Hàm SendMessage() hoạt động như sau:

 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;
}

Các bước chi tiết như sau:

  1. Mã hóa tin nhắn thành một mảng byte.
  2. Ghi mảng byte vào luồng.
  3. Đọc phản hồi từ luồng.
  4. Tăng số thứ tự tin nhắn.
  5. Mã hóa tin nhắn thành một chuỗi.

Hàm này nên trả về tin nhắn FIX được gửi bởi máy chủ.

Như bạn có thể đoán, bạn không thể hiển thị một tin nhắn FIX thô cho người dùng, vì vậy cần phát triển thêm một bước phân tích c

Kết luận

Ứng dụng này là một minh họa ngắn gọn về cách giao tiếp với cServer bằng các thông điệp FIX. Đây chỉ là một ví dụ minh họa các khái niệm của Giao thức FIX và hoàn toàn không phải là một công cụ FIX đầy đủ. Nếu bạn muốn tránh việc tự xây dựng công cụ FIX của riêng mình, bạn có thể cân nhắc sử dụng một trong các công cụ FIX của bên thứ ba có sẵn.

Ghi chú

Bài viết này được cập nhật vào ngày 03/02/2017 và được phát triển với sự cân nhắc cho công cụ FIX của cTrader, Quy tắc Tham gia phiên bản 2.9.1.