1. Требования к VPS для торгового робота
Криптовалютные рынки работают 24/7/365. Торговый робот должен быть постоянно подключён к бирже — локальный компьютер или домашний сервер не подходят из-за перебоев в питании, интернете и необходимости перезагрузок.
| Параметр | Минимум | Рекомендуется | Зачем |
|---|---|---|---|
| RAM | 1 ГБ | 2 ГБ | Python-бот занимает 80–150 МБ |
| CPU | 1 vCPU | 2 vCPU | Не узкое место; важна сеть |
| Диск | 20 ГБ SSD | 20 ГБ NVMe | Только код + логи |
| Сеть | 100 Мбит/с | 1 Гбит/с | API-запросы небольшие |
| Uptime | 99.9% | 99.95%+ | Пропущенная сделка = убыток |
| ОС | Ubuntu 22.04 | Ubuntu 22.04 LTS | systemd, поддержка до 2027 |
Расположение сервера — важно для скорости
Для позиционных стратегий (удержание позиции часами) расположение некритично. Для скальпинга и арбитража — задержка решает: снижение пинга с 70 мс до 5 мс может поднять успешность арбитражных сделок вдвое.
| Биржа | Серверы биржи | Оптимальный VPS |
|---|---|---|
| Binance | AWS ap-northeast-1 (Tokyo) | Tokyo / Osaka |
| Bybit | AWS ap-southeast-1 (Singapore) | Singapore |
| OKX | AWS ap-east-1 (Hong Kong) | Hong Kong / Singapore |
| Kraken | Amsterdam (EU) | Netherlands / Frankfurt |
| Позиционный трейдинг | Любая | Любой стабильный VPS |
2. CCXT — единый API для 107 бирж
CCXT (CryptoCurrency eXchange Trading Library) — open-source библиотека с единым интерфейсом для 107+ бирж. Один и тот же код работает на Binance, Bybit, OKX, Kraken без изменений. Версия на февраль 2026: 4.5.40.
pip install ccxt python-dotenv
# Опционально: быстрый JSON-парсер
pip install ccxt[orjson]import ccxt
exchange = ccxt.bybit({
'apiKey': 'YOUR_API_KEY',
'secret': 'YOUR_API_SECRET',
'enableRateLimit': True, # ОБЯЗАТЕЛЬНО — иначе забанят по IP
})
# Баланс
balance = exchange.fetch_balance()
usdt = balance['USDT']['free'] # доступный баланс
# Свечи OHLCV — [timestamp, open, high, low, close, volume]
candles = exchange.fetch_ohlcv('BTC/USDT', timeframe='1h', limit=100)
# Текущая цена
ticker = exchange.fetch_ticker('BTC/USDT')
price = ticker['last']
# Стакан заявок
orderbook = exchange.fetch_order_book('BTC/USDT')
# Рыночный ордер
order = exchange.create_market_buy_order('BTC/USDT', 0.001)
# Лимитный ордер
order = exchange.create_limit_buy_order('BTC/USDT', 0.001, 60000)
# Открытые ордера
open_orders = exchange.fetch_open_orders('BTC/USDT')
# Отмена ордера
exchange.cancel_order(order['id'], 'BTC/USDT')Иерархия ошибок CCXT
ccxt.NetworkErrorСетевые проблемы, таймауты, RateLimitExceeded → повторяем запросccxt.ExchangeErrorОшибки биржи (неверный ордер, нет ликвидности) → не повторяемccxt.InsufficientFundsНедостаточно средств → ждём пополнения или уведомляемccxt.AuthenticationErrorНеверные API-ключи → немедленно останавливаем бота3. Установка окружения на VPS
Создаём отдельного пользователя и виртуальное окружение — хорошая практика для любого production-сервиса.
# Обновляем систему
apt update && apt upgrade -y
# Устанавливаем Python 3.11 (есть в Ubuntu 22.04)
apt install -y python3.11 python3.11-venv python3.11-dev git
# Создаём пользователя для бота
useradd -r -m -s /bin/bash botusersu - botuser
mkdir -p ~/trading-bot && cd ~/trading-bot
# Виртуальное окружение
python3.11 -m venv venv
source venv/bin/activate
# Устанавливаем зависимости
pip install --upgrade pip
pip install ccxt python-dotenv pandas requeststrading-bot/
├── venv/ # виртуальное окружение (не в git!)
├── bot.py # основной файл
├── config.py # загрузка настроек из .env
├── .env # API-ключи (не в git!)
├── .env.example # шаблон без реальных ключей
├── .gitignore
└── requirements.txt.env
venv/
__pycache__/
*.pyc
*.logpip freeze > requirements.txt
# Позже на новом сервере: pip install -r requirements.txt4. Безопасность API-ключей
API-ключ с правами торговли — это доступ к вашим деньгам. Три правила, которые нельзя нарушать.
BYBIT_API_KEY=ваш_ключ_здесь
BYBIT_API_SECRET=ваш_секрет_здесь
# Для Telegram-уведомлений
TELEGRAM_BOT_TOKEN=5123456789:ABCDefgh...
TELEGRAM_CHAT_ID=123456789chmod 600 /home/botuser/trading-bot/.envfrom dotenv import load_dotenv
import os
load_dotenv()
BYBIT_API_KEY = os.getenv('BYBIT_API_KEY')
BYBIT_API_SECRET = os.getenv('BYBIT_API_SECRET')
TG_TOKEN = os.getenv('TELEGRAM_BOT_TOKEN')
TG_CHAT_ID = os.getenv('TELEGRAM_CHAT_ID')
if not BYBIT_API_KEY or not BYBIT_API_SECRET:
raise ValueError("API ключи не заданы в .env!")- Account → API Management → Create New Key
- Тип: System-Generated API Key
- Права: Read-Write → Spot Trading / Derivatives (только нужное)
- Withdrawals — оставьте выключенным
- IP Restriction → добавьте статический IP вашего VPS
5. Пример торгового робота (SMA-кроссовер)
Классическая стратегия: покупаем когда быстрая скользящая средняя пересекает медленную снизу вверх (золотой крест), продаём при обратном пересечении (мёртвый крест). Это учебный пример — не финансовый совет.
import ccxt
import pandas as pd
import time
import logging
import requests
from config import BYBIT_API_KEY, BYBIT_API_SECRET, TG_TOKEN, TG_CHAT_ID
# Логирование — идёт в journald через systemd
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s %(levelname)s %(message)s',
)
logger = logging.getLogger(__name__)
# --- Telegram-уведомления ---
def notify(message: str) -> None:
if not TG_TOKEN or not TG_CHAT_ID:
return
try:
requests.post(
f"https://api.telegram.org/bot{TG_TOKEN}/sendMessage",
json={'chat_id': TG_CHAT_ID, 'text': message},
timeout=10,
)
except Exception as e:
logger.error(f"Ошибка Telegram: {e}")
# --- Инициализация биржи ---
exchange = ccxt.bybit({
'apiKey': BYBIT_API_KEY,
'secret': BYBIT_API_SECRET,
'enableRateLimit': True, # ОБЯЗАТЕЛЬНО
})
SYMBOL = 'BTC/USDT'
TIMEFRAME = '1h'
SHORT_MA = 20
LONG_MA = 50
ORDER_AMOUNT = 0.001 # BTC — задайте свой размер позиции
def get_signal() -> str | None:
"""Возвращает 'buy', 'sell' или None."""
ohlcv = exchange.fetch_ohlcv(SYMBOL, TIMEFRAME, limit=LONG_MA + 10)
df = pd.DataFrame(ohlcv, columns=['ts', 'o', 'h', 'l', 'close', 'v'])
df['sma_short'] = df['close'].rolling(SHORT_MA).mean()
df['sma_long'] = df['close'].rolling(LONG_MA).mean()
df = df.dropna()
prev = df.iloc[-2]
curr = df.iloc[-1]
# Золотой крест — покупаем
if prev['sma_short'] <= prev['sma_long'] and curr['sma_short'] > curr['sma_long']:
return 'buy'
# Мёртвый крест — продаём
if prev['sma_short'] >= prev['sma_long'] and curr['sma_short'] < curr['sma_long']:
return 'sell'
return None
def main() -> None:
logger.info("Торговый робот запущен")
notify("Торговый робот запущен ✅")
while True:
try:
signal = get_signal()
price = exchange.fetch_ticker(SYMBOL)['last']
if signal == 'buy':
order = exchange.create_market_buy_order(SYMBOL, ORDER_AMOUNT)
msg = f"BUY {ORDER_AMOUNT} BTC @ {price:.2f}$ \nID: {order['id']}"
logger.info(msg)
notify(f"📈 {msg}")
elif signal == 'sell':
order = exchange.create_market_sell_order(SYMBOL, ORDER_AMOUNT)
msg = f"SELL {ORDER_AMOUNT} BTC @ {price:.2f}$ \nID: {order['id']}"
logger.info(msg)
notify(f"📉 {msg}")
else:
logger.info(f"Сигналов нет. {SYMBOL}: {price:.2f}$")
time.sleep(3600) # ждём следующую часовую свечу
except ccxt.InsufficientFunds as e:
msg = f"Недостаточно средств: {e}"
logger.error(msg)
notify(f"⚠️ {msg}")
time.sleep(300)
except ccxt.NetworkError as e:
# Сетевые ошибки — повторяем
logger.warning(f"Сетевая ошибка (повтор через 60 сек): {e}")
time.sleep(60)
except ccxt.AuthenticationError as e:
# Неверные ключи — останавливаем бота
msg = f"ОШИБКА АУТЕНТИФИКАЦИИ: {e}"
logger.error(msg)
notify(f"🚨 {msg} — бот остановлен!")
raise SystemExit(1)
except ccxt.ExchangeError as e:
logger.error(f"Ошибка биржи: {e}")
notify(f"❌ Ошибка биржи: {e}")
time.sleep(300)
except Exception as e:
logger.error(f"Неизвестная ошибка: {e}", exc_info=True)
notify(f"🚨 Критическая ошибка: {e}")
time.sleep(60)
if __name__ == '__main__':
main()cd ~/trading-bot
source venv/bin/activate
python bot.py
# INFO: Торговый робот запущен
# INFO: Сигналов нет. BTC/USDT: 85234.50$'options': {'defaultType': 'spot'} + exchange.set_sandbox_mode(True)6. Автозапуск через systemd
Systemd перезапустит бота при сбое и включит его автоматически после перезагрузки сервера. RestartSec=30 — даём бирже время восстановиться перед повторным подключением.
[Unit]
Description=Python Trading Bot (CCXT)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=botuser
WorkingDirectory=/home/botuser/trading-bot
# Загружаем .env — ключи не нужно прописывать в unit-файле
EnvironmentFile=/home/botuser/trading-bot/.env
# Запуск через venv
ExecStart=/home/botuser/trading-bot/venv/bin/python bot.py
# Перезапуск при любом падении
Restart=always
RestartSec=30
# Graceful shutdown
KillMode=mixed
TimeoutStopSec=30
# Логи → journald
StandardOutput=journal
StandardError=journal
SyslogIdentifier=trading-bot
# Минимальные привилегии
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.targetsystemctl daemon-reload
systemctl enable trading-bot # автозапуск при загрузке сервера
systemctl start trading-bot # запустить сейчас
# Статус
systemctl status trading-bot# Логи в реальном времени
journalctl -u trading-bot -f
# Логи за сегодня
journalctl -u trading-bot --since today
# Последние 100 строк
journalctl -u trading-bot -n 100
# Только ошибки
journalctl -u trading-bot -p err
# За конкретный период
journalctl -u trading-bot --since "2026-02-28 00:00" --until "2026-02-28 23:59"7. Мониторинг и Telegram-уведомления
Бот уже отправляет уведомления о сделках через notify(). Добавим уведомление при неожиданной остановке через systemd.
Уведомление при остановке сервиса
# Запускаем при падении сервиса
OnFailure=trading-bot-notify@%n.service[Unit]
Description=Notify on trading-bot failure
[Service]
Type=oneshot
User=botuser
EnvironmentFile=/home/botuser/trading-bot/.env
ExecStart=/bin/bash -c 'curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" -d "chat_id=${TELEGRAM_CHAT_ID}&text=🚨 Торговый робот упал! Сервис: %i"'Что логировать обязательно
- ✅Запуск и остановка бота
- 📈Каждая открытая и закрытая позиция (символ, цена, объём, ID ордера)
- ⚠️Недостаточно средств, превышен лимит запросов
- ❌Ошибки биржи и аутентификации
- 🔄Статус каждые N часов — бот жив, баланс X USDT
Heartbeat — бот жив?
# Счётчик итераций
iteration = 0
while True:
iteration += 1
# Heartbeat каждые 6 часов (при sleep=3600 → каждые 6 итераций)
if iteration % 6 == 0:
balance = exchange.fetch_balance()
usdt = balance.get('USDT', {}).get('free', 0)
notify(f"💚 Робот работает. Баланс: {usdt:.2f} USDT")
# ... остальная логика8. Обновление кода без долгих остановок
Деплой нового кода: копируем файлы и перезапускаем сервис. Systemd отправит SIGTERM, бот корректно завершит текущий цикл, затем запустит новую версию.
Через rsync с локальной машины
#!/bin/bash
VPS_IP="YOUR_VPS_IP"
# Копируем код (исключаем venv, .env и кэш)
rsync -avz \
--exclude='venv/' \
--exclude='.env' \
--exclude='__pycache__/' \
--exclude='*.pyc' \
--exclude='*.log' \
./trading-bot/ \
botuser@${VPS_IP}:/home/botuser/trading-bot/
# Устанавливаем новые зависимости и перезапускаем
ssh botuser@${VPS_IP} "
cd ~/trading-bot
source venv/bin/activate
pip install -r requirements.txt -q
sudo systemctl restart trading-bot
echo 'Деплой завершён!'
"chmod +x deploy.sh
./deploy.shЧерез git
cd ~/trading-bot
git pull origin main
source venv/bin/activate
pip install -r requirements.txt -q
sudo systemctl restart trading-bot
journalctl -u trading-bot -n 20 # проверяем что запустилсяexchange.fetch_open_orders()Чеклист перед запуском
- ✓VPS с минимум 1 ГБ RAM, Ubuntu 22.04, статический IP
- ✓API-ключ создан: торговые права, без вывода средств, IP-вайтлист
- ✓Ключи в .env, файл chmod 600, .env в .gitignore
- ✓python bot.py запускается без ошибок в тестовом режиме
- ✓systemd сервис создан, включён (systemctl enable), запущен
- ✓Telegram-уведомление о запуске пришло
- ✓journalctl -u trading-bot не показывает ошибок
- ✓Сервер перезагружен — бот поднялся автоматически