Настройка автоматичного перезапуску торгового бота
Торговий бот повинен працювати 24/7. Процес падає — через помилку мережі, OOM, необроблене винятком або системний перезапуск. Без автоматичного перезапуску бот лежить доки хтось не помітить. Правильна настройка — це не просто "restart always", це ще й перевірка стану, graceful shutdown та алерти.
systemd: Production-стандарт на Linux
systemd — правильний інструмент для управління довгоживущими сервісами на Linux. Підтримує автоматичний перезапуск, залежності між сервісами, ліміти ресурсів, ротацію логів.
# /etc/systemd/system/trading-bot.service
[Unit]
Description=Trading Bot
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=botuser
WorkingDirectory=/opt/trading-bot
ExecStart=/opt/trading-bot/venv/bin/python -u bot.py
Restart=always
# Чекаємо 10 секунд перед перезапуском (не спамимо при швидких падіннях)
RestartSec=10
# Якщо бот падає швидше 5 разів за 60 секунд — зупинимо (щось серйозно не так)
StartLimitIntervalSec=60
StartLimitBurst=5
# Переменні окружу
EnvironmentFile=/opt/trading-bot/.env
# Ліміти ресурсів
MemoryLimit=2G
CPUQuota=80%
# Логування через journald
StandardOutput=journal
StandardError=journal
SyslogIdentifier=trading-bot
[Install]
WantedBy=multi-user.target
Активація та управління:
systemctl daemon-reload
systemctl enable trading-bot # автозапуск при старті системи
systemctl start trading-bot
systemctl status trading-bot
journalctl -u trading-bot -f # live логи
StartLimitBurst=5 + StartLimitIntervalSec=60 — важливий захист. Без нього бот у crash loop буде безперервно перезапускатися, накопичуючи помилки (відкриті позиції, дублюючи ордери). Після 5 швидких падінь systemd зупинить сервіс та відправить алерт (якщо налаштовано).
Graceful shutdown: Коректне завершення
Не можна вбивати бота SIGKILL — можна залишити відкриті ордери, незафіксовані позиції, невідправлені алерти. Обробляємо SIGTERM:
import signal
import asyncio
class TradingBot:
def __init__(self):
self.running = True
self.open_orders: list = []
async def shutdown(self):
self.running = False
# Скасовуємо всі відкриті лімітні ордери
for order_id in self.open_orders:
try:
await self.exchange.cancel_order(order_id)
except Exception as e:
logger.error(f"Failed to cancel order {order_id}: {e}")
logger.info("Graceful shutdown complete")
async def run(self):
loop = asyncio.get_event_loop()
loop.add_signal_handler(
signal.SIGTERM,
lambda: asyncio.create_task(self.shutdown())
)
while self.running:
try:
await self.main_loop()
except Exception as e:
logger.exception(f"Error in main loop: {e}")
await asyncio.sleep(5) # backoff перед наступною ітерацією
systemd при systemctl stop шле SIGTERM, потім через TimeoutStopSec (за замовчуванням 90 сек) — SIGKILL. Для бота з позиціями 90 секунд зазвичай достатньо.
Docker: Альтернатива для контейнеризованих ботів
Якщо бот запускається в Docker:
# docker-compose.yml
services:
trading-bot:
image: trading-bot:latest
restart: unless-stopped # перезапускати завжди, крім явної зупинки
env_file: .env
volumes:
- ./data:/app/data # персистентне зберігання стану
- ./logs:/app/logs
mem_limit: 2g
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
healthcheck:
test: ["CMD", "python", "-c", "import requests; requests.get('http://localhost:8080/health', timeout=5)"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
restart: unless-stopped — перезапускає при падінні та при перезавантаженні хоста, але не перезапускає якщо зупинен вручну через docker-compose stop.
Health check — критично важливий. Docker перезапускає контейнер тільки якщо він повністю упав. Якщо бот завис (hanging без помилки) — Docker цього не помітить. Health check endpoint повинен перевіряти не просто "процес живий", а реальний стан:
from aiohttp import web
async def health_check(request):
# Перевіряємо що останній цикл був не більше 5 хвилин тому
last_loop_age = time.time() - bot.last_loop_time
if last_loop_age > 300: # 5 хвилин
return web.Response(status=503, text=f"Bot stuck: last loop {last_loop_age:.0f}s ago")
# Перевіряємо підключення до біржи
if not bot.exchange_connected:
return web.Response(status=503, text="Exchange disconnected")
return web.Response(status=200, text="OK")
app = web.Application()
app.router.add_get('/health', health_check)
Алерти при падінні
Сам факт перезапуску повинен генерувати сповіщення — навіть якщо бот відновився автоматично.
systemd → Telegram:
# /etc/systemd/system/trading-bot-notify.service
[Unit]
Description=Notify on trading bot failure
[Service]
Type=oneshot
ExecStart=/opt/scripts/notify-telegram.sh "Trading bot restarted on $(hostname)"
# У trading-bot.service додаємо:
OnFailure=trading-bot-notify.service
Prometheus Alertmanager для складніших сценаріїв:
# alert rule
- alert: TradingBotRestarted
expr: changes(process_start_time_seconds{job="trading-bot"}[5m]) > 0
annotations:
summary: "Trading bot restarted {{ $value }} times in last 5 minutes"
Розумний мінімум: Telegram-сповіщення при кожному (пере)запуску з ім'ям хоста та часом. Достатньо для 95% випадків.







