Разработка grid-бота для торговли
Grid-бот расставляет сетку лимитных ордеров выше и ниже текущей цены и автоматически переставляет их при исполнении. Зарабатывает на боковом движении рынка: цена "прыгает" вверх-вниз, бот покупает при падении и продаёт при росте, фиксируя прибыль на каждом шаге сетки.
Типы grid-ботов
Arithmetic grid (равномерная сетка)
Ордера расставлены на фиксированном расстоянии в USDT:
$51,000 SELL
$50,500 SELL
$50,000 — текущая цена
$49,500 BUY
$49,000 BUY
Шаг = $500 (фиксированный). Прост в понимании, но % доход на каждом уровне разный.
Geometric grid (геометрическая сетка)
Ордера расставлены на фиксированном % расстоянии:
$51,020 SELL (+2%)
$50,020 SELL (+2% от предыдущего)
$49,040 BUY (-2%)
$48,060 BUY (-2%)
Шаг = 2% (фиксированный процент). Более правильно: % прибыль одинакова на каждом шаге.
Реализация
Инициализация сетки
from decimal import Decimal
import math
class GridBot:
def __init__(self, config: GridConfig, exchange_client):
self.config = config
self.exchange = exchange_client
self.active_orders: dict[str, GridOrder] = {}
self.realized_pnl = Decimal(0)
def calculate_grid_levels(self) -> list[Decimal]:
"""Рассчитываем ценовые уровни сетки"""
lower = self.config.lower_price
upper = self.config.upper_price
num_grids = self.config.grid_count
levels = []
if self.config.grid_type == 'arithmetic':
step = (upper - lower) / num_grids
for i in range(num_grids + 1):
levels.append(lower + step * i)
elif self.config.grid_type == 'geometric':
# Геометрическая прогрессия
ratio = (upper / lower) ** (Decimal(1) / num_grids)
for i in range(num_grids + 1):
levels.append(lower * (ratio ** i))
return levels
async def initialize_grid(self, current_price: Decimal):
levels = self.calculate_grid_levels()
investment_per_grid = self.config.total_investment / self.config.grid_count
for i in range(len(levels) - 1):
lower_level = levels[i]
upper_level = levels[i + 1]
mid_level = (lower_level + upper_level) / 2
if mid_level < current_price:
# Ниже текущей цены — BUY ордер
quantity = investment_per_grid / lower_level
order = await self.exchange.place_limit_order(
side='buy',
price=lower_level,
quantity=quantity
)
self.active_orders[order.id] = GridOrder(
order_id=order.id,
side='buy',
price=lower_level,
quantity=quantity,
grid_index=i
)
else:
# Выше текущей цены — SELL ордер (если есть позиция)
# При старте без базового актива — только BUY ордера
pass
Обработка исполненных ордеров
async def on_order_filled(self, order_id: str, fill_price: Decimal):
grid_order = self.active_orders.pop(order_id, None)
if not grid_order:
return
levels = self.calculate_grid_levels()
step_profit = Decimal(0)
if grid_order.side == 'buy':
# Ордер на покупку исполнен → выставляем SELL на следующий уровень вверх
sell_price = levels[grid_order.grid_index + 1]
sell_order = await self.exchange.place_limit_order(
side='sell',
price=sell_price,
quantity=grid_order.quantity
)
self.active_orders[sell_order.id] = GridOrder(
order_id=sell_order.id,
side='sell',
price=sell_price,
quantity=grid_order.quantity,
grid_index=grid_order.grid_index + 1,
buy_price=fill_price # запоминаем цену покупки
)
elif grid_order.side == 'sell':
# Ордер на продажу исполнен → выставляем BUY обратно
buy_price = levels[grid_order.grid_index - 1]
# Фиксируем прибыль
step_profit = (grid_order.price - grid_order.buy_price) * grid_order.quantity
self.realized_pnl += step_profit
buy_order = await self.exchange.place_limit_order(
side='buy',
price=buy_price,
quantity=grid_order.quantity
)
self.active_orders[buy_order.id] = GridOrder(
order_id=buy_order.id,
side='buy',
price=buy_price,
quantity=grid_order.quantity,
grid_index=grid_order.grid_index - 1
)
logger.info(f"Grid step profit: {step_profit:.4f} USDT, Total realized: {self.realized_pnl:.4f}")
Параметры и конфигурация
Ключевые параметры
| Параметр | Описание | Типичное значение |
|---|---|---|
lower_price |
Нижняя граница сетки | -15% от текущей |
upper_price |
Верхняя граница | +15% от текущей |
grid_count |
Количество уровней | 10-50 |
total_investment |
Общий бюджет | По балансу |
grid_type |
arithmetic / geometric | geometric |
stop_loss |
Остановить при выходе за границу | Опционально |
take_profit |
Закрыть при достижении целевого PnL | Опционально |
Оптимизация параметров
Оптимальный шаг сетки зависит от волатильности актива:
def suggest_grid_step(volatility_daily_percent: float, holding_days: int = 30) -> float:
"""
Если актив двигается в среднем X% в день,
шаг сетки должен быть достаточным для захвата движения.
Примерно: 0.5x - 1x дневной волатильности.
"""
suggested_step = volatility_daily_percent * 0.7
return max(0.5, min(5.0, suggested_step)) # от 0.5% до 5%
BTC с волатильностью 3%/день → шаг ~2%. ETH (5%/день) → шаг ~3.5%.
Риски и ограничения
Трендовый рынок — главный враг grid-бота. При сильном тренде вниз: BUY ордера исполняются один за другим, SELL не исполняются, накапливается убыточная позиция в базовом активе.
Управление: ограничение количества открытых BUY ордеров, автоматический stop при выходе цены за нижнюю границу сетки, trailing grid (сетка следует за ценой).
Комиссии: при маленьком шаге сетки и высоких fees прибыль на каждом шаге может быть меньше комиссии. Формула: min_grid_step = 2 × fee_rate × 1.2 (буфер). При fee 0.1%: минимальный шаг = 0.24%.
Grid-бот идеально работает на боковом рынке и боковых парах (стейблкоины, asset/stablecoin пары с низкой волатильностью на длинных горизонтах). На растущем рынке он зарабатывает меньше, чем просто холдить.







