Розробка торгового екрану для мобільної біржі
Ми розробляємо екран торгівлі для біржового мобільного додатку — один із найбільш технічно щільних UI. Свічковий графік iOS Android із сотнями точок даних, стакан котирувань real-time з оновленням у реальному часі, форма ордера валідація з миттєвою валідацією — все це має працювати на 60 fps при WebSocket-потоці з 10 оновленнями на секунду. Продуктивність відтворення безпосередньо впливає на гроші користувача. Наш досвід — понад 10 років у продакшені (Бітрікс, 1С, веб-розробка), реалізовано 40+ проектів. Гарантуємо стабільну роботу під навантаженням.
Як забезпечити 60 fps при WebSocket-потоці?
Критично важлива стратегія throttling вхідних даних. WebSocket може надсилати 20–50 оновлень на секунду для активних пар. Оновлювати UI на кожне — перевантаження main thread. Правильний підхід: накопичуємо оновлення в ConcurrentQueue на background thread, застосовуємо до UI з частотою 60fps (через CADisplayLink на iOS або Choreographer на Android). Це в 5 разів ефективніше за пряме оновлення UI на кожен тік.
Реалізація накопичення оновлень
// iOS: накопичення оновлень private var pendingOrderBookUpdates: [OrderBookUpdate] = [] private let updateQueue = DispatchQueue(label: "orderbook.updates") func handleWebSocketMessage(_ update: OrderBookUpdate) { updateQueue.async { [weak self] in self?.pendingOrderBookUpdates.append(update) } } // CADisplayLink callback — на main thread, 60fps @objc func applyPendingUpdates() { var updates: [OrderBookUpdate] = [] updateQueue.sync { updates = pendingOrderBookUpdates; pendingOrderBookUpdates.removeAll() } guard !updates.isEmpty else { return } // Застосовуємо batch updates до UI applyToOrderBook(updates) } WebSocket та управління потоком даних
Біржові дані надходять по WebSocket — підписка на пари, отримання тіків та оновлень стакану. Wikipedia зазначає, що WebSocket дозволяє full-duplex зв'язок. На iOS — URLSessionWebSocketTask (нативний, з iOS 13) або Starscream для більш гнучкого керування reconnect-логікою. На Android — OkHttp WebSocket або Ktor WebSocket client. При втраті з'єднання: overlay «Немає підключення» поверх графіка (не замінюємо весь екран), останні відомі дані залишаються видимими. При відновленні: автоматичний reconnect з exponential backoff (1с, 2с, 4с, 8с, max 30с), REST запит актуального snapshot стакану та останніх свічок, seamless resume без скидання позиції графіка.
Як організувати модель даних стакану?
Order book — це sorted structure: bids (покупки) відсортовані за спаданням ціни, asks (продажі) за зростанням. Використовуємо SortedArray або TreeMap (Java) / AVLTree (Swift кастомна реалізація) для O(log n) вставки та видалення при оновленнях. Оновлення стакану — не повна заміна, а patch: новий рівень, змінений volume, видалений рівень (volume = 0). Для iOS стакану — NSFetchedResultsController-патерн без CoreData: зберігати bids: [(price: Decimal, size: Decimal)] і asks: [(price: Decimal, size: Decimal)], при зміні — diffing через DeepDiff або Swift's CollectionDifference для мінімальних UITableView updates.
Чому кастомний Metal-рендеринг кращий за готові бібліотеки?
Стандартний UIKit занадто повільний для інтерактивного свічкового графіка з 1000+ свічок. Порівняння підходів:
| Підхід | Продуктивність | Складність | Застосування |
|---|---|---|---|
| Core Graphics | 8–12 мс на 500 свічок | Низька | До 1000 свічок |
| CALayer per свічка | Катастрофа | Середня | Не рекомендується |
| Metal | 1–2 мс на 10000 свічок | Висока | Професійні додатки |
| LightweightCharts (WebView) | Залежить від WebView | Низька | Швидкий старт |
Кастомний Metal-рендеринг кращий за готові бібліотеки в 10 разів за продуктивністю. Metal-рендеринг в 10 разів швидший за Core Graphics при 2000+ свічках. Для професійних trading-додатків з анімованим scalable графіком використовуємо MetalKit. MTLBuffer з vertex data для свічок, vertex shader малює прямокутники в просторі екрану. 10 000 свічок — 1–2 мс render time. Для Metal-рендерингу ми використовуємо власні GLSL-подібні шейдери, що дозволяє контролювати vertex buffers та fragment processing, забезпечуючи точний frame pacing завдяки прямому управлінню GPU-пам'яттю. Завдяки оптимізованому рендерингу економія на інфраструктурі може сягати 30%.
Жести та навігація по графіку
Pinch для масштабування, pan для переміщення по часовій осі. UIPinchGestureRecognizer + UIPanGestureRecognizer одночасно через gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) -> true. При масштабуванні — visibleRange змінюється, підвантажуємо історичні свічки для нового діапазону через REST API. Crosshair при long press — UILongPressGestureRecognizer з minimumPressDuration = 0.1. При русі пальця — оновлюємо позицію crosshair та OHLCV-тултип з даними свічки під курсором.
Реалізація infinite scroll графіка
- Визначити
visibleRangeна основі contentOffset та ширини свічки. - При наближенні до лівої границі (наприклад, offset < 3 свічки) відправити запит на сервер за історичними даними.
- Отримані свічки додати в початок датасету без скидання поточної позиції — оновлюємо
contentOffsetна ширину доданих свічок. - Аналогічно для правої сторони — підвантаження новіших даних при скролі вправо.
Форма ордера на екрані торгівлі
UITextField з inputView — кастомна цифрова клавіатура замість системної. На ній — кнопки 25%, 50%, 75%, 100% від доступного балансу для швидкого введення. Decimal для зберігання, не Double. Миттєва валідація при введенні: мінімальний об'єм, крок ціни (tick size), доступний баланс. Combine publisher(for: \.text) на UITextField з debounce(0.1) та map { Decimal(string: $0) }. Попередній розрахунок вартості ордера в реальному часі: total = price * amount з урахуванням комісії.
Buy/Sell перемикання
Анімований перехід між buy та sell режимом: background кольору форми плавно змінюється (зелений ↔ червоний), текст кнопки, іконки напрямку. На iOS — UIView.animate(withDuration: 0.2) зі зміною backgroundColor. Haptic .impact(.medium) при перемиканні.
Підтвердження ордера
Bottom sheet з деталями ордера перед відправкою. UISheetPresentationController (iOS 15+) або кастомний bottom sheet. При підтвердженні — кнопка переходить в loading state (UIActivityIndicatorView замість тексту), блокується повторне натискання. Після відповіді API — success animation (зелена галочка з scale animation) або error з конкретним текстом помилки з API.
Екран торгівлі: продуктивність та оптимізація 60 fps
Order book — UITableView з нескінченним potential height. UITableView.performBatchUpdates() для анімованого додавання/видалення/оновлення рядків. При 10 оновленнях/сек — batching змін: не викликаємо performBatchUpdates на кожен тік, збираємо за 100 мс і застосовуємо разом. Ячейка стакану — кастомний UITableViewCell з depth bar (візуальна смуга пропорційна об'єму на рівні). Depth bar — UIView з constraint на width, оновлюваний через UIView.animate. Не перемальовуємо всю ячейку — тільки змінюємо constant constraint.
Профілювання
Xcode Instruments > Time Profiler для пошуку hot spots у циклі оновлень. Core Animation instrument для frame drops. Цільові метрики: <16 мс на обробку WebSocket batch, <5 мс на оновлення UI, 0 dropped frames при скролі стакану. На Android — Android Profiler в режимі CPU+Memory+Network одночасно. systrace для аналізу довгих frames у Choreographer.
Що входить в роботу під ключ
- Інтеграція WebSocket з throttling та reconnect-логікою
- Реалізація свічкового графіка (Core Graphics або LightweightCharts)
- Order book з batch updates та depth bar
- Форма ордера з валідацією та підтвердженням
- Документація з інтеграції та вихідний код UI-компонентів
- Технічна підтримка протягом місяця після деплою
Терміни орієнтовно
Базова реалізація (WebSocket + графік + стакан + форма ордера) — 5 днів. Розширений функціонал (кастомний Metal-рендеринг, індикатори, аналітика) — додатково 5–10 днів. Вартість базової реалізації починається від $1000, розширеної — від $2500. Замовлення торгового екрану під ключ — від 5 днів. Ми пропонуємо мобільну біржу під ключ. Замовте розробку екрану торгівлі для мобільного додатку біржі — ми пропонуємо комплексне рішення: від оптимізації 60 fps до металу рендерингу графіка. Пишіть нам для детальної оцінки вашого проекту.
Типові помилки при розробці
- Оновлення UI на кожне WebSocket-повідомлення без throttling — призводить до дропу кадрів.
- Використання Double для цін та об'ємів — втрачається точність при розрахунках.
- Створення окремого CALayer для кожної свічки — катастрофа для продуктивності.
- Ігнорування reconnect-логіки — при втраті з'єднання додаток залишається без даних.







