⬆️ ⬇️

Financial Transmission Ways # 4: ASTS Bridge Protocol





In addition to international standards and financial information transfer protocols such as FIX and FAST , which we described earlier, the so-called “native” financial data transfer protocols operate on the stock market. They are used to obtain the necessary information by both private traders and brokerage companies - such native protocols are more functional than generally accepted standards (like the same FIX) that attract brokers.



Earlier in Russia there were two large stock exchanges - MICEX and RTS. Subsequently, they merged into a single Moscow Exchange, but each of the two trading platforms during the years of independence managed to develop their own native protocol. We talked about the Plaza II protocol, which was created by RTS specialists, in one of the past materials, and today we will speak about the ASTS Bridge project, which their colleagues from the MICEX began to develop.

')

A bit of history



The first version of the electronic trading system of the Moscow Interbank Currency Exchange (MICEX) was developed and implemented in 1993-1994 of the last century. The system was named ASTS (Automated Securities Trading System) and its software was developed by the Australian company FMSC (Financial Market Software Consultants) and adapted for the Russian market through the joint efforts of foreign and domestic IT specialists.



Later, the FMSC was named Compu ShareLtd with which the MICEX signed a partnership agreement, including further independent development of the trading system, during mergers and acquisitions.







Stages of development of the MICEX trading system until the mid-2000s; source: micex.ru



Further, the system was developed by the specialists of the MICEX exchange, and later the Moscow Exchange. As a result, a three-tier client-server system architecture was implemented.



At the head of the hierarchy was the central server of the trading system (it is responsible for processing transactions) with which the access servers interacted (all the trading system transactions were replicated to them), which in turn were connected to client applications (trading terminals of traders and administrators, brokerage trading systems, complexes of distribution of exchange information, etc.):







Source: MICEX documentation



In this configuration, the system exists since 1998.



Data Transfer: Protocols Used



To connect external systems to the trading system, a universal bidirectional software gateway (UDPSH) mechanism was developed - that is, software, through which data was exchanged between the exchange trading system and applications connected to it.



ASTS Bridge


The program provided bidirectional communication with the trading system and had an API for receiving data (transactions, quotes, tools, etc.) and executing transactions (placing / withdrawing orders, etc.).



There were two versions of UDPSH - the TCP / IP version of the system was called TEAP, and the option to connect using a serial interface (RS-232) was called TEServer (Trade Engine Server). Since the summer of 2015, support for this version has been discontinued.



Subsequently UDPSH received a new name - ASTS Bridge. In Exchange terminology, the gateway (bridge) is the native protocol of the trading and clearing system,



A feature of the gateway protocol is support for so-called "interfaces". As stated in the material "Moscow Exchange" on Habrahabr:



An interface is a versioned set of user-accessible tables and transactions, with the appropriate structure and data types.


Versioning allows system users to remain on older versions of interfaces after upgrades of the exchange system, which entail the need to modify the structure of the data tables or change transaction formats. Currently, there is a possibility of connecting with all versions of interfaces created during all the years of the system, but it is planned to tighten the requirements for them.



Detailed ASTS Bridge documentation is available on the Moscow Exchange FTP. Among other things, there are descriptions of existing interfaces .



How it works



At the moment, the ASTS Moscow Exchange trading and clearing system ensures the functioning of the main markets of the trading floor and supports several trading methods, such as the Order-Driven Market and the Quote-Driven Market. More information about the functionality of the system can be found on the website of the Exchange .



The server part of the client application is installed on the server connected to the closed trading network of the exchange, and the client part runs on a computer connected to the client’s network. There is also the possibility of placing the client’s trading application on collocations in the data center of the M1 exchange. In this case, the use of the so-called built-in version of the gateway is allowed, which allows you to connect directly to exchange access servers.



The client’s trading application must use the provided API functions to retrieve data from the ASTS system and perform transactions. The data is obtained using the client pull technology - the application must “pull” the data itself, that is, interrogate the tables to obtain the actual market information.



Thus, to start work, the developer of the trading application needs to download the ASTS Bridge gateway on the Exchange website , and then connect it to the test version of ASTS. Upon completion of development and debugging, each application connected to the system is certified by the Exchange.



After passing through all these stages, the scheme of the application operation with the ASTS system is as follows.





The application should provide mechanisms for restoring the state of open tables after failures and loss of communication - without this, a certificate for the subsequent operation of the application will not succeed.



Below is an example of implementing a Java demo application that connects and requests data from the ASTS system:



