Langkau tajuk talian

Hantar dan terima mesej

Pengenalan

Artikel ini berfungsi sebagai pengenalan kepada penggunaan FIX API untuk sesiapa yang berminat untuk berinteraksi dengan Spotware cServer menggunakan FIX.

Dalam artikel ini, kami akan menggunakan contoh C# untuk menerangkan secara terperinci prinsip-prinsip bagaimana untuk membina mesej FIX, menghantar ia kepada pelayan dan menerima maklum balas. Contoh ini bukanlah aplikasi yang sempurna dan telah disederhanakan seboleh mungkin untuk membolehkan pengaturcara memahami konsep penggunaan mesej FIX API dengan mudah.

Untuk mewujudkan dan mengekalkan komunikasi yang betul dengan pelayan dan pengendalian maklum balas yang sewajarnya, fungsi tambahan diperlukan, yang telah diabaikan demi kesederhanaan dan kejelasan. Kami akan membincangkan subjek-subjek ini dalam artikel akan datang.

Sampel kod

Anda boleh mendapatkan sampel kod yang dibincangkan dalam artikel ini di repositori GitHub kami

Gambaran keseluruhan komunikasi FIX

Mesej FIX hanyalah rentetan yang terdiri daripada set tag nombor dan nilai yang dipisahkan oleh bar menegak (|). Setiap tag mewakili medan yang berbeza di mana set nilai tertentu dibenarkan. Di bawah anda boleh lihat contoh mesej FIX yang meminta pengesahan daripada pelayan.

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|

Seperti yang anda lihat, corak yang berulang dalam setiap mesej FIX adalah:

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

Bergantung kepada tujuan setiap mesej, set tag dan nilai yang berbeza diperlukan setiap kali. Tag dan nilai yang diperlukan untuk setiap mesej diterangkan secara terperinci dalam Rules of Engagement enjin FIX cTrader (sentiasa semak rules of engagement terkini).

Dengan cara yang sama, maklum balas dihantar balik dari pelayan. Di bawah anda boleh lihat maklum balas pelayan untuk mesej di atas.

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|

Langkah-langkah yang terlibat dalam proses berkomunikasi dengan pelayan FIX adalah seperti berikut:

  1. Bina mesej FIX

  2. Hantar mesej FIX

  3. Terima mesej FIX

  4. Hurai mesej FIX

Mesej FIX mentah bukanlah format yang mudah dibaca kerana ia direka dengan mengutamakan kecekapan berbanding kebolehfahaman. Oleh itu, untuk setiap aplikasi perisian akan sentiasa ada proses menterjemah maklumat yang diberikan kepada mesej FIX yang berkaitan.

Dalam aplikasi sampel C# kami, kami telah mencipta kelas untuk mengendalikan pembinaan mesej serta fungsi untuk mencipta mesej FIX berdasarkan maklumat yang berkaitan.

Selepas mesej dibina, ia dihantar antara pelayan dan pelanggan melalui internet menggunakan soket rangkaian. Apabila mesej diterima, ia perlu dihurai untuk dipersembahkan dalam format yang boleh dibaca.

Dalam artikel ini kami akan membincangkan proses pembinaan, penghantaran dan penerimaan balasan. Kami akan membincangkan penghuraian dalam artikel akan datang.

Bina mesej FIX

Struktur mesej

Dalam aplikasi sampel kami, kami telah mencipta kelas yang bertanggungjawab untuk mencipta mesej FIX. Kelas ini dipanggil MessageConstructor dan boleh didapati dalam projek FIX API Library.

MessageConstructor diinisialisasikan dengan parameter berikut:

  1. Host(*) – alamat di mana cServer kami terletak.

  2. Username(*) – nombor akaun

  3. Password(*) – kata laluan

  4. SenderCompID(*) – ia disediakan dalam borang FIX API cTrader. Ia dalam format

  5. SenderSubID* – ia adalah bahagian kedua SenderCompID

  6. TargetCompID(*) – ia disediakan dalam borang FIX API cTrader (biasanya ia adalah cServer)

Anda boleh mendapatkan maklumat ini dalam borang FIX API cTrader anda.

Selepas kita menginisialisasikan MessageConstructor maka kita bersedia untuk membina mesej FIX API.

Semua mesej dibina dengan cara yang serupa. Di bawah terdapat sampel kod untuk membina mesej Logon.

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

       }

Anda boleh lihat bahawa kita mula-mula membina bahagian badan, kemudian kita memasukkannya ke dalam fungsi pengepala dan akhirnya kita memasukkan kedua-dua bahagian ke dalam fungsi trailer. Ketiga-tiga bahagian ini diterangkan secara terperinci di bawah.

Proses pembinaan mesej hanyalah penambahan tag yang diperlukan, nilai dan pemisah ke dalam rentetan.

Badan

Pertama kita akan mula dengan menerangkan pembinaan badan, kerana badan mesej perlu dicipta terlebih dahulu. Kita boleh lihat contoh di atas (mencipta mesej Logon).

Kita mula dengan menginisialisasikan kelas StringBuilder dan kita menambah tag satu demi satu berdasarkan input fungsi. Berdasarkan jenis mesej, badan mesti terdiri daripada set tag yang berbeza, sesetengahnya wajib dan yang lain pilihan.

Anda boleh mendapatkan struktur setiap mesej dalam Rules of Engagement kami (/FIX).

