אנו מפתחים מסך מסחר עבור אפליקציית מסחר ניידת — אחד מממשקי המשתמש הצפופים ביותר מבחינה טכנית. תרשים נרות עם מאות נקודות נתונים, פנקס הזמנות בזמן אמת וטופס הזמנה עם אימות מיידי חייבים לפעול ב-60 פריימים לשנייה תחת זרם WebSocket עם 10 עדכונים בשנייה. ביצועי העיבוד משפיעים ישירות על הכסף של המשתמש. הניסיון שלנו כולל למעלה מ-5 שנים בפיתוח אפליקציות מסחר ניידות, עם יותר מ-30 פרויקטים שהושלמו. אנו מבטיחים פעולה יציבה תחת עומס.
הבטחת 60 פריימים לשנייה תחת זרם WebSocket
ויסות נתונים נכנסים הוא קריטי. WebSocket יכול לשלוח 20–50 עדכונים בשנייה עבור זוגות פעילים. עדכון ממשק המשתמש על כל הודעה מעמיס על ה-thread הראשי. הגישה הנכונה: צבירת עדכונים ב-ConcurrentQueue על thread רקע והחלתם על ממשק המשתמש ב-60 פריימים לשנייה (באמצעות CADisplayLink ב-iOS או Choreographer באנדרואיד).
// 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 — הרשמה לזוגות, קבלת ticks ועדכוני פנקס הזמנות. ב-iOS, // 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) } (native, iOS 13+) או URLSessionWebSocketTask ללוגיקת התחברות מחדש גמישה יותר. באנדרואיד, Starscream או OkHttp WebSocket. בעת אובדן חיבור: הצגת שכבת-על "אין חיבור" מעל התרשים (לא החלפת כל המסך), שמירת הנתונים האחרונים גלויים. בעת התחברות מחדש: התחברות אוטומטית עם backoff אקספוננציאלי (1s, 2s, 4s, 8s, מקסימום 30s), בקשת REST לתמונת מצב עדכנית של פנקס ההזמנות ונרות אחרונים, חידוש חלק ללא איפוס מיקום התרשים.
מודל נתונים לפנקס הזמנות
פנקס ההזמנות הוא מבנה ממוין: הצעות קנייה (bids) ממוינות יורדות לפי מחיר, הצעות מכירה (asks) עולות. אנו משתמשים ב-Ktor WebSocket client או SortedArray (Java) / TreeMap מותאם אישית (Swift) להוספה ומחיקה ב-O(log n) בעדכונים. העדכונים אינם החלפות מלאות אלא patches: רמה חדשה, נפח שהשתנה, רמה שהוסרה (נפח = 0). עבור פנקס ההזמנות ב-iOS, אנו משתמשים בתבנית AVLTree ללא CoreData: אחסון NSFetchedResultsController ו-bids: [(price: Decimal, size: Decimal)], בעת שינוי אנו מבצעים diff באמצעות asks: [(price: Decimal, size: Decimal)] או DeepDiff של Swift לעדכוני CollectionDifference מינימליים.
מדוע עיבוד Metal מותאם אישית עדיף על ספריות מוכנות?
UIKit הסטנדרטי איטי מדי עבור תרשים נרות אינטראקטיבי עם 1000+ נרות. השוואת גישות:
| גישה | ביצועים | מורכבות | מקרה שימוש |
|---|---|---|---|
| Core Graphics | 8–12 אלפיות השנייה עבור 500 נרות | נמוכה | עד 1000 נרות |
| CALayer לכל נר | קטסטרופלי | בינונית | לא מומלץ |
| Metal | 1–2 אלפיות השנייה עבור 10000 נרות | גבוהה | יישומים מקצועיים |
| LightweightCharts (WebView) | תלוי ב-WebView | נמוכה | התחלה מהירה |
עיבוד Metal מהיר פי 10 מ-Core Graphics עם 2000+ נרות. עבור אפליקציות מסחר מקצועיות עם תרשימים מונפשים וניתנים לקנה מידה, אנו משתמשים ב-UITableView. אנו מאחסנים נתוני קודקודים ב-MetalKit עבור נרות, ו-vertex shader מצייר מלבנים במרחב המסך. 10,000 נרות מעובדים ב-1–2 אלפיות השנייה.
מחוות וניווט בתרשים
צביטה לזום, גרירה לתנועה לאורך ציר הזמן. שימוש ב-MTLBuffer + UIPinchGestureRecognizer בו-זמנית דרך UIPanGestureRecognizer. בעת זום, gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) -> true משתנה ואנו טוענים נרות היסטוריים עבור הטווח החדש דרך REST API. Crosshair בלחיצה ארוכה: visibleRange עם UILongPressGestureRecognizer. בעת תנועת האצבע, עדכון מיקום ה-crosshair ו-tooltip עם נתוני OHLCV של הנר תחת הסמן.
יישום גלילה אינסופית לתרשים
- קביעת
minimumPressDuration = 0.1בהתבסס על contentOffset ורוחב הנר. - בהתקרבות לקצה השמאלי (לדוגמה, offset < 3 נרות), שליחת בקשה לשרת לנתונים היסטוריים.
- הוספת הנרות שהתקבלו לתחילת מערך הנתונים ללא איפוס המיקום הנוכחי — עדכון
visibleRangeברוחב הנרות שנוספו. - בדומה לצד ימין — טעינת נתונים חדשים בעת גלילה ימינה.
טופס הזמנה
שימוש ב-contentOffset עם מקלדת מספרית מותאמת אישית (UITextField) במקום מקלדת המערכת. היא כוללת כפתורים עבור 25%, 50%, 75%, 100% מהיתרה הזמינה להזנה מהירה. אחסון ערכים כ-inputView, לא כ-Decimal. אימות מיידי בעת הזנה: נפח מינימלי, גודל tick, יתרה זמינה. שימוש ב-Combine Double על publisher(for: \.text) עם UITextField ו-debounce(0.1). חישוב עלות בזמן אמת: map { Decimal(string: $0) } כולל עמלה.
מעבר בין קנייה למכירה
מעבר מונפש בין מצבי קנייה ומכירה: צבע הרקע של הטופס משתנה בצורה חלקה (ירוק ↔ אדום), טקסט הכפתור וסמלי הכיוון משתנים. ב-iOS, total = price * amount עם שינוי backgroundColor. משוב הפטי UIView.animate(withDuration: 0.2) בעת המעבר.
אישור הזמנה
bottom sheet עם פרטי ההזמנה לפני השליחה. שימוש ב-.impact(.medium) (iOS 15+) או bottom sheet מותאם אישית. בעת אישור, הכפתור נכנס למצב טעינה (UISheetPresentationController מחליף את הטקסט) ומושבת ללחיצות חוזרות. בתגובת API, הצגת אנימציית הצלחה (סימן ירוק עם אנימציית קנה מידה) או שגיאה עם טקסט השגיאה הספציפי מה-API.
ביצועים ואופטימיזציה
פנקס ההזמנות הוא UIActivityIndicatorView עם גובה פוטנציאלי אינסופי. שימוש ב-UITableView להוספה/מחיקה/עדכון מונפש של שורות. עם 10 עדכונים בשנייה, בצע batch לשינויים: אל תקרא ל-UITableView.performBatchUpdates() על כל tick, אסוף שינויים במשך 100 אלפיות השנייה והחל בבת אחת. תא פנקס הזמנות מותאם אישית כולל פס עומק (פס ויזואלי פרופורציונלי לנפח באותה רמה). פס העומק הוא performBatchUpdates עם אילוץ רוחב, המעודכן דרך UITableViewCell. שנה רק את קבוע האילוץ, אל תצייר מחדש את כל התא.
פרופיילינג
שימוש ב-UIView לאיתור נקודות חמות בלולאת העדכון. מכשיר UIView.animate לזיהוי נפילות פריימים. מדדי מטרה: <16 אלפיות השנייה לעיבוד batch של WebSocket, <5 אלפיות השנייה לעדכוני ממשק משתמש, 0 פריימים שנפלו בעת גלילת פנקס ההזמנות. באנדרואיד, שימוש ב-constant במצב CPU+Memory+Network ו-Xcode Instruments > Time Profiler לפריימים ארוכים ב-Core Animation.
מה כלול בעבודה
- אינטגרציית WebSocket עם ויסות ולוגיקת התחברות מחדש
- מימוש תרשים נרות (Core Graphics או LightweightCharts)
- פנקס הזמנות עם עדכוני batch ופס עומק
- טופס הזמנה עם אימות ואישור
- תיעוד אינטגרציה וקוד מקור לרכיבי ממשק משתמש
- תמיכה טכנית לחודש לאחר ההשקה
לוחות זמנים משוערים
מימוש בסיסי (WebSocket + תרשים + פנקס הזמנות + טופס הזמנה) — 5 ימים. פונקציונליות מורחבת (עיבוד Metal מותאם אישית, אינדיקטורים, אנליטיקה) — 5–10 ימים נוספים. התמחור מחושב באופן אישי — צור קשר להערכת הפרויקט שלך.







