Манитон Docs

Торговля и «стакан»

Ордербук, matching и атомарные расчёты

Торговля и «стакан»

1) Режимы торгов

Платформа поддерживает вторичное обращение ЦФА. Это позволяет инвесторам продавать активы до срока погашения.

Типы заявок (Orders)

  • Limit Order: Купить/продать по фиксированной цене (или лучше). Попадает в стакан.
  • Market Order: Купить/продать немедленно по лучшей доступной цене.

2) Процесс матчинга (Matching Engine)

Схематичное представление того, как заявка превращается в сделку.

Loading diagram...

3) Атомарность (DvP - Delivery vs Payment)

Ключевой принцип безопасной торговли — Delivery versus Payment. Передача актива и оплата происходят одновременно.

Поток событий расчёта (Settlement Flow)

Loading diagram...

Модель проводок

При совершении сделки Ledger-DB выполняет следующие записи:

СубсчетДебетКредитОписание
User A (Buyer)-5000 RUBСписание средств за покупку
User B (Seller)+4950 RUBЗачисление средств за продажу
System Fees+50 RUBКомиссия платформы (1%)
User B (Seller)-10 CFAСписание актива
User A (Buyer)+10 CFAЗачисление актива покупателю

4) Контроль и Ограничения

  • Режим работы: Торги могут быть приостановлены (Trading Halt) администратором или автоматически при высокой волатильности.
  • Манипулирование: Anti-Fraud модуль анализирует сделки на предмет Wash Trading (торговля с самим собой) и других аномалий.

5) Размещение ордера

Create Order

const order = await marketService.placeOrder(
  { requestId, correlationId, idempotencyKey },
  {
    userId: 'user-123',
    instrumentId: 'instrument-456',
    side: OrderSide.BUY,
    type: OrderType.LIMIT,
    price: { amount: '100', currencyCode: 'RUB' },
    quantity: 10,
    timeInForce: TimeInForce.GTC,
  },
);

Проверки:

  1. KYC статус пользователя
  2. Лимиты торговли
  3. Баланс (для покупки)
  4. Количество актива (для продажи)

Cancel Order

const cancelled = await marketService.cancelOrder(
  { requestId, correlationId, idempotencyKey },
  'order-789',
);

6) Исполнение сделки

DvP Settlement

Атомарный расчет с гарантией поставки против платежа.

Проводки:

СубсчетДебетКредитОписание
User A (Buyer)-5000 RUBСписание средств за покупку
User B (Seller)+4950 RUBЗачисление средств за продажу
System Fees+50 RUBКомиссия платформы (1%)
User B (Seller)-10 CFAСписание актива
User A (Buyer)+10 CFAЗачисление актива покупателю

Процесс:

Loading diagram...

7) Order Book

Структура

class OrderBook {
  private bids: PriorityQueue<Order> = new PriorityQueue(
    (a, b) => b.price - a.price, // Высокая цена в начале
  );

  private asks: PriorityQueue<Order> = new PriorityQueue(
    (a, b) => a.price - b.price, // Низкая цена в начале
  );

  addOrder(order: Order): void {
    if (order.side === OrderSide.BUY) {
      this.bids.push(order);
    } else {
      this.asks.push(order);
    }
  }

  bestBid(): Order | null {
    return this.bids.peek();
  }

  bestAsk(): Order | null {
    return this.asks.peek();
  }

  getSnapshot(): OrderBookSnapshot {
    return {
      instrumentId: this.instrumentId,
      bids: this.bids.toArray().map((o) => ({
        price: o.price,
        quantity: o.quantity,
      })),
      asks: this.asks.toArray().map((o) => ({
        price: o.price,
        quantity: o.quantity,
      })),
      capturedAt: new Date(),
    };
  }
}

Хранение в Redis

class OrderBookStorage {
  private redis: Redis;

  async saveSnapshot(instrumentId: string, snapshot: OrderBookSnapshot): Promise<void> {
    const key = `orderbook:${instrumentId}`;
    await this.redis.setex(key, 3600, JSON.stringify(snapshot));
  }

  async getSnapshot(instrumentId: string): Promise<OrderBookSnapshot | null> {
    const key = `orderbook:${instrumentId}`;
    const data = await this.redis.get(key);
    return data ? JSON.parse(data) : null;
  }

  async publishUpdate(instrumentId: string, snapshot: OrderBookSnapshot): Promise<void> {
    await this.redis.publish(`orderbook:${instrumentId}`, JSON.stringify(snapshot));
  }
}

8) WebSocket Updates

Подписка на стакан

// Клиент подписывается на обновления стакана
const ws = new WebSocket('wss://api.maniton.io/orderbook?instrument_id=instrument-456');

ws.onmessage = (event) => {
  const snapshot = JSON.parse(event.data);
  updateOrderBook(snapshot);
};

Публикация обновлений

// Сервис публикует обновления стакана
await this.orderBookStorage.publishUpdate(instrumentId, snapshot);

9) Мониторинг

Метрики

export const tradingMetrics = {
  ordersPlacedTotal: new Counter({
    name: 'orders_placed_total',
    help: 'Total number of orders placed',
    labelNames: ['instrument_id', 'side'],
  }),

  tradesExecutedTotal: new Counter({
    name: 'trades_executed_total',
    help: 'Total number of trades executed',
    labelNames: ['instrument_id'],
  }),

  orderBookDepth: new Gauge({
    name: 'order_book_depth',
    help: 'Order book depth by instrument',
    labelNames: ['instrument_id', 'side'],
  }),

  tradeExecutionDuration: new Histogram({
    name: 'trade_execution_duration_seconds',
    help: 'Time spent executing trades',
    buckets: [0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5],
  }),
};

Логи

this.logger.log('Order placed', {
  orderId: order.orderId,
  userId: order.userId,
  instrumentId: order.instrumentId,
  side: order.side,
  price: order.price,
  quantity: order.quantity,
});

this.logger.log('Trade executed', {
  tradeId: trade.tradeId,
  buyerId: trade.buyerId,
  sellerId: trade.sellerId,
  instrumentId: trade.instrumentId,
  quantity: trade.quantity,
  price: trade.price,
  fee: trade.fee,
});

10) Troubleshooting

Проблема: Ордер не исполняется

Диагностика:

# Проверка стакана
curl http://market-service:3004/orderbook?instrument_id=instrument-456

# Проверка ордера
curl http://market-service:3004/orders/$ORDER_ID

# Проверка холдов
curl http://ledger-service:3003/holds

Решение:

  1. Проверьте, что цена соответствует стакану
  2. Проверьте, что холды созданы
  3. Проверьте, что баланс достаточен

Проблема: Сделка не проходит

Диагностика:

# Проверка статуса сделки
curl http://market-service:3004/trades/$TRADE_ID

# Проверка операции в Ledger
curl http://ledger-service:3003/operations/$OPERATION_ID

# Проверка транзакцию в блокчейне
curl -X POST http://besu-connector:3004/getTransactionReceipt \
  -H "Content-Type: application/json" \
  -d '{"tx_hash": "0x..."}'

Решение:

  1. Проверьте, что транзакция прошла в блокчейне
  2. Проверьте, что холды потреблены
  3. Ручной разбор если нужно

Дополнительные ресурсы

On this page