Разработка системы копитрейдинга
Копитрейдинг — это автоматическое копирование сделок опытного трейдера (лидера) на аккаунте подписчика (follower-а). Для биржи это мощный инструмент retention и монетизации: follower-ы удерживаются платформой, лидеры привлекают аудиторию, биржа получает дополнительный trading volume. Технически система нетривиальна: нужно обеспечить пропорциональное копирование, управление рисками follower-а и минимальную latency между сигналом и исполнением.
Архитектура системы
Ключевые компоненты
Leader Trading Account
│ trade events
▼
Signal Processor ──── Position Normalizer
│
▼
Distribution Engine ──── Risk Filter
│
├──► Follower 1 Order ──► Exchange OMS
├──► Follower 2 Order ──► Exchange OMS
└──► Follower N Order ──► Exchange OMS
Signal Processor — перехватывает торговые события лидера в реальном времени. Если лидер торгует на той же бирже — это интеграция с internal OMS через event bus. Если лидер на внешней бирже — polling API или WebSocket подписка.
Position Normalizer — переводит действие лидера в абстрактный сигнал: "открыть long BTC на 5% от портфеля с leverage 2x".
Distribution Engine — рассылает сигналы всем follower-ам. При тысячах подписчиков — это высоконагруженный fanout.
Risk Filter — проверяет, можно ли исполнить сигнал для конкретного follower-а с учётом его баланса, лимитов и настроек риска.
Режимы копирования
| Режим | Описание | Для кого |
|---|---|---|
| Fixed amount | Каждую сделку лидера копируем на фиксированную сумму ($100) | Консервативные |
| Proportional | Копируем пропорционально балансу (лидер открывает 10% капитала → follower тоже) | Сбалансированный |
| Multiplier | Proportional × коэффициент (0.5x — 2x от размера лидера) | Гибкий |
| Fixed ratio | Фиксированный leverage ratio | Продвинутые |
Пропорциональный режим — стандарт индустрии. Реализация:
def calculate_follower_order_size(
leader_trade: Trade,
follower: FollowerSettings,
leader_portfolio_value: float
) -> float:
leader_position_percent = leader_trade.notional_value / leader_portfolio_value
if follower.copy_mode == 'proportional':
raw_size = follower.allocated_amount * leader_position_percent * follower.multiplier
elif follower.copy_mode == 'fixed':
raw_size = follower.fixed_amount_per_trade
else:
raw_size = leader_trade.quantity # прямое копирование
# Ограничения риска
max_allowed = follower.allocated_amount * (follower.max_position_percent / 100)
raw_size = min(raw_size, max_allowed)
# Минимальный ордер биржи
min_order = get_min_order_size(leader_trade.symbol)
if raw_size < min_order:
return 0 # не копируем слишком маленький ордер
return raw_size
Latency и исполнение
Почему latency критична
Копитрейдинг — это гонка за исполнением. Если лидер открыл позицию, а follower-ы получают ордер через 500ms — цена уже сдвинулась. Особенно критично на волатильных рынках.
Оптимизации:
Event bus вместо polling: сигнал лидера → Kafka topic → Consumer group для каждого сегмента follower-ов. Latency распределения: <10ms для 10,000 подписчиков.
from aiokafka import AIOKafkaProducer, AIOKafkaConsumer
import asyncio
class CopyTradingDistributor:
async def distribute_signal(self, signal: TradeSignal):
"""Распределяем сигнал через Kafka"""
producer = AIOKafkaProducer(bootstrap_servers='localhost:9092')
# Партиционируем по leader_id — все follower-ы лидера в одной партиции
await producer.send(
topic='copy_signals',
key=signal.leader_id.encode(),
value=signal.to_json().encode()
)
async def process_signals(self, partition_id: int):
"""Каждый consumer обрабатывает свой набор follower-ов"""
consumer = AIOKafkaConsumer(
'copy_signals',
bootstrap_servers='localhost:9092',
group_id=f'copy_processor_{partition_id}'
)
async for msg in consumer:
signal = TradeSignal.from_json(msg.value)
followers = await self.db.get_followers(signal.leader_id)
# Параллельное создание ордеров
tasks = [
self.create_follower_order(follower, signal)
for follower in followers
]
await asyncio.gather(*tasks, return_exceptions=True)
Pre-allocated order capacity: для топ-лидеров с тысячами follower-ов — заранее резервируем capacity в matching engine.
Order batching: если follower-ы используют market ордера — можно агрегировать их в один batch ордер и затем распределять fills. Снижает нагрузку на matching engine.
Slippage управление
При большом числе копировщиков суммарный объём их ордеров может двигать рынок, ухудшая execution для всех. Решения:
- TWAP execution: разбить крупный агрегированный ордер на части по времени
- VWAP execution: исполнять по алгоритму VWAP для минимального impact
- Slippage cap: если slippage превышает N% — ордер не исполняется, follower-у уведомление
Рейтинг лидеров
Метрики производительности
Публичный рейтинг лидеров — витрина продукта. Метрики должны быть защищены от манипуляций:
class LeaderPerformanceMetrics:
def calculate(self, leader_id: str, period_days: int = 30) -> dict:
trades = self.db.get_closed_trades(leader_id, days=period_days)
if not trades:
return {"insufficient_data": True}
# Базовые метрики
total_pnl = sum(t.pnl for t in trades)
total_pnl_percent = (total_pnl / self.get_start_capital(leader_id)) * 100
# Win rate
wins = [t for t in trades if t.pnl > 0]
win_rate = len(wins) / len(trades)
# Profit factor
gross_profit = sum(t.pnl for t in wins)
gross_loss = abs(sum(t.pnl for t in trades if t.pnl < 0))
profit_factor = gross_profit / gross_loss if gross_loss > 0 else float('inf')
# Max drawdown
equity_curve = self.build_equity_curve(trades)
max_drawdown = self.calculate_max_drawdown(equity_curve)
# Sharpe Ratio (если достаточно данных)
daily_returns = self.calculate_daily_returns(equity_curve)
sharpe = self.sharpe_ratio(daily_returns)
# Calmar Ratio (return / max_drawdown)
calmar = total_pnl_percent / abs(max_drawdown) if max_drawdown != 0 else 0
return {
"roi": total_pnl_percent,
"win_rate": win_rate,
"profit_factor": profit_factor,
"max_drawdown": max_drawdown,
"sharpe_ratio": sharpe,
"calmar_ratio": calmar,
"total_trades": len(trades),
"avg_trade_duration_hours": self.avg_duration(trades)
}
Защита от metric gaming
Лидеры могут пытаться манипулировать метриками:
Проблема: открыть много маленьких прибыльных сделок → высокий win rate. Держать один большой убыток открытым → он не отражается в closed PnL.
Решение: включать unrealized PnL в расчёты. Позиции открытые >N дней считаются по mark price.
Проблема: торговать только на демо-счёте первые 30 дней, потом переключить на live с красивой историей.
Решение: требовать минимальный реальный объём для получения статуса лидера. Проверять соответствие исполненных ордеров заявленным.
Проблема: cherry-picking — показывать только прибыльные периоды в рекламе.
Решение: публикуется полная история с момента регистрации как лидера, без возможности скрыть периоды.
Риск-менеджмент для follower-ов
Защитные настройки
Каждый follower должен иметь настраиваемые stoploss лимиты:
@dataclass
class FollowerRiskSettings:
# Максимальные потери на одну сделку
max_loss_per_trade_percent: float = 2.0
# Максимальные дневные потери (stop copying если достигнуто)
daily_loss_limit_percent: float = 5.0
# Максимальные потери за всё время (pause copying)
total_loss_limit_percent: float = 20.0
# Максимальная позиция в % от allocated
max_position_size_percent: float = 30.0
# Разрешённые инструменты
allowed_symbols: list = field(default_factory=list)
# Максимальное leverage для копирования
max_leverage: int = 10
# Остановить копирование при drawdown лидера
stop_if_leader_drawdown_percent: float = 15.0
При нарушении любого лимита — система автоматически останавливает копирование и уведомляет пользователя.
Монетизация
Модели комиссий
Performance fee (profit sharing): лидер получает % от прибыли follower-а. Стандарт: 5-30%. Выплачивается ежемесячно или при закрытии позиций.
Management fee: фиксированная ежемесячная плата за подписку на лидера. Независимо от результата.
Hybrid: небольшая management fee + performance fee. Balances incentives.
Platform fee: биржа берёт долю от performance fee лидера (например, 20% от его 15% = 3% от прибыли follower-а идёт на биржу).
Расчёт High Water Mark для performance fee:
def calculate_performance_fee(
follower_id: str,
leader_id: str,
fee_rate: float = 0.15
) -> float:
"""
Performance fee только с новой прибыли сверх максимума (High Water Mark)
Предотвращает двойное взимание комиссии при recovery после убытков
"""
account = self.db.get_copy_account(follower_id, leader_id)
current_value = account.current_value
hwm = account.high_water_mark # максимальный исторический peak
if current_value <= hwm:
return 0.0 # нет новой прибыли — нет комиссии
new_profit = current_value - hwm
fee = new_profit * fee_rate
# Обновляем HWM
self.db.update_hwm(follower_id, leader_id, current_value)
return fee
High Water Mark — обязательный элемент честной системы. Без него: лидер теряет 30%, затем отрабатывает 30% — follower платит комиссию дважды.
Compliance и регуляторные вопросы
В ряде юрисдикций копитрейдинг квалифицируется как investment advice или portfolio management, что требует лицензирования лидеров. Необходимо проконсультироваться с юристами применительно к целевым рынкам. Минимальные меры предосторожности: дисклеймеры "past performance не гарантирует будущих результатов", ограничения на максимальные суммы для неверифицированных пользователей.
Технически система копитрейдинга — это event-driven архитектура с жёсткими требованиями к latency и надёжности. Потеря одного сигнала может означать открытую позицию у лидера без соответствующей у follower-а — это risk exposure без возможности хеджирования.







