СЭТ СР СПбМТСБ Шлюз коммуникационной системы для подключения внешних систем к Системе Электронных Торгов Срочного Рынка СПбМТСБ Руководство разработчикa v.1.15 CMA Small Systems AB 2010 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика Содержание Введение .................................................................................................................................................. 3 Архитектура системы .............................................................................................................................. 4 Функциональные возможности ШКС ..................................................................................................... 5 Особенности реализации ШКС .............................................................................................................. 6 Системные требования .......................................................................................................................... 6 Протокол Protobuf и его применение ..................................................................................................... 6 Требования к длине полей сообщений ...............................................................................................16 Обязательность полей заявок в зависимости от типа заявок ...........................................................18 Пример .proto-файла для обмена сообщениями со шлюзом (ШКС) ................................................19 Библиотека zmqCommClient.dll ............................................................................................................36 Интерфейс коммуникационного модуля ICommModule - описание методов ..................................37 Интерфейс очередей ICommQueue - описание методов ..................................................................38 Сценарий использования коммуникационного модуля и очередей .................................................40 Описание поставки ................................................................................................................................42 © CMA Small Systems AB, 2010 2 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика Введение В данном руководстве описано применение шлюза коммуникационной системы (ШКС), предназначенного для подключения к Системе Электронных Торгов Срочного Рынка Санкт-Петербургской Международной Товарно-сырьевоя Биржи (СЭТ СР СПбМТСБ) внешних (по отношению к ней) систем различного типа, осуществляющих обмен информацией с СЭТ СР СПбМТСБ в режиме реального времени. ШКС представляет собой программную часть аппаратно-программного интерфейса, предназначенного для обмена информацией между СЭТ СР СПбМТСБ и внешними системами. Обмен информацией происходит в режиме реального времени и с обеспечением приемлемого (с точки зрения СПбМТСБ) уровня защиты данных. ШКС обеспечивает двунаправленную связь с торговой системой и содержит программный интерфейс (API), который предназначен как для получения информации из Фьючерсной Торговой Системы (сделки, котировки, инструменты и т.п.), так и для выполнения активных транзакций (постановка/снятие заявок и т.п.). ШКС позволяет подключать к Фьючерсной Торговой Системе СПбМТСБ внешние системы распространения торговой информации и сбора клиентских заявок. © CMA Small Systems AB, 2010 3 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика Архитектура системы Архитектура системы приведена на следующей диаграмме: © CMA Small Systems AB, 2010 4 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика Функциональные возможности ШКС ШКС обеспечивает выполнение следующих основных операций: подключение к СЭТ СР СПбМТСБ от имени пользователя СЭТ СР СПбМТСБ, являющегося пользователем шлюза, с использованием передаваемого из внешней системы идентификатора и пароля; передачу от внешней системы в СЭТ СР СПбМТСБ параметров подписки на получение торговой информации или обновлений торговой информации в соответствии с подписками; передачу от СЭТ СР СПбМТСБ во внешнюю систему торговой информации (или ее обновлений), в соответствии с параметрами подписки; передачу от внешней системы в СЭТ СР СПбМТСБ требований на выполнение активных транзакций (постановку и снятие заявок); передачу от СЭТ СР СПбМТСБ во внешнюю систему ответов СЭТ СР СПбМТСБ на требования внешней системы на выполнение активных транзакций; передачу от СЭТ СР СПбМТСБ во внешнюю систему сообщений об ошибках, произошедших при обработке торговой информации (обновлений), генерируемой по подписке, и ошибок при попытках исполнения активных транзакций; отключение отдельных пользователей от СЭТ СР СПбМТСБ по команде администратора шлюза. Программа обеспечивает протоколирование следующих событий: подключение пользователей к СЭТ СР СПбМТСБ; получение от внешних систем требований транзакций и содержание этих требований; на выполнение активных получение от внешних систем параметров подписок для получения торговой информации (обновлений) и значения этих параметров; отключение пользователей от СЭТ СР СПбМТСБ. © CMA Small Systems AB, 2010 5 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика Особенности реализации ШКС ШКС реализован в виде двух программных компонентов. 1. Шлюз предназначен для подключения к СЭТ СР СПбМТСБ по протоколу Protobuf. Доступны следующие варианты функционирования компонента: 2. Под управление операционных систем семейства Microsoft Windows Windows Server 2008 R2 или Microsoft Windows 7 и выше. Под управлением операционной системы Linux. Доступны консольный и фоновый (daemon) режимы работы. Динамическая библиотека (zmqCommClient.dll), предоставляет прикладной программный интерфейс (API) для подключения к ШКС. Передача данных между внешними системами и шлюзом осуществляется при помощи протокола TCP/IP с использованием брандмауэра. Данные при этом передаются в виде двух отдельных потоков: торговая информация, передаваемая от СЭТ СР СПбМТСБ в режиме подписки; транзакционные данные, необходимые для осуществления торговых транзакций, передаваемые в режиме «запрос-ответ». Внешние системы могут используют сразу оба варианта подключения, так и каждый поток в отдельности. Одно соединение используется для информационного обмена, второе – для обеспечения транзакционного обмена. Шлюз работает в многопользовательском режиме, то есть он может работать одновременно с многими внешними системами, каждая из которых поддерживает один или два потока данных. Системные требования Для функционирования внешней системы с использованием библиотеки zmqCommClient.dll предъявляются следующие минимальные требования к программному и аппаратному обеспечению комплекса: Операционная система: Microsoft Windows Server R2 и Microsoft Windows 7 с установленным Service Pack 2 и выше; Компьютеры: o Процессор – Intel Core 2 Duo или выше; o ОЗУ – от 2Gb; o Жесткий диск с 10Gb свободного пространства; o Наличие Ethernet сетевой карты; Данные требования не учитывают особенностей внешней системы и могут быть скорректированы в сторону повышения с учетом ресурсов, необходимых внешней системы для нормального функционирования. Протокол Protobuf и его применение Для подключения внешних систем к СЭТ СР СПбМТСБ, данные, которыми обмениваются внешние системы и шлюз, соответствуют форматам, определяемым содержимым .protoфайлов протокола Google Protocol Buffers (Protobuf). © CMA Small Systems AB, 2010 6 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика В .proto-файлах хранятся следующие настройки соединений: описание доступных внешней системе информационных объектов (наименований информационных объектов, состава полей и дополнительной информации); описание доступных внешней системе полей информационных объектов (наименований полей информационных объектов, их типа, формата и дополнительной информации); описание доступных внешней системе активных транзакций (наименований активных транзакций, состава полей и дополнительной информации); описание доступных внешней системе полей активных транзакций (наименований полей активных транзакций, их типа, формата и дополнительной информации); описание откликов транзакции; СЭТ СР на доступные внешней системе Протокол Protobuf предложен фирмой Google для хранения структурированных данных, он обладает эффективностью и гибкостью. и активные передачи Если не применять Protobuf, то задачу упаковки извлечения данных можно было бы выполнять различными способами, например одним из перечисленных вариантов: Можно сохранять и/или передавать данные в бинарном формате. Впоследствии это приведет к проблемам из-за соответствий полей. После того, как ПО, обрабатывающее эти данные, будет передано многим пользователям, станет трудно менять формат данных, т.к. старое ПО не сможет обрабатывать данные в новом формате. Можно создать собственный формат сообщений, например, передавая четыре целых числа 12, 3, -23, 67 в текстовой строке с разделителями "12:3:-23:67". Этот подход прост и гибок, но требует написания индивидуальных фрагментов кода для упаковки и синтаксического анализа (парсинга) передаваемых данных. Этот подход приемлем только для очень простых структур данных. Можно передавать структурированные данные в формате XML. Это хороший способ, т.к. XML в значительной мере понятен для восприятия человеком и для его обработки существует множество библиотек. Он особенно полезен, когда нужно передавать ваши данные во многие сторонние приложения или проекты. Однако, по сравнению с Protobuf, сообщения XML недостаточно компактны, а их обработка достаточно сложна. Protobuf был разработан, чтобы решать эту задачу без указанных выше недостатков. Протокол Protobuf можно применять в ситуациях, когда не требуется передавать большие и сложные структуры данных и когда не требуется такое присущее XML свойство, как самоописывание (self-describing). При этом, по сравнению с XML, сообщения Protobuf оказываются компактнее, проще и обрабатываются быстрее. Сравните примеры сообщений в протоколах XML и Protobuf: XML <person> <name>John Doe</name> <email>[email protected]</email> </person> Для синтаксического анализа требуется около 5000-10000 наносекунд. Protobuf person { name: "John Doe" email: "[email protected]" } (в упакованном формате занимает около 28 бит) Для синтаксического анализа требуется около 100-200 наносекунд Пример кода, обрабатывающего сообщение XML из примера: © CMA Small Systems AB, 2010 7 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика cout << "Name: " << person.getElementsByTagName("name")->item(0)->innerText() << endl; cout << "E-mail: " << person.getElementsByTagName("email")->item(0)->innerText() << endl; Пример кода, обрабатывающего сообщение Protobuf из примера: cout << "Name: " << person.name() << endl; cout << "E-mail: " << person.email() << endl; По оценкам Google, по сравнению с XML, Protobuf обладает следующим достоинствами: проще объем передаваемых данных меньше в 3-10 раз скорость обработки выше в 20-100 раз обладает более простой, однозначной структурой на нем легче программировать классы доступа к данным Информацию о протоколе Protobuf смотрите здесь: http://code.google.com/p/protobuf/ Документацию и учебные материалы по Protobuf смотрите здесь: http://code.google.com/intl/ru/apis/protocolbuffers/docs/overview.html http://code.google.com/intl/ru/apis/protocolbuffers/docs/proto.html http://code.google.com/intl/ru/apis/protocolbuffers/docs/tutorials.html Protobuf позволяет добавлять новые структуры в формат передаваемых данных без необходимости внесения изменений в старые отлаженные и скомпилированные программы. Основные принципы использования Protobuf Сообщения Protobuf не являются самоописывающими. Сами передаваемые сообщения являются по сути парами "имя/значение". Структура передаваемых данных задается в специальных .proto-файлах (определениях сообщений). Ниже приведен пример .proto-файла, задающего структуру информации о людях: message Person { required string name = 1; required int32 id = 2; optional string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME]; } repeated PhoneNumber phone = 4; } Формат сообщений несложен: каждый тип сообщений содержит одно или несколько уникально нумерованных полей. Каждое поле имеет имя и тип. Возможны следующие типы полей: числовой (целый, вещественный) логический строковый © CMA Small Systems AB, 2010 8 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика бинарные данные без обработки (raw bytes) другие типы данных, позволяющие создавать структурированные данные (как в приведенном выше примере) Когда определения сообщений созданы, они компилируются для применяемого вами языка программирования, для формирования классов доступа к данным. Благодаря этому становится возможным удобный доступ как к отдельным полям, так и к методам для синтаксического анализа бинарных данных. Например, если вы программируете на С++, то вышеприведенного примера будет создан класс Person, использовать для соответствующих сообщений. при компилировании который можно будет Вы сможете писать, например, такой код, формирующий исходящее сообщение и записывающий его в файл: Person person; person.set_name("John Doe"); person.set_id(1234); person.set_email("[email protected]"); fstream output("myfile", ios::out | ios::binary); person.SerializeToOstream(&output); Это сообщение можно будет затем прочитать из файла и разобрать при помощи следующего кода: fstream input("myfile", ios::in | ios::binary); Person person; person.ParseFromIstream(&input); cout << "Name: " << person.name() << endl; cout << "E-mail: " << person.email() << endl; При добавлении новых полей в сообщения сохраняется обратная совместимость со старыми версиями программ (новые поля просто игнорируются при синтаксическом анализе). Поэтому, при внесении дополнений в протоколы обмена сообщениями, старые программы не перестанут работать. Применение Protobuf при программировании на С++ Рассмотрим пример использования сообщений Protobuf в программе, обрабатывающей адресную книгу. Предполагается, что каждый человек имеет имя, идентификатор, адрес электронной почты и контактный телефон. © CMA Small Systems AB, 2010 9 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика Для обработки этих данных потребуется создать специфичный для них .proto-файл. Синтаксис .proto-файлов несложен: для каждой передаваемой структуры данных создается блок message, в котором для каждого поля указываются имя (name) и тип (type). Синтаксис .proto-файлов похож на синтаксис C++ или Java. Ниже приведен пример файла addressbook.proto для адресной книги: package tutorial; message Person { required string name = 1; required int32 id = 2; optional string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME]; } repeated PhoneNumber phone = 4; } message AddressBook { repeated Person person = 1; } Ниже описаны отдельные фрагменты этого файла: package tutorial; .proto-файл начинается с объявления пакета, что помогает предотвращать конфликты именования между различными проектами. Для C++ будут генерироваться классы, помещаемые в пространства имен, соответствующие именам пакетов. Далее следуют определения типов сообщений (блоки message). Каждое определение сообщения содержит описания одного или нескольких полей, относящихся к тем или иным типам. Типом поля может быть стандартный тип (среди которых: логический bool, целый32 - int32, вещественный - float, двойной точности - double, строковый string). Структуру передаваемых данных можно усложнять, указывая в качестве типа поля типы сообщений, например, в вышеприведенном примере, определяется, что: сообщение Person содержит сообщения PhoneNumber сообщение AddressBook содержит сообщения Person Можно даже определять типы сообщений внутри определений других типов сообщений. Так, тип сообщений PhoneNumber определен внутри типа сообщений Person Также можно создавать типы сообщений при помощи ключевого слова enum, задающего возможные значения поля. В нашем примере поля с типом PhoneType могут иметь только следующие значения: MOBILE HOME WORK Цифры после знаков равенства (= 1 , = 2 и т.д.) около каждого из элементов являются уникальными тегами, используемыми для преобразования в двоичный код. Для тегов от 1 до 15 при преобразовании в двоичный код требуется лишь один байт, поэтому их рекомендуется использовать для часто передаваемых данных. Каждое поле обязательно должно сопровождаться одним из модификаторов (modifier): © CMA Small Systems AB, 2010 10 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика required ("обязательное") - означает обязательность данного поля. При отсутствии поля, имеющего этот модификатор, сообщение считается неинициализированным ("uninitialized"). Создание неинициализированных сообщений может приводить к сообщению об ошибках, а синтаксический анализ неинициализированных сообщений всегда возвращает значение false. Рекомендуется использовать этот модификатор с осторожностью, т.к. впоследствии могут возникнуть трудности при попытке отказаться от таких полей. optional ("необязательное", "опциональное") - означает необязательность данного поля. Если такое поле не задано, применяется значение по умолчанию (default). Для простых типов вы можете задать свои собственные значения по умолчанию, как в вышеприведенном примере типом телефона по умолчанию является домашний телефон (default = HOME). Также применяются системные значения по умолчанию: для чисел - ноль, для строк - пустая строка, для логического типа - false. repeated ("повторяющееся") - такое поле может повторяться много раз либо отсутствовать (т.е. количество повторений поля - любое, включая ноль). Protobuf не меняет порядка следования повторяющихся полей. Повторяющиеся поля напоминают массивы переменной длины. © CMA Small Systems AB, 2010 11 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика Генерирование исходного кода на С++ из .proto-файла В комплекте поставки Protobuf имеется программа-кодогенератор, которая на основе .proto-файла генерирует исходный код на поддерживаемых языков высокого уровня, в том числе, код на языке С++. Программа-кодогенератор предоставляется бесплатно на условиях, записанных в ее комментариях. // // // // // // // // // // // // // // // // // // // // // // // // // // // // // Protocol Buffers - Google's data interchange format Copyright 2008 Google Inc. All rights reserved. http://code.google.com/p/protobuf/ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Author: [email protected] (Kenton Varda) // Based on original Protocol Buffers design by // Sanjay Ghemawat, Jeff Dean, and others. // // Generates C++ code for a given .proto file. #ifndef GOOGLE_PROTOBUF_COMPILER_CPP_GENERATOR_H__ #define GOOGLE_PROTOBUF_COMPILER_CPP_GENERATOR_H__ #include <string> #include <google/protobuf/compiler/code_generator.h> namespace namespace namespace namespace google { protobuf { compiler { cpp { // CodeGenerator implementation which generates a C++ source file and // header. If you create your own protocol compiler binary and you want // it to support C++ output, you can do so by registering an instance of this // CodeGenerator with the CommandLineInterface in your main() function. class LIBPROTOC_EXPORT CppGenerator : public CodeGenerator { public: CppGenerator(); ~CppGenerator(); // implements CodeGenerator ---------------------------------------bool Generate(const FileDescriptor* file, const string& parameter, OutputDirectory* output_directory, string* error) const; private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CppGenerator); }; } } } // namespace cpp // namespace compiler // namespace protobuf } // namespace google #endif // GOOGLE_PROTOBUF_COMPILER_CPP_GENERATOR_H__ © CMA Small Systems AB, 2010 12 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика Подробности о генерировании кода, соответствующего .proto-файлу см. здесь: http://code.google.com/intl/ru/apis/protocolbuffers/docs/proto.html http://code.google.com/intl/ru/apis/protocolbuffers/docs/reference/cppgenerated.html#plugins http://code.google.com/intl/ru/apis/protocolbuffers/docs/cpptutorial.html Нашему примеру с адресной книгой будет соответствовать такой код: // name inline inline inline inline inline inline // id inline inline inline inline bool has_name() const; void clear_name(); const ::std::string& name() const; void set_name(const ::std::string& value); void set_name(const char* value); ::std::string* mutable_name(); bool has_id() const; void clear_id(); int32_t id() const; void set_id(int32_t value); // email inline bool has_email() const; inline void clear_email(); inline const ::std::string& email() const; inline void set_email(const ::std::string& value); inline void set_email(const char* value); inline ::std::string* mutable_email(); // phone inline int phone_size() const; inline void clear_phone(); inline const ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >& phone() const; inline ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >* mutable_phone(); inline const ::tutorial::Person_PhoneNumber& phone(int index) const; inline ::tutorial::Person_PhoneNumber* mutable_phone(int index); inline ::tutorial::Person_PhoneNumber* add_phone(); Пример создания сообщения #include <iostream> #include <fstream> #include <string> #include "addressbook.pb.h" using namespace std; // This function fills in a Person message based on user input. void PromptForAddress(tutorial::Person* person) { cout << "Enter person ID number: "; int id; cin >> id; person->set_id(id); cin.ignore(256, '\n'); cout << "Enter name: "; getline(cin, *person->mutable_name()); cout << "Enter email address (blank for none): "; string email; getline(cin, email); if (!email.empty()) { person->set_email(email); } while (true) { cout << "Enter a phone number (or leave blank to finish): "; string number; getline(cin, number); if (number.empty()) { break; } tutorial::Person::PhoneNumber* phone_number = person->add_phone(); phone_number->set_number(number); © CMA Small Systems AB, 2010 13 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика cout << "Is this a mobile, home, or work phone? "; string type; getline(cin, type); if (type == "mobile") { phone_number->set_type(tutorial::Person::MOBILE); } else if (type == "home") { phone_number->set_type(tutorial::Person::HOME); } else if (type == "work") { phone_number->set_type(tutorial::Person::WORK); } else { cout << "Unknown phone type. Using default." << endl; } } } // Main function: Reads the entire address book from a file, // adds one person based on user input, then writes it back out to the same // file. int main(int argc, char* argv[]) { // Verify that the version of the library that we linked against is // compatible with the version of the headers we compiled against. GOOGLE_PROTOBUF_VERIFY_VERSION; if (argc != 2) { cerr << "Usage: return -1; } " << argv[0] << " ADDRESS_BOOK_FILE" << endl; tutorial::AddressBook address_book; { // Read the existing address book. fstream input(argv[1], ios::in | ios::binary); if (!input) { cout << argv[1] << ": File not found. Creating a new file." << endl; } else if (!address_book.ParseFromIstream(&input)) { cerr << "Failed to parse address book." << endl; return -1; } } // Add an address. PromptForAddress(address_book.add_person()); { // Write the new address book back to disk. fstream output(argv[1], ios::out | ios::trunc | ios::binary); if (!address_book.SerializeToOstream(&output)) { cerr << "Failed to write address book." << endl; return -1; } } // Optional: Delete all global objects allocated by libprotobuf. google::protobuf::ShutdownProtobufLibrary(); return 0; } © CMA Small Systems AB, 2010 14 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика Пример чтения сообщения #include <iostream> #include <fstream> #include <string> #include "addressbook.pb.h" using namespace std; // Iterates though all people in the AddressBook and prints info about them. void ListPeople(const tutorial::AddressBook& address_book) { for (int i = 0; i < address_book.person_size(); i++) { const tutorial::Person& person = address_book.person(i); cout << "Person ID: " << person.id() << endl; cout << " Name: " << person.name() << endl; if (person.has_email()) { cout << " E-mail address: " << person.email() << endl; } for (int j = 0; j < person.phone_size(); j++) { const tutorial::Person::PhoneNumber& phone_number = person.phone(j); switch (phone_number.type()) { case tutorial::Person::MOBILE: cout << " Mobile phone #: "; break; case tutorial::Person::HOME: cout << " Home phone #: "; break; case tutorial::Person::WORK: cout << " Work phone #: "; break; } cout << phone_number.number() << endl; } } } // Main function: Reads the entire address book from a file and prints all // the information inside. int main(int argc, char* argv[]) { // Verify that the version of the library that we linked against is // compatible with the version of the headers we compiled against. GOOGLE_PROTOBUF_VERIFY_VERSION; if (argc != 2) { cerr << "Usage: return -1; } " << argv[0] << " ADDRESS_BOOK_FILE" << endl; tutorial::AddressBook address_book; { // Read the existing address book. fstream input(argv[1], ios::in | ios::binary); if (!address_book.ParseFromIstream(&input)) { cerr << "Failed to parse address book." << endl; return -1; } } ListPeople(address_book); // Optional: Delete all global objects allocated by libprotobuf. google::protobuf::ShutdownProtobufLibrary(); return 0; } © CMA Small Systems AB, 2010 15 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика Требования к длине полей сообщений При проектировании клиентского ПО, разработчики должны обеспечить, передаваемые поля сообщений не превышали следующих значений: Поле Код участника Наименование участника Код ИНН участника Код КПП участника Код ОГРН участника Код ОКПО участника Код пользователя (имя для входа в систему, логин) Наименование пользователя Пароль пользователя для входа в систему Код позиционного регистра Наименование позиционного регистра Код клиента Наименование клиента Код ИНН клиента Код секции Наименование секции Код режима торгов Наименование режима торгов Код типа финансовых инструментов Наименование типа финансовых инструментов Код базиса поставки Наименование базиса поставки Код финансового инструмента Наименование финансового инструмента Код валюты финансового инструмента Наименование валюты финансового инструмента Код открытой позиции чтобы Макс. длина 12 70 20 20 20 20 12 70 32 25 70 16 70 20 16 70 16 70 12 70 16 70 16 70 12 32 Определяется по формуле: (Код финансового инструмента) + (Код позиционного регистра) Код инструмента вместе с кодом режима торгов Определяется по формуле: (Код финансового инструмента) + (Код режима торгов) Наименование сессии Код заявки Уникальный (в пределах сессии) код заявки Текст комментария в заявке Описание результата исполнения заявки Код сделки Код события Описание события Код входящего сообщения Текст входящего сообщения Код состояния Обозначение операционного дня Наименование состояния Переход между состояниями (ручной или автоматический) Код времени состояния © CMA Small Systems AB, 2010 70 12 31 255 255 12 12 511 12 511 12 10 64 32 8 16 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Код статуса состояния Код текстового сообщения Текстовое сообщение Код станции назначения Наименование станции назначения © CMA Small Systems AB, 2010 Руководство разработчика 32 12 512 12 64 17 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика Обязательность полей заявок в зависимости от типа заявок В зависимости от типа, заявки имеют различный состав обязательных полей, в соответствии с нижеприведенной таблицей: ОБЯЗАТЕЛЬНОСТЬ ПОЛЕЙ В РАЗЛИЧНЫХ ТИПАХ ЗАЯВОК ●- обязательные поля Тип заявки ○ - необязательные поля Специальная Рыночная Поставочная Лимитированная Ликвидационная Поля Адресная × - не должно заполняться Идентификатор сообщения (item_id) × × × × × × Уникальный (в пределах сессии) номер транзакции заявки (trn) Тип заявки (type) ● ● ● ● ● ● ● ● ● ● ● ● ● ● × ● ● ● ● ● ● ● ● ● ● × ● ● ● ● ● ● ● ● ● ● × ● ● ● ○ ● ● ● ● ○ ● × ● ● ● ● ● ● ● ● ○ ● × ● ● ● ● ● ● ● ● ● ● × ● ● ● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ × × × × × × × × × × × × ○ ○ ○ ○ ○ ○ × × × ● × × Параметры заявки (params) Направление заявки (buy_sell) Режим торгов (board) Финансовый инструмент (security) Позиционный регистр (account) Цена (price) Количество (qty) Время жизни заявки (time_in_force) Фирма (firm) Клиент (client) Контрагент для адресной заявки (cp_firm) Комментарий (comment) Идентификатор заявки, формируемый торговым сервером. Заполняется только при отправлении из торговой системы в клиринговую. (id) Метка времени (time_stamp) Флаг принадлежности заявки маркет мейкеру (isMarketMaker) Станция отправления/назначения (destination) © CMA Small Systems AB, 2010 18 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика Пример .proto-файла для обмена сообщениями со шлюзом (ШКС) Ниже приведен пример .proto-файла для обмена сообщениями с ШКС. В рамках примера использования библиотеки zmqCommClient.dll, поставляемой совместно с данным руководством, разработчикам внешних систем будет предоставлен заранее сгенерированный код на языке С++ для сборки и разбора сообщений. В случае изменения используемого ё.proto-файла, разработчикам внешних систем надо будет скомпилировать измененный .proto-файл и заменить соответствующие фрагменты исходного кода внешних систем. // // Protocol Buffer messages description file for Futures Trading System // $Id: FTEMessages.proto 37765 2010-12-06 15:38:20Z anpav $ // package FTE; option java_package = "se.highex.core2CF.server.ft.fmt.protobuf"; option java_outer_classname = "FTEProtos"; option optimize_for = SPEED; // Набор типов возможных сообщений // Префикс REQ_* используется для запросов // Префикс REP_* используется для ответов на соответствующие запросы // enum MsgType { REQ_UNKNOWN = -1; /* Requests */ REQ_NEW_PARTICIPANT = 1; // Запрос на создание нового участника REQ_NEW_USER = 2; // Запрос на создание нового пользователя REQ_NEW_CLIENT = 3; // Запрос на создание нового клиента REQ_NEW_ACCOUNT = 4; // Запрос на создание нового счета REQ_NEW_SECTION = 5; // Запрос на создание новой секции REQ_NEW_BOARD = 6; // Запрос на создание нового режима торгов REQ_NEW_SECTYPE = 7; // Запрос на создание нового типа срочного контракта REQ_NEW_SECURITY = 8; // Запрос на создание нового срочного контракта REQ_LOGON = 9; // Регистрация в информационной или транзакционной подсистеме REQ_LOGOFF = 10; // Завершение работы в информационной или транзакционной подсистеме REQ_ORDER = 11; // Посылка заявки REQ_CANCEL_ORDER = 12; // Запрос на сняние заявки REQ_OPEN_SECBOARD = 13; // Открытие режима торгов REQ_CLOSE_SECBOARD = 14; // Закрытие режима торгов REQ_SUSPEND_SECBOARD = 15; // Приостановка режима торгов REQ_RESUME_SECBOARD = 16; // Продолжение нормального функционирования режима торгов REQ_SUBSCRIBE = 17; // Подпись на определенную информационную таблицу REQ_UNSUBSCRIBE = 18; // Запрос на завершение получения данных по определенной информационной таблицы REQ_ORDER_ALARM = 19; // Сообщение о необычной заявке REQ_TRADE_ALARM = 20; // Сообщение о необычной сделке REQ_CLOSE_DAY = 21; // Запрос на закрытие торгового дня REQ_SUSPEND_PARTICIPANT = 22; // Блокировка участника REQ_RESUME_PARTICIPANT = 23; // Разблокировка участника REQ_SUSPEND_USER = 24; // Блокировка пользователя REQ_RESUME_USER = 25; // Разблокировка пользователя REQ_SUSPEND_CLIENT = 26; // Блокировка клиента REQ_RESUME_CLIENT = 27; // Разблокировка клиента REQ_SUSPEND_ACCOUNT = 28; // Блокировка регистра REQ_RESUME_ACCOUNT = 29; // Разбокировка регистра REQ_SUSPEND_SECTION = 30; // Блокировка секции REQ_RESUME_SECTION = 31; // Разблокировка секции REQ_MEDDELANDE = 32; // Посылки текстового сообщения REQ_CLR_TRADE = 33; // Запрос на регистрацию сделки в клиринговой системе REQ_CLR_SETTLE_PRICE = 34; // Запрос на регистрацию новой расчетной цены в клиринговой системе REQ_ACCEPT_ORDER = 35; // Посылка аксепта из клиринговой системы REQ_CLR_ORDER_REESTR = 36; // Запрос на пересылку в клиринговую систему реестра заявок REQ_CLR_TRADE_REESTR = 37; // Запрос на пересылку в клиринговую систему реестра сделок REQ_CLR_ORDER_CANC_REESTR = 38; // Запрос на пересылку в клиринговую систему реестра снятых заявок REQ_BRD_SECURITIES = 39; // Запрос на подготовку данных для открыти торгов по серии срочного контракта REQ_OPEN_BOARD = 40; // Запрос на открытия для торгов режима торгов REQ_CLOSE_BOARD_FOR_ORDERS= 41; // Запрос на закрытие приема заявок и "заморозка" принятых заявокдля режима торгов REQ_CLOSE_BOARD = 42; // Запрос на закрытие режима торгов REQ_HOLDING = 43; // Запрос на передачу данных по холдингу из клиринговой системы REQ_BD_STATE = 44; // Зарезервировано. В текущей версии не используется © CMA Small Systems AB, 2010 19 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика REQ_OPEN_BOARD_FOR_ORDERS = 45; // Запрос на повторное открытие режима торгов для приема заявок REQ_CLR_ORDER_REESTR_RFQ = 46; // Запрос на пересылку в клиринговую систему реестра заявок RFQ REQ_CLR_TRADE_REESTR_RFQ = 47; // Запрос на пересылку в клиринговую систему реестра сделок RFQ REQ_CLR_ORDER_REESTR_DELI = 48; // Запрос на пересылку в клиринговую систему реестра заявок на физ. исполнение REQ_CLR_TRADE_REESTR_DELI = 49; // Запрос на пересылку в клиринговую систему реестра сделок на физ. исполнение REQ_CLR_ORDER_REESTR_SPEC = 50; // Запрос на пересылку в клиринговую систему реестра заявок специальной торговой сессии REQ_CLR_TRADE_REESTR_SPEC = 51; // Запрос на пересылку в клиринговую систему реестра сделок специальной торговой сессии REQ_CLR_ORDER = 52; // Запрос на проверку заявки в клиринговой системе REQ_CLR_ORDER_RFQ = 53; // Запрос на проверку заявки во время ликвидационной сессии REQ_CLR_ORDER_DELI = 54; // Запрос на проверку заявки на физическое исполнение REQ_CLR_ORDER_SPEC = 55; // Запрос на проверку заявки во время специальной сессии REQ_CLR_DELI_MATCH = 56; // Запрос на регистрацию сделки во время предпоставочной сессии REQ_CLR_TRADE_RFQ = 57; // Запрос на регистрацию сделки в клиринговой системе во время RFQ сессии REQ_CLR_TRADE_DELI = 58; // Запрос на регистрацию сделки на физическую поставку в клиринговой системе REQ_CLR_TRADE_SPEC = 59; // Запрос на регистрацию сделки в клиринговой системе во воремя специальной сессии REQ_BD_SCHEDULE_UPDATE = 60; // Запрос на изменение расписания бизнес-дня REQ_FORCE_LOGOFF = 61; // Принудительное завершение работы в информационной или транзакционной подсистеме REQ_CHANGE_PASSWORD = 62; // Запрос на изменение пароля REQ_CANC_ORDERS_BY_PARTICIPANT = 63; // Запрос на снятие всех заявок партисипанта REQ_CANC_ORDERS_BY_USER = 64; // Запрос на снятие всех заявок пользователя REQ_CANC_ORDERS_BY_CLIENT = 65; // Запрос на снятие всех заявок клиента REQ_CANC_ORDERS_BY_ACCOUNT = 66; // Запрос на снятие всех заявок по счету REQ_CANC_ORDERS_BY_SECURITY = 67; // Запрос на снятие всех заявок по инструменту REQ_CANC_ORDERS_BY_BOARD = 68; // Запрос на снятие всех заявок по борду REQ_CANC_ORDERS_BY_SECBOARD = 69; // Запрос на снятие всех заявок по секборду REQ_TABS_PARAMS = 100; // Запрос на пересылку параметров внутренних таблиц /*Replies*/ REP_OK = 10000; // Успешное завершение запроса REP_BAD_UNKNOWN_REQ_TYPE = 10001; // REP_BAD_USER_NOT_FOUND = 10002; // REP_BAD_USER_NOT_LOGGED_ON = 10003; // REP_BAD_USER_SUSPENDED = 10004; // REP_BAD_PARTICIPANT_SUSPENDED = 10005; REP_BAD_USER_ALREADY_LOGGED_ON = 10006; REP_BAD_INVALID_PASSWORD = 10007; // REP_BAD_BOARD_NOT_FOUND = 10008; // REP_BAD_SECBOARD_OVERFLOW = 10009; // REP_BAD_SECURITY_NOT_FOUND = 10010; // REP_BAD_SECBOARD_EXISTS = 10011; // REP_BAD_ORDER_OVERFLOW = 10012; // REP_BAD_SECBOARD_NOT_FOUND = 10013; // REP_BAD_SECBOARD_NOT_OPEN = 10014; // открыты REP_BAD_ACCOUNT_NOT_FOUND = 10015; // REP_BAD_ACCOUNT_NOT_OWNED = 10016; // REP_BAD_ORDER_NOT_FOUND = 10017; // REP_BAD_INVALID_ORDER_STATUS = 10018; // REP_BAD_CLIENT_NOT_FOUND = 10019; // REP_BAD_CLIENT_NOT_OWNED = 10020; // REP_BAD_PARTICIPANT_NOT_FOUND = 10021; REP_BAD_PARTICIPANT_OVERFLOW = 10022; // REP_BAD_USER_OVERFLOW = 10023; // REP_BAD_CLIENT_OVERFLOW = 10024; // REP_BAD_SECTION_OVERFLOW = 10025; // REP_BAD_INVALID_SECBOARD_STATUS = 10026; контракта REP_BAD_BOARD_OVERFLOW = 10027; // REP_BAD_SECTION_NOT_FOUND = 10028; // REP_BAD_SECTYPE_OVERFLOW = 10029; // REP_BAD_SECURITY_OVERFLOW = 10030; // REP_BAD_SECTYPE_NOT_FOUND = 10031; // REP_BAD_TRADE_OVERFLOW = 10032; // REP_BAD_ACCOUNT_OVERFLOW = 10033; // REP_BAD_CLOWN_NE_ACCOWN = 10034; // REP_BAD_INMSG_OVERFLOW = 10035; // REP_BAD_INVALID_PRICE = 10036; // © CMA Small Systems AB, 2010 Неизвестный тип запроса Пользователь не найден Пользователь не зарегистрирован в системе Пользователь заблокирован // Участник заблокирован // Пользователь уже зарегистрирован в системе Неправильный пароль Режим торгов не найден Таблица серий срочных контрактов переполнена Серия срочного контракта не найдена Серия срочного контракта уже существует Таблица заявок переполнена Серия срочного контракта не найдена Торги по данной серии срочных контрактов не Позиционный регистр не найден Неверный идентификатор позиционного регистра Заявка не найдена Неверное состояние заявки Клиент не найден Неверный идентификатор клиента // Участник торгов не найден Таблица Участников торгов переполнена Таблица пользователей переполнена Таблица клиентов переполнена Таблица секций переполнена // Неверное состояние серии срочного Таблица режимов торгов переполнена Секция не найдена Таблица типов срочных контрактов переполнена Таблица срочных контрактов переполнена Тип срочных контрактов не найден Таблица сделок переполнена Таблица позиционных регистров переполнена Клиент не является владельцем счета Таблица сообщений переполнена Ошибка в указании цены 20 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика REP_BAD_WRONG_PARTICIPANT = 10037; // Ошибка в идентификаторе участника REP_BAD_CLEARING_NOT_AVAILABLE = 10038; // Клиринговая система недоступна REP_BAD_CLIENT_SUSPENDED = 10039; REP_BAD_ACCOUNT_SUSPENDED = 10040; позиционного регистра приостановлен REP_BAD_SECTION_SUSPENDED = 10041; // Участие клиента приостановлено // Допуск к осуществлению операций с данного // Работа Секции приостановлена REP_BAD_PARTICIPANT_NOT_SUSPENDED = 10042; // Допуск Участника торгов не приостановлен REP_BAD_USER_NOT_SUSPENDED = 10043; // Допуск пользователя к торгам не приостановлен REP_BAD_CLIENT_NOT_SUSPENDED = 10044; // Участие клиента не приостановлено REP_BAD_ACCOUNT_NOT_SUSPENDED = 10045; // Допуск к осуществлению операций с данного позиционного регистра не приостановлен REP_BAD_SECTION_NOT_SUSPENDED = 10046; // Работа Секции не приостановлена REP_BAD_TXTMSG_OVERFLOW = 10047; // Таблица сообщений переполнена REP_BAD_ACCESS_RIGHTS = 10048; // Доступ запрещен // Ответы из клиринга REP_CLR_LOGON_OK = 10049; REP_CLR_LOGON_BAD = 10050; REP_CLR_ORDER_OK = 10051; REP_CLR_ORDER_BAD = 10052; REP_CLR_ORDER_CANCEL_OK = REP_CLR_ORDER_CANCEL_BAD = REP_CLR_TRADE_OK = 10055; REP_CLR_TRADE_BAD = 10056; REP_CLR_SETTLE_PRICE_OK = REP_CLR_SETTLE_PRICE_BAD = // Регистрация в клиринговой системе завершилось успешно // Регистрация в клиринговой системе была отклонена // Заявка может участвововать в мэтчинге // Заявка должна быть отклонена торговой системой 10053; // Подтверждение снятия заявки в клиринговой системе 10054; // Снятие заявки невозможно(***) // Подтверждение регистрации сделки в клиринговой системе // Регистрация сделки невозможна (***) 10057; // Подтверждение регистрации новой расчетной цены 10058; // Регистрация расчетной цены невозможна (***) REP_BAD_UNKNOWN_REQUEST = 10059; // Неизвестный запрос REP_BAD_INVALID_QTY = 10060; // Неверно указано количество REP_BAD_ACCOUNT_TYPE = 10061; // Ошибка в спецификации типа счета REP_BAD_NO_BOARD_SECURITIES = 10062; // Ни одна серия срочных контрактов не представлена для режима торгов REP_BAD_INVALID_SEC_PARAMS = 10063; // Зарезервировано. Не используется в текущей версии REP_BAD_OPERDAY_CLOSED = 10064; REP_BAD_NO_BOARD_POSITIONS = 10065; режиме торгов REP_BAD_ACCOUNT_NOT_ALLOWED = 10066; торгов REP_CLR_ORDER_REESTR_OK REP_CLR_ORDER_REESTR_ERROR REP_CLR_TRADE_REESTR_OK REP_CLR_TRADE_REESTR_ERROR = = = = REP_BAD_STATION_NOT_FOUND REP_BAD_WRONG_STATION = 10071; = 10072; REP_BAD_NOT_NEGDEALS = 10073; 10067; 10068; 10069; 10070; // Торги закрыты // Ни один из регистров не представлен в данном // Регистр не может использоваться в данном режиме // // // // Подтверждение Ошибка приема Подтверждение Ошибка приема приема и сверки реестра заявок или сверки реестра заявок приема и сверки реестра сделок или сверки реестра сделок // Неверный код станции // Неверная станция для указаного направления платежа // Тип заявки не адресная... поле Контрагент заполнено... REP_BAD_NOT_ALL_MANDATORY_FIELDS = 10074; // Не все обязательные поля заполнены REP_BAD_INVALID_ADDRESSEE = 10075; // Неверный адресат REP_BAD_CLIENT_ISNOT_ACC_OWNER = 10076; // Клиент не является владельцем счета REP_BAD_MESSAGE_NOT_CRYPTED = 10080; // Сообщение не зашифровано REP_BAD_MESSAGE_NOT_SIGNED = 10081; // Сообщение не подписано REP_BAD_INVALID_SIGNATURE = 10082; // Ошибка проверки подписи REP_BAD_CERTIFICATE_NOT_FOUND = 10083; // Сертификат не зарегистрирован на пользователя REP_BAD_ORDER_TYPE_UNDEFINED = 10084; // Не задан тип заявки REP_BAD_ORDER_PARAMS_UNDEFINED = 10085; // Не заданы параметры заявки REP_BAD_ORDER_BS_UNDEFINED = 10086; // Не задано направление заявки REP_BAD_COUNTERPARTY_UNDEFINED = 10087; // Не задан контрагент REP_BAD_INVALID_MMF = 10088; // Тип заявки не лимитированная... Задан флаг маркетмейкера... REP_BAD_ORDER_INVALID_PARAMS = 10089; // Неверные параметры заявки REP_TABS_PARAMS = 10100; // Подтверждение получения параметров талиц OK_CHANGES = 11000; // Новые данные по подписке на информационную таблицу REP_BAD_FATAL_ERROR = 13000; // Внутренняя ошибка } © CMA Small Systems AB, 2010 21 Шлюз коммуникационной системы СЭТ СР СПбМТСБ enum EncryptStatus { UNENCRYPTED = 0; CPRO = 1; } // Сообщение не зашифровано // Сообщение зашифровано Crypto PRO enum SignStatus { UNSIGNED = 0; CPRO_COMBINED = 1; CPRO_SEPARATE = 2; } // Без подписи // Подпись комбинирована с сообщением // Подпись отдельно от сообщения enum SignCheckResult { SCHECK_OK = 0; SCHECK_OTHER = 1; SCHECK_NOCERT = 2; SCHECK_EXPIRED = 3; SCHECK_NOTRUST = 4; SCHECK_NOSIGN = 5; } // // // // // // Руководство разработчика Подпись проверена успешно Подпись не валидна Нет сертификата Сертификат просрочен Сертификат подписан неизвестным центром сертификации Не подписано message CryptoEnvelope { required int32 ver = 1; // Номер версии протокола required EncryptStatus encrypted = 2; // Признак использования шифрования required SignStatus signed = 3; // Признак использования подписи optional bytes data = 4; optional bytes signature = 5; // Контейнер для пакета сообщений // Контейнер для подписи, в случае SignStatus == CPRO_SEPARATE } // Обрамляющее сообщение "конверт" для всех прочих типов сообщений // внутреннее сообщение помещается в поле data как сериализованное // message Envelope { optional int32 ver = 1; // Версия optional string uid = 2; // Идентификатор пользователя optional string sid = 3; // Идентификатор сессии пользователя optional uint64 reqid = 4; // Идентификатор запроса. // В ответе на данный запрос сервер поместит в данное поле // значение указанное клиентом в соответстующем запросе. // Клиент может использовать данное поле для реализации алгоритмов // связи запросов и ответов optional uint64 cryptoMsgid = 7; // Служебное поле подситемы криптозащиты optional uint64 timestamp = 8; // Служебное поле подситемы криптозащиты optional string cryptoUid = 9; // Служебное поле подситемы криптозащиты optional SignCheckResult cryptoresult = 10; // Результат проверки подписи optional bool origEncrypted = 11; // Исходное сообщение было зашифровано optional bool origSigned = 12; // Исходное сообщение было подписано repeated MsgType type = 5; // Тип сообщения repeated bytes data = 6; // Внутреннее сообщение } // Набор возможных статусов объектов // enum RowStatus { ACTIVE = 1; // SUSPENDED = 2; // DEFAULTED = 3; // } // Набор возможных статусов // enum SBStatus { SB_OPENED = SB_SUSPENDED = SB_CLOSED = SB_CLOSED_FOR_ORDERS = } системы Активный Приостановленный Удаленный режимов торгов 1; 2; 3; 4; // Набор возможных типов заявок // enum OrderType { LIMIT = 0; © CMA Small Systems AB, 2010 // // // // Открыт Приостановлен Закрыт Закрыт для заявок // Лимитированная 22 Шлюз коммуникационной системы СЭТ СР СПбМТСБ MARKET NEGDEALS LIQUIDATION DELIVERY DELI_SPECIAL = = = = = 1; 2; 3; 4; 5; // // // // // Рыночная Адресная Ликвидационная Поставочная Специальная // // // // Безадресная Адресная Ликвидационная Поставочная Руководство разработчика } // Набор возможных типов сделок // enum TradeType { TRD_ORDINARY = 0; TRD_ADDRESS = 1; TRD_RFQ = 2; TRD_DELIVERY = 3; } // Набор возможных типов режимов торгов // enum BoardType { BRD_NORMAL = 0; // Нормальный BRD_RFQ = 1; // Ликвидационный BRD_DELIVERY = 2; // Предпоставочный BRD_DELI_SPECIAL = 3; // Специальный предпоставочный } // Набор возможных типов позиционных регистров // enum AccountType { ACC_NORMAL = 0; // Нормальный ACC_RFQ = 1; // Ликвидационный ACC_DELIVERY = 2; // Поставочный ACC_GARANT = 3; // Гарантирующий } // Набор возможных параметров заявок // enum OrderParams { PIQ = 0; // put-in-queue, поставить в очередь IOC = 1; // immediate-or-cancel, немедленно или отклонить FOK = 2; // fill-or-kill, все или отклонить } // Набор возможных статусов // enum OrderStatus { QUEUED = MATCHED = CANCELED = WAIT_APPROVAL = FREEZED = } заявок 0; 1; 2; 3; 4; // // // // // Помещена в очередь Удовлетворена Отменена Ждет подтверждения Заморожена (пользователь не может снять заявку) // Набор возможных направлений заявок // enum BuySell { BUY = 1; // Купить SELL = 2; // Продать } // Набор возможных типов пользователей // enum UserType { TRADER = 0; // Трейдер MAKLER = 1; // Маклер CLEARING = 2; // Клиринг GATEWAY = 3; // Гейтвей } enum AddresseeType { © CMA Small Systems AB, 2010 23 Шлюз коммуникационной системы СЭТ СР СПбМТСБ ADDR_USER ADDR_FIRM ADDR_BROADCAST = 0; = 1; = 2; Руководство разработчика // Пользователь // Фирма // Всем пользователям } // Набор возможных // enum Table { PARTICIPANTS USERS CLIENTS ACCOUNTS SECTIONS BOARDS SECTYPES SECURITIES SECBOARDS HOLDINGS ORDERS TRADES INMSGS EVENTS SCHEDULE PRICE_HISTORY TEXT_MESSAGES STATIONS DELIVERYBASIS CERTIFICATES } типов информационных таблиц = = = = = = = = = = = = = = = = = = = = 1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; 17; 18; 19; 20; // // // // // // // // // // // // // // // // // // // // Участники Пользователи Клиенты Регистры Секции Режимы Типы инструментов Инструменты Доступные для операций инструменты для определенных режимов торгов Открытые позиции Заявки Сделки Входные сообщения События Расписание История изменений рыночной цены Текстовые сообщения Станции назначения Базисы поставки пользовательские сертификаты // Типы событий в подсистеме наблюдения за нестандартными заявками и сделками enum EventType { EVENT_ORDER_ALARM = 1; // Нестандартная заявка EVENT_TRADE_ALARM = 2; // Нестандартная сделка } // Типы шифрования enum EncryptType { ENCRYPT_NO ENCRYPT_ALL ENCRYPT_LOG_ON_OFF } // Типы подписи enum SignType { SIGN_NO SIGN_ALL SIGN_LOG_ON_OFF } = 1; // Не шифровать = 2; // Шифровать все сообщения = 3; // Шифровать только Logon и Logoff = 1; // Не подписывать = 2; // Подписывать все сообщения = 3; // Подписывать только Logon и Logoff //Requests // Рассылка информации о текстовых сообщениях // message Meddelande { optional string addressee = 1; // Адресат optional string text = 2; // Текст сообщения optional uint32 priority = 3; // Приоритет optional AddresseeType addr_type = 4 [default = ADDR_USER]; // Тип адресата } // Рассылка информации об участниках // message Participant { optional string code = 1; optional string name = 2; optional string inn = 3; optional string kpp = 4; optional string ogrn = 5; optional string okpo = 6; © CMA Small Systems AB, 2010 // // // // // // Код Наименование Код ИНН Код КПП Код ОГРН Код ОКПО 24 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика } // Рассылка информации о пользователях // message User { optional string code = 1; optional string name = 2; optional string firm = 3; optional string password = optional UserType type = 5; optional EncryptType encrypt = 6; optional SignType sign = 7; } // // // 4; // // // Код Наименование Участник торгов // Пароль Тип пользователя Тип шифрования Тип подписи // Рассылка информации о клиентах // message Client { optional string code = 1; // Код optional string name = 2; // Наименование optional string firm = 3; // Участник торгов optional string inn = 4; // Код ИНН } // Рассылка информации о позиционных регистрах // message Account { optional string code = 1; // Код optional string name = 2; // Наименование optional string firm = 3; // Участник торгов optional string client = 4; // Клиент optional AccountType type = 5; // Тип регистра } // Рассылка информации о секциях // message Section { optional string code optional string name } = 1; // Код = 2; // Наименование // Рассылка информации о режимах торгов // message Board { optional string code = 1; // Код optional string name = 2; // Наименование optional string section = 3; // Секция optional BoardType type = 4; // Тип режима торгов } // Рассылка информации о типах срочных контрактов // message SecType { optional string code = 1; // Код optional string name = 2; // Наименование } // Рассылка информации о срочных контрактах // message Security { optional string code = 1; // Код optional string name = 2; // Наименование optional string sec_type = 3; // Тип срочного контракта optional uint32 lot_size = 4; // Размер лота optional uint64 price_step = 5; // Шаг цены optional string currency_code = 6; // Код валюты optional string currency_name = 7; // Наименование валюты optional int64 price_move_limit = 8; // Абсолютное значение предела движения цены optional uint32 price_move_limit_prc = 9; // Процентное значение предела движения цены optional uint32 limit_market_share = 10; // Процентное значение лимита на долю рынка optional int64 limit_market_share_threshold= 11; // Порог срабатывания лимита на долю рынка optional string delivery_basis = 12; // Наименование базиса поставки © CMA Small Systems AB, 2010 25 Шлюз коммуникационной системы СЭТ СР СПбМТСБ optional string delivery_basis_id Руководство разработчика = 13; // Код базиса поставки } // Запрос на изменение пароля пользователя // message ChangePassword { optional string user_name = 1; // Идентификатор пользователя optional string old_password = 2; // Старый пароль optional string new_password = 3; // Новый пароль } // информации о периоде бизнес-дня // message BDState { required uint32 code optional string name optional string handle optional string start_time optional string act_start_time optional string status optional string bday } = = = = = = = 1; 2; 3; 4; 5; 6; 7; // // // // // // // Код Название периода Режим периода Время начала периода Актуальное время начала периода Состояние Бизнес день // Запрос изменения расписания бизнес-дня // message BDScheduleUpdate { repeated BDState state = 1; // периоды бизнес-дня } // Регистрация в информационном или транзакционном фиде // message Logon { optional uint64 item_id = 1; // Идентификатор сообщения optional string uid = 2; // Идентификатор пользователя optional string password = 3; // Пароль optional uint32 decimal_scale = 4 [default = 2]; // Количество десятичных знаков после запятой } // Завершение работы с информационным или транзакционным фидом // message Logoff { optional uint64 item_id = 1; // Идентификатор сообщения optional string uid = 2; // Идентификатор пользователя optional string sid = 3; // Идентификатор сессии } // Открытие режима торгов // message OpenSecBoard { required string board = 1; // Режим required string security = 2; // Инструмент //For Normal Board optional uint64 open_price = 3; // Зарезервировано. optional uint64 prev_market_price = 4; // Расчетная цена предыдушей сессии optional string order_metrix = 5; // Метрики для выявления необычных заявок optional string trade_metrix = 6; // Метрики для выявления необычных сделок //For RFQ Board optional uint32 qty = 7; // Количество ликвидируемых контрактов optional uint64 price_min = 8; // Минимальная цена optional uint64 price_max = 9; // Максимальная цена //Common optional uint64 event_id = 10; // Идентификатор события } // Посылка заявки // message Order { optional uint64 item_id © CMA Small Systems AB, 2010 = 1; // Идентификатор сообщения 26 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика optional string trn = 2; // Номер транзакции. Формируется клиентом и должен быть уникальным в рамках сессии optional OrderType type = 3; // Тип заявки optional OrderParams params = 4; // Параметры заявки optional BuySell buy_sell = 5; // Направление заявки optional string board = 6; // Режим торгов optional string security = 7; // Инструмент optional string account = 8; // Регистр optional uint64 price = 9; // Цена optional uint32 qty = 10;// Количество optional uint64 time_in_force = 11 [default = 0]; // values from 0 to 10 are reserved, 0 good-for-day optional string firm = 12; // Фирма optional string client = 13; // Клиент optional string cp_firm = 14; // Контрагент для адресной заявки optional string comment = 15; // Комментарий optional string id = 16; // Идентификатор заявки. Формируется торговым сервером. Заполняется только при отправлении из торговой системы в клиринговую. optional uint64 time_stamp = 17; // Время поступления заявки. Формируется торговым сервером. Заполняется только при отправлении из торговой системы в клиринговую. optional bool isMarketMaker = 18 [default = false]; // Флаг принадлежности заявки маркет мейкеру optional string destination = 19; // Код Станция отправления/назначения. Заполняется клиентом только для поставочных заявок. } // Ответ на посылку заявки // message OrderRepOK { optional string code optional OrderStatus status optional uint32 qty optional uint32 qtyLeft optional uint32 qty_executed optional string info если статус заявки = Отменена } = = = = = = 1; 2; 3; 4; 5; 6; // // // // // // Идентификатор заявки Статус Количество Количество неудовлетворенных инструментов Количество удовлетворенных инструментов Дополнительная информация. Заполняется только // Запрос на акцепт заявки (только Клиринговая система может послать такой запрос) // message AcceptOrder { optional string order_id = 1; // Идентификатор заявки optional uint32 qty = 2; // Количество } // Запрос на сняние заявки // message CancelOrder { optional uint64 item_id = 1; optional string order_id = 2; optional uint64 time_stamp = 3; } // Ответ на сняние заявки // message CancelOrderRepOK { optional string optional uint32 optional uint32 optional uint32 } code qty qtyLeft qty_executed // Идентификатор сообщения // Идентификатор заявки // Штамп времени = = = = 1; 2; 3; 4; // // // // Идентификатор заявки Количество Количество неудовлетворенных инструментов Количество удовлетворенных инструментов // Подписка на определенную информационную таблицу // message Subscribe { required Table tab_id = 1; // Идентификатор таблицы optional int32 max_row_id = 2; // Зарезервировано. Не используется в текущей версии optional uint32 last_change_id = 3; // Идентификатор последнего полученного обновления данной таблицы // Используется при необходимости получения данных с определенного // момента, а не с начала учета обновлений по данной таблице } © CMA Small Systems AB, 2010 27 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика // Запрос на отмену подписки // message Unsubscribe { required Table tab_id = 1; // Идентификатор таблицы } // Информация о позиционном счете, допущенному к торговле серией срочного контракта (BoardSecurity) // message BoardAccount { optional string account = 1; // Код позиционного регистра optional uint32 buy_qty = 2; // Количество контрактов на покупку optional uint32 sell_qty = 3; // Количество контрактов на продажу } // Информация о серии срочного контракта допущенного к торговле // message BoardSecurity { optional string security = 1; // Серия срочного контракта //For Normal Board optional uint64 optional uint64 optional string optional string open_price prev_market_price order_metrix trade_metrix //For RFQ Board optional uint32 optional uint64 optional uint64 qty = 6; // Количество ликвидируемых контрактов price_min = 7; // Минимальная цена price_max = 8; // Максимальная цена repeated BoardAccount = = = = board_account 2; 3; 4; 5; // Зарезервировано // Расчетная цена предыдушей сессии // Метрики для выявления необычных заявок // Метрики для выявления необычных сделок = 100; // Информация о позиционном регистре } // Запрос на загрузку серий срочных документов с набором допущенных позиционных регистров для режима торгов // message BoardSecurities { optional string board_name = 1; // Режим торгов repeated BoardSecurity security = 2; // Информация о серии срочного контракта и регистрах optional uint64 event_id = 3; // Идентификатор события } //Replies // // message TabParams { required Table required uint32 } message OrderAlarm { optional string optional string optional string } message TradeAlarm { optional string optional string optional string } tab_id = 1; // Идентификатор таблицы size = 2; // Размер в записях order_id metrix info = 1; // Идентификатор заявки = 2; // Метрика, которая сработала = 3; // Расширенная информация trade_id = 1; // Идентификатор сделки metrix = 2; // Метрика, которая сработала info = 3; // Расширенная информация // Новые данные по подписке на информационную таблицу // Данное сообщение является обрамляющим для сообщений типа Info* // Сообщение типа Info* помещается в поле rowData как сериализованное. // message RowChange { required Table tabId = 1; // Тип таблицы © CMA Small Systems AB, 2010 28 Шлюз коммуникационной системы СЭТ СР СПбМТСБ required uint32 rowId required uint32 changeId optional bytes rowData // в виде сериализованного Руководство разработчика = 2; // Идентификатор строки = 3; // Идентификатор обновления = 4; // Новые или измененные строки информационной таблицы сообщения типа Info* } // Новые данные по подпискам на информационные таблицы // Данное сообщение является обрамляющим для сообщения типа RowChange // Сообщения типа RowChange помещаются в поле row как сериализованные. // message Changes { required int32 ver = 1; // Номер версии repeated bytes row = 2; // Набор сериализованных сообщений типа RowChange } // Строка данных для информационной таблицы "Заявки" // message InfoOrder { optional string code = 1; // Идентификатор заявки optional OrderStatus status = 2; // Статус optional OrderType type = 3; // Тип optional OrderParams params = 4; // Параметры optional BuySell buy_sell = 5; // Направление optional string board = 6; // Режим optional string security = 7; // Инструмент optional string account = 8; // Регистр optional uint64 price = 9; // Цена optional uint32 qty = 10; // Количество optional uint32 qtyLeft = 11; // Количество неудовлетворенных инструментов optional uint64 time_in_force = 12 [default = 0]; // Время действия заявки. 0 - до конца торговой сессии optional string user = 13; // Пользователь optional string firm = 14; // Участник optional string client = 15; // Клиент optional string cp_firm = 16; // Контрагент для адресной заявки optional uint64 entry_time = 17; // Время ввода optional uint64 cancel_time = 18 [default = 0]; // Время отмены optional string res_code = 19; // Код результата optional string res_descr = 20; // Описание результата optional string res_info = 21; // Информация результата optional string comment = 22; // Комментарий optional string trn = 23; // Номер транзакции optional uint32 qty_executed = 24; // Количество удовлетворенных инструментов optional bool isMarketMaker = 25 [default = false]; // Флаг принадлежности заявки маркет мейкеру optional OrderStatus prev_status = 26; // Предыдущий статус optional string destination = 27; // Код станция отправления/назначения. Заполняется клиентом только для поставочных заявок. optional string dest_name = 28; // Наименование станции отправления/назначения. Заполняется клиентом только для поставочных заявок. } // Строка данных для информационной таблицы "Сделки" // message InfoTrade { required string code = 1; // Код optional BuySell buy_sell = 2; // Направление optional string board = 3; // Режим optional string security = 4; // Инструмент optional uint64 price = 5; // Цена optional uint32 qty = 6; // Количество optional string order_id = 7; // Идентификатор заявки optional uint64 trade_time = 8; // Время сделки optional string firm = 9; // Фирма optional uint64 match_id = 10; // Идентификатор спаривания optional TradeType trade_type = 11; // Тип сделки optional string cp_firm = 12; // Контрагент для адресной сделки } // Строка данных для таблицы событий // message InfoEvent { required string code = 1; // Код required EventType type = 2; // Тип события optional string info = 3; // Развернутая информация о событии © CMA Small Systems AB, 2010 29 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика } // Строка данных для информационной // message InfoParticipant { required string code = optional string name = optional RowStatus status = таблицы "Участники" 1; // Код 2; // Имя 3; // Статус } // Строка данных для информационной таблицы "Пользователи" // message InfoUser { required string code = 1; // Код optional string name = 2; // Имя optional RowStatus status = 3; // Статус optional string firm = 4; // Участник optional UserType type = 5; // Тип optional uint64 logon_time = 6; // Время последней регистрации с системе optional uint64 logoff_time = 7; // Время последнего завершения работы с системой optional string password = 8; // Пароль } // Строка данных для информационной // message InfoClient { required string code = optional string name = optional RowStatus status = optional string firm = } // Строка данных для информационной // message InfoAccount { required string code = optional string name = optional RowStatus status = optional string firm = optional string client = optional AccountType type = } таблицы "Клиенты" 1; 2; 3; 4; // // // // Код Имя Статус Участник таблицы "Регистры" 1; 2; 3; 4; 5; 6; // // // // // // Код Имя Статус Участник Клиент Тип // Строка данных для информационной таблицы "Открытые позиции" // message InfoHolding { optional string code = 1; // Код optional string account = 2; // Регистр optional string security = 3; // Серия срочного контракта optional uint32 trade_income_buy_qty = 4; // Входящие Покупка optional uint32 trade_income_sell_qty = 5; // Входящие Продажа optional uint32 trade_buy_qty = 6; // Текущая Покупка optional uint32 trade_sell_qty = 7; // Текущая Продажа optional uint64 trade_buy_value = 8; // Сумма затраченная на покупку optional uint64 trade_sell_value = 9; // Сумма вырученная при продаже optional uint32 order_buy_qty = 10; // План Покупка optional uint32 order_sell_qty = 11; // План Продажа optional uint64 order_buy_value = 12; // План трат на покупку optional uint64 order_sell_value = 13; // План выручки при продаже optional uint64 settle_price = 14; // Не используется optional string firm = 15; // Участник } // Строка данных для информационной // message InfoSection { required string code = optional string name = optional RowStatus status = } таблицы "Секции" 1; // Код 2; // Имя 3; // Статус // Строка данных для информационной таблицы "Режимы" © CMA Small Systems AB, 2010 30 Шлюз коммуникационной системы СЭТ СР СПбМТСБ // message InfoBoard { required string optional string optional RowStatus optional string optional BoardType } code = name = status = section = type = // Строка данных для информационной // message InfoSectype { required string code = optional string name = optional RowStatus status = } // Строка данных для информационной // message InfoDeliveryBasis { required string code = optional string name = optional RowStatus status = } 1; 2; 3; 4; 5; // // // // // Руководство разработчика Код Имя Статус Секция Тип таблицы "Типы инструментов" 1; // Код 2; // Имя 3; // Статус таблицы "Базисы поставки" 1; // Код 2; // Имя 3; // Статус // Строка данных для информационной таблицы "Инструменты" // message InfoSecurity { required string code = 1; // Код optional string name = 2; // Имя optional RowStatus status = 3; // Статус optional string sec_type = 4; // Тип optional uint32 lot_size = 5; // Размер лота optional uint64 price_step = 6; // Шаг цены optional string currency_code = 7; // Код валюты optional string currency_name = 8; // Наименование валюты optional uint64 price_move_limit = 9; // Абсолютное значение предела движения цены optional uint64 price_move_limit_prc = 10; // Процентное значение предела движения цены optional uint64 limit_market_share = 11; // Процентное значение лимита на долю рынка optional uint64 limit_market_share_threshold = 12; // Порог срабатывания лимита на долю рынка optional string delivery_basis_code = 13; // Код базиса поставки optional string delivery_basis_name = 14; // Название базиса поставки optional uint32 delivery_min_qty = 15; // минимальное кол-во поставки optional uint32 delivery_qty_step = 16; // шаг кол-ва поставки (вагонная норма) optional string first_trade_day = 17; // первый торговый день для инструмента optional string last_trade_day = 18; // последний торговый день для инструмента optional string execution_day = 19; // день исполнения инструмента optional uint32 optional uint32 income_qty current_qty = 20; // Сумма входных позиций = 21; // Сумма текущих позиций } // Строка данных для информационной таблицы "Доступные для операций инструменты для определенных режимов торгов" // message InfoSecboard { optional string code = 1; // Код optional string security = 2; // Инструмент optional string board = 3; // Режим optional optional optional optional uint64 uint32 uint64 uint32 best_buy buy_orders best_sell sell_orders = = = = optional uint64 optional uint64 value_today volume_today = 8; // Объем в деньгах сегодня = 9; // Объем в инструментах сегодня optional uint64 optional uint64 optional uint64 open_price prev_market_price market_price = 10; // Цена открытия = 11; // Предыдущая расчетная цена = 12; // Расчетная цена © CMA Small Systems AB, 2010 4; 5; 6; 7; // // // // Лучшая Заявок Лучшая Заявок покупка на покупку продажа на продажу 31 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика optional int64 market_price_change = 13; // Изменение расчетной цены optional uint64 optional uint32 optional uint64 last_trade_price last_trade_qty last_trade_time = 14; // Цена последней сделки = 15; // Количество последней сделки = 16; // Время последней сделки optional uint64 optional uint64 high_trade_price low_trade_price = 17; // Наибольшая цена сделки = 18; // Наименьшая цена сделки optional uint32 optional uint32 num_of_orders = 19; // Количество заявок num_of_active_orders= 20; // Количество активных заявок optional uint32 num_of_trades = 21; // Количество сделок optional SBStatus status = 22; // Статус optional BoardType board_type // Поля для режима RFQ optional uint32 qty optional uint64 price_min optional uint64 price_max optional uint64 addr_value_today optional uint64 addr_volume_today сделкам optional uint64 sell_order_value optional uint64 sell_order_volume продажу optional uint64 buy_order_value optional uint64 buy_order_volume покупку } // Строка данных для информационной // message InfoState { required string code optional string name optional string bday optional string handle optional string start_time optional string act_start_time optional string finish_time optional string act_finish_time optional string status } = 23; // Тип режима = = = = = 24; 25; 26; 27; 28; // // // // // Количество Минимальная цена Максимальная цена Объем в деньгах сегодня по адресным сделкам Объем в инструментах сегодня по адресным = 29; // Объем в деньгах по активным заявкам на продажу = 30; // Объем в инструментах по активным заявкам на = 31; // Объем в деньгах по активным заявкам на покупку = 32; // Объем в инструментах по активным заявкам на таблицы "Расписание" = = = = = = = = = 1; 2; 3; 4; 5; 6; 7; 8; 9; // // // // // // // // // Код Название периода Бизнес день Режим торгов Время начала периода Актуальное время начала периода Время окончания периода Актуальное время окончания периода Состояние // Строка данных для информационной таблицы // message InfoMeddelande { optional string code = 1; optional string from = 2; optional string to = 3; optional string text = 4; optional uint64 entry_time = 5; optional uint32 priority = 6; optional string to_firm = 7; } "Текстовые сообщения" // Строка данных для информационной таблицы // message InfoStation { optional string basis_code = 1; optional string code = 2; optional string name = 3; optional uint32 distance = 4; optional string security = 5; optional BuySell buy_sell = 6; } "Станции назначения" // // // // // // // // // // // // // Код Отправитель Получатель Текст сообщения Время посылки сообщения Приоритет фирма получатель Код станции базиса поставки Код станции Наименование станции Расстояние от базиса поставки до станции Инструмент доставка/отгрузка //Clearing requests. Запросы отправляемые в Клиринговую систему // Запрос на регистрацию сделки // © CMA Small Systems AB, 2010 32 Шлюз коммуникационной системы СЭТ СР СПбМТСБ message Trade { optional uint64 optional string optional string optional uint64 optional uint32 optional uint64 optional uint64 } item_id = 1; trade_id = 2; order_id = 3; time_stamp = 4; exec_qty = 5; exec_price = 6; match_id = 7; // // // // // // // Идентификатор Идентификатор Идентификатор Штамп времени Количество Цена Идентификатор Руководство разработчика сообщения сделки заявки сделки спаривания // Запрос на регистрацию расчетной цены // message SettlementPrice { optional uint64 item_id = 1; // Идентификатор сообщения optional uint64 time_stamp = 2; // Штамп времени изменения расчетной цены optional string board = 3; // Режим optional string seccode = 4; // Серия срочного контракта optional uint64 price = 5; // Цена optional uint64 price_id = 6; // Идентификатор цены optional uint32 id = 7; // Идентификатор изменения } //Clearing Replies. Ответы Клиринговой системы // Описание причины // message Reason { optional string optional string optional string } отклонения заявки code = 1; // Код ошибки desc = 2; // Описание ошибки info = 3; // Расширеная информация об ошибке // Регистрация в системе завершилась успешно // message LogonOK { optional uint64 item_id = 1; // Идентификатор сообщения optional string sid = 2; // Иденификатор сессии optional uint64 last_item_id = 3; // Идентификатор последнего сообщения optional uint64 last_order_id = 4; // Идентификатор последней заявки optional uint64 last_trade_id = 5; // Идентификатор последней сделки optional uint64 last_price_id = 6; // Идентификатор последнего изменения цены } // Регистрация в системе была отклонена // message LogonBAD { optional uint64 item_id = 1; // Идентификатор сообщения optional Reason reason = 2; // Описание ошибки } // Загрузка данных // message Holding { optional string optional string optional uint32 optional uint32 optional uint32 optional uint32 optional uint64 optional uint64 optional uint32 optional uint32 optional uint64 optional uint64 } о позиционных регистрах account = 1; security = 2; trade_income_buy_qty = 3; trade_income_sell_qty = 4; trade_buy_qty = 5; trade_sell_qty = 6; trade_buy_value = 7; trade_sell_value = 8; order_buy_qty = 9; order_sell_qty = 10; order_buy_value = 11; order_sell_value = 12; // // // // // // // // // // // // Регистр Серия срочного контракта Входящие Покупка Входящие Продажа Текущая Покупка Текущая Продажа Сумма затраченная на покупку Сумма вырученная при продаже План Покупка План Продажа План трат на покупку План выручки при продаже // Клиринговая система разрешает заявке участие в мэтчинге // message OrderOK { optional uint64 item_id = 1; // Идентификатор сообщения optional string order_id = 2; // Идентификатор заявки © CMA Small Systems AB, 2010 33 Шлюз коммуникационной системы СЭТ СР СПбМТСБ optional optional optional optional optional uint32 uint32 uint64 uint64 uint64 order_buy_qty = 3; order_sell_qty = 4; order_buy_value = 5; order_sell_value = 6; order_settle_price = 7; // // // // // Руководство разработчика План Покупка План Продажа План трат на покупку План выручки при продаже Не используется } // Клиринговая система отклоняет заявку // message OrderBAD { optional uint64 item_id = 1; optional string order_id = 2; optional Reason reason = 3; optional bool nak = 4 [default=false]; } // // // // Идентификатор сообщения Идентификатор заявки Описание причины отвергнута КС // Клиринговая система успешно зарегистрировала снятие заявки // message OrderCancOK { optional uint64 item_id = 1; // Идентификатор сообщения optional string order_id = 2; // Идентификатор заявки optional uint32 order_buy_qty = 3; // План Покупка optional uint32 order_sell_qty = 4; // План Продажа optional uint64 order_buy_value = 5; // План трат на покупку optional uint64 order_sell_value = 6; // План выручки при продаже optional uint64 order_settle_price = 7; // Не используется } // Клиринговая система не зарегистрировала снятие заявки // message OrderCancBAD { optional uint64 item_id = 1; // Идентификатор сообщения optional string order_id = 2; // Идентификатор заявки optional Reason reason = 3; // Описание причины } // Клиринговая система успешно зарегистрировала сделку // message TradeOK { optional uint64 item_id = 1; // Идентификатор сообщения optional string trade_id = 2; // Идентификатор сделки optional uint32 trade_buy_qty = 3; // Текущая Покупка optional uint32 trade_sell_qty = 4; // Текущая Продажа optional uint64 trade_buy_value = 5; // Сумма затраченная на покупку optional uint64 trade_sell_value = 6; // Сумма вырученная при продаже optional uint32 order_buy_qty = 7; // План Покупка optional uint32 order_sell_qty = 8; // План Продажа optional uint64 order_buy_value = 9; // План трат на покупку optional uint64 order_sell_value = 10; // План выручки при продаже optional uint64 trade_settle_price = 11; // Не используется } // Клиринговая система не зарегистрировала сделку // message TradeBAD { optional uint64 item_id = 1; // Идентификатор сообщения optional string trade_id = 2; // Идентификатор сделки optional Reason reason = 3; // Описание причины } // Клиринговая система успешно приняла и проверила реестр заявок // message OrderReestrOK { optional uint64 item_id = 1; // Идентификатор сообщения } // Клиринговая система отвергла реестр заявок // message OrderReestrBAD { optional uint64 item_id = 1; // Идентификатор сообщения optional Reason reason = 2; // Описание причины } © CMA Small Systems AB, 2010 34 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика // Клиринговая система успешно приняла и проверила реестр сделок // message TradeReestrOK { optional uint64 item_id = 1; // Идентификатор сообщения } // Клиринговая система отвергла реестр сделок // message TradeReestrBAD { optional uint64 item_id = 1; // Идентификатор сообщения optional Reason reason = 2; // Описание причины } // Клиринговая система успешно зарегистрировала изменение расчетной цены // message SettlePriceOK { optional uint64 item_id = 1; // Идентификатор сообщения optional string board = 2; // Режим торгов optional string seccode = 3; // Серия срочного контракта optional uint64 price_id = 4; // Идентификатор цены optional uint32 id = 5; // идентификатор изменения } // Клиринговая система не зарегистрировала изменение рачетной цены // message SettlePriceBAD { optional uint64 item_id = 1; // Идентификатор сообщения optional string board = 2; // Режим торгов optional string seccode = 3; // Серия срочного контракта optional Reason reason = 4; // Описание причины optional uint64 price_id = 5; // Идентификатор цены optional uint32 id = 6; // Идентификатор изменения } // Запрос на снятие заявок по списку (Принимается только от Клиринговой системы) // message OrderCancReestr { repeated string order_id = 1; // Идентификатор заявки } // Реестр заявок // message OrderReestr { optional uint64 item_id = 1; repeated InfoOrder order = 2; } // Реестр сделок // message TradeReestr { optional uint64 item_id = 1; repeated InfoTrade trade = 2; } © CMA Small Systems AB, 2010 // Идентификатор сообщения // Информация по заявке // Идентификатор сообщения // Информация по сделке 35 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика Библиотека zmqCommClient.dll Программирование информационного обмена внешних систем с СЭТ СР СПбМТСБ через шлюз ШКС осуществляется при помощи обращений к методам динамической библиотеки zmqCommClient.dll. Коммуникационный модуль экспортирует одну единственную функцию zmqCommClient.dll – GetICommModule. Остальное взаимодействие библиотеки с внешним приложением организовано с использованием интерфейсов, построенных на базе абстрактных классов C++. Для передачи сообщений между коммуникационным модулем и программой используется абстрация очереди. Реализация очереди не включена в код коммуникационного модуля ввиду того, что очередь является внешним, по отношению к модулю, объектом. Поэтому реализации очередей, т.е. интерфейса IcommQueue, является задачей программистов внешней системы. Для этого можно воспользоваться образцом реализации, приведенного в подразделе "Пример кода для реализации интерфейса ICommQueue". Функция GetICommModule не имеет параметров и возвращает интерфейс ICommModule (в виде указателя на С++ класс). © CMA Small Systems AB, 2010 36 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика Интерфейс коммуникационного модуля ICommModule - описание методов bool Start(const char *iniFile, const char *commonSectonName); Производит инициализацию коммуникационного модуля с использованием параметров определенных в инициализационном файле. Аргументы: const char *iniFile – имя конфигурационного .ini-файла const char *commonSectonName - имя секции конфигурационного .ini-файла модуля (не используется коммуникационным модулем, поставляемым с примером) Возвращаемое значение: true в случае успешной инициализации. bool Stop() Завершает работу коммуникационного модуля. void* Connect(ICommQueue* input, const char *connSectonName) Производит открытие соединения с сервисом торговой системы. Аргументы: ICommQueue* input – указатель на очередь сообщений передаваемых клиентским приложением коммуникационному модулю (исходящих с точки зрения клиентского приложения). const char *connSectonName - имя секции инициализационного файла модуля с параметрами соединения (не используется коммуникационным модулем, поставляемым с примером) Возвращаемое значение: указатель на объект, идентифицирующий успешно установленное соединение, NULL в случае неуспеха. void SetQueue(void* connect, ICommQueue* output); Устанавливает в соответствия успешно открытому соединению очередь передаваемых клиентскому программному обеспечению сообщений. Аргументы: void* connect – указатель на объект, идентифицирующий успешно установленное соединение (возвращенное в качестве результата вызово функции Connect) ICommQueue* output - указатель на очередь сообщений передаваемых клиентскому приложению коммуникационным модулем (входящих с точки зрения клиентского приложения) void Disconnect(void* connect); Закрывает ранее открытое соединение с сервисом торгового сервера Аргумент: void* connect – указатель на объект, идентифицирующий успешно установленное соединение (возвращенное в качестве результата вызово функции Connect) int GetLastError(void* connect, char *descrBuf, int bufLen); Позволяет получить расширенную информацию об последней ошибке коммуникационного модуля возникшей при при вызове функций Start и Connect Аргументы: void* connect – указатель на объект, идентифицирующий успешно установленное соединение (возвращенное в качестве результата вызово функции Connect) char *descrBuf – указатель на текстовый буфер для получения описания ошибки int bufLen – длина текстового буфера Возвращаемое значение: расширенная информацию об последней ошибке © CMA Small Systems AB, 2010 37 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика Интерфейс очередей ICommQueue - описание методов Для простоты изложения, в настоящем разделе описано применение методов интерфейса ICommQueue в предположении, что они вызываются внешней системой. bool Put(void* connect, char* buffer, int32_t length, int32_t error ); Помещает информацию в буфер для передачи ее шлюзу (т.е. в конечном итоге для передачи торговому серверу). Аргументы: void* connect – указатель на объект - идентификатор соединения (возвращаемое значение после вызова функции ICommModule->Connect). В наших примерах обозначается как Идентификатор_соединения. char* buffer – указатель на буфер с данными int32_t length – длина буфера с данными int32_t error – признак того, что данные, передаваемые коммуникационному модулю, представляют собой сообщение об ошибке. Клиентское программное обеспечение должно передавать в качестве данного параметра только 0. bool Get(void** connect, char** buffer, int32_t* length, int32_t* error, int64_t timeout) Получает из буфера сообщений информацию от торговой системы, помещенную туда шлюзом. При отсутствии сообщений данная функция блокирует выполнение соответствующего потока до момента появления сообщения в очереди либо до истечения определенного таймаута. Аргументы: void** connect – указатель на указатель куда будет помещен указатель на объект, идентифицирующий успешно установленное соединение (возвращенное в качестве результата вызова функции Connect) char** buffer – указатель на указатель куда будет помещен указатель на буфер данных полученных от сервиса торговой системы int32_t* length – указатель на целое число куда будет помещено значение длины данных полученных от сервиса торговой системы int32_t* error – указатель на целое число куда будет помещено неравное нулю значение в случае если данные в буфере являются сообщением об ошибке. В этом случае клиентское программное должно запротоколировать данное сообщение об ошибке, тем или иным образом сообщить о ней пользователю и прекратить работу. int64_t timeout – таймаут ожидания в миллисекундах. Для бесконечного ожидания следует использовать отрицательное значение -1. Возвращаемое значение: true - если функция успешно поместила в переданные указатели указатели на сообщение (информационный блок). Следует отметить что помимо нормального получения данных от торгового сервиса, это может означать получения сообщения о коммуникационной ошибке о чем будет сигнализировать ненулевое значение помещенное по адресу, переданному функции в качестве предыдущего аргумента. false - если функции не удалось поместить в переданные указатели указатели на сообщение (информационный блок). Это может произойти по причине истекшего таймаута или по причине того, что очередь неработоспособна (остановлена по причине завершения приложения либо коммуникационного сбоя). Признаком последнего является ненулевое значение помещенное по адресу, переданному функции в качестве предыдущего аргумента. bool Stop(); Останавливает работу очереди. Вызывается в процессе завершения работы приложения (закрытия соединения). © CMA Small Systems AB, 2010 38 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика char* Alloc(int32_t length); Выделяет память для информационного буфера, предназначенного для помещения в очередь функцией Put Аргумент: int32_t length – требуемая длина информационного буфера. bool Free(char* buffer); Освобождает память информационного буфера, полученного из очереди функцией Get int GetLastError(void* connect, char *descrBuf, int bufLen); Возвращает расширенную информацию об последней ошибке вызова функций модуля Аргументы: void* connect – указатель на объект, идентифицирующий успешно установленное соединение (возвращенное в качестве результата вызова функции Connect) char *descrBuf – указатель на текстовый буфер для получения описания ошибки int bufLen – длина текстового буфера int GetCount(); Возвращает количество сообщений (информационных буферов) в очереди. © CMA Small Systems AB, 2010 39 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика Сценарий использования коммуникационного модуля и очередей Коммуникационный модуль (Client Communication Module) и клиентское ПО (внешние системы) обмениваются данными при помощи пар очередей. Каждый канал данных обслуживается одной парой очередей. Во входной очереди передаются данные от Client Communication Module к клиенту, а в выходной очереди передаются данные от клиента к Client Communication Module. Программный компонент, передающий данные, должен сначала выделить место для них в очереди, а компонент, принимающий данные, должен освободить место в очереди, после того, как он сохранит и/или обработает полученные данные. В настоящее время предполагается использовать два потока данных: данные, получаемые по подписке (Информационный канал) транзакционные данные (Транзакционный канал) ПО-клиент (внешняя система) может работать как с каким-либо одним каналом данных, так и с двумя каналами (в последнем случае один клиент работает с четырьмя очередями - двумя входными и двумя выходными). Схема передачи данных показана ниже: Основные действия клиента 1. Клиент загружает коммуникационный модуль (Client Communication Module) и вызывает функцию GET_ICOM_MODULE_FUNCTION_NAME, чтобы получить указатель на реализацию ICommModule. 2. Клиент вызывает ICommModule->Start(Имя ini-файла, Имя секции ini-файла), чтобы инициализировать Client Communication Module. Такой вызов можно выполнить внутри оператора if, чтобы совместить его с проверкой успешности инициализации, например, так: if(!pCommModule->Start(Имя ini-файла, Имя секции ini-файла)) { Действия, выполняемые при неуспешной инициализации © CMA Small Systems AB, 2010 40 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика } 3. Клиент создает объекты для входной и выходной очередей (обозначаются в наших примерах как Входная_Очередь и Выходная_Очередь), реализующие интерфейс ICommQueue. 4. Клиент вызывает ICommModule->Connect(Входная_Очередь), чтобы соединиться с сервером. В качестве возвращаемого значения функции Connect, Client Communication Module передает идентификатор соединения (connection_handle) или, в случае ошибки, значение NULL. Client Communication Module помещает данные, полученные от сервера во входную очередь, указывая идентификатор соединения в качестве первого параметра для ICommQueue->Put(). Для обнаружения ошибок соединения можно использовать оператор if с условием, полученным как логическое отрицание возвращаемого значения функции Connect: connection = pCommModule->Connect(Входная_Очередь); if(!connection) { Действия, выполняемые при неуспешном соединении } else { ... } 5. Клиент вызывает ICommModule->SetQueue(Идентификатор_соединения, Выходная_Очередь), чтобы сообщить Client Communication Module выходную очередь, в которую клиент будет выкладывать данные для передачи на сервер. 6. Клиент производит прием данных из входной очереди, обрабатывает их и помещает свои выходные данные в выходную очередь. После обработки входных данных клиент должен освобождать место в очереди, чтобы не допускать утечки памяти. 7. После окончания своей основной работы, получив от сервера подтверждения о приеме отправленных ему данных, клиент вызывает ICommModule->Disconnect(connection_handle), чтобы разорвать соединение и отсоединиться от сервера. После разрыва соединения, идентификатор соединения больше применяться не должен, поэтому, чтобы повысить надежность работы ПО, значение переменной, используемой для его хранения можно затереть: pCommModule->Disconnect(Идентификатор_соединения); connection = NULL; 8. Клиент вызывает ICommModule->Stop(), чтобы закрыть соединения и завершить работу Client Communication Module. 9. При возникновении ошибок Client Communication Module записывает во входную очередь (при помощи Put()) ненулевой признак ошибки, в буфере очереди при этом передается краткое описание ошибки. В такой ситуации connection_handle становится некорректным (invalid) и производится разрыв соединения. 10. Подробное описание последней ошибки вызова функций Client Communication Module доступно через вызов: ICommModule->GetLastError(Идентификатор_соединения, Буфер_с_описанием_ошибки, Длина_буфера) © CMA Small Systems AB, 2010 41 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика Использование ICommQueue Интерфейс ICommQueue реализует обмен данными при помощи входной и выходной очередей. В нижеприведенном описании сценария обмена данных ПО-клиент может быть как отправителем, так и получателем данных. 1. Чтобы поместить данные в очередь, отправитель данных должен выделить соответствующий буфер данных (память) при помощи вызова ICommQueue->Alloc(). 2. Этот буфер данных передается при помощи вызова ICommQueue->Put(). Он принадлежит очереди (ICommQueue) и не должен освобождаться отправителем данных. Поэтому данные появляются во входной очереди после того, как коммуникационный модуль выполнит сначала вызов ICommQueue->Alloc(), а затем ICommQueue->Put(). 3. Получатель данных получает доступ к данным через вызов ICommQueue->Get(). 4. После того, как получатель обработает и/или сохранит полученные данные, он должен освободить их при помощи вызова ICommQueue->Free(). 5. Чтобы узнать количество сообщений, ожидающих получения во входной очереди, применяется вызов ICommQueue->GetCount(). 6. Чтобы сообщить коммуникационному модулю, чтобы он прекратил ожидание данных от клиента (т.е. завершить коммуникационные потоки), нужно вызвать ICommQueue->Stop(). Для идентификации потоков передаваемых данных в вызовах в качестве параметра указывается идентификатор соединения. Описание поставки Внешние системы взаимодействуют с ШКС, используя динамическую библиотеку zmqCommClient.dll (входящую в комплект поставки) и ПО для работы с очередями, которое должно быть написано разработчиками внешних систем самостоятельно (по образцу, входящему в комплект поставки). Примеры ПО для реализации очередей поставляются в виде упакованного архива, в котором упакованы необходимые файлы, в том числе.proto-файл и сгенерированные из него файлы для сборки, а также нербходимые для сборки файлы из дистрибутива protobuf. Благодаря этому, разработчики внешних систем смогут ознакомиться с работающими примерами кода для реализации взаимодействия со шлюзом, даже не устанавливая protobuf. Для сборки потребутеся ПО Visual Studio 2008 SP1 и Windows 2008 R2 или Windows 7, при этом к характеристикам компьютера серьезных требований не предъявляется. При распаковке архив раскрывается в директорию Samples, содержащую файл Samples.sln и три поддиректории: Samples\protobuf - директория для средств по преобразованию .proto-файлов во фрагменты кода на языках высокого уровня, реализующие обмен данными. Samples\SampleInfoClient - директория примера работы с информационным потоком Samples\SampleTransClient - директория примера работы с транзакционным потоком © CMA Small Systems AB, 2010 42 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика Файл Samples.sln содержит проектное решение (solution), открываемое в Visual Studio. В каждой из двух поддиректорий Samples\SampleInfoClient Samples\SampleTransClient содержатся следующие файлы и поддиректории: Имя файла или поддиректории Описание FTEMessages.proto FTEMessages.pb.h FTEMessages.pb.cc IOQueueEx.h zmqCommClientI.ini ZMQCommClient.lib QCommModule.h SampleInfoClient.cpp или SampleTransClient.cpp SampleInfoClient.vcproj или SampleTransClient.vcproj stdafx.h stdafx.cpp targetver.h Release © CMA Small Systems AB, 2010 и .proto-файл, определяет структуру передаваемых данных. В случае изменения .proto-файла, описывающего форматы СЭТ СР СПбМТСБ, разработчики получат обновленный .proto-файл, который они должны будут преобразовать в код на С++.. Для генерирования кода не потребуется создавать нового ПО, т.к. разработчики протокола Protobuf предоставляют бесплатное ПО (включая исходные коды) для выполнения такого преобразования. Результат работы кодогенератора Protobuf Результат работы кодогенератора Protobuf Исходный код на С++ Конфигурационные настройки коммуникационного модуля (см. описание после данной таблицы) Библиотека коммуникационного модуля Исходный код на С++ Исходный код примеров на С++. Файл SampleInfoClient.cpp имеется только в директории SampleInfoClient и реализует очереди для передачи информационных сообщений. Файл SampleTransClient.cpp имеется только в директории SampleTransClient и реализует очереди для передачи транзакционных сообщений. Файл проектов для сборки транзакционного и информационного клиентов Исходный код на С++ Исходный код на С++ для подключения файла stdafx.h Исходный код на С++ Поддиректория для релизной сборки 43 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Release\LibTrace.dll Release\ZMQCommClient.dll Release\LibThread.dll Release\libzmq.dll Release\SampleInfoClient.exe Руководство разработчика Динамическая библиотека трассировки Основная динамическая библиотека коммуникационного модуля Динамическая библиотека многопотоковой обработки Динамическая библиотека ZeroMQ Исполняемая программа для передаваемого примера Далее приведены примеры и подробные описания некоторых из этих файлов. Конфигурационные .ini-файлы для настройки коммуникационного модуля Оба конфигурационных файла (для информационного обмена и для транзакций) имеют одинаковую структуру и могут быть одинаковыми. Пример конфигурационного файла: [zmqclient] LOG_FILE=zmqclientI.trc LOG_LEVEL=10 ENDPOINT=tcp://192.168.19.10:35354 HEARTBEAT_INTERVAL=1500 HEARTBEAT_TIMEOUT=3000 Назначение настроек: Параметр Описание Секция [zmqclient] (единственная секция, содержит все настройки) Указывает трассировочный файл, в котором будут сохранятся отладочные сообщения и сообщения об ошибках LOG_LEVEL Уровень логирования. При нулевом значении этого параметра регистрируются только сообщения об ошибках. Максимальное значение обычно равно 10-12. ENDPOINT Характеристики соединения: обозначение протокола (tcp) ip-адрес компьютера, на котором запущен шлюз порт компьютера, на котором запущен шлюз. Для информационных сообщений по умолчанию применяется порт 35355, для транзакций порт 35354. Эти значения разделены символами ":" (двоеточием) HEARTBEAT_INTERVAL Интервал времени, через который отсылается пакет, даже если отсутствует необходимость в передаче данных (это нужно, чтобы дать серверу знать, что клиент активен. Задается в миллисекундах. HEARTBEAT_TIMEOUT Время, по истечении которого клиент считает соединение с сервером разорванным, в случае отсутствия активности, т.е. входящих сообщений от сервера к клиенту. Задается в миллисекундах. LOG_FILE © CMA Small Systems AB, 2010 44 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика Пример кода для реализации интерфейса ICommQueue #pragma once #include <list> #include "QCommModule.h" struct sQueueData_t { sQueueData_t( void* connect, char* buffer, int32_t length, int32_t error ) : m_Connect( connect ) , m_Buf( buffer ) , m_Length( length ) , m_error(error) {} void* m_Connect; char* m_Buf; int32_t m_Length; int32_t m_error; }; class IOQueueEx : public ICommQueue { public: IOQueueEx() : m_non_empty(FALSE, TRUE) , m_CanWait( true ) {} ~IOQueueEx() {} void Reset() { CSingleLock l(&m_cs, TRUE); m_Queue.clear(); m_CanWait = true; m_non_empty.ResetEvent(); } bool Put(void* connect, char* buffer, int32_t length, int32_t error ) { CSingleLock l(&m_cs, TRUE); m_Queue.push_back( sQueueData_t( connect, buffer, length, error ) ); m_non_empty.SetEvent(); return true; } bool Get(void** connect, char** buffer, int32_t* int64_t nMilliSeconds) { if(m_non_empty.Lock( (DWORD) nMilliSeconds )) { CSingleLock l(&m_cs, TRUE); if( !m_Queue.size() ) { *error = 0; return false; } if(!m_CanWait) { *error = -1; return false; } length, int32_t* error, sQueueData_tdata = *m_Queue.begin(); m_Queue.erase( m_Queue.begin() ); *connect = data.m_Connect; *buffer = data.m_Buf; *length = data.m_Length; *error = data.m_error; if( !m_Queue.size() ) m_non_empty.ResetEvent(); return true; } *error = 0; return false; } © CMA Small Systems AB, 2010 45 Шлюз коммуникационной системы СЭТ СР СПбМТСБ bool Stop() { CSingleLock l(&m_cs, TRUE); m_CanWait = false; m_non_empty.SetEvent(); return true; } char* Alloc(int32_t length) { return new char[length]; } bool Free(char* buffer) { delete [] buffer; return true; } int GetLastError(void* connect, char* { return 0; } int GetCount() { CSingleLock l(&m_cs, TRUE); int i = m_Queue.size(); return i; } void SetAlive() { } private: CCriticalSection m_cs; CEventm_non_empty; boolm_CanWait; std::list<sQueueData_t> m_Queue; }; Руководство разработчика descrBuf, int bufLen) Пример кода для работы с информационным каналом Ниже дан пример кода, реализующего передачу информационных сообщений, поступающих от торгового сервера через ШКС к внешним системам в соответствии с настройками подписки: #include "stdafx.h" #include "IOQueueEx.h" CWinApp theApp; using namespace std; // декларация единственной экспортируемой функции коммуникационного модуля // вообще говоря данная декларация должна находится в заголовочном файле модуля // но ввиду многоплатформенности данного заголовочного файла ее там нет // для возможности статической линковки коммуникационного модуля ее следует // объявить нижеследующим образом extern "C" { ICommModule* GetICommModule(); } // экземпляр очереди для входящих сообщений // оттуда следует читать сообщения приходящие от // торгового сервера. Для чтения таких сообщений будет // создан отдельный поток исполнения IOQueueEx queueIn; // экземпляр очереди для исходящих сообщений // туда следует помещать сообщения // для их передачи торговому серверу IOQueueEx queueOut; // хендл соединения с торговым сервисом void* connection = NULL; // указатель на поток читающий очередь входящих сообщений CWinThread* thread = NULL; // флаг сигнализирующий работоспособность соединения // используется для корректной остановки потока © CMA Small Systems AB, 2010 46 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика // читающего очередь входящих сообщений при закрытии // соединения (завершении работы приложения) bool bOn = false; // указатель на конверт сообщения посланный торговому серверу // как синхронный запрос - запрос требующий ожидания аппликационного // ответа FTE::Envelope* pRequestSync = NULL; // указатель на конверт сообщения аппликационного ответа на синхронный запрос // в данном приложении используется для передачи ответа на синхронный запрос // в главный поток приложения (из потока чтения очереди входящих сообщений) // где производится его аналих FTE::Envelope* pReplySync = NULL; // эвент блокирующий работу приложения при посылке синхронного запроса CEvent waitReqSync(FALSE, TRUE); // таймаут ожидания аппликационного ответа на синхронный запрос (в секундах) int nAppReplyTimeout = 10; // асинхронная посылка сообщения (определенного параметром функции) // торговому сервису bool Request(FTE::Envelope* pReq) { int size = pReq->ByteSize(); if(size) { char* buffer = queueOut.Alloc(size); pReq->SerializeToArray(buffer, size); if(queueOut.Put(connection, buffer, size, 0)) return true; queueOut.Free(buffer); } return false; } // синхронная посылка сообщения (определенного параметром функции) // торговому сервису bool RequestSync(FTE::Envelope* pReq) { bool bRes = false; pRequestSync = pReq; waitReqSync.ResetEvent(); if(Request(pReq)) { bRes = ( (waitReqSync.Lock( nAppReplyTimeout != -1 ? nAppReplyTimeout * 1000 : nAppReplyTimeout )) ? true : false); } pRequestSync = NULL; return bRes; } // функция потока читающего очередь входящих сообщений UINT AFX_CDECL ListeningThreadFunc(LPVOID p) { while(true) { void* connect = NULL; char* buffer = NULL; int32_t length = 0; int32_t error = 0; if(queueIn.Get(&connect, &buffer, &length, &error, -1)) { if(error) { // получено сообщение об ошибке // приложение должно запротоколировать данное сообщение // и завершить работу (не реализовано в рамках данного примера) cout << "Some error arrived" << buffer << endl; queueIn.Free(buffer); } else { // получено сообщение от торгового сервиса - парсим его FTE::Envelope* reply = new FTE::Envelope; reply->ParseFromArray( buffer, length ); FTE::Envelope* pReq = NULL; © CMA Small Systems AB, 2010 47 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика // в поле reqid мы могли передавать ссылку на синхронный запрос // к торговому сервису. В этом случае сервис был обязан вернуть // данную ссылку в поле reqid ответа // проверяем наличие данного поля и его запролненность if(!reply->has_reqid() || reply->reqid() == 0) {} else pReq = (FTE::Envelope*) reply->reqid(); // проверяем является ли полученное сообщение ответом на синхронный запрос // который мы с настоящее время ожидаем if(pRequestSync != NULL && pReq == pRequestSync) { // полученное сообщение является ответом // на посланный ранее синхронный запрос // присваиваем ссылку на конверт ответа глобальной переменной pReplySync // для последующего анализа в главном потоке приложения и // выставляем эвент сигнализирующий о полученном ответе // на синхронный запрос pReplySync = reply; waitReqSync.SetEvent(); } else { // в рамках данного примера в качестве асинхронных ответов // мы обрабатываем только сообщения типа FTE::OK_CHANGES // (данные по открытым подпискам на информационные таблицы) if( reply->type().size() == 0 || reply->type().Get(0) != FTE::OK_CHANGES ) { // неизвестный тип сообщения - игнорируем его } else { FTE::Changes* change = new FTE::Changes; if(reply->data().size() == 0) { // пустое сообщение - игнорируем его // (вообще говоря такого быть не должно // при попадании сюда управления возможна ошибка в сервере) } else { // парсим ответ change->ParseFromString( reply->data().Get(0) ); for( int i = 0; i < change->row_size(); i++ ) { FTE::RowChange* rc = new FTE::RowChange; rc->ParseFromString( change->row( i ) ); // в рамках данного примера мы ожидает получения данных // только по таблице Securities // ответы по другим типам таблиц обрабатываются совершенно аналогично if(rc->tabid() == FTE::SECURITIES) { FTE::InfoSecurity *pSec = new FTE::InfoSecurity; pSec->ParseFromString( rc->rowdata() ); cout << "Got security information: " << pSec->code() << endl; delete pSec; } delete rc; } delete change; } } delete reply; } queueIn.Free(buffer); } } // при сброшенном флаге завершаем управляющую функцию потока if(!bOn) break; } return 0; © CMA Small Systems AB, 2010 48 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика } void Test() { cout << "Sample information client" << endl; string userId; string password; // = "MKL000010001"; // = "1"; // запрашиваем идентификатор пользователя и пароль cout << "Please enter your user id: "; cin >> userId; cout << "Please enter your password: "; cin >> password; // инициализируем коммуникационный модуль cout << "Initializing communication module..."; ICommModule* pCommModule = GetICommModule(); if(!pCommModule->Start("zmqCommClientI.ini")) { cout << "Communication module initialization failed" << endl; return; } cout << "OK" << endl; cout << "Connecting to trading service..."; // на всякий случай чистим очереди // в данном примере они и так чистые, но в реальной приложении при повторном // соединении с сервером следует не забывать об данной чистке queueIn.Reset(); queueOut.Reset(); // устанавливаем соединение connection = pCommModule->Connect(&queueIn); if(!connection) { cout << "Failed" << endl; return; } else { // устанавливаем очередь исходящих сообщений pCommModule->SetQueue( connection, &queueOut ); // создаем поток для чтения очереди входящих сообщений // для возможности дополнительной инициализации данных влияющих // потока на работу потока создаем его в остановленном виде thread = AfxBeginThread(ListeningThreadFunc, 0, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED); thread->m_bAutoDelete = FALSE; // флаг работоспособности соединения устанавливаем в true bOn = true; // пускаем поток thread->ResumeThread(); } cout << "OK" << endl; // посылаем синхронное сообщение Logon cout << "Sending logon..."; int size; char* tmpBuf; // создаем "protobuf" сообщение Logon FTE::Logon* logon = new FTE::Logon; // заполняем его данными logon->set_uid( userId ); logon->set_password( password ); size = logon->ByteSize(); tmpBuf = new char[size]; logon->SerializeToArray( tmpBuf, size ); delete logon; // создаем "protobuf" сообщение Envelope // данное сообщение является обрамляющим для всех прочих сообщений FTE::Envelope* env = new FTE::Envelope; © CMA Small Systems AB, 2010 49 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика env->set_ver( 1 ); // ВАЖНО // заполняем поле reqid ссылкой на конверт нашего сообщения // торговый сервис должен прислать данную ссылку в рамках ответа env->set_reqid( (google::protobuf::uint64) env ); env->add_type(FTE::REQ_LOGON ); env->set_uid( userId ); env->add_data(tmpBuf, size ); delete [] tmpBuf; // посылаем синхронный ответ if(!RequestSync(env)) { cout << "Failed" << endl; return; } delete env; // анализируем ответ if(pReplySync && pReplySync->type().size() && pReplySync->type().Get(0) == FTE::REP_OK) { cout << "OK" << endl; delete pReplySync; pReplySync = NULL; } else { cout << "Failed - reply is bad" << endl; return; } // подписываемся на таблицу Securities // подписка на остальные типы таблиц производится совершенно аналогично cout << "Subscribing to securities..."; FTE::Subscribe* subscr = new FTE::Subscribe; subscr->set_tab_id( FTE::SECURITIES ); subscr->set_max_row_id( -1 ); subscr->set_last_change_id( 0 ); size = subscr->ByteSize(); tmpBuf = new char[size]; subscr->SerializeToArray( tmpBuf, size ); delete subscr; FTE::Envelope* env1 = new FTE::Envelope; env1->set_ver( 1 ); env1->set_reqid( 1 ); env1->add_type(FTE::REQ_SUBSCRIBE ); env1->set_uid( userId ); env1->add_data(tmpBuf, size ); delete [] tmpBuf; // посылает запрос на подписку асинхронным образом if(!Request(env1)) { cout << "Failed" << endl; return; } delete env1; cout << "OK" << endl; // ждем некоторое время пока прибудут данные по нашей подписке // данный буду обработаны функцией потока чтения входящих сообщений // и полученная информация выведена на консоль ::Sleep(10000); // посылаем асинхронное сообщение Logoff cout << "Sending logoff..."; FTE::Envelope* env2 = new FTE::Envelope; env2->set_ver( 1 ); env2->set_reqid( (google::protobuf::uint64) env2 ); env2->add_type(FTE::REQ_LOGOFF ); env2->set_uid( userId ); if(!Request(env2)) { cout << "Failed" << endl; © CMA Small Systems AB, 2010 50 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика return; } cout << "OK" << endl; // закрываем соединение cout << "Closing connection..."; // сбрасываем флаг для корректного завершения потока чтения // очереди входящих сообщений bOn = false; // останавливаем очерель входящих сообщений // функция Get очереди вернет false и признак ошибки queueIn.Stop(); // ожидаем завершения потока чтения очереди входящих сообщений ::WaitForSingleObject(thread->m_hThread, INFINITE); // очищаем данный потока thread->Delete(); thread = NULL; // разрываем соединение pCommModule->Disconnect(connection); connection = NULL; cout << "OK" << endl; } int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { int nRetCode = 0; // initialize MFC and print and error on failure if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { // TODO: change error code to suit your needs _tprintf(_T("Fatal Error: MFC initialization failed\n")); nRetCode = 1; } else { // TODO: code your application's behavior here. Test(); } return nRetCode; } © CMA Small Systems AB, 2010 51 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика Пример кода для работы с транзакционным каналом Ниже дан пример кода, реализующего передачу транзакционных сообщений: #include "stdafx.h" #include "IOQueueEx.h" CWinApp theApp; using namespace std; // декларация единственной экспортируемой функции коммуникационного модуля // вообще говоря данная декларация должна находится в заголовочном файле модуля // но ввиду многоплатформенности данного заголовочного файла ее там нет // для возможности статической линковки коммуникационного модуля ее следует // объявить нежеследующем образом extern "C" { ICommModule* GetICommModule(); } // экземпляр очереди для входящих сообщений // оттуда следует читать сообщения приходящие от // торгового сервера. Для чтения таких сообщений будет // создан отдельный поток исполнения IOQueueEx queueIn; // экземпляр очереди для исходящих сообщений // туда следует засовывать сообщения // для их передачи торговому серверу IOQueueEx queueOut; // хендл соединения с торговым сервисом void* connection = NULL; // указатель на поток, читающий очередь входящих сообщений CWinThread* thread = NULL; // флаг сигнализирующий работоспособность соединения // используется для корректной остановки потока // читающего очередь входящих сообщений при закрытии // соединения (завершении работы приложения) bool bOn = false; // указатель на конверт сообщения посланный торговому серверу // как синхронный запрос - запрос требующий ожидания аппликационного // ответа FTE::Envelope* pRequestSync = NULL; // указатель на конверт сообщения аппликационного ответа на синхронный запрос // в данном приложении используется для передачи ответа на синхронный запрос // в главный поток приложения (из потока чтения очереди входящих сообщений) // где производится его аналих FTE::Envelope* pReplySync = NULL; // эвент блокирующий работу приложения при посылке синхронного запроса CEvent waitReqSync(FALSE, TRUE); // таймаут ожидания аппликационного ответа на синхронный запрос (в секундах) int nAppReplyTimeout = 10; // асинхронная посылка сообщения (определенного параметром функции) // торговому сервису bool Request(FTE::Envelope* pReq) { int size = pReq->ByteSize(); if(size) { char* buffer = queueOut.Alloc(size); pReq->SerializeToArray(buffer, size); if(queueOut.Put(connection, buffer, size, 0)) return true; queueOut.Free(buffer); } return false; } © CMA Small Systems AB, 2010 52 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика // синхронная посылка сообщения (определенного параметром функции) // торговому сервису bool RequestSync(FTE::Envelope* pReq) { bool bRes = false; pRequestSync = pReq; waitReqSync.ResetEvent(); if(Request(pReq)) { bRes = ( (waitReqSync.Lock( nAppReplyTimeout != -1 ? nAppReplyTimeout * 1000 : nAppReplyTimeout )) ? true : false); } pRequestSync = NULL; return bRes; } // функция потока читающего очередь входящих сообщений UINT AFX_CDECL ListeningThreadFunc(LPVOID p) { while(true) { void* connect = NULL; char* buffer = NULL; int32_t length = 0; int32_t error = 0; if(queueIn.Get(&connect, &buffer, &length, &error, -1)) { if(error) { // получено сообщение об ошибке // приложение должно запротоколировать данное сообщение // и завершить работу (не реализовано в рамках данного примера) cout << "Some error arrived" << buffer << endl; queueIn.Free(buffer); } else { // получено сообщение от торгового сервиса - парсим его FTE::Envelope* reply = new FTE::Envelope; reply->ParseFromArray( buffer, length ); FTE::Envelope* pReq = NULL; // в поле reqid мы могли передавать ссылку на синхронный запрос // к торговому сервису. В этом случае сервис был обязан вернуть // данную ссылку в поле reqid ответа // проверяем наличие данного поля и его запролненность if(!reply->has_reqid() || reply->reqid() == 0) {} else pReq = (FTE::Envelope*) reply->reqid(); // проверяем является ли полученное сообщение ответом на синхронный запрос // который мы в настоящее время ожидаем if(pRequestSync != NULL && pReq == pRequestSync) { // полученное сообщение является ответом // на посланный ранее синхронный запрос // присваиваем ссылку на конверт ответа глобальной переменной pReplySync // для последующего анализа в главном потоке приложения и // выставляем эвент сигнализирующий о полученном ответе // на синхронный запрос pReplySync = reply; waitReqSync.SetEvent(); } else { // в рамках данного примера в качестве асинхронных ответов // мы обрабатываем только сообщения типа FTE::OK_CHANGES // (данные по открытым подпискам на информационные таблицы) if( reply->type().size() == 0 || reply->type().Get(0) != FTE::OK_CHANGES ) { // неизвестный тип сообщения - игнорируем его } else { FTE::Changes* change = new FTE::Changes; if(reply->data().size() == 0) © CMA Small Systems AB, 2010 53 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика { // пустое сообщение - игнорируем его // (вообще говоря такого быть не должно // при попадании сюда управления возможна ошибка в сервере) } else { // парсим ответ change->ParseFromString( reply->data().Get(0) ); for( int i = 0; i < change->row_size(); i++ ) { FTE::RowChange* rc = new FTE::RowChange; rc->ParseFromString( change->row( i ) ); // в рамках данного примера мы ожидает получения данных // только по таблице Securities // ответы по другим типам таблиц обрабатыватся совершенно аналогично if(rc->tabid() == FTE::SECURITIES) { FTE::InfoSecurity *pSec = new FTE::InfoSecurity; pSec->ParseFromString( rc->rowdata() ); cout << "Got security information: " << pSec->code() << endl; delete pSec; } delete rc; } delete change; } } delete reply; } queueIn.Free(buffer); } } // при сброшенном флаге завершаем управляющую функцию потока if(!bOn) break; } return 0; } void Test() { cout << "Sample transaction client" << endl; string userId; string password; // = "MKL000010001"; // = "1"; // запрашиваем идентификатор пользователя и пароль cout << "Please enter your user id: "; cin >> userId; cout << "Please enter your password: "; cin >> password; // инициализируем коммуникационный модуль cout << "Initializing communication module..."; ICommModule* pCommModule = GetICommModule(); if(!pCommModule->Start("zmqCommClientI.ini")) { cout << "Communication module initialization failed" << endl; return; } cout << "OK" << endl; cout << "Connecting to trading service..."; // на всякий случай чистим очереди // в данном примере они и так чистые, но в реальной приложении при повторном // соединении с сервером следует не забывать об данной чистке queueIn.Reset(); queueOut.Reset(); // устанавливаем соединение connection = pCommModule->Connect(&queueIn); if(!connection) { © CMA Small Systems AB, 2010 54 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика cout << "Failed" << endl; return; } else { // устанавливаем очередь исходящих сообщений pCommModule->SetQueue( connection, &queueOut ); // создаем поток для чтения очереди входящих сообщений // для возможности дополнительной инициализации данных влияющих // потока на работу потока создаем его в остановленном виде thread = AfxBeginThread(ListeningThreadFunc, 0, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED); thread->m_bAutoDelete = FALSE; // флаг работоспособности соединения в true bOn = true; // пускаем поток thread->ResumeThread(); } cout << "OK" << endl; // посылаем синхронное сообщение Logon cout << "Sending logon..."; int size; char* tmpBuf; // создаем "protobuf" сообщение Logon FTE::Logon* logon = new FTE::Logon; // заполняем его данными logon->set_uid( userId ); logon->set_password( password ); size = logon->ByteSize(); tmpBuf = new char[size]; logon->SerializeToArray( tmpBuf, size ); delete logon; // создаем "protobuf" сообщение Envelope // данное сообщение является обрамляющим для всех прочих сообщений FTE::Envelope* env = new FTE::Envelope; env->set_ver( 1 ); // ВАЖНО // заполняем поле reqid ссылкой на конверт нашего сообщения // торговый сервис должен прислать данную ссылку в рамках ответа env->set_reqid( (google::protobuf::uint64) env ); env->add_type(FTE::REQ_LOGON ); env->set_uid( userId ); env->add_data(tmpBuf, size ); delete [] tmpBuf; // посылаем синхронный ответ if(!RequestSync(env)) { cout << "Failed" << endl; return; } delete env; // анализируем аппликационный ответ if(pReplySync && pReplySync->type().size() && pReplySync->type().Get(0) == FTE::REP_OK) { cout << "OK" << endl; delete pReplySync; pReplySync = NULL; } else { cout << "Failed - reply is bad" << endl; return; } // посылаем синхронное сообщение выставления заявки по инструменту // cout << "Sending order..."; FTE::Order* © CMA Small Systems AB, 2010 order = new FTE::Order; 55 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика // тип заявки - лимитированная order->set_type( FTE::LIMIT ); // поместить заявку в очередь в случае невозможности ее немедленного исполнения order->set_params( FTE::PIQ ); // параметры инструмента // ВАЖНО! для успешного прогона теста нижеуказанный режим торгов для нижеуказанного // инструмента должен быть открыт order->set_board( "BRD_NORMAL" ); order->set_security( "FDDTS2AVI60" ); // торговый счет order->set_account( "HMPC011000001001" ); // мы хотим купить order->set_buy_sell( FTE::BUY ); // фирма, клиент, контрагент ... order->set_firm( "ICP000010000" ); order->set_client( "GCP000110000" ); // определяем цену // цена определяется в виде целого числа с предопределенный количеством // условных десятичных знаком - 2 в нашем случае: // цена ниже определяется как 10.00 единиц DECIMAL d; d.Lo64 = 1000; order->set_price( d.Lo64 ); // определяем количество инструментов order->set_qty( 5 ); size = order->ByteSize(); tmpBuf = new char[size]; order->SerializeToArray( tmpBuf, size ); delete order; // создаем "protobuf" сообщение Envelope // данное сообщение является обрамляющим для всех прочих сообщений env = new FTE::Envelope; env->set_ver( 1 ); // ВАЖНО // заполняем поле reqid ссылкой на конверт нашего сообщения // торговый сервис должен прислать данную ссылку в рамках ответа env->set_reqid( (google::protobuf::uint64) env ); env->add_type(FTE::REQ_ORDER ); env->set_uid( userId ); env->add_data(tmpBuf, size ); delete [] tmpBuf; // посылаем синхронный ответ if(!RequestSync(env)) { cout << "Failed" << endl; return; } delete env; // анализируем ответ if(pReplySync && pReplySync->type().size() && pReplySync->type().Get(0) == FTE::REP_OK) { cout << "OK" << endl; delete pReplySync; pReplySync = NULL; } else { cout << "Failed - reply is bad" << endl; return; } // посылаем асинхронное сообщение Logoff cout << "Sending logoff..."; FTE::Envelope* env2 = new FTE::Envelope; env2->set_ver( 1 ); © CMA Small Systems AB, 2010 56 Шлюз коммуникационной системы СЭТ СР СПбМТСБ Руководство разработчика env2->set_reqid( (google::protobuf::uint64) env2 ); env2->add_type(FTE::REQ_LOGOFF ); env2->set_uid( userId ); if(!Request(env2)) { cout << "Failed" << endl; return; } cout << "OK" << endl; // закрываем соединение cout << "Closing connection..."; // сбрасываем флаг для корректного завершения потока чтения // очереди входящих сообщений bOn = false; // останавливаем очередь входящих сообщений // функция Get очереди вернет false и признак ошибки queueIn.Stop(); // ожидаем завершения потока чтения // очереди входящих сообщений ::WaitForSingleObject(thread->m_hThread, INFINITE); // очищаем данные потока thread->Delete(); thread = NULL; // разрываем соединение pCommModule->Disconnect(connection); connection = NULL; cout << "OK" << endl; } int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { int nRetCode = 0; // initialize MFC and print and error on failure if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { // TODO: change error code to suit your needs _tprintf(_T("Fatal Error: MFC initialization failed\n")); nRetCode = 1; } else { // TODO: code your application's behavior here. Test(); } return nRetCode; } © CMA Small Systems AB, 2010 57