Engine gRPC API¶
Торговый движок (engine) предоставляет gRPC-интерфейс для управления ордерами и получения рыночных данных. Сервер слушает на Unix-сокете для минимизации задержек при взаимодействии с сервисом api.
Стриминговые методы (SubscribeOrderbookSnapshots, SubscribeTrades) реализованы как server-side streaming: сервер непрерывно отправляет данные клиенту до разрыва соединения.
Определение сервиса¶
syntax = "proto3";
package exchange.engine.v1;
option go_package = "exchange/gen/engine/v1";
service EngineService {
// Проверка доступности
rpc Ping(PingRequest) returns (PingResponse);
// Получение текущей книги ордеров (L2-агрегация)
rpc GetOrderbook(GetOrderbookRequest) returns (GetOrderbookResponse);
// Размещение нового ордера
rpc AddOrder(AddOrderRequest) returns (AddOrderResponse);
// Отмена конкретного ордера
rpc CancelOrder(CancelOrderRequest) returns (CancelOrderResponse);
// Отмена всех ордеров по торговой паре
rpc CancelAllOrders(CancelAllOrdersRequest) returns (CancelAllOrdersResponse);
// Стрим снапшотов книги ордеров (server-side streaming)
rpc SubscribeOrderbookSnapshots(SubscribeOrderbookSnapshotsRequest)
returns (stream OrderbookSnapshot);
// Стрим публичных сделок (server-side streaming)
rpc SubscribeTrades(SubscribeTradesRequest)
returns (stream Trade);
}
Перечисления (Enums)¶
// Сторона ордера
enum OrderSide {
ORDER_SIDE_UNSPECIFIED = 0;
ORDER_SIDE_BUY = 1;
ORDER_SIDE_SELL = 2;
}
// Тип ордера
enum OrderType {
ORDER_TYPE_UNSPECIFIED = 0;
ORDER_TYPE_LIMIT = 1;
ORDER_TYPE_MARKET = 2;
}
// Статус ордера
enum OrderStatus {
ORDER_STATUS_UNSPECIFIED = 0;
ORDER_STATUS_OPEN = 1;
ORDER_STATUS_PARTIAL = 2;
ORDER_STATUS_FILLED = 3;
ORDER_STATUS_CANCELLED = 4;
}
// Тип исполнения (Time-in-Force)
enum TimeInForce {
TIME_IN_FORCE_UNSPECIFIED = 0;
TIME_IN_FORCE_GTC = 1; // Good Till Cancelled
TIME_IN_FORCE_IOC = 2; // Immediate Or Cancel
TIME_IN_FORCE_FOK = 3; // Fill Or Kill
}
Типы сообщений¶
Ping¶
GetOrderbook¶
Запрос текущего состояния книги ордеров с L2-агрегацией (уровни цен с суммарными объёмами).
message GetOrderbookRequest {
string ticker = 1; // торговая пара, например "BTC-USDT"
int32 depth = 2; // количество уровней с каждой стороны (1–100)
}
message GetOrderbookResponse {
string ticker = 1;
repeated PriceLevel bids = 2; // заявки на покупку, сортировка по убыванию цены
repeated PriceLevel asks = 3; // заявки на продажу, сортировка по возрастанию цены
int64 timestamp = 4; // Unix timestamp в миллисекундах
}
// Ценовой уровень в агрегированной книге (L2)
message PriceLevel {
string price = 1; // decimal-строка
string quantity = 2; // суммарный объём на уровне
}
AddOrder¶
Размещение нового ордера в книге. Если ордер немедленно исполняется (matching), возвращается статус FILLED или PARTIAL.
message AddOrderRequest {
string account_id = 1;
string balance_account_id = 2;
string ticker = 3;
OrderSide side = 4;
OrderType type = 5;
string price = 6; // decimal; обязательно для LIMIT, игнорируется для MARKET
string quantity = 7; // decimal
TimeInForce time_in_force = 8; // по умолчанию GTC
string client_order_id = 9; // опционально; уникальный ID клиента для идемпотентности
}
message AddOrderResponse {
string order_id = 1; // UUID ордера в системе
string client_order_id = 2;
OrderStatus status = 3;
string filled_qty = 4; // decimal; объём, исполненный немедленно
int64 exchange_time = 5; // Unix timestamp (мс) принятия ордера движком
}
CancelOrder¶
Отмена одного открытого ордера по системному ID или клиентскому ID.
message CancelOrderRequest {
string account_id = 1;
string order_id = 2; // UUID ордера в системе
string client_order_id = 3; // альтернатива order_id
// Одно из двух полей (order_id или client_order_id) обязательно
}
message CancelOrderResponse {
string order_id = 1;
OrderStatus status = 2; // ORDER_STATUS_CANCELLED при успехе
}
CancelAllOrders¶
Отмена всех открытых ордеров аккаунта по указанной торговой паре.
message CancelAllOrdersRequest {
string account_id = 1;
string ticker = 2; // торговая пара
}
message CancelAllOrdersResponse {
int32 orders_canceled = 1; // количество отменённых ордеров
}
SubscribeOrderbookSnapshots¶
Серверный стрим снапшотов книги ордеров. Движок отправляет полный снапшот при каждом изменении книги (добавление/отмена/исполнение ордера).
message SubscribeOrderbookSnapshotsRequest {
string ticker = 1;
int32 depth = 2; // количество уровней (по умолчанию 20)
}
// Полный снапшот книги ордеров (L2)
message OrderbookSnapshot {
string ticker = 1;
repeated PriceLevel bids = 2;
repeated PriceLevel asks = 3;
int64 sequence = 4; // монотонно возрастающий номер снапшота
int64 timestamp = 5; // Unix timestamp (мс)
}
Поведение стрима:
- При подключении движок немедленно отправляет первый снапшот с текущим состоянием книги.
- Последующие снапшоты отправляются при каждом изменении книги.
- Поле
sequenceпозволяет клиенту обнаружить пропущенные обновления. - При разрыве соединения клиент должен переподключиться и запросить новый начальный снапшот.
SubscribeTrades¶
Серверный стрим исполненных сделок в реальном времени.
message SubscribeTradesRequest {
string ticker = 1;
}
// Исполненная сделка
message Trade {
string id = 1; // UUID сделки
string ticker = 2;
string price = 3; // decimal — цена исполнения
string quantity = 4; // decimal — объём
OrderSide aggressor_side = 5; // сторона агрессивного ордера (инициировавшего матч)
int64 executed_at = 6; // Unix timestamp (мс)
}
Поведение стрима:
- Движок отправляет сообщение
Tradeпри каждом новом матче в книге ордеров. - Стрим публичный: данные не содержат идентификаторов аккаунтов.
Внутренние типы данных¶
// L3-ордер (уровень хранения в движке, не экспортируется напрямую)
// Используется внутри движка для представления отдельного ордера в книге
message L3Order {
string order_id = 1;
string account_id = 2;
string ticker = 3;
OrderSide side = 4;
OrderType type = 5;
string price = 6;
string quantity = 7;
string remaining_qty = 8;
TimeInForce time_in_force = 9;
int64 created_at = 10;
}
Обработка ошибок¶
Движок использует стандартные gRPC-коды статусов:
| Код | Название | Ситуация |
|---|---|---|
0 |
OK |
Успешное выполнение |
3 |
INVALID_ARGUMENT |
Неверные параметры (отсутствует тикер, некорректная цена, нулевой объём) |
5 |
NOT_FOUND |
Ордер не найден при отмене |
6 |
ALREADY_EXISTS |
client_order_id уже используется |
9 |
FAILED_PRECONDITION |
Ордер не может быть отменён (уже исполнен или отменён) |
13 |
INTERNAL |
Внутренняя ошибка движка |
14 |
UNAVAILABLE |
Движок перегружен или недоступен |
Примечания по производительности¶
| Аспект | Описание |
|---|---|
| Транспорт | Unix-сокет вместо TCP устраняет сетевой стек ОС и снижает задержки. |
| L3 книга ордеров | Движок хранит каждый ордер отдельно (L3), что обеспечивает точный матчинг и аудиторский след. Клиентам возвращается L2-агрегация (уровни цен). |
| Стримы | SubscribeOrderbookSnapshots и SubscribeTrades используют server-side streaming gRPC — одно постоянное соединение вместо поллинга. |
| Персистентность | Движок записывает ордера и сделки в PostgreSQL синхронно перед подтверждением клиенту, обеспечивая консистентность при перезапуске. |
Пример использования (псевдокод Go)¶
// Подключение через Unix-сокет
conn, err := grpc.Dial(
"unix:///var/run/engine.sock",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
client := enginev1.NewEngineServiceClient(conn)
// Размещение лимитного ордера
resp, err := client.AddOrder(ctx, &enginev1.AddOrderRequest{
AccountId: "acc-uuid",
BalanceAccountId: "bal-uuid",
Ticker: "BTC-USDT",
Side: enginev1.OrderSide_ORDER_SIDE_BUY,
Type: enginev1.OrderType_ORDER_TYPE_LIMIT,
Price: "65000.00",
Quantity: "0.001",
TimeInForce: enginev1.TimeInForce_TIME_IN_FORCE_GTC,
ClientOrderId: "my-order-001",
})
// Подписка на стрим книги ордеров
stream, err := client.SubscribeOrderbookSnapshots(ctx, &enginev1.SubscribeOrderbookSnapshotsRequest{
Ticker: "BTC-USDT",
Depth: 20,
})
for {
snapshot, err := stream.Recv()
if err != nil {
break
}
// обработка снапшота
}