Pull to refresh

Comments 24

>После перехода на х86 производительность выросла почти в три раза, средняя длительность обработки транзакции снизилась до 60 мс. наверно 60мкс?
UFO just landed and posted this here

Любопытства ради спрошу, а какие протоколы использует ваша биржа? Для market data, order entry итп.
Что то самодельное свое, или были взяты готовые наработки?

Доступно и то, и другое.

Есть стандартные протоколы – FIX для подачи заявок и FAST для маркет-даты. На срочном рынке для быстрой торговли есть также бинарный TWIME (про него на Хабре была отдельная подробная статья).

Также на всех рынках есть совсем свои: CGate на срочном рынке и ASTS Bridge на фондовом, валютном, денежном. Необходимость своего протокола вызвана, в частности, тем, что на рынках есть много специфичной для Московской Биржи функциональности, которой нет на других биржах и которая не предусмотрена стандартами. Например, клиринговые и позиционные данные и транзакции, переговорные режимы торгов, различные виды аукционов.
Спасибо. Ещё вопрос, если можно.
А вы ведёте какую либо статистику скорости реакции HFT участников?
Понятное дело что все, кто не пришел первым — будут сильно с задержкой обработаны, т.к. встанут в очередь, но вот по round trip времени первого агрессивного пакета, за вычетом задержки непосредственно сети, вполне можно судить.
Ну и если вы ведёте такую статистику — какая средняя сейчас скорость у HFT игроков на московской бирже. Спрашиваю т.к любопытно, насколько большая разница с тем же Eurex-ом например, где лидеры HFT гонки уже давно считают скорость реакции в десятках наносекунд… (У eurex-а очень много статистики доступно в сыром открытом виде, так что любой клиент биржа вооружившись python/pandas-ом может много этих данных вытащить несложным анализом).
Данные для такой статистики мы не собираем. Но у нас работают примерно на тех же скоростях, что и у при работе с немцами… согласно полученной в кулуарах неофициальной информации :)
А на чем терминал написан, если не секрет? Мы сейчас в поиске какой-нибудь библиотеки для гуя, и пока не нашли ничего кроме QT, а плюсцы не хочется.

Говорят у Microsoft есть такая штука, WPF, которая скоро станет кросс-платформенной

Вопрос в том, потянет ли wpf + дотнет стек, допустим, два 4к монитора по 50 окон с таблицами на каждом. Что в исходном сообщении я забыл упомянуть, да )

Не совсем так. Не обязательно держать в один момент времени все 50 во внимании. "одномоментно и одновременно".
По тематике сабжа, например, могут быть пара десятков окон с графиками и стаканами по инструментам и десяток индикаторов. Трейдер сосредоточен только на интересующих его в данный момент, но боковым зрением отслеживает какие-то всплески в соседних. Ну и иногда осматривая все. Делать это взглядом гораздо удобнее и быстрее, чем переключать экраны/вкладки/рабочие столы. Так что 50 одновременно открытых и отображаемых окон вполне себе нормальное и оправданное желание.

Там хотят два по 50, а всплески на втором телевизоре могут только отвлекать и рассеивать внимание человека. Не даром на КДПВ ограничились 6 графиками.

В целом да, 2 по 50 на 4К наверное только для информационного табло в зале (если они ещё существуют и там ими кто-нибудь пользуется)…
но думаю проще (дешевле) повесить для таких целей кучу (пару десятков) простых FHD мониторов (телевизоров) с мини-компами и на каждом по 5-10 окон показывать.
PS: Но относительно гуя могу выразить свою уверенность что обычные таблицы и графики на «два 4к монитора по 50 окон» потянет любой стек на нормальном (да и не очень) железе. Вопрос только в адекватности скорости обсчёта (получения) отображаемых данных. Поскольку сфера достаточно специфична, вполне нормально (берите пример с GameDev) выставить требования к железу для вашего ПО
Это вполне конкретный запрос от вполне конкретных людей, которые работают именно в такой конфигурации. Да, информацию предоставляет боковое зрение на уровне «что-то в правом верхнем углу начало двигаться», «начало двигаться быстро, или медленно», «начало двигаться рывками, или равномерно» с последующей мгновенной реакцией на событие фокусировкой внимания, а не открыванием еще одного окна.

А собственно почему и нет. У нас в компании все средства мониторинга в основном на devexpress-е реализованы, которое по сути нашлепка поверх WPF, и все прекрасно работает. Хотя я не берусь утверждать какие у нас пропорции количества мониторов к количеству серверов, т.к почти все PROD приложения запущены через citrix "где-то там" на citrix farm-е…

Скажу по себе, что все торговые терминалы я пишу либо на DirectX либо на OpenGL. Никаких «толстых» гуи фреймворков с кнопочками, списочками и прочим бредом. Для торговли это не надо, и скорости отрисовки совершенно нехватает для быстрой отдачи.
Если говорить про терминалы к ASTS, то секрет из этого сделать сложно – достаточно посмотреть на набор файлов в дистрибутиве, чтобы увидеть, что он написан на… Delphi. С одной стороны, он прекрасно справляется с большими объёмами данных, но с другой, мы все понимаем, что Delphi уже несколько вышел из моды… Но конкретно эти терминалы заточены под весьма специфичный круг задач и достаточно узкую целевую аудиторию, которая, согласно опросам, всё ещё отдаёт предпочтение толстым клиентам. Для приложений аналогичного класса и с аналогичными пользовательскими требованиями потихоньку мигрируем на .net и C#. Из библиотек, и там, и там, активно используем то, что делает DevExpress.

