Інтеграція FIX-протоколу
FIX (Financial Information eXchange) — стандартний протокол обміну фінансовими даними, який використовується з 1992 року. Більшість інституціональних трейдерів, prime brokers та HFT-фірм вимагають FIX API для підключення до біржі. Без FIX біржа закрита для професійних учасників ринку.
Що таке FIX та навіщо він потрібен
FIX — це текстовий протокол над TCP. Повідомлення виглядає як набір тег=значення полів, розділених символом SOH (0x01):
8=FIX.4.4 | 9=178 | 35=D | 49=CLIENT1 | 56=EXCHANGE | 34=123 | 52=20240115-14:30:00.000 |
11=ORDER-001 | 55=BTC/USD | 54=1 | 38=0.1 | 40=2 | 44=42000 | 59=1 | 10=087 |
Ключові теги:
-
35=D— тип повідомлення (D = New Order Single) -
55— символ інструменту -
54— сторона (1=Buy, 2=Sell) -
38— кількість -
40— тип ордера (1=Market, 2=Limit, 3=Stop) -
44— ціна (для limit) -
59— Time in Force (0=DAY, 1=GTC, 3=IOC, 4=FOK)
FIX переважний для інститеціоналів з кількох причин:
- Стандартний протокол — їхні системи вже вміють працювати з FIX
- Низька latency: бінарно ефективніше ніж HTTP/JSON
- Надійна сесійна модель: автоматичне відновлення після розривів, message sequencing
Архітектура FIX сервера
QuickFIX/Go — основний вибір
QuickFIX — reference implementation FIX engine, порти для Go, Java, C++, Python. Go версія (quickfixgo) — хороша база для production біржи.
import (
"github.com/quickfixgo/quickfix"
"github.com/quickfixgo/quickfix/field"
"github.com/quickfixgo/quickfix/fix44"
"github.com/quickfixgo/quickfix/fix44/newordersingle"
)
type FIXApplication struct {
orderEngine *OrderEngine
sessionManager *SessionManager
}
// OnCreate — викликається при створенні FIX сесії
func (app *FIXApplication) OnCreate(sessionID quickfix.SessionID) {
log.Info("FIX session created", "sessionID", sessionID)
}
// OnLogon — викликається при успішному логоні
func (app *FIXApplication) OnLogon(sessionID quickfix.SessionID) {
log.Info("FIX client logged on", "sessionID", sessionID)
app.sessionManager.SetOnline(sessionID)
}
// OnLogout — викликається при розриві з'єднання
func (app *FIXApplication) OnLogout(sessionID quickfix.SessionID) {
log.Info("FIX client logged out", "sessionID", sessionID)
app.sessionManager.SetOffline(sessionID)
}
// FromApp — обробка вхідних повідомлень від клієнта
func (app *FIXApplication) FromApp(msg *quickfix.Message, sessionID quickfix.SessionID) quickfix.MessageRejectError {
msgType, err := msg.Header.GetString(field.NewMsgType())
if err != nil {
return err
}
switch msgType {
case "D": // New Order Single
return app.handleNewOrder(msg, sessionID)
case "F": // Order Cancel Request
return app.handleCancelOrder(msg, sessionID)
case "G": // Order Cancel/Replace Request (amend)
return app.handleAmendOrder(msg, sessionID)
case "H": // Order Status Request
return app.handleStatusRequest(msg, sessionID)
}
return quickfix.NewMessageRejectError("Unknown message type", 35, nil)
}
Обробка New Order Single (D)
func (app *FIXApplication) handleNewOrder(msg *quickfix.Message, sessionID quickfix.SessionID) quickfix.MessageRejectError {
nos := newordersingle.New(
field.NewClOrdID(""),
field.NewSide(0),
field.NewTransactTime(time.Now()),
field.NewOrdType(0),
)
if err := quickfix.Unmarshal(msg, &nos); err != nil {
return err
}
// Парсим поля
clOrdID, _ := nos.GetClOrdID()
symbol, _ := nos.GetSymbol()
sideInt, _ := nos.GetSide()
ordType, _ := nos.GetOrdType()
qty, _ := nos.GetOrderQty()
price, _ := nos.GetPrice()
tif, _ := nos.GetTimeInForce()
// Конвертуємо FIX типи у внутрішні
order := Order{
ClientOrderID: string(clOrdID),
Pair: normalizePair(string(symbol)),
Side: fixSideToInternal(sideInt),
Type: fixOrdTypeToInternal(ordType),
Quantity: decimal.NewFromFloat(float64(qty)),
Price: decimal.NewFromFloat(float64(price)),
TimeInForce: fixTIFToInternal(tif),
}
// Відправляємо Execution Report — Pending New
app.sendExecReport(sessionID, order, ExecTypeNew, OrdStatusPendingNew)
// Розміщуємо ордер у matching engine
trades, err := app.orderEngine.PlaceOrder(order)
if err != nil {
app.sendExecReport(sessionID, order, ExecTypeRejected, OrdStatusRejected)
return nil
}
// Відправляємо Execution Reports за кожним fill
for _, trade := range trades {
app.sendFillReport(sessionID, order, trade)
}
// Якщо есть залишок — New або PartiallyFilled
if order.RemainingQty().IsPositive() {
status := OrdStatusNew
if len(trades) > 0 {
status = OrdStatusPartiallyFilled
}
app.sendExecReport(sessionID, order, ExecTypeNew, status)
}
return nil
}
Execution Report (8) — відповідь на ордер
func (app *FIXApplication) sendExecReport(sessionID quickfix.SessionID, order Order,
execType ExecType, ordStatus OrdStatus) {
report := fix44executionreport.New(
field.NewOrderID(order.ID),
field.NewExecID(generateExecID()),
field.NewExecType(fix44.ExecType(execType)),
field.NewOrdStatus(fix44.OrdStatus(ordStatus)),
field.NewSymbol(denormalizePair(order.Pair)),
field.NewSide(fix44.Side(internalSideToFIX(order.Side))),
field.NewLeavesQty(order.RemainingQty().InexactFloat64(), 8),
field.NewCumQty(order.FilledQty.InexactFloat64(), 8),
field.NewAvgPx(order.AvgPrice().InexactFloat64(), 8),
)
report.SetClOrdID(order.ClientOrderID)
report.SetOrderQty(order.Quantity.InexactFloat64(), 8)
report.SetTransactTime(time.Now())
quickfix.SendToTarget(report.ToMessage(), sessionID)
}
Сесійна модель FIX
FIX сесія підтримує sequencing повідомлень. Кожне повідомлення має MsgSeqNum (34). При розриві з'єднання та переподключенні:
- Клієнт відновлює сесію з останнім відомим SeqNum
- Сервер може відправити ResendRequest (2) для запиту пропущених повідомлень
- Або відправити SequenceReset (4) якщо зберігання історії не підтримується
Аутентифікація та безпека
FIX 4.4 не має вбудованої аутентифікації. Стандартні підходи:
- IP whitelist: лише дозволені IP можуть підключитися до FIX порту
- TLS: шифрування з'єднання (FIX over SSL)
- Logon (35=A) з паролем: поле 96 (RawData) або кастомний тег для API ключа + підписи
FIX версії
| Версія | Застосування |
|---|---|
| FIX 4.2 | Legacy системи, широко підтримується |
| FIX 4.4 | Сучасний стандарт для більшості бірж |
| FIXT 1.1 + FIX 5.0 | Розділення транспорту та додатку, нішові випадки |
| FIX/FAST | Бінарна компресія для market data, ultra-low latency |
Для крипто-біржі: FIX 4.4 оптимальний. Більшість інституціональних клієнтів його підтримають, документація обширна.
Тестування
FIX інтеграція тестується з FIX клієнтами:
- QuickFIX Executor: базовий FIX client для тестування
- ATDL: специфікація для алготрейдингу
- Нагрузкове тестування: 1000+ ордерів/сек через FIX
Сроки розробки
- Базовий FIX 4.4 сервер (New Order, Cancel, Execution Reports): 4–6 тижнів
- Market Data feed через FIX: 2–3 тижні
- TLS + аутентифікація + IP whitelist: 1–2 тижні
- Drop Copy: 1–2 тижні
- Тестування та документація: 2–3 тижні
- Повна production-ready FIX інтеграція: 2–3 місяці