Kemudian kita mencipta pengepala untuk mesej Logon dan menambah kepadanya badan mesej. Akhirnya, menggunakan rentetan headerAndBody kita menjana trailer. Seterusnya, kita akan lihat bagaimana kita membina pengepala dan trailer.

Pengepala

Pengepala adalah bahagian pertama mesej FIX dan ia terdiri daripada medan berikut (sama untuk semua mesej):

  1. BeginString – rentetan permulaan menentukan versi protokol FIX dan dalam kes kami ditetapkan kepada FIX4.4.
  2. BodyLength – panjang badan menyatakan panjang mesej dalam aksara, tidak termasuk medan BeginString, BodyLength dan trailer.
  3. MsgType – dalam medan ini kita menentukan jenis mesej, supaya penerima tahu bagaimana untuk menghurai badan.
  4. SenderCompID – di sini kita menetapkan SenderCompID.
  5. TargetCompID – ini adalah sasaran mesej kita. Dalam kes kami, ia akan sentiasa menjadi cServer.
  6. SenderSubID – log masuk pedagang.
  7. MsgSeqNum – ini adalah nombor urutan mesej. Ia perlu ditingkatkan untuk setiap mesej yang dihantar dalam sesi yang sama.
  8. Sending Time – masa penghantaran mesej.

Di bawah anda boleh lihat fungsi ConstructHeader, yang bertanggungjawab untuk membina pengepala.

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

    }

Trailer

Trailer hanyalah tag yang mengandungi checksum bagi baki mesej.

 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;

       }

Mesej sistem

Sampel kami mengandungi fungsi yang mengembalikan mesej sistem berikut:

  • Denyutan jantung: MessageConstructor.HeartbeatMessage()
  • Permintaan ujian: MessageConstructor.TestRequestMessage()
  • Log masuk: MessageConstructor.LogonMessage()
  • Log keluar: MessageConstructor.LogoutMessage()
  • Permintaan hantar semula: MessageConstructor.ResendMessage()
  • Tolak: MessageConstructor.RejectMessage()
  • Tetapan semula urutan: MessageConstructor.SequenceResetMessage()

Mesej aplikasi

Sampel kami mengandungi fungsi yang mengembalikan mesej sistem berikut:

  • Permintaan data pasaran: MessageConstructor.HeartbeatMessage()
  • Snapshot data pasaran/penyegaran penuh: MessageConstructor.MarketDataSnapshotMessage()
  • Penyegaran tambahan data pasaran: MessageConstructor.MarketDataIncrementalRefreshMessage()
  • Pesanan baharu tunggal: MessageConstructor.NewOrderSingleMessage()
  • Permintaan status pesanan: MessageConstructor.OrderStatusRequest()
  • Laporan pelaksanaan: MessageConstructor.ExecutionReport()
  • Penolakan mesej perniagaan: MessageConstructor.BusinessMessageReject()
  • Permintaan untuk posisi: MessageConstructor.RequestForPositions()
  • Laporan posisi: MessageConstructor.PositionReport()

Hantar mesej dan terima maklum balas

Untuk menghantar mesej FIX ke cServer anda perlu mewujudkan sambungan dengan pelayan terlebih dahulu. Anda boleh melakukan ini dengan mencipta TcpClient. Dalam kes kami, kami mencipta dua pelanggan, kerana mesej sebut harga dan mesej dagangan dikendalikan oleh port yang berbeza pada pelayan.

Kemudian, kita perlu mendapatkan dua aliran di mana mesej akan dihantar. Proses ini berlaku dalam pembina borang seperti yang ditunjukkan di bawah:

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

}

Dalam pembina, kami juga menginisialisasikan kelas MessageConstructor yang akan digunakan untuk menjana mesej.

Seterusnya, untuk menghantar mesej, kami mencipta dua fungsi yang berbeza, SendPriceMessage() dan SendTradeMessage(). Setiap satu mengambil mesej FIX sebagai input dan kemudian memanggil fungsi SendMessage() dengan mesej dan aliran masing-masing sebagai input.

Fungsi SendMessage() berfungsi seperti berikut:

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

Langkah-langkah terperinci adalah seperti berikut:

  1. Enkod mesej kepada tatasusunan bait.
  2. Tulis tatasusunan bait pada aliran.
  3. Baca balasan daripada aliran.
  4. Tingkatkan nombor urutan mesej.
  5. Enkod mesej kepada rentetan.

Fungsi ini sepatutnya mengembalikan mesej FIX yang dihantar oleh pelayan.

Seperti yang anda andaikan, anda tidak boleh menunjukkan mesej FIX mentah kepada pengguna, jadi langkah tambahan untuk menghurai mesej yang masuk perlu dibangunkan.

Kesimpulan

Aplikasi ini adalah demonstrasi ringkas tentang cara berkomunikasi dengan cServer menggunakan mesej FIX. Ia hanyalah contoh yang menggambarkan konsep Protokol FIX dan bukanlah enjin FIX yang lengkap. Jika anda ingin mengelakkan pembinaan enjin FIX anda sendiri, anda mungkin boleh mempertimbangkan untuk menggunakan salah satu enjin FIX pihak ketiga yang tersedia.

Nota

Artikel ini adalah terkini setakat 03/02/2017 dan dibangunkan dengan mempertimbangkan enjin FIX cTrader, Rules of Engagement v2.9.1.