🦀 Как написать торгового робота на Rust: от идеи до продакшена


На главную > Блог > Категория > 🦀 Как написать торгового робота на Rust: от идеи до продакшена

rust_bot

Почему Rust? А не Python? (спойлер: скорость и безопасность)

Вы наверняка слышали, что большинство торговых ботов пишут на Python. И это правда — Python простой, порог входа низкий, библиотек полно. Но если вы хотите высокочастотную торговлю (HFT), где каждая микросекунда на счету, или просто не хотите, чтобы бот упал посреди ночи из-за `NoneType has no attribute` — присмотритесь к Rust [citation:7].

Rust даёт вам скорость C++ и безопасность памяти без сборщика мусора. Это значит, что ваш бот будет обрабатывать тысячи ордеров в секунду без неожиданных пауз на «уборку мусора». Гарантия отсутствия гонок данных (data races) через ownership system — страховка от трудноуловимых багов в многопоточном коде.

«Python — для прототипов и стратегий с одной сделкой в минуту. Rust — для тех, кто хочет выжать максимум из железа и спать спокойно».

Конечно, Rust требует больше времени на старте — borrow checker может довести до белого каления. Но поверьте: один скомпилировавшийся проект на Rust дарит ощущение, будто вы подписали контракт с самим богом безопасности. И да, многие криптобиржи вроде Hyperliquid имеют официальные Rust SDK — не просто так [citation:2][citation:6].

Архитектура торгового робота на Rust: основные компоненты

Перед тем как писать код, давайте разберёмся, из каких частей состоит типичный бот. Архитектура во многом похожа на Python-версию, но с учётом фишек Rust — async, каналы и типы [citation:1][citation:2].

1. Подключение к бирже (Exchange Connection)

REST API для получения данных и отправки ордеров, WebSocket для стриминга цен в реальном времени. В Rust экосистеме для этого есть tokio-tungstenite (WebSockets) и reqwest (HTTP) [citation:8]. Многие проекты используют SDK, например dex-rs для Hyperliquid с единым интерфейсом для всех бирж [citation:2].

2. Обработка рыночных данных (Market Data Handler)

Поток данных с биржи — стакан ордеров, сделки, свечи — нужно правильно обрабатывать и хранить. В Rust для этого часто используют структуры с `Arc>` для разделяемого состояния между потоками [citation:8].

3. Стратегия и генерация сигналов (Strategy Engine)

Сердце бота — код, который решает, когда покупать и продавать. Может использовать индикаторы (RSI, EMA, MACD) или чистый анализ стакана. В хороших реализациях стратегия вынесена в отдельный трейт [citation:5].

4. Исполнение ордеров (Order Executor)

Отправка лимитных/рыночных ордеров, отмена, управление client order ID для трекинга. Здесь же — логирование всех действий [citation:6].

5. Риск-менеджмент (Risk Manager)

Критически важный блок, без которого бот может слить депозит за минуту. Проверка размера позиции, stop-loss, daily loss limit [citation:3][citation:7].


┌───────────────────────────────────────────────────────────────────┐
│                      ТОРГОВЫЙ РОБОТ (RUST)                        │
├─────────────────┬─────────────────┬────────────────┬──────────────┤
│   Подключение   │  Рыночные данные│   Стратегия    │ Исполнение   │
│   к бирже       │   (WebSocket)   │   (трейт)      │ ордеров      │
├─────────────────┼─────────────────┼────────────────┼──────────────┤
│ • REST client   │ • OrderBook     │ • Индикаторы   │ • limit buy  │
│ • WS connection │ • Trades        │ • Сигналы      │ • market sell│
│ • reconnection  │ • Candles       │ • ML           │ • cancel     │
├─────────────────┴─────────────────┴────────────────┴──────────────┤
│                    РИСК-МЕНЕДЖМЕНТ (ядро)                         │
│    max position │ stop-loss │ take-profit │ daily loss limit      │
└───────────────────────────────────────────────────────────────────┘

Экосистема: библиотеки и SDK для торговли на Rust

Rust-экосистема для трейдинга активно растёт. Вот ключевые инструменты, которые пригодятся:

Hyperliquid — самый продвинутый API

Hyperliquid — децентрализованная биржа с фокусом на перпетуалы, имеет сразу несколько Rust-решений:

  • dex-rs — унифицированный SDK для перпетуалов, поддерживает WebSocket стриминг и типизированные ответы от API [citation:2].
  • motosan-hyperliquid — модульный SDK с крейтами под каждую задачу: `hl-client` для HTTP, `hl-market` для данных, `hl-executor` для ордеров. Есть примеры для каждого сценария [citation:6].
  • hyperliquid_rust_sdk — используется в полноценном проекте торгового бота с UI на React [citation:1].

