Блог им. bcs

«А FIX у вас есть?» — один из самых частых вопросов, которые задают брокеру перед подключением робота. Короткого ответа на него нет. Под словом FIX скрываются как минимум три разных способа отправить заявку, и только два из них — прямой доступ к бирже.
Разберем, чем они отличаются и как понять, какой из них нужен именно вам.
FIX (Financial Information eXchange) — протокол обмена сообщениями о заявках и сделках. Он описывает, как выглядит сообщение: какие поля в заявке, как приходит подтверждение, как передается информация об исполнении. Торговые системы по всему миру используют его как общий стандарт, поэтому многие платформы и готовые роботы «говорят» на FIX из коробки.
Чего протокол не описывает — это маршрута. Одно и то же FIX-сообщение может уйти на сервер брокера, а может сразу в торговое ядро биржи. От маршрута зависит все остальное: задержка, оформление, требования к вашему ПО и цена.
Поэтому вопрос «есть ли FIX» стоит переформулировать: куда вы хотите отправлять заявки по FIX? Вариантов три.
Ваша платформа подключается по FIX к серверу QUIK брокера. Терминал при этом не нужен: робот работает с сервером программно, без запущенного QUIK и привязки к рабочему месту.
Маршрут заявки: робот → сервер QUIK брокера → торговая система брокера → биржа.
Это не DMA. Заявка проходит через ту же инфраструктуру, что и заявки из терминала, и потолок по скорости задает она. Зато и требований меньше: отдельная сеть не нужна, сертифицировать свое ПО на бирже тоже не нужно.
У БКС этот вариант реализован двумя модулями к QUIK:
Кому подходит: у вас уже есть система, которая работает по FIX, нужно торговать с нескольких счетов по единым правилам, а скорость вторична. Главное здесь — совместимость: не переписывать то, что уже работает.
FIX Gate — шлюз Московской биржи на срочном рынке (торговая система SPECTRA), работает по FIX версии 4.4. Ваше ПО подключается к нему напрямую, минуя торговую систему брокера.
Маршрут заявки: робот → шлюз биржи → торговое ядро.
Это уже DMA, и вместе с ним приходят три обязательных элемента:
Важная деталь: FIX Gate — шлюз транзакционный. Он принимает заявки и возвращает ответы по ним, но котировки и стакан не отдает. Рыночные данные подключаются отдельно — через FAST или более быструю SIMBA.
MFIX — FIX-шлюз фондового и валютного рынков, которые работают на торговой системе ASTS. Проще всего описать его через сравнение с FIX Gate.
Что общего: FIX 4.4, прямое подключение к ядру биржи, свой логин, сертификация ПО, котировки и стакан подключаются отдельно.
Чем отличается:
Котировки к MFIX обычно подключают через FAST.
Drop Copy есть на обоих рынках. Это отдельная FIX-сессия, через которую приходят отчеты о заявках и сделках — не только ваших собственных, а в пределах заданного круга: по фирме, брокеру или набору счетов. Торговать через нее нельзя, она только для чтения.
На срочном рынке Drop Copy — отдельный сервис со своим FIX-логином, и он транслирует заявки и сделки, прошедшие через любой шлюз: FIX Gate, Plaza II и TWIME. То есть даже робот на бинарном TWIME может получать контрольную копию своего потока по FIX. На фондовом и валютном рынках Drop Copy входит в семейство MFIX.
Используют его для контроля: например, чтобы риск-менеджмент видел весь поток заявок независимо от торговых сессий и чтобы восстановить данные после обрыва. Скорость у него не главное: на фондовом рынке задержка в обычных условиях до 100 миллисекунд.
Иногда FIX вспоминают и в разговоре о самых быстрых шлюзах биржи — TWIME и FIFO TWIME. Формально они относятся к тому же семейству стандартов: TWIME построен на FIX Simple Binary Encoding (бинарное кодирование сообщений) и сессионном протоколе FIXP. Но это другой протокол со своей спецификацией. FIX Gate и MFIX обмениваются текстовыми сообщениями FIX 4.4, TWIME — бинарными, и сессия устроена иначе. Робот, написанный под FIX Gate, на TWIME без доработки не заработает.
Так что если вам нужна минимальная задержка, разговор уже не о FIX, а о TWIME и колокации.
Частично. FIX — общий стандарт, но каждый шлюз публикует свою спецификацию поверх него: какие сообщения и поля обязательны, как указывать инструмент и счет, какие дополнительные поля используются. В спецификации FIX Gate отличия от стандартного протокола прямо отмечены, и в ней есть собственные поля, которых нет у FIX-подключения к серверу QUIK. Поэтому при переходе:
Как это выглядит на практике. Так устроена заявка New Order Single (тип сообщения D) для FIX Gate — поля взяты из спецификации биржи, значения условные, разделитель полей SOH для читаемости заменен на |:
8=FIX.4.4|9=…|35=D|49=<ваш логин>|56=<TargetCompID из спецификации>|34=215|52=20261001-07:15:03.120|11=ord-000215|1=A01|55=SiZ6|54=1|38=5|40=2|44=81250|59=0|60=20261001-07:15:03.120000000|10=…|—
Большая часть здесь — стандартный FIX 4.4: 11 — идентификатор заявки, 54 — направление, 38 — количество, 44 — цена. Но есть и детали, которые задает именно биржа:
Все это и есть та часть, которая отличается от шлюза к шлюзу. Поэтому работу с конкретным шлюзом удобно держать в отдельном модуле: торговая логика формирует заявку в своих терминах, а модуль переводит ее в поля нужного шлюза. Упрощенно:
from dataclasses import dataclass—
SOH = "\x01" @data classclass Order:
order_id: str
instrument: str # код инструмента в терминах робота
side: str # «buy» / «sell»
qty: int
price: float
def fix_message(fields: list[tuple[int, str]], header: list[tuple[int, str]]) -> str:
""«Собирает FIX 4.4: считает BodyLength (9) и CheckSum (10).»""
body = "".join(f"{tag}={val}{SOH}" for tag, val in header + fields)
head = f«8=FIX.4.4{SOH}9={len(body.encode())}{SOH}»
checksum = sum((head + body).encode()) % 256
return f"{head}{body}10={checksum:03d}{SOH}"
class FixGateAdapter: """
Всё, что специфично для FIX Gate, живёт здесь."""
def __init__(self, sender: str, target: str, client_code: str):
self.sender, self.target, self.client_code = sender, target, client_code
self.seq = 0
def new_order(self, o: Order, sending_time: str, transact_time: str) -> str:
self.seq += 1 header = [(35, «D»), (49, self.sender), (56, self.target),
(34, str(self.seq)), (52, sending_time)]
fields = [(11, o.order_id), (1, self.client_code), (55, o.instrument),
(54, «1» if o.side == «buy» else «2»), (38, str(o.qty)),
(40, «2»), # FIX Gate: только лимитные заявки
(44, f"{o.price:g}"), (59, «0»), (60, transact_time)]
return fix_message(fields, header)
При переходе на другой шлюз меняется только класс адаптера и настройки сессии, а торговая логика, которая создает Order, остается прежней. Готовый FIX-движок в реальном роботе все равно нужен: этот пример показывает разделение ответственности, а не заменяет библиотеку.
Объем доработки зависит от того, как написан робот. Если работа с конкретным шлюзом вынесена в отдельный модуль, переход сводится к новому модулю и сертификации. Если поля брокерского FIX разбросаны по всему коду, работы больше.