Ana içeriğe geç

Mesaj gönderme ve alma

Giriş

Bu makale, FIX kullanarak Spotware cServer ile etkileşim kurmak isteyen herkes için FIX API'sini kullanmaya yönelik bir giriş niteliğindedir.

Bu makalede, bir FIX mesajının nasıl oluşturulacağı, sunucuya nasıl gönderileceği ve yanıtın nasıl alınacağı prensiplerini ayrıntılı olarak açıklamak için bir C# örneği kullanacağız. Bu örnek hiçbir şekilde hatasız bir uygulama değildir ve programcıların FIX API mesajlarını kullanma kavramını kolayca anlamalarını sağlamak için olabildiğince basit tutulmuştur.

Sunucuyla uygun iletişimi kurmak ve sürdürmek ve yanıtların uygun şekilde işlenmesi için, basitlik ve netlik uğruna atlanan ek işlevler gereklidir. Gelecekteki makalelerde bu konuları ele alacağız.

Kod örneği

Bu makalede tartışılan kod örneğini GitHub depomuzda bulabilirsiniz.

FIX iletişimine genel bakış

Bir FIX mesajı, dikey bir çubuk (|) ile ayrılmış sayısal etiketler ve değerler kümelerinden oluşan bir dizeden ibarettir. Her etiket, belirli bir değer kümesine izin verilen farklı bir alanı temsil eder. Aşağıda, sunucudan kimlik doğrulama isteyen örnek bir FIX mesajı görebilirsiniz.

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|

Gördüğünüz gibi, her FIX mesajında bulunan tekrarlanabilir kalıp şöyledir:

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

Her mesajın amacına bağlı olarak, her seferinde farklı bir etiket ve değer kümesi gerekir. Her mesaj için gerekli olan etiketler ve değerler, cTrader FIX motoru Etkileşim Kuralları'nda ayrıntılı olarak açıklanmıştır (her zaman en son etkileşim kurallarını kontrol edin).

Benzer şekilde, yanıtlar sunucudan geri gönderilir. Aşağıda, yukarıdaki mesaj için sunucunun yanıtını görebilirsiniz.

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|

Bir FIX sunucusuyla iletişim kurma sürecindeki adımlar şunlardır:

  1. Bir FIX mesajı oluşturun

  2. Bir FIX mesajı iletin

  3. Bir FIX mesajı alın

  4. Bir FIX mesajını ayrıştırın

Ham bir FIX mesajı, anlaşılırlıktan ziyade verimlilik düşünülerek tasarlandığı için çok okunabilir bir format değildir. Bu nedenle, her yazılım uygulaması için sağlanan bilgileri ilgili FIX mesajına çevirme süreci her zaman olacaktır.

C# örnek uygulamamızda, mesaj oluşturmayı yönetmek için bir sınıf ve ilgili bilgilere dayanarak FIX mesajları oluşturmak için fonksiyonlar oluşturduk.

Mesajlar oluşturulduktan sonra, internet üzerinden ağ soketleri aracılığıyla bir sunucu ile bir istemci arasında iletilir. Mesajlar alındığında, okunabilir bir formatta sunulmak üzere ayrıştırılmaları gerekir.

Bu makalede, oluşturma, iletim ve yanıtın alınması süreçlerini ele alacağız. Ayrıştırma konusunu gelecekteki bir makalede ele alacağız.

Bir FIX mesajı oluşturun

Mesaj yapısı

Örnek uygulamamızda, FIX mesajları oluşturmaktan sorumlu bir sınıf oluşturduk. Sınıfın adı MessageConstructor olup FIX API Kütüphanesi projesinde bulunabilir.

MessageConstructor aşağıdaki parametrelerle başlatılır:

  1. Host(*) – cServer'ımızın bulunduğu adres.

  2. Username(*) – hesap numarası

  3. Password(*) – parola

  4. SenderCompID(*) – cTrader'ın FIX API formunda sağlanır. Şu formattadır

  5. SenderSubID* – SenderCompID'nin ikinci kısmıdır

  6. TargetCompID(*) – cTrader'ın FIX API formunda sağlanır (genellikle cServer'dır)

Bu bilgiyi cTrader FIX API formunuzda bulabilirsiniz.

Bir MessageConstructor başlattıktan sonra, FIX API mesajlarını oluşturmaya hazırız.

Tüm mesajlar benzer şekilde oluşturulur. Aşağıda bir Oturum Açma mesajı oluşturmaya dair bir kod örneği bulunmaktadır.

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

       }

Önce gövde kısmını oluşturduğumuzu, ardından bunu başlık fonksiyonuna geçirdiğimizi ve son olarak her iki kısmı da son kısım fonksiyonuna geçirdiğimizi görebilirsiniz. Bu 3 kısım aşağıda detaylandırılmıştır.

Mesaj oluşturma süreci, gerekli etiketlerin, değerlerin ve ayırıcıların bir dizeye eklenmesinden ibarettir.

Gövde

İlk olarak, mesajın gövdesinin önce oluşturulması gerektiğinden, gövde yapısını açıklayarak başlayacağız. Yukarıda bir örnek görebiliriz (Logon mesajını oluşturma).

Bir StringBuilder sınıfını başlatarak başlıyoruz ve etiketleri işlev girişlerine göre tek tek ekliyoruz. Mesaj türüne bağlı olarak, gövde farklı etiket kümelerinden oluşmalıdır; bunlardan bazıları zorunlu, diğerleri ise isteğe bağlıdır.