Binance и другие централизованные биржи

Прямой поддержки Rust от Binance нет, но есть отличные community-проекты:

  • binance-rs — неофициальный SDK для REST и WebSocket API Binance [citation:7].
  • crypto-rest-client — универсальный REST-клиент для всех криптобирж [citation:9].
  • OpenBook — реальный проект на Rust, который стримит данные с Binance Futures и отрисовывает heatmap стакана. Отличный пример для изучения [citation:8].

Общие библиотеки

  • tokio — асинхронный runtime, сердце любого нетривиального бота [citation:1][citation:2][citation:7].
  • serde / serde_json — парсинг JSON от API бирж.
  • flume — каналы для коммуникации между потоками (альтернатива стандартным mpsc) [citation:1].
  • rust_decimal / ordered-float — корректная работа с десятичными числами (финансы любят точность) [citation:8].
  • egui / eframe — если захотите сделать GUI для мониторинга бота [citation:8].

Пошаговый пример: простой робот на Hyperliquid (с кодом)

Hyperliquid — отличный выбор для первого бота на Rust: хорошая документация, тестнет, несколько SDK. Напишем простого бота, который смотрит на разницу между последней ценой и EMA и открывает позицию при сильном расхождении. Используем dex-rs для простоты [citation:2].

Шаг 1: Создание проекта и зависимости

cargo new hyperliquid_bot
cd hyperliquid_bot

Откройте `Cargo.toml` и добавьте:

[dependencies]
dex-rs = "0.1.0"
tokio = { version = "1", features = ["full"] }
anyhow = "1"
futures-util = "0.3"

Шаг 2: Инициализация клиента

use dex_rs::prelude::*;
use anyhow::Result;

#[tokio::main]
async fn main() -> Result<()> {
    // Подключаемся к тестнету Hyperliquid (без авторизации для чтения)
    let hl = Hyperliquid::builder()
        .testnet()
        .connect()
        .await?;
    
    println!("✅ Подключены к Hyperliquid Testnet");
    
    // Получаем текущую цену BTC
    let trades = hl.trades("BTC", 5).await?;
    if let Some(last_trade) = trades.last() {
        println!("📊 Последняя цена BTC: {}", last_trade.px);
    }
    
    Ok(())
}

Шаг 3: Реализация простой EMA-стратегии

use dex_rs::prelude::*;
use anyhow::Result;
use std::collections::VecDeque;

/// Рассчёт экспоненциальной скользящей средней
fn ema(prices: &[f64], period: usize) -> Option<f64> {
    if prices.len() < period {
        return None;
    }
    let multiplier = 2.0 / (period as f64 + 1.0);
    let mut ema_val = prices[0];
    for &price in &prices[1..period] {
        ema_val = (price - ema_val) * multiplier + ema_val;
    }
    Some(ema_val)
}

/// Простая стратегия: покупаем, если цена ниже EMA на 1%
async fn simple_strategy(
    hl: &Hyperliquid, 
    symbol: &str, 
    lookback: usize
) -> Result<Option<OrderReq>> {
    // Получаем свечи (1-часовые)
    let candles = hl.candles(symbol, "1h", lookback).await?;
    let prices: Vec<f64> = candles.iter().map(|c| c.close).collect();
    
    if let Some(ema_val) = ema(&prices, 20) {
        let current_price = prices.last().unwrap();
        let diff_pct = (current_price - ema_val) / ema_val * 100.0;
        
        println!("📈 EMA20: {:.2}, Цена: {:.2}, Разница: {:.2}%", 
                 ema_val, current_price, diff_pct);
        
        // Если цена упала более чем на 1% ниже EMA -> потенциал отскока
        if diff_pct < -1.0 {
            println!("🔔 Сигнал! Цена ниже EMA, рассматриваем покупку");
            // Возвращаем заявку на покупку (0.001 BTC, лимит)
            let order = OrderReq {
                coin: symbol.to_string(),
                is_buy: true,
                px: current_price.clone(),
                qty: qty(0.001),
                tif: Tif::Gtc,
                reduce_only: false,
                cloid: Some(generate_cloid()),
            };
            return Ok(Some(order));
        }
    }
    
    Ok(None)
}

Шаг 4: Добавляем риск-менеджмент перед исполнением

