Настройка логирования сделок криптобота

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1Все 1306 услуг
Настройка логирования сделок криптобота
Простой
~1 день
Часто задаваемые вопросы

Направления блокчейн-разработки

Этапы блокчейн-разработки

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1307
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1219
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    920
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1148
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    611
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    886

Настройка логирования сделок крипто-бота

Большинство торговых ботов логируют либо слишком мало (только факт исполнения ордера), либо в неструктурированном виде (console.log в файл). Это создаёт проблемы при отладке просадок, расчёте реальной P&L с учётом fees, и налоговой отчётности. Правильное логирование — это структурированные события с достаточным контекстом для полного аудита.

Что нужно логировать

Минимальный набор событий:

type TradeEvent =
  | { type: "order_placed"; orderId: string; symbol: string; side: "buy" | "sell"; quantity: number; price: number; orderType: "market" | "limit"; timestamp: number; }
  | { type: "order_filled"; orderId: string; executedQty: number; executedPrice: number; fee: number; feeCurrency: string; timestamp: number; }
  | { type: "order_cancelled"; orderId: string; reason: string; timestamp: number; }
  | { type: "position_opened"; positionId: string; entryPrice: number; size: number; leverage: number; timestamp: number; }
  | { type: "position_closed"; positionId: string; exitPrice: number; realizedPnl: number; timestamp: number; }
  | { type: "signal_generated"; strategy: string; signal: string; params: Record<string, unknown>; timestamp: number; }
  | { type: "error"; code: string; message: string; context: Record<string, unknown>; timestamp: number; };

Каждое событие должно содержать strategy (имя стратегии), exchange, sessionId (ID запуска бота) — это позволяет фильтровать логи по конкретному прогону.

Структурированное логирование: pino или winston

Не пишите в файл сырым текстом. JSON-логи парсятся инструментами, plain text — нет:

import pino from "pino";

const logger = pino({
  level: process.env.LOG_LEVEL ?? "info",
  base: {
    strategy: process.env.STRATEGY_NAME,
    exchange: process.env.EXCHANGE,
    sessionId: process.env.SESSION_ID ?? Date.now().toString(36),
  },
  transport:
    process.env.NODE_ENV === "development"
      ? { target: "pino-pretty" }  // читаемый вывод в dev
      : undefined,                  // JSON в production
});

// Использование
logger.info({ type: "order_filled", orderId, executedQty, executedPrice, fee }, "Order filled");
logger.error({ type: "error", code: "EXCHANGE_TIMEOUT", context: { orderId } }, "Exchange request timeout");

Хранение: PostgreSQL + TimescaleDB

Для серьёзного анализа логи нужно писать в базу, а не только в файл:

-- Основная таблица событий
CREATE TABLE trade_events (
    id          BIGSERIAL,
    session_id  TEXT NOT NULL,
    strategy    TEXT NOT NULL,
    exchange    TEXT NOT NULL,
    event_type  TEXT NOT NULL,
    payload     JSONB NOT NULL,
    ts          TIMESTAMPTZ NOT NULL DEFAULT now(),
    PRIMARY KEY (id, ts)
);

-- TimescaleDB гипертаблица для эффективных time-range запросов
SELECT create_hypertable('trade_events', 'ts');
CREATE INDEX ON trade_events (session_id, ts DESC);
CREATE INDEX ON trade_events USING GIN (payload);

С TimescaleDB запрос "все сделки стратегии X за прошлую неделю" выполняется за миллисекунды даже на миллионах строк.

P&L расчёт из логов

SELECT
    strategy,
    date_trunc('day', ts) AS day,
    COUNT(*) FILTER (WHERE event_type = 'order_filled') AS trades,
    SUM((payload->>'executedQty')::numeric * (payload->>'executedPrice')::numeric)
        FILTER (WHERE payload->>'side' = 'sell') AS gross_revenue,
    SUM((payload->>'fee')::numeric) FILTER (WHERE event_type = 'order_filled') AS total_fees,
    SUM((payload->>'realizedPnl')::numeric) FILTER (WHERE event_type = 'position_closed') AS realized_pnl
FROM trade_events
WHERE ts > now() - interval '30 days'
GROUP BY strategy, day
ORDER BY day DESC;

Алерты на аномалии

Логирование бесполезно без реакции на проблемы. Простой мониторинг через периодический запрос:

// Запускать каждые 5 минут
async function checkAnomalies() {
  // Много ошибок подряд — вероятно, проблема с exchange API
  const recentErrors = await db.countEvents({
    type: "error",
    since: minutesAgo(5),
  });
  if (recentErrors > 10) await alertService.send("High error rate: " + recentErrors + " errors in 5m");

  // Нет заполненных ордеров за долгое время — бот завис?
  const lastFill = await db.lastEventTime({ type: "order_filled" });
  if (minutesSince(lastFill) > 60 && isMarketHours()) {
    await alertService.send("No fills in 60 minutes — bot may be stuck");
  }
}

Что делаем за 1 день

  • Замена console.log на pino/winston с JSON-форматом
  • Добавление sessionId, strategy, exchange во все события
  • Таблица trade_events в PostgreSQL с индексами
  • Async writer (буферизованная вставка батчами, не блокирует торговый loop)
  • Базовый SQL-запрос P&L по дням
  • Алерт в Telegram при >N ошибках за период