Реализация сигналов для Copy-Trading в мобильном приложении
Copy-trading — это когда сделки мастер-трейдера автоматически копируются на счета подписчиков. Мобильное приложение здесь решает две принципиально разные задачи: для мастера — публикация сделок и управление подписчиками, для копировщика — получение сигналов, настройка параметров копирования и мониторинг результатов.
Архитектура сигналов
Сигнал — это не просто уведомление «купи BTC». Это структурированный объект с достаточным контекстом для автоматического исполнения:
struct TradingSignal: Codable {
let id: UUID
let masterId: String
let pair: String // "BTC/USDT"
let side: TradeSide // .long, .short
let entryPrice: Decimal? // nil для market ордера
let takeProfit: [Decimal] // несколько TP уровней
let stopLoss: Decimal
let leverage: Int? // для фьючерсов
let riskPercent: Decimal // % от депозита подписчика
let comment: String?
let publishedAt: Date
let validUntil: Date? // сигнал устаревает
}
riskPercent вместо фиксированной суммы — потому что подписчики с разным депозитом. 2% от $1,000 и 2% от $50,000 — разные суммы, но одинаковый риск.
Доставка сигналов: latency имеет значение
Сигнал на вход в позицию — время-критичный объект. Цена движется, пока сигнал летит. Требования к latency зависят от стратегии мастера: для swing-трейдинга (позиции на дни) достаточно push с задержкой 5–10 секунд. Для скальпинга — WebSocket обязателен, push слишком медленный.
WebSocket для активных пользователей в приложении. Бэкенд рассылает сигнал всем подключённым подписчикам мастера одновременно.
FCM/APNs push для фоновых уведомлений. Важный нюанс: push — не гарантированная доставка, FCM может буферизовать. Для торговых сигналов это неприемлемо. Поэтому при открытии приложения — всегда синхронизируем непропущенные сигналы через GET /signals?since={lastReceivedAt}.
// Android — получение и обработка сигнала
class SignalReceiver @Inject constructor(
private val signalRepository: SignalRepository,
private val orderExecutor: OrderExecutor,
private val notificationManager: AppNotificationManager,
) {
suspend fun handle(signal: TradingSignal) {
signalRepository.save(signal)
val subscription = signalRepository.getSubscription(signal.masterId) ?: return
if (!subscription.autoExecute) {
// Только уведомление, пользователь решает вручную
notificationManager.showSignal(signal)
return
}
// Автоисполнение с проверками
val account = accountRepository.getCurrent()
val orderSize = account.balance * subscription.riskPercent / 100
if (orderSize < exchange.minimumOrderSize(signal.pair)) {
notificationManager.showError("Недостаточно баланса для исполнения сигнала")
return
}
val result = orderExecutor.execute(signal, orderSize)
notificationManager.showExecutionResult(signal, result)
}
}
Настройки копирования
Каждый подписчик настраивает параметры отдельно для каждого мастера:
Размер позиции:
- Фиксированный % от депозита (следовать риску мастера)
- Фиксированная сумма в USDT
- Множитель от размера мастера (например, 0.5x если у мастера объём слишком большой)
Фильтры сигналов:
- Только long / только short / оба направления
- Пары (разрешить только BTC, ETH; игнорировать альткоины)
- Максимальное плечо (не копировать, если мастер использует > 10x)
- Максимальное число одновременных позиций
Режим исполнения:
- Авто (сразу при получении сигнала)
- С подтверждением (push → пользователь нажимает «Исполнить»)
- Только уведомления (без исполнения, мониторинг стратегии мастера)
Форма настроек — ключевой UX-элемент. Пользователь должен понимать последствия каждого параметра. Для % от депозита — показываем расчёт: «При депозите $2,000 и риске 2% — $40 на сделку».
Экран мастера: что видит подписчик
Профиль мастера — это показатели его торговли, а не маркетинговый текст:
| Метрика | Значение |
|---|---|
| Win Rate | 64% |
| Profit Factor | 1.87 |
| Max Drawdown | −18.4% |
| Sharpe Ratio | 1.31 |
| Сделок (30д) | 142 |
| Подписчиков | 2,840 |
| Monthly PnL | +12.3% |
График equity curve мастера — обязателен. Подписчик должен видеть не просто «+30% за год», а как именно: было ли длительное просадочное плато? Резкий рост в начале и стагнация потом?
Мои результаты как копировщика
Отдельный экран — PnL от копирования конкретного мастера. Данные отличаются от мастера: разные цены исполнения (slippage), разное время входа (задержка доставки сигнала), разные размеры позиций.
Сравнение: equity curve мастера vs. мои результаты на одном графике. Если расхождение большое — анализ причин (slippage, пропущенные сигналы, лимиты по фильтрам).
// iOS, SwiftUI — сравнение кривых
Chart {
ForEach(masterEquity) { point in
LineMark(
x: .value("Дата", point.date),
y: .value("PnL", point.pnl),
series: .value("Тип", "Мастер")
)
.foregroundStyle(.blue)
}
ForEach(myEquity) { point in
LineMark(
x: .value("Дата", point.date),
y: .value("PnL", point.pnl),
series: .value("Тип", "Мои результаты")
)
.foregroundStyle(.green)
}
}
.chartLegend(.visible)
Поиск и рейтинг мастеров
Каталог мастеров с фильтрами: по win rate, по просадке, по числу сделок, по периоду работы (минимум 3 месяца с активной торговлей). Сортировка по Sharpe Ratio по умолчанию — единственная метрика, которая учитывает и доход, и риск одновременно.
Карточка мастера в списке — компактно: аватар, ник, 3 ключевые метрики, мини-спарклайн equity curve, кнопка «Подписаться».
Что входит в работу
- Доставка сигналов через WebSocket + FCM/APNs с синхронизацией пропущенных
- Форма настроек копирования с фильтрами и расчётом размера позиции
- Профиль мастера с метриками и equity curve
- Экран моих результатов со сравнением с мастером
- Каталог мастеров с фильтрацией и рейтингом
- История сигналов: получен, исполнен, пропущен (с причиной)
Сроки
| Роль | Функции | Срок |
|---|---|---|
| Копировщик (подписчик) | Сигналы, настройки, результаты | 10–14 дней |
| Мастер | Публикация, подписчики, аналитика | 7–10 дней |
| Полная система | Каталог, рейтинги, обе роли | 20–28 дней |
Стоимость рассчитывается индивидуально после анализа требований.







