На главную > Блог > Категория > 🦀 Как написать торгового робота на Rust: от идеи до продакшена
Вы наверняка слышали, что большинство торговых ботов пишут на 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].
Перед тем как писать код, давайте разберёмся, из каких частей состоит типичный бот. Архитектура во многом похожа на Python-версию, но с учётом фишек Rust — async, каналы и типы [citation:1][citation:2].
REST API для получения данных и отправки ордеров, WebSocket для стриминга цен в реальном времени. В Rust экосистеме для этого есть tokio-tungstenite (WebSockets) и reqwest (HTTP) [citation:8]. Многие проекты используют SDK, например dex-rs для Hyperliquid с единым интерфейсом для всех бирж [citation:2].
Поток данных с биржи — стакан ордеров, сделки, свечи — нужно правильно обрабатывать и хранить. В Rust для этого часто используют структуры с `Arc
Сердце бота — код, который решает, когда покупать и продавать. Может использовать индикаторы (RSI, EMA, MACD) или чистый анализ стакана. В хороших реализациях стратегия вынесена в отдельный трейт [citation:5].
Отправка лимитных/рыночных ордеров, отмена, управление client order ID для трекинга. Здесь же — логирование всех действий [citation:6].
Критически важный блок, без которого бот может слить депозит за минуту. Проверка размера позиции, 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 │
└───────────────────────────────────────────────────────────────────┘
Rust-экосистема для трейдинга активно растёт. Вот ключевые инструменты, которые пригодятся:
Hyperliquid — децентрализованная биржа с фокусом на перпетуалы, имеет сразу несколько Rust-решений:
Прямой поддержки Rust от Binance нет, но есть отличные community-проекты:
Hyperliquid — отличный выбор для первого бота на Rust: хорошая документация, тестнет, несколько SDK. Напишем простого бота, который смотрит на разницу между последней ценой и EMA и открывает позицию при сильном расхождении. Используем dex-rs для простоты [citation:2].
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"
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(())
}
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)
}
/// Простой риск-менеджер
#[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;
}
}
#[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 проектов для изучения [citation:1][citation:3][citation:4]:
Самый красивый код на 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].
В асинхронном боте состояние (стакан ордеров, позиции) часто нужно из нескольких потоков. Решение: оборачивайте данные в `Arc
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());
WebSocket могут рваться. Используйте паттерн с `select!` и таймером для автоматического переподключения. В `dex-rs` и других SDK эта логика уже встроена [citation:2][citation:8].
f64 для денег — зло. Используйте `rust_decimal` или типы из `dex-rs` (`Price`, `Qty`), которые безопасно работают с десятичной арифметикой [citation:2][citation:8].
Логируйте всё! Используйте `tracing` или `log` + `env_logger`. В боевых проектах добавьте `console-subscriber` для визуализации работы tokio-задач.
Итак, вы написали бота, он работает на тестнете, не падает и даже что-то зарабатывает на фейковых деньгах. Что теперь?
«Первый год алгоритмической торговли на Rust ты учишься писать код, который компилируется. Второй год — код, который торгует. Третий год — сидишь и не веришь, что всё работает без сбоев уже полгода».
Rust — не самый простой путь в мир торговых роботов. Но это путь, на котором вас ждёт скорость, надёжность и уважение сообщества. Если ваш проект требует обработки тысяч сообщений в секунду или вы просто не хотите просыпаться от «ошибка сегментации» — Rust стоит того.
Начните с малого: подключитесь к тестнету Hyperliquid, считайте свечи, попробуйте отправить ордер. Потом добавьте простую стратегию. Потом риск-менеджмент. Потом мониторинг. Главное — начать. А borrow checker и tokio станут вашими лучшими друзьями (через пару недель, обещаем).
Удачи в кодинге, да прибудет с вами cargo run! 🦀
Дата размещения статьи: 2026-05-16T11:25:46