דיווח מלקוח על זמן השהיה (latency) של 200 אלפיות השנייה בהתאמת הזמנות הוא סימפטום ברור לבעיות בארכיטקטורת ספר ההזמנות. בניסיון שלנו, בורסה אחת איבדה עד 10% מההזמנות עקב נעילות ברמת מסד הנתונים. הפתרון: ספר הזמנות בזיכרון (in-memory) עם סנכרון מינימלי. אנו מספקים ספרי הזמנות בעלי ביצועים גבוהים, מוכנים לפריסה, עבור בורסות קריפטו. הניסיון שלנו: 15+ פרויקטים. ההשקעה האופיינית לספר הזמנות סוהר נעה בין $30,000 ל-$50,000, עם החזר השקעה (ROI) תוך פחות מ-6 חודשים באמצעות הפחתת עלויות תשתית ושיפור ביצועי פלטפורמת המסחר. החיסכון בעלויות התשתית מהגישה של זיכרון פנימי יכול להגיע ל-40%, מה שמתורגם לעשרות אלפי דולרים בשנה עבור פלטפורמות בעלות נפח גבוה. הלקוחות שלנו חוסכים בדרך כלל $20,000-$50,000 בשנה על תשתית ענן לאחר מעבר לספר ההזמנות בזיכרון שלנו.
ספר ההזמנות הוא הרכיב המרכזי של כל בורסה — רשימה מסודרת של הזמנות קנייה ומכירה עבור נכס. הביצועים שלו קובעים ישירות את היכולות של מערכת המסחר כולה, מזמן השהיה בביצוע ועד לתפוקה המקסימלית. הוא תומך בהזמנות מגבלה (limit) והזמנות שוק (market). החיסכון הממוצע בעלויות התשתית לאחר פריסת פתרון בזיכרון מגיע ל-40% בעומס של 50,000+ הזמנות בשנייה.
מה העלות של פיתוח ספר הזמנות?
העלות לפתרון סוהר היא $30k–$50k, עם חיסכון שנתי אופייני של $20k–$50k על תשתית לאחר המעבר.
איך מושג זמן השהיה נמוך?
ארכיטקטורת ספר הזמנות בזיכרון
ספר ההזמנות שוכן כולו בזיכרון RAM; גישה לדיסק בכל התאמה אינה מקובלת בשל זמן השהיה. כדי להבטיח יעילות של שורות מטמון (cache-line) ולמזער תקורה של מחסומי זיכרון, אנו משתמשים בעיצוב ללא נעילות (lock-free) היכן שאפשר. המבנה הקלאסי משתמש בשני מיכלים ממוינים — צד הצעות קנייה (bids, מחיר יורד) וצד הצעות מכירה (asks, מחיר עולה). כל רמת מחיר מכילה תור FIFO של הזמנות בהתאם לעדיפות מחיר-זמן.
type Order struct {
ID string
UserID int64
Side Side // Buy | Sell
Type OrderType // Limit | Market
Price decimal.Decimal
Quantity decimal.Decimal
FilledQty decimal.Decimal
CreatedAt int64 // nanosecond timestamp
}
type PriceLevel struct {
Price decimal.Decimal
Orders []*Order // FIFO queue
}
type OrderBook struct {
Bids *redblacktree.Tree // Price -> *PriceLevel, descending
Asks *redblacktree.Tree // Price -> *PriceLevel, ascending
Orders map[string]*Order // OrderID -> Order (для быстрой отмены)
mu sync.RWMutex
}בחירות מבנה עץ:
- עץ אדום-שחור: O(log n) לכל הפעולות. הספרייה emirpasic/gods היא מימוש Go מוצק.
- רשימת דילוג (Skip list): מקבילית אך מורכבת יותר; רשימות דילוג ללא נעילות מציעות יכולת הרחבה טובה יותר במערכות מרובות ליבות עם פעולות לא חוסמות.
- מערך ממוין + חיפוש בינארי: הכנסה ב-O(n), חיפוש ב-O(log n). עובד עבור ספרים קטנים (< 1000 רמות).
החשיבות של ביטול הזמנה ב-O(1)
אם ביטול דרש חיפוש בכל הרמות, זמן השהיה גדל ליניארית; לכן, אנו משתמשים ב-hashmap type Order struct { ID string UserID int64 Side Side // Buy | Sell Type OrderType // Limit | Market Price decimal.Decimal Quantity decimal.Decimal FilledQty decimal.Decimal CreatedAt int64 // nanosecond timestamp } type PriceLevel struct { Price decimal.Decimal Orders []*Order // FIFO queue } type OrderBook struct { Bids *redblacktree.Tree // Price -> *PriceLevel, descending Asks *redblacktree.Tree // Price -> *PriceLevel, ascending Orders map[string]*Order // OrderID -> Order (для быстрой отмены) mu sync.RWMutex } לאינדוקס ישיר לפי מזהה, דבר קריטי למסחר בתדירות גבוהה שבו כל מיקרו-שנייה חשובה.
איך עובד אלגוריתם ההתאמה?
FIFO לעומת Pro-Rata
עדיפות מחיר-זמן (FIFO) היא התקן עבור רוב הבורסות. FIFO מהיר עד פי 2 מ-Pro-Rata בעומס גבוה בשל ניהול פשוט יותר, אך Pro-Rata מתמרץ הזמנות גדולות על ידי חלוקת ביצועים פרופורציונלית. אנו בוחרים את האלגוריתם בהתאם לדרישות שלך.
func (ob *OrderBook) matchOrder(taker *Order) []Trade {
var trades []Trade
counterSide := ob.getCounterBook(taker.Side)
for taker.RemainingQty().IsPositive() {
bestLevel := ob.getBestLevel(counterSide)
if bestLevel == nil {
break
}
if !ob.priceCrosses(taker, bestLevel.Price) {
break
}
for len(bestLevel.Orders) > 0 && taker.RemainingQty().IsPositive() {
maker := bestLevel.Orders[0]
fillQty := decimal.Min(taker.RemainingQty(), maker.RemainingQty())
trades = append(trades, Trade{
Price: bestLevel.Price,
Quantity: fillQty,
TakerOrderID: taker.ID,
MakerOrderID: maker.ID,
TakerSide: taker.Side,
Timestamp: time.Now().UnixNano(),
})
taker.FilledQty = taker.FilledQty.Add(fillQty)
maker.FilledQty = maker.FilledQty.Add(fillQty)
if maker.RemainingQty().IsZero() {
bestLevel.Orders = bestLevel.Orders[1:]
delete(ob.Orders, maker.ID)
}
}
if len(bestLevel.Orders) == 0 {
ob.removeLevel(counterSide, bestLevel.Price)
}
}
return trades
} תמונות מצב (Snapshots) ועדכונים מצטברים
לקוחות אינם מקבלים את ספר ההזמנות המלא בעת החיבור בשל גודל פוטנציאלי של מספר מגה-בייטים; במקום זאת, הם מבקשים תמונת מצב של רמות ה-N העליונות דרך REST ולאחר מכן נרשמים לערוץ WebSocket לעדכוני דיפרנציאל (diff). כל דיפרנציאל נושא מספר רצף מונוטוני כדי לזהות פערים.
| תרחיש | פעולה |
|---|---|
| חיבור חדש | GET /api/v1/orderbook/snapshot?symbol=BTCUSDT&depth=50 |
| דיפרנציאל חסר (פער ברצף) | בקשת תמונת מצב חדשה |
| זרם רגיל | החלת דיפרנציאלים עם רצף+1 |
type OrderBookDiff = {
sequence: number;
bids: [string, string][];
asks: [string, string][];
};
class OrderBookClient {
private bids = new Map<string, string>();
private asks = new Map<string, string>();
private lastSeq = 0;
applyDiff(diff: OrderBookDiff) {
if (diff.sequence <= this.lastSeq) return;
if (diff.sequence !== this.lastSeq + 1) {
this.requestSnapshot();
return;
}
diff.bids.forEach(([p, s]) => s === '0' ? this.bids.delete(p) : this.bids.set(p, s));
diff.asks.forEach(([p, s]) => s === '0' ? this.asks.delete(p) : this.asks.set(p, s));
this.lastSeq = diff.sequence;
}
} ויזואליזציה: קיבוץ טיקים ותרשים עומק
ספר הזמנות אמיתי יכול להכיל אלפי רמות. לצורך תצוגה, נפחים מקובצים לפי טיק (מרווח מחיר מינימלי). משתמשים יכולים לעבור בין גדלי טיק (לדוגמה, 0.01, 0.1, 1 עבור BTC/USDT) והמערכת מצרפת נפחים בהתאם. תרשים העומק מציג את העומק היחסי של כל רמה — ירוק עבור הצעות קנייה, אדום עבור הצעות מכירה.
בעיות נפוצות ופתרונותיהן
- **תנאי מרוץ בביטול וביצוע**: השתמש ב-RWMutex; עבור מקביליות גבוהה, מבנים ללא נעילות. - **עדכוני דיפרנציאל חסרים**: הלקוח שומר את הרצף; על פער, מבקש תמונת מצב מלאה. - **יותר מדי רמות בתגובה**: הגבל עומק (50 העליונות) וספק קיבוץ.מה כלול ביצירת ספר הזמנות
- ארכיטקטורה ועיצוב: אלגוריתם התאמה, תוכנית שכפול.
- מימוש מנוע בזיכרון ב-Go עם זמן השהיה < 100 מיקרו-שניות.
- REST API ועדכון WebSocket עם תמונות מצב ועדכוני דיפרנציאל.
- רכיבי Frontend ב-React עם קיבוץ ותרשים עומק.
- אינטגרציה עם PostgreSQL לאחסון הזמנות.
- בדיקות עומס (100k+ הזמנות/שנייה).
- תיעוד: OpenAPI, תיאור פרוטוקול, מדריך פריסה.
- הכשרת צוות הלקוח.
- אחריות ותמיכה: 3 חודשים לאחר הפריסה.
תהליך היישום הסוהר שלנו
- ניתוח דרישות וארכיטקטורה.
- מימוש מנוע התאמה בזיכרון.
- REST API + עדכון WebSocket.
- ויזואליזציה ב-Frontend.
- אינטגרציה עם מערכות חיצוניות.
- בדיקות עומס ואופטימיזציה.
- תיעוד ומסירה.
ביצועים
שירות פיתוח ספר ההזמנות שלנו מתמקד בעיצוב מנוע התאמה בזיכרון עבור בורסות קריפטו, המבטיח זמן השהיה של פחות מ-100 מיקרו-שניות. אנו מתמחים בארכיטקטורת ספר הזמנות למסחר בתדירות גבוהה עם התאמה בזמן השהיה נמוך ועדכוני WebSocket. עבור בורסה עם 10–20 זוגות ונפח בינוני: תהליך Go יחיד מטפל ב-> 50,000 הזמנות/שנייה. במידת הצורך, ניתן לפצל לפי זוגות ולהרחיב את ה-API אופקית. החיסכון בעלויות התשתית מהגישה של זיכרון פנימי מגיע ל-30-40% בעומס גבוה.
| מדד | ערך | תנאים |
|---|---|---|
| זמן השהיה בהתאמה | < 100 מיקרו-שניות | בזיכרון, ליבה אחת |
| תפוקת הוספת הזמנות | 100k+ פעולות/שנייה | Go, עץ אדום-שחור |
| ביטול הזמנה | O(1) | חיפוש ב-Hash map |
| יצירת תמונת מצב | < 1 אלפית השנייה | 50 הרמות העליונות |
| שידור דיפרנציאל WS | < 1 אלפית השנייה | לאחר כל עסקה |
צור קשר כדי להעריך את הפרויקט שלך. הזמן יישום סוהר של ספר הזמנות — קבל ייעוץ ממהנדס עם 10+ שנות ניסיון בבלוקצ'יין. חיסכון בעלויות תשתית ושיפור ביצועי פלטפורמת המסחר הם תוצאות אמיתיות מהפרויקטים שלנו.