/// Простой риск-менеджер
#[derive(Debug)]
struct RiskManager {
    max_position_notional: f64,
    daily_loss_limit: f64,
    daily_loss: f64,
}

impl RiskManager {
    fn new(max_position_notional: f64, daily_loss_limit: f64) -> Self {
        Self {
            max_position_notional,
            daily_loss_limit,
            daily_loss: 0.0,
        }
    }
    
    fn can_trade(&mut self, order: &OrderReq, current_price: f64) -> bool {
        let order_notional = order.qty.0 * order.px;
        
        if order_notional > self.max_position_notional {
            println!("❌ Риск: размер заявки {} превышает лимит {}", 
                     order_notional, self.max_position_notional);
            return false;
        }
        
        if self.daily_loss > self.daily_loss_limit {
            println!("❌ Риск: дневной лимит убытков {} превышен", 
                     self.daily_loss_limit);
            return false;
        }
        
        true
    }
    
    fn record_loss(&mut self, loss: f64) {
        self.daily_loss += loss;
    }
}

Шаг 5: Основной цикл бота с отправкой ордеров

#[tokio::main]
async fn main() -> Result<()> {
    // Подключаемся к тестнету с авторизацией (для торговли)
    let private_key = std::env::var("HYPERLIQUID_PRIVATE_KEY")
        .expect("Установите HYPERLIQUID_PRIVATE_KEY");
    
    let hl = Hyperliquid::builder()
        .testnet()
        .credentials(&private_key)
        .connect()
        .await?;
    
    let mut risk = RiskManager::new(1000.0, 200.0);  // макс позиция $1000, дневной лимит убытков $200
    let symbol = "BTC";
    
    println!("🚀 Запуск бота на Hyperliquid Testnet для {symbol}");
    println!("🛡️ Риск-лимиты: позиция ≤ ${}, дневной убыток ≤ ${}", 
             risk.max_position_notional, risk.daily_loss_limit);
    
    loop {
        // Проверяем стратегию каждые 30 секунд
        if let Some(order) = simple_strategy(&hl, symbol, 100).await? {
            // Получаем текущую цену для проверки рисков
            let trades = hl.trades(symbol, 1).await?;
            let current_price = trades.last().unwrap().px;
            
            if risk.can_trade(&order, current_price) {
                println!("📝 Отправка ордера: {:?}", order);
                match hl.place_order(order).await {
                    Ok(resp) => println!("✅ Ордер исполнен: ID={}, ClientID={}", 
                                         resp.order_id.0, resp.client_order_id),
                    Err(e) => println!("❌ Ошибка при отправке ордера: {}", e),
                }
            }
        }
        
        tokio::time::sleep(tokio::time::Duration::from_secs(30)).await;
    }
}

⚠️ Важно: Это обучающий пример. Никогда не запускайте его с реальными деньгами без доработок и тщательного тестирования [citation:3][citation:7].

Где брать вдохновение: Open Source проекты на Rust

Лучший способ научиться — читать чужой код. Вот несколько качественных open-source проектов для изучения [citation:1][citation:3][citation:4]:

  • HFT-bot-mainscript — высокочастотный бот с несколькими стратегиями (моментная и возврат к среднему), продвинутым риск-менеджментом и многопоточностью. Показательный пример промышленного уровня [citation:7].
  • Market-Maker — маркет-мейкер с алгоритмом Avellaneda-Stoikov (профессиональная стратегия ценообразования). Есть реализации и на Python, и на Rust — можно сравнить [citation:3].
  • hyperliquid-trading-bot-rust — полноценный бот для Hyperliquid с UI на React. Стратегия на комбинации индикаторов (RSI, StochRSI, EMA cross, ADX). Отличная архитектура с каналами `flume` [citation:1].
  • OpenBook — не совсем бот, но мощный инструмент визуализации рыночных данных с Binance Futures. Показывает, как строить стакан ордеров и работать с WebSocket в реальном времени [citation:8].
  • Rugs.fun-tradingbot — бот для Solana-платформы. Демонстрирует работу с Solana SDK, подписку на on-chain события и автоматические покупки [citation:4].
  • dr-manhattan-rust — CCXT-подобный унифицированный API для рынков предсказаний. Полезен, если нужна абстракция над разными биржами [citation:5].

Риск-менеджмент: то, без чего бот становится казино

Самый красивый код на Rust бесполезен, если бот может слить депозит за час. Вот обязательный минимум для production-бота [citation:3][citation:7]:

