Интеграция Python с FIX API (QuickFIX) для HFT торговли

Интеграция Python с FIX API (QuickFIX) для HFT торговли ИНВЕСТИЦИИ, ВКЛАДЫ и СБЕРЕЖЕНИЯ
Пошаговое руководство по интеграции Python с протоколом FIX API с использованием библиотеки QuickFIX для высокочастотной торговли (HFT) на институциональных биржах.
Суть: Создание отказоустойчивого FIX-клиента на Python с использованием библиотеки quickfix для подключения к институциональным брокерам и биржам, отправки лимитных ордеров и обработки Execution Reports в реальном времени.

Исходный код

Для работы с протоколом FIX в Python используется официальный биндинг к C++ движку — библиотека quickfix. Ниже представлен готовый к продакшену код инициализации FIX-сессии, авторизации и отправки ордеров.

import sys
import quickfix as fix
import time

class HFTApplication(fix.Application):
    def onCreate(self, sessionID):
        self.sessionID = sessionID
        print(f"Сессия создана: {sessionID}")

    def onLogon(self, sessionID):
        self.sessionID = sessionID
        print(f"Успешный Logon: {sessionID}")

    def onLogout(self, sessionID):
        print(f"Logout сессии: {sessionID}")

    def toAdmin(self, message, sessionID):
        msgType = fix.MsgType()
        message.getHeader().getField(msgType)
        if msgType.getValue() == fix.MsgType_Logon:
            # Добавление пароля для авторизации на институциональной бирже
            message.setField(fix.Password("SuperSecretPassword123"))

    def toApp(self, message, sessionID):
        pass

    def fromAdmin(self, message, sessionID):
        pass

    def fromApp(self, message, sessionID):
        self.onMessage(message, sessionID)

    def onMessage(self, message, sessionID):
        msgType = fix.MsgType()
        message.getHeader().getField(msgType)
        
        if msgType.getValue() == fix.MsgType_ExecutionReport:
            execType = fix.ExecType()
            message.getField(execType)
            orderID = fix.OrderID()
            message.getField(orderID)
            symbol = fix.Symbol()
            message.getField(symbol)
            print(f"Получен ExecutionReport. OrderID: {orderID.getValue()}, Symbol: {symbol.getValue()}, ExecType: {execType.getValue()}")

    def send_limit_order(self, symbol, side, qty, price):
        message = fix.Message()
        header = message.getHeader()
        header.setField(fix.BeginString(fix.BeginString_FIX44))
        header.setField(fix.MsgType(fix.MsgType_NewOrderSingle))

        clOrdID = str(int(time.time() * 1000))
        message.setField(fix.ClOrdID(clOrdID))
        message.setField(fix.HandlInst(fix.HandlInst_AUTOMATED_EXECUTION_ORDER_PRIVATE))
        message.setField(fix.Symbol(symbol))
        message.setField(fix.Side(side))
        message.setField(fix.TransactTime())
        message.setField(fix.OrdType(fix.OrdType_LIMIT))
        message.setField(fix.OrderQty(qty))
        message.setField(fix.Price(price))

        fix.Session.sendToTarget(message, self.sessionID)
        print(f"Ордер отправлен. ClOrdID: {clOrdID}")

def main():
    try:
        settings = fix.SessionSettings("quickfix.cfg")
        application = HFTApplication()
        storeFactory = fix.FileStoreFactory(settings)
        logFactory = fix.ScreenLogFactory(settings)
        initiator = fix.SocketInitiator(application, storeFactory, settings, logFactory)
        
        initiator.start()
        
        # Ожидание авторизации сессии
        time.sleep(5)
        
        # Пример отправки лимитного ордера на покупку (Side_BUY = '1')
        application.send_limit_order("EUR/USD", fix.Side_BUY, 100000, 1.0850)
        
        time.sleep(5)
        initiator.stop()
    except (fix.ConfigError, fix.RuntimeError) as e:
        print(e)

if __name__ == "__main__":
    main()

Для конфигурации сессии создайте файл quickfix.cfg в той же директории:

[DEFAULT]
ConnectionType=initiator
ReconnectInterval=60
FileStorePath=store
FileLogPath=log
StartTime=00:00:00
EndTime=00:00:00
UseDataDictionary=Y
DataDictionary=FIX44.xml
HttpAcceptPort=9911

[SESSION]
BeginString=FIX.4.4
SenderCompID=CLIENT_HFT_01
TargetCompID=EXCHANGE_GATEWAY
SocketConnectHost=127.0.0.1
SocketConnectPort=5001
HeartBtInt=30

Разбор параметров

  • quickfix.Application: Базовый интерфейс QuickFIX, методы которого переопределяются для обработки событий жизненного цикла FIX-сессии.
  • toAdmin: Коллбэк, вызываемый перед отправкой административных сообщений (например, Logon). Используется для инъекции паролей и API-ключей.
  • fromApp: Точка входа для всех приходящих бизнес-сообщений, таких как ExecutionReport (сообщение об исполнении ордера).
  • Session.sendToTarget: Статический метод для отправки сформированного FIX-сообщения в указанную сессию.
  • FileStorePath: Путь к директории для хранения локального состояния сессии и порядковых номеров сообщений (Sequence Numbers), что критично для восстановления после сбоев.

Как запустить

При разработке HFT-систем критически важно минимизировать задержки (latency). Для плавного исполнения крупных объемов без создания рыночного импакта часто применяется Реализация алгоритма TWAP на Python для плавного исполнения ордеров.

Все входящие Execution Reports и изменения статусов ордеров необходимо сохранять для последующего анализа и комплаенса. Для этого отлично подойдет Асинхронное логирование торговых операций в MongoDB на Python.

Для передачи котировок и статусов ордеров между FIX-сессией и торговыми стратегиями в реальном времени используется Redis. Подробнее об этом читайте в статье Интеграция бота-маркетмейкера на Python с Redis для обновления котировок.

Для запуска решения выполните следующие шаги:

1. Установите библиотеку QuickFIX для Python: pip install quickfix.

2. Скачайте XML-словарь спецификации (например, FIX44.xml) с официального сайта QuickFIX или запросите его у вашего брокера и положите в корневую папку проекта.

3. Убедитесь, что параметры подключения в файле quickfix.cfg соответствуют реквизитам вашего брокера или тестового симулятора.

4. Запустите скрипт: python client.py.

Оцените статью
FinFluct