import com.micex.client.API.ServerInfo; import com.micex.client.Binder; import com.micex.client.Client; import com.micex.client.ClientException; import com.micex.client.Filler; import com.micex.client.Meta; import com.micex.client.Parser; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; public class Demo implements Binder { public static void main(String[] args) throws ClientException { final Map<String,String> m = new HashMap<String, String>(); m.put("PacketSize","60000"); m.put("Interface","IFCBroker_20"); m.put("Server","INET_GATEWAY"); m.put("Service","inet_gateway"); m.put("Broadcast","91.208.232.101"); m.put("PrefBroadcast","91.208.232.101"); m.put("UserID", "MU0000800001"); m.put("Password", ""); m.put("Language", "English"); new Demo().run(m); } public void run(Map<String,String> parameters) throws ClientException { Client client = new Client(); client.start(parameters); try { // Some useful info about connection System.out.println(String.format("Connected to MICEX, handle=%d", client.handle())); final ServerInfo info = client.getServerInfo(); System.out.println(String.format("SystemID=%s; SessionID=%d; UserID=%s", info.systemID, info.sessionID, info.userID)); // Parsed market interface, contains meta-information // about available requests (tables) / transactions // and their structure definition. final Meta.Market market = client.getMarket(); System.out.println(String.format("Market: %s", market.name)); // Optional MTESelectBoards final Set<String> b = new HashSet<String>(); b.add("TQBR"); // Use only one - limit number of SECURITIES in demo client.selectBoards(b); Parser parser; // load() table - mimics a sequence of MTEOpenTable/MTECloseTable. // Use it for non-updateable info-requests parser = client.load("MARKETS", null); parser.execute(this); // open() table - it will also be added to the list // of requests to be updated at refresh() call parser = client.open("TESYSTIME", null, true); parser.execute(this); // open() SECURITIES (params==null - all securities) parser = client.open("SECURITIES", null, false); parser.execute(this); // another (better) way to specify table name Meta.Message orderbooks = market.tables().find(Meta.TableType.Orderbooks); if (orderbooks != null) { parser = client.open(orderbooks.name, null, false); parser.execute(this); } // make 10 refresh() iterations with some delay between them for (int i = 0; i < 10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { return; } parser = client.refresh(); if (parser.empty()) continue; // nothing to parse, skip the rest int bytes = parser.length(); int count = parser.execute(this); System.out.println("===================="); System.out.println("Parsed " + bytes + " bytes, " + count + " rows"); } } finally { client.close(); System.out.println("===================="); System.out.println("Done."); } } /** * Very simplistic table storage. */ final Map<String, Table> database = new HashMap<String, Table>(); /** * @param source - a TE request which is gonna be parsed * @return - a Filler instance which will be used to * store parsed values in some kind of storage or {@code null} * if not interested in storing parsed data. */ public Filler getFiller(Meta.Message source) { // Of course, IRL you shouldn`t return a new instance // of Filler instance, but search some kind of // internal database and return the same "table" // instance for every source message. Table table = database.get(source.name); if (table == null) { table = new Table(); database.put(source.name, table); } return table; } /** * Very simplistic record. */ static class Record { int decimals; final Map<String,Object> values = new LinkedHashMap<String, Object>(); } /** * Very simplistic table. */ static class Table implements Filler { /** * ,      ( ) *        . */ final Map<String, Record> records = new HashMap<String, Record>(); /** *     "" -  , *     (SECBOARD + SECCODE). */ final Map<String, List<Record>> orderbooks = new HashMap<String, List<Record>>(); public boolean initTableUpdate(Meta.Message table) { //     . //       //      // table.isClearOnUpdate() table.isOrderbook(); if (table.isClearOnUpdate()) records.clear(); return true; //   1.1.0   . } public void doneTableUpdate(Meta.Message table) { //    , - commit. //    . orderbook = null; } final Map<String, Object> keys = new LinkedHashMap<String, Object>(); public void setKeyValue(Meta.Field field, Object value) { //    . //  - -    map. //        //  (. doneRecordUpdate()) keys.put(field.name, value); } Record current; List<Record> orderbook; public boolean initRecordUpdate(Meta.Message table) { //   . //       //   true,     (.. ) if (table.isOrderbook()) { //    "orderbook" -   //    ,    "" current = new Record(); orderbook.add(current); return true; } else { System.out.println("Table:" + table.name +"; keys: " + keys.toString()); if (keys.isEmpty()) { // setKeyValue()      - //      . //     ,  //     . current = new Record(); records.put(Integer.toString(records.size()), current); return true; } else { //      - //       . final String key = keys.toString(); current = records.get(key); if (current == null) { //      -   current = new Record(); records.put(key, current); return true; } //    return false; } } } public void setRecordDecimals(int decimals) { //      // -   -  ( ). current.decimals = decimals; } public int getRecordDecimals() { //      //  -     //   -  . return current.decimals; } public void setFieldValue(Meta.Field field, Object value) { //       current.values.put(field.name, value); } public void doneRecordUpdate(Meta.Message table) { //   . //       -. //  - -    . System.out.println("Table:" + table.name +"; data: " + current.values.toString()); //        , //        . keys.clear(); current = null; } public void switchOrderbook(Meta.Message table, Meta.Ticker ticker) { //      "" (table.isOrderbook()) //       setKeyField()    //  ,    " " -  . //      ,     //    ticker. orderbook = orderbooks.get(ticker.toString()); if (orderbook == null) { //   , //    "" orderbook = new ArrayList<Record>(); orderbooks.put(ticker.toString(), orderbook); } else { // ""   -   , // ..     . orderbook.clear(); } } } } 


Important point: despite the fact that the example above is in Java, in fact, you can use any languages ​​to work with the ASTS Bridge protocol. There are no restrictions for developers of trading systems in this regard.



Conclusion



According to representatives of the Moscow Exchange in June 2015, its own native protocol is still inferior in popularity among traders to the FIX protocol. In the stock market, FIX accounts for up to 60% of applications.



Nevertheless, ASTS Bridge has its own advantages, for example, the unity of information objects (for example, tables and transactions), which are the same for all markets, which makes it easier to adapt trading applications at work on each of them. One more advantage of ASTS Bridge is the low system requirements - to use this protocol, a dedicated server is not required, the client application can be run even on a personal computer.



To work on this and other protocols described in our previous articles for direct access to the exchange, it is necessary to conclude an agreement with a broker (for example, ITinvest), which helps to organize access to trading on the selected technology.



Today, thank you for your attention, we will be happy to answer questions in the comments.



In our next articles we will continue to talk about existing exchange technologies, in particular, we will discuss the protocol Simple Binary Encoding, which, to a certain extent, is the successor of the FIX case.

Source: https://habr.com/ru/post/270961/



All Articles