Her mesajın yapısını Katılım Kurallarımızda (/FIX) bulabilirsiniz.

Ardından bir Logon mesajı için bir başlık oluştururuz ve mesajın gövdesini buna ekleriz. Son olarak, headerAndBody dizesini kullanarak son bölümü oluştururuz. Daha sonra, bir başlık ve bir son bölümü nasıl oluşturduğumuzu göreceğiz.

Başlık

Başlık, FIX mesajının ilk kısmıdır ve aşağıdaki alanlardan oluşur (tüm mesajlar için aynıdır):

  1. BeginString – başlangıç dizesi FIX protokol sürümünü tanımlar ve bizim durumumuzda FIX4.4 olarak sabittir.
  2. BodyLength – gövde uzunluğu, BeginString, BodyLength ve son bölüm alanları hariç, mesajın karakter cinsinden uzunluğunu belirtir.
  3. MsgType – bu alanda mesaj türünü tanımlarız, böylece alıcı gövdeyi nasıl ayrıştıracağını bilir.
  4. SenderCompID – burada SenderCompID değerini ayarlarız.
  5. TargetCompID – bu, mesajımızın hedefidir. Bizim durumumuzda, her zaman cServer olacaktır.
  6. SenderSubID – trader girişi.
  7. MsgSeqNum – bu, mesajın sıra numarasıdır. Aynı oturumda gönderilen her mesaj için artırılması gerekir.
  8. Sending Time – mesaj iletim zamanı.

Aşağıda, başlıkları oluşturmaktan sorumlu olan ConstructHeader işlevini görebilirsiniz.

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

    }

Alt bilgi

Son bölüm, mesajın geri kalanının sağlama toplamını içeren bir etikettir.

 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;

       }

Sistem mesajları

Örneğimiz, aşağıdaki sistem mesajlarını döndüren işlevler içerir:

  • Heartbeat: MessageConstructor.HeartbeatMessage()
  • Test isteği: MessageConstructor.TestRequestMessage()
  • Oturum açma: MessageConstructor.LogonMessage()
  • Oturum kapatma: MessageConstructor.LogoutMessage()
  • Yeniden gönderme isteği: MessageConstructor.ResendMessage()
  • Reddetme: MessageConstructor.RejectMessage()
  • Sıra sıfırlama: MessageConstructor.SequenceResetMessage()

Uygulama mesajları

Örneğimiz, aşağıdaki sistem mesajlarını döndüren işlevler içerir:

  • Piyasa verisi isteği: MessageConstructor.HeartbeatMessage()
  • Anlık piyasa görüntüsü/tam yenileme: MessageConstructor.MarketDataSnapshotMessage()
  • Artımlı piyasa verisi yenileme: MessageConstructor.MarketDataIncrementalRefreshMessage()
  • Yeni tekli emir: MessageConstructor.NewOrderSingleMessage()
  • Emir durumu isteği: MessageConstructor.OrderStatusRequest()
  • İşlem raporu: MessageConstructor.ExecutionReport()
  • İş mesajı reddi: MessageConstructor.BusinessMessageReject()
  • Pozisyonlar için istek: MessageConstructor.RequestForPositions()
  • Pozisyon raporu: MessageConstructor.PositionReport()

Mesaj gönderin ve yanıt alın

cServer'a bir FIX mesajı göndermek için öncelikle sunucuyla bir bağlantı kurmanız gerekir. Bunu bir TcpClient oluşturarak yapabilirsiniz. Bizim durumumuzda iki istemci oluştururuz, çünkü fiyat teklifi mesajları ve işlem mesajları sunucudaki farklı bağlantı noktaları tarafından işlenir.

Ardından, mesajların gönderileceği iki akışı almamız gerekir. Bu süreç, aşağıda gösterildiği gibi formun yapıcısında gerçekleşir:

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

}

Yapıcıda, mesajları oluşturmak için kullanılacak bir MessageConstructor sınıfını da başlatırız.

Ardından, mesajları göndermek için SendPriceMessage() ve SendTradeMessage() adında iki farklı işlev oluşturduk. Her biri FIX mesajını girdi olarak alır ve ardından mesajı ve ilgili akışı girdi olarak kullanarak SendMessage() işlevini çağırır.

SendMessage() işlevi şu şekilde çalışır:

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

Ayrıntılı adımlar şunlardır:

  1. Mesajı bir bayt dizisine kodlayın.
  2. Bayt dizisini akışa yazın.
  3. Akıştan yanıtı okuyun.
  4. Mesaj sıra numarasını artırın.
  5. Mesajı bir dizeye kodlayın.

İşlev, sunucu tarafından gönderilen FIX mesajını döndürmelidir.

Tahmin edebileceğiniz gibi, kullanıcıya ham bir FIX mesajı gösteremezsiniz, bu nedenle gelen mesajı ayrıştırmak için ek bir adım geliştirilmelidir.

Sonuç

Bu uygulama, FIX mesajlarını kullanarak cServer ile nasıl iletişim kurulacağını gösteren kısa bir tanıtımdır. Bu sadece FIX Protokolü kavramlarını açıklayan bir örnektir ve kesinlikle tam bir FIX motoru değildir. Kendi FIX motorunuzu oluşturmaktan kaçınmak isterseniz, mevcut üçüncü taraf FIX motorlarından birini kullanmayı düşünebilirsiniz.

Not

Bu makale 03/02/2017 tarihi itibarıyla günceldir ve cTrader FIX motoru, Katılım Kuralları v2.9.1 dikkate alınarak geliştirilmiştir.