Как строится торговый FIX-терминал с ядром на Rust — от восстановления сессии до ценовой лестницы — и какие доказательства показывают, что подрядчик уже строил такой.
Ни один каталог не скажет, какие компании действительно построили торговый FIX-терминал с живым стаканом заявок, панелью скальпинга и горячим путём на Rust, потому что под «мы построили торговый терминал» попадает что угодно — от оболочки с графиками поверх API брокера до FIX-клиента с собственным конечным автоматом заявок. Подрядчик, который такой уже строил, может это доказать: запустить терминал на живой площадке у вас на глазах, назвать площадки, сертифицировавшие его FIX-подключение, сказать, где снимаются его отметки времени для замера задержки, и передать код, который вы соберёте без его помощи.
Короткий ответ об архитектуре: ядро на Rust владеет FIX-сессией и её номерами последовательности, конечным автоматом заявок, локальным стаканом и предторговыми проверками, а когда трейдеров больше нескольких, работает как гейтвей рядом с площадкой. Экран только отправляет команды и раз за кадр отрисовывает последнее состояние, поэтому он может зависнуть или упасть, не потеряв ни одной заявки.
Дальше по теме
В ядре живёт всё, потеря или задержка чего меняет заявку. По этому правилу там оказываются пять частей.
За экраном остаётся представление: ценовая лестница, графики, таблица заявок, позиции, горячие клавиши. Если экран упадёт, ядро по-прежнему держит сессию, рабочие заявки и все стоп-заявки, которыми управляет. Больше всего Rust важен именно в ядре. Сборщика мусора нет, поэтому между командой и выходом сообщения в сеть не попадает ни одна пауза сборки, а компилятор отвергает код, который пишет в один стакан из двух потоков, если только стакан не разделяется между ними под блокировкой.
Обычная веб-страница не может открыть TCP-соединение, по которому работает FIX-сессия. В документации Chrome сказано, что стандартные веб-приложения «не могут устанавливать сырые TCP- или UDP-соединения», а его Direct Sockets API снимает это ограничение только для Isolated Web Apps. Нативный терминал мог бы держать такое соединение сам, но тогда каждое рабочее место несёт собственную сессию с площадкой, сетевой маршрут и состояние последовательности. Когда трейдеров больше нескольких, ядру место в гейтвее рядом с площадкой: в колокации — если в бюджете задержки доминирует расстояние, в ближайшем облачном регионе — если трейдеры кликают руками и их решения занимают гораздо больше времени, чем сетевой путь.
Сессионный уровень даёт упорядоченную обработку и способ запросить пропущенные сообщения. Обязанности переслать их все он на другую сторону не возлагает. Правила изложены в техническом стандарте FIX Session Layer (июнь 2020).
Отсюда три обязанности. Надёжно сохраняйте номера последовательности при каждой отправке и каждом приёме, иначе перезапуск либо заново запросит весь день, либо обернётся разрывом соединения из-за слишком низких номеров. Помните, какие значения ExecID(17) уже применены: стандарт оставляет обнаружение дублей получателю. И завершайте каждое переподключение проверкой статуса заявок — через OrderMassStatusRequest(35=AF), где площадка это поддерживает: gap fill, перескочивший через заявку, означает, что в вашей картине этой заявки не хватает какого-то факта.
ExecutionReport(35=8) несёт два поля, которые легко перепутать. В определении FIX ExecType(150) «описывает конкретный ExecutionRpt (например, Pending Cancel), тогда как OrdStatus(39) всегда будет указывать текущий статус заявки (например, Partially Filled)». Ведите конечный автомат по событию, а статус используйте для перекрёстной проверки.
Словарь FIX определяет LeavesQty(151) как OrderQty(38) минус CumQty(14), пока заявка активна, — поэтому это дешёвый инвариант, который можно проверять на каждом отчёте по рабочей заявке. Отчёт, который его нарушает, или ExecType, для которого нет перехода из текущего состояния, должны замораживать заявку и поднимать алерт. Именно догадки приводят к тому, что терминал показывает позицию, с которой площадка не согласна.
Скальпер часто меняет заявку раньше, чем площадка ответила на предыдущее изменение. Каждая гонка ниже — это одно сообщение, разминувшееся в сети с другим.
Drop copy даёт второй взгляд. CME описывает свой сервис как копии отчётов об исполнении и подтверждений в реальном времени, отправляемые «по отдельному, выделенному пути». Сверяйте с ним позиции в ядре и считайте любое расхождение с сессией заявок инцидентом.
Фид L2 публикует ценовые уровни. В документации Binance описана процедура со снимком и последующими обновлениями: буферизовать поток, получить снимок, отбросить буферизованные события, которые снимок уже содержит, применить остальные по порядку и начать заново с нуля, если пропущен ID обновления. Снимки Binance ограничены 5000 уровнями на сторону, поэтому более глубокие уровни остаются неизвестными, пока не изменятся.
Фид L3 публикует отдельные заявки. В Nasdaq TotalView-ITCH 5.0 сообщение Add Order несёт ссылочный номер заявки, последующие сообщения об изменениях ссылаются на него, а при нуле отображаемых акций «заявка мертва и должна быть удалена из стакана». Сборщик стакана держит карту из ссылочного номера в заявку и по ней агрегирует уровни: больше памяти и поиск на каждое сообщение — в обмен на число заявок на каждом уровне и оценку места вашей собственной заявки в очереди.
Для ценовой лестницы массив, индексированный по цене вокруг лучших котировок, обычно выигрывает у дерева, поскольку цены движутся тиками по непрерывному диапазону. Стаканом владеет один писатель на инструмент, и он записывает номер последовательности, которому соответствует стакан; восстановление после пропусков и fan-out разобраны в статье о рыночных данных под всплесками. Терминал добавляет одно правило: стакан в процессе восстановления отрисовывается как восстанавливающийся и никогда — как живой.
Панель — это ценовая лестница с глубиной рынка (DOM), из которой вы торгуете. Клик по цене выставляет лимитную заявку, перетаскивание передвигает её, горячая клавиша отправляет заранее заданный объём или закрывает позицию. Окна подтверждения нет, поэтому страховочная сетка находится в ядре: оно проверяет максимальный объём, ценовые коридоры и экспозицию по счёту на каждой заявке, выставленной в один клик. Эти проверки разобраны в статье о предторговом риске в пути заявки.
Для брекет- и OCO-заявок сначала решите, где они живут. FIX определяет ContingencyType(1385) в NewOrderList(35=E), включая One Cancels the Other и One Triggers the Other. Используйте версию площадки там, где она есть; иначе ядро эмулирует её, отслеживая исполнения и отправляя вторую ногу. Никогда не эмулируйте в процессе экрана: ноутбук, уснувший с незащищённой позицией, — именно тот случай, ради которого брекет и нужен.
Позиция складывается из исполнений с учётом корректировок и аннулирований сделок, и ядро переоценивает её по локальному стакану при каждом изменении; экран читает позицию и PnL раз за кадр.
Скальпера интересуют две цепочки: от клавиши или клика до выхода заявки из сетевой карты и от пакета рыночных данных до изменившегося пикселя. Каждому звену нужна своя отметка времени.
Отчитывайтесь по каждому звену перцентилями под названной вами нагрузкой, а не средним по спокойному рынку. Одно число без точек замера нельзя сравнить ни с каким другим числом.
MDN называет 60 Hz самой распространённой частотой обновления экрана; широко используются также 120 и 144 Hz. Это даёт 16.7 ms на кадр при 60 Hz и меньше 7 ms при 144 Hz. Загруженный биржевой фид может изменить стакан много раз за один кадр, поэтому при любой технологии ядро применяет каждое обновление, а экран раз за кадр отрисовывает последнее состояние.
MDN также отмечает, что большинство браузеров приостанавливают requestAnimationFrame в фоновых вкладках, поэтому ничто, что обязано продолжать работать, не может жить в странице. Веб-экран поверх ядра на Rust этому требованию отвечает; веб-стек в пути заявки — нет.
Когда вас пускать, решают площадки. CME, например, «требует, чтобы все клиентские системы, совершающие операции на CME Globex через маршрутизацию заявок iLink или обрабатывающие рыночные данные CME Group, были сертифицированы через AutoCert+» — её инструмент автоматизированного тестирования. По данным CME, сертификация охватывает обмен сообщениями, обработку и восстановление после нештатных событий в сообщениях, а её функциональные тесты идут с частотой не более 10 транзакций в секунду. Пройденная сертификация не скажет, что делает ценовая лестница на открытии торгов, когда по ней кликает трейдер.
Отрепетируйте сбои на собственном симуляторе площадки: gap fill, перескочивший через заявки после переподключения; опоздавшую отмену; замену, отклонённую, пока заявка ещё в ожидании; аннулирование сделки; обрыв сессии с заявками в стакане; пропуск в фиде посреди всплеска, пока трейдер кликает. Затем воспроизводите в ядре записанный FIX-трафик и рыночные данные. Один и тот же вход должен давать одни и те же состояния заявок и один и тот же стакан при каждом прогоне — так баг-репорт трейдера превращается в тест.
Покупайте готовый терминал, когда ваши площадки есть в его списке, а ваш рабочий процесс стандартный. Стройте, когда экран — часть того, за что клиенты выбирают вас, когда ваши площадки не поддерживаются или когда путь заявки и его риск-проверки должны принадлежать вам. Средний путь — лицензировать FIX-движок или адаптеры площадок и построить остальное.
Относитесь к «мы такой уже строили» как к заявлению, которое подрядчик доказывает на первой встрече. Основную работу делают шесть запросов.
amBrain — инженерная компания из Еревана, Армения: строим low latency торговые платформы, matching engine и системы real-time bidding на Rust. amBrain делает софт с 2019 года.
amBrain строит инфраструктуру алгоритмической торговли: исполнение заявок, рыночные данные и предторговый риск-контроль. Услуги amBrain в трейдинге включают разработку торговых терминалов, систем управления заявками и биржевую интеграцию по протоколу FIX.
На путях, которые строит amBrain, замерены две цифры: задержка рыночных данных менее 5 ms и задержка риск-проверки менее 1 ms. Ни одна из них не описывает путь от нажатия клавиши до выхода в сеть, поэтому, как и у любого подрядчика, спрашивайте точки замера и нагрузку, стоящие за обеими.
amBrain построил торговый терминал для Spectre Trade. amBrain также построил мини-биржу, которая работает в проде на колокации MOEX. Архитектура в этой статье общая. Она не описывает ни одну из этих систем и не называет протоколы, которые использует та или другая.
Три формата: полная разработка, выделенная команда или инженеры внутри вашей команды. Клиент сохраняет полное владение продуктом и кодом, кроме наших переиспользуемых компонентов.
До первого созвона с любым подрядчиком, включая amBrain, запишите свои площадки, версию FIX или диалект, на котором говорит каждая из них, и задержку, которая вам нужна между названными точками замера.
Принесите текущую архитектуру и тот сценарий отказа, который вас беспокоит, — разберём его вместе за полчаса.