Реалізація розміщення лімітного ордера в мобільному додатку біржі
Лімітний ордер — тип заявки, яку біржа виконує тільки при досягненні вказаної ціни або краще. Користувач вводить ціну та об'єм, натискає «Купити» — ордер висить в ордербуці до виконання, відміни або закінчення часу (GTC, IOC, FOK). На перший погляд це форма з двома полями. На практиці — пов'язана логіка трьох InputField, перерахунок у реальному часі, валідація балансу, підтвердження з актуальною ціною та обробка edge-case на стороні API.
Форма ордера: взаємний перерахунок полів
Три поля — Price, Amount, Total — пов'язані формулою Total = Price × Amount. Користувач може змінювати будь-яке з них, два інші перераховуються автоматично. Це ломає просту реактивність: якщо слухати onChange всіх трьох полів одночасно, отримаєш нескінченний цикл.
Рішення — джерело правди: при зміні Price або Amount перраховується Total, при зміні Total перраховується Amount (Price залишається фіксованою). Флаг isUserEditing або debounce 150мс запобігають зациклюванню.
// iOS — взаємний перерахунок полів через Combine
class LimitOrderViewModel: ObservableObject {
@Published var price: String = ""
@Published var amount: String = ""
@Published var total: String = ""
private var cancellables = Set<AnyCancellable>()
private var isUpdating = false
init() {
Publishers.CombineLatest($price, $amount)
.debounce(for: .milliseconds(100), scheduler: RunLoop.main)
.sink { [weak self] p, a in
guard let self, !self.isUpdating else { return }
guard let price = Decimal(string: p), let amount = Decimal(string: a) else { return }
self.isUpdating = true
self.total = "\(price * amount)"
self.isUpdating = false
}
.store(in: &cancellables)
}
}
На Android — TextWatcher або Flow з distinctUntilChanged() у ViewModel. MutableStateFlow для кожного поля, combine у CoroutineScope.
Слайдер відсотка від балансу
Стандартний UX: кнопки 25% / 50% / 75% / 100% під полем Amount. При натиску на 50% — Amount встановлюється як доступний баланс / 2 / поточна ціна. Якщо Price пуста — кнопки неактивні. Якщо баланс менше мінімального лоту біржі — показуємо попередження, не блокуємо кнопку.
Валідація перед відправкою
Мінімальний набір перевірок на клієнті:
- Price > 0 та Price в допустимому діапазоні (біржа повертає
minPrice,maxPrice,tickSizeуexchangeInfo) - Amount >=
minQty, Amount кратенstepSize - Total >=
minNotional(мінімальна сума угоди, наприклад 10 USDT) - Доступний баланс >= Total (при покупці) або >= Amount (при продажу)
tickSize та stepSize — важливіші, ніж здається. Binance API вернеться помилкою -1013 LOT_SIZE, якщо Amount не кратен stepSize. Округлення через floor(amount / stepSize) * stepSize з BigDecimal (не float!) запобігає цій помилці.
// Android — округлення з урахуванням stepSize
fun roundToStep(value: BigDecimal, step: BigDecimal): BigDecimal {
return (value.divide(step, 0, RoundingMode.FLOOR)).multiply(step)
.setScale(step.scale(), RoundingMode.FLOOR)
}
Підтвердження ордера
Перед відправкою на API — діалог підтвердження з підсумковими параметрами. Ціна в цей момент може змінитися — показуй актуальну ринкову ціну рядом з лімітною, щоб користувач бачив відстань до ринку. Кнопка підтвердження — з haptic feedback (UIImpactFeedbackGenerator / HapticFeedback у Jetpack Compose).
Після успішного відповіді API — оновити список відкритих ордерів. Це либо WebSocket-подія executionReport (Binance), либо polling через 1–2 секунди.
Обробка помилок API
Типові коди помилок Binance REST API при розміщенні ордера:
| Код | Причина | Рішення в UI |
|---|---|---|
| -1013 LOT_SIZE | Amount не кратен stepSize | Округлити автоматично |
| -1013 MIN_NOTIONAL | Total < minNotional | Показати мінімальну суму |
| -2010 Account has insufficient balance | Недостатньо средств | Підсвітити Amount червоним |
| -1021 Timestamp for this request | Рассинхрон часу | Синхронізувати timestamp з сервером |
Ошибку -1021 клієнт не повинен показувати користувачу — потрібно повторити запит з скорегованим recvWindow або синхронізувати час через /api/v3/time.
Часовий графік: 2–3 дні: взаємний перерахунок полів, слайдер відсотка, валідація за правилами біржі, діалог підтвердження, обробка помилок API.