Кстати, небольшой совет – при выборе библиотек для корпоративных решений не забыть исследовать их совместимость с системами автотестирования. У нас, например, было долгое хождение по граблям, пока не научили их распознавать некоторые 3rd-party гуёвые контролы.

В других решениях биржи, нацеленных на более широкую аудиторию, сейчас применяем хорошо всем известный стек ныне популярных технологий – Vue.js, Kubernetes, Kafka, Camunda,…
Спасибо, у нас как раз «весьма специфичный круг задач и достаточно узкая целевая аудитория»
Спасибо за статью! А можно пару комментариев про in-memory DB, из текста следует, что Вы написали что то свое? Сколько это заняло по времени? Сколько серверов используется под базу? Кластер растянут между площадками или все в рамках одного ЦОДа?
Вообще говоря, наша in-memory DB достаточно сильно отличается от популярных на текущий момент, таких, как Aerospike, Tarantool и т.п., и по современным понятиям назвать её DB можно с натяжкой. Основная задача была в минимизации оверхеда. Полностью написана на С, описание таблиц — это C-структуры. По этим описаниям генерируются сериализаторы/десериализаторы, чтобы можно было сбрасывать структуры на диск или в сеть. Вся работа идет непосредственно со структурами в памяти. Репликация данных идет отдельно, и вообще ее может не быть. Для отображений и связей используются интрузивные списки (элементы prev/next лежат в самой записи), а голова — в другой таблице. Их тоже нужно объявлять в заголовке структуры. Также есть интрузивные деревья поиска для более сложных структур. Принцип работы с инрузивными типами очень похож на работу в ядре ОС Linux (https://notes.shichao.io/lkd/ch6/). Есть много вспомогательных функций, макросов, автогенераторов, но всё равно это больше программирование на С, чем работа с NoSQL-базой. Сделано это было, так как корни растут еще с начала 90-х, да и на PA-RISC родной компилятор С++ оставлял желать лучшего, а g++ под PA-RISC генерировал код раза в 3-4 медленнее, чем родной, поэтому до 2011 был только С. Сейчас, конечно, boost::intrusive более предпочтительный способ :)

Непосредственно перед стартом определятся максимальный размер каждой таблицы — и именно такой размер выделяется в оперативной памяти (с mlock). Это сделано для обеспечения не только минимальной задержки, но и стабильного джиттера, т.к. если в процессе работы будет увеличение размера, это приведет к небольшим паузам. Из-за особенности торгов мы каждый день перезапускаем систему и, соответственно, корректируем эти значения. Также в ходе старта происходит загрузка всех справочных данных на текущей день из SQL-базы. Можно выделить 4 класса таблиц: вспомогательные (хеши, индексы), статические данные (загружаются в отсортированном виде при старте из SQL-базы и более не меняются), динамические и динамические с реюзингом. Последний тип достаточно интересный, ~70-80 % RAM занимает таблица заявок, количество таких заявок может быть под 100 млн за день, и каждая строчка — это около ~450 байт. Хотя серверы с 1Тб+ RAM вполне доступны сейчас, но лет 6 назад этот объем был существенен, да и серверов с учетом всех гейтвеев более 100. Но самое главное — хранить их всех нет необходимости. Большая часть заявок в ходе торгового дня становится неактивна и нужна только для истории, сразу скажу, эта задача решается online-импортом в SQL-базу. Поэтому в ходе работы с этой таблицей ведется список LRU (least recently used), и когда все доступные записи таблицы использованы, начинается алгоритм реюзинга (отвязывание записей из LRU, входящих в отображения, и перевод в новый список доступных после реюзинга). Эта операция идет блочно по ~32 записи, т.е. мы освободили место под текущую транзакцию и сделали задел на будущее. Если меньше, то операции синхронизации (чтобы безопасно отвязать) на каждой транзакции будут давать замедление (производительность падает на 20 % в этом случае, по сравнению, когда реюзинга еще нет). Если делать больше то на транзакции, в которой происходит операция, будет всплеск задержки в обработки. Поэтому было найдено оптимальное значение (падение производительности ~1 %).

Все запросы пишутся тоже с нуля. Вообще говоря, в запросах сразу заложен нативный протокол Mustang и все запросы сразу формируют Mustang-пакеты без какого-либо внутреннего промежуточного представления. Собственно, по этой причине и нет сейчас «бинарного» протокола на фондовом и валютном рынке.

Данная база модифицируется всё время, пока функционирует Биржа, начиная с австралийской версии ASTS.

База на текущий момент не распределенная и все серверы имеют ее реплики на, в теории, неограниченное число серверов. Об этом расскажем в следующей части
Более хитрый баг заключался в том, что PA RISC использует последовательно консистентный (Sequential consistent) доступ к памяти, тогда как x86 может переупорядочивать операции на чтение, поэтому код, абсолютно валидный на одной платформе, стал нерабочим на другой.

А можно об этом поподробнее? Ведь даже если сам чип на своей шине производит переупорядочивание запросов на чтение памяти, с программной точки зрения, даже в мультипроцессорной системе, это вроде не должно быть заметно…
Еще как заметно, особенно на ARM'ах каких-нибудь. x86 в этом плане поскромнее, но тоже приходится время от времени использовать барьеры памяти. Можно тут посмотреть несколько неплохих визуализаций (или тут как реализовано в Qt, неабстрактное описание для разработчика, возможно будет легче разобраться).
много воды и фаллометрии. Где исходники посмотреть?
Sign up to leave a comment.