#[derive(Debug, Clone)]
pub struct RiskParams {
    pub max_position_size: f64,        // максимальный размер позиции в единицах актива
    pub max_notional_value: f64,       // максимальная стоимость позиции в USD
    pub max_loss_per_trade: f64,       // максимальный убыток на одну сделку
    pub max_daily_loss: f64,           // дневной лимит убытков
    pub stop_loss_pct: f64,            // процент для стоп-лосса (0.02 = 2%)
    pub take_profit_pct: f64,          // процент для тейк-профита
    pub max_leverage: u32,             // максимальное плечо
}

/// Функция проверки ордера перед отправкой
fn validate_order(order: &OrderReq, params: &RiskParams, current_position: f64) -> bool {
    // 1. Проверка лимитов по размеру
    if order.qty.0 > params.max_position_size {
        log::warn!("Превышен max_position_size");
        return false;
    }
    
    // 2. Проверка дневного убытка (требует сохранения состояния)
    // 3. Проверка стоп-лосса
    // 4. Проверка маржинальных требований
    
    true
}

В проекте Market-Maker реализована система с kill switch — возможностью мгновенно отменить все ордера и закрыть позиции. Рекомендую взять на вооружение [citation:3].

Типичные трудности при разработке на Rust и как их преодолеть

1. Borrow checker и разделяемое состояние

В асинхронном боте состояние (стакан ордеров, позиции) часто нужно из нескольких потоков. Решение: оборачивайте данные в `Arc>` или используйте каналы (`tokio::sync::mpsc`, `flume`) [citation:1][citation:8].

type SharedOrderBook = Arc<Mutex<OrderBook>>;

// В одном потоке обновляем
let mut book = shared_book.lock().unwrap();
book.update(diff);

// В другом читаем
let book = shared_book.lock().unwrap();
println!("Best bid: {}", book.bids.first());

2. Обработка WebSocket соединений и переподключений

WebSocket могут рваться. Используйте паттерн с `select!` и таймером для автоматического переподключения. В `dex-rs` и других SDK эта логика уже встроена [citation:2][citation:8].

3. Работа с десятичными числами

f64 для денег — зло. Используйте `rust_decimal` или типы из `dex-rs` (`Price`, `Qty`), которые безопасно работают с десятичной арифметикой [citation:2][citation:8].

4. Отладка асинхронного кода

Логируйте всё! Используйте `tracing` или `log` + `env_logger`. В боевых проектах добавьте `console-subscriber` для визуализации работы tokio-задач.

Что дальше: от тестнета к реальной торговле

Итак, вы написали бота, он работает на тестнете, не падает и даже что-то зарабатывает на фейковых деньгах. Что теперь?

  1. Продолжайте тестировать. Месяц на тестнете — минимальный срок. Лучше — 3 месяца, захватив разные рыночные условия [citation:3].
  2. Начните с минимальных сумм ($10-50). Убедитесь, что API ключи настроены правильно и бот не глючит на реальных данных [citation:4].
  3. Добавьте мониторинг. Бот должен слать уведомления в Telegram при срабатывании стоп-лосса, дневного лимита убытков или ошибках подключения [citation:3].
  4. Ведите журнал сделок. Каждый ордер, каждая ошибка, каждое изменение позиции — в лог. Без этого невозможно анализировать убытки [citation:7].
  5. Имейте план отступления. Кнопка «экстренная остановка» (kill switch), которая отменяет все ордера и закрывает позиции, должна быть у вас под рукой [citation:3].
«Первый год алгоритмической торговли на Rust ты учишься писать код, который компилируется. Второй год — код, который торгует. Третий год — сидишь и не веришь, что всё работает без сбоев уже полгода».

Заключение: Rust — ваш выбор для серьёзной автоматизации

Rust — не самый простой путь в мир торговых роботов. Но это путь, на котором вас ждёт скорость, надёжность и уважение сообщества. Если ваш проект требует обработки тысяч сообщений в секунду или вы просто не хотите просыпаться от «ошибка сегментации» — Rust стоит того.

Начните с малого: подключитесь к тестнету Hyperliquid, считайте свечи, попробуйте отправить ордер. Потом добавьте простую стратегию. Потом риск-менеджмент. Потом мониторинг. Главное — начать. А borrow checker и tokio станут вашими лучшими друзьями (через пару недель, обещаем).

Удачи в кодинге, да прибудет с вами cargo run! 🦀

 

Дата размещения статьи: 2026-05-16T11:25:46