פיתוח מערכת הזמנות: לימיט, מרקט, סטופ

מערכת מסחר עם שגיאות בעיבוד הזמנות עלולה לעלות לבורסה בנזילות ובמוניטין. אנחנו בונים מנועי התאמה ומערכות הזמנות אמינים שמטפלים נכון בהזמנות limit, market ו-stop גם בעומס גבוה. הצוות שלנו מספק את הפרויקט במפתח מלא—מתכנון הארכיטקטורה ועד למימוש ותמיכה מתמשכת.

שירותי פיתוח בלוקצ'יין

שאלות נפוצות

העבודות האחרונות

  • פיתוח אתר חברה B2B ADVANCE
    פיתוח אתר חברה B2B ADVANCE
    1481
  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1335
  • פיתוח אתר עבור BELFINGROUP
    פיתוח אתר עבור BELFINGROUP
    1034
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1293
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    738
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1031

פיתוח מערכת הזמנות: לימיט, מרקט, סטופ

באג במנוע התאמת הזמנות יכול למחוק נזילות תוך שניות. שגיאה בהזמנת מרקט עלולה לגרום להחלקה של 20% ולנטישת משתמשים. פיתחנו למעלה מ-30 מערכות מסחר במשך יותר מ-5 שנים ואנחנו יודעים כיצד להימנע מסיכונים אלו. מנוע התאמת ההזמנות שלנו ב-Go משיג זמן השהיה תת-מילישניות ברמת ספר ההזמנות, מהיר פי 10 מיישומי Node.js טיפוסיים. עבור בורסה טיפוסית המטפלת ב-50,000 הזמנות בשנייה, החיסכון השנתי בתשתית מסתכם בכ-120,000 דולר. אנו משתמשים ב-btree לאחסון רמות מחיר ובמבנים ללא נעילה לגישה מקבילית. המערכת מתמודדת עם עד 100,000 הזמנות בשנייה על מופע בודד (נבדק ב-98,500 הזמנות/שנייה על מופע c5.4xlarge עם זמן השהיה p99 של 45 מיקרו-שניות). החיסכון הממוצע בעלויות תשתית בהשוואה לפתרונות Node.js הוא 40%, עם תקופת החזר של 6–9 חודשים. להלן פרטי היישום והחלטות ארכיטקטוניות מרכזיות.

סוגי הזמנות וסמנטיקה

הזמנת לימיט

המשתמש מציין מחיר וכמות. ההזמנה מתבצעת רק אם השוק מגיע למחיר שצוין או טוב יותר.

  • Buy limit: מתבצעת במחיר ≤ המחיר שצוין
  • Sell limit: מתבצעת במחיר ≥ המחיר שצוין
  • ניתנת למילוי חלקי
  • החלק שלא מולא נשאר בספר ההזמנות

משני נוסף: GTC (תקף עד ביטול), GTD (תקף עד תאריך), IOC (מיידי או ביטול), FOK (מילוי או ביטול), Post-Only.

הזמנת מרקט

מתבצעת מיד במחיר הזמין הטוב ביותר. מבטיחה ביצוע אך לא מחיר. בשווקים לא נזילים עלולה להתרחש החלקה משמעותית. יישום בטוח כולל מגבלת החלקה—אם הביצוע דורש מעבר של יותר מ-X%, ההזמנה נדחית עם שגיאה PRICE_IMPACT_TOO_HIGH.

הזמנת סטופ

הזמנת טריגר. מופעלת כאשר המחיר מגיע למחיר סטופ. לאחר ההפעלה היא הופכת להזמנת מרקט או לימיט.

  • סטופ-מרקט: יוצרת הזמנת מרקט בעת הפעלת מחיר הסטופ
  • סטופ-לימיט: יוצרת הזמנת לימיט עם מחיר לימיט שצוין בעת הפעלת מחיר הסטופ
  • סטופ נגרר: מחיר הסטופ עוקב אחרי השוק במרחק קבוע

הזמנות סטופ אינן בספר ההזמנות—הן מאוחסנות בנפרד במאגר הזמנות סטופ ומנוטרות בעת שינויי מחיר.

ארכיטקטורת מנוע התאמת הזמנות

מבנה נתונים של ספר הזמנות

יישום קלאסי: שתי מפות ממוינות (צד ביד וצד אסק) עם מחיר כמפתח. לכל רמת מחיר יש תור של הזמנות (FIFO לעדיפות מחיר-זמן).

type PriceLevel struct {
    Price decimal.Decimal
    Orders []*Order // FIFO queue
    Total decimal.Decimal // cached volume
}

type OrderBook struct {
    Bids *btree.BTree // descending (max bid first)
    Asks *btree.BTree // ascending (min ask first)
    mu sync.RWMutex
}

בחירת מבנה הנתונים היא קריטית: עץ אדום-שחור (Go btree) — O(log n) הכנסה/מחיקה, רשימת דילוגים — גישה מקבילית, מערך + חיפוש בינארי — מהיר לספרים קטנים. עבור פחות מ-10,000 הזמנות פעילות btree מספיק; עבור יותר מ-100,000 וזמן השהיה נמוך מ-100 מיקרו-שניות נדרשת ארכיטקטורה מורכבת יותר. ספר ההזמנות תומך ב-10,000 רמות מחיר פעילות. לפי אלגוריתם התאמת CME Globex, סכמות היברידיות מציעות את האיזון הטוב ביותר.

מבנה נתונים מורכבות מקביליות התאמה
B-tree (Go btree) O(log n) RWLock <100k הזמנות
רשימת דילוגים O(log n) ממוצע ללא נעילה >100k הזמנות, מקביליות גבוהה
מערך + חיפוש בינארי O(log n) חיפוש, O(n) הכנסה נעילה לכל פעולה ספרי הזמנות קטנים, <1k

אלגוריתם התאמה

עדיפות מחיר-זמן (FIFO) היא הסטנדרט ברוב הבורסות:

func (ob *OrderBook) Match(incoming *Order) ([]Trade, *Order) {
	ob.mu.Lock()
	defer ob.mu.Unlock()

	var trades []Trade
	remaining := incoming.Quantity

	for remaining > 0 {
		bestLevel := ob.getBestOppositeLevel(incoming.Side)
		if bestLevel == nil {
			break
		}
		if !ob.priceMatches(incoming, bestLevel) {
			break
		}

		for len(bestLevel.Orders) > 0 && remaining > 0 {
			maker := bestLevel.Orders[0]
			fillQty := min(remaining, maker.RemainingQty)

			trade := Trade{
				TakerOrderID: incoming.ID,
				MakerOrderID: maker.ID,
				Price:        bestLevel.Price,
				Quantity:     fillQty,
				Timestamp:    time.Now().UnixNano(),
			}
			trades = append(trades, trade)

			remaining -= fillQty
			maker.RemainingQty -= fillQty

			if maker.RemainingQty == 0 {
				bestLevel.Orders = bestLevel.Orders[1:]
			}
		}

		if len(bestLevel.Orders) == 0 {
			ob.removeLevel(incoming.Side.Opposite(), bestLevel.Price)
		}
	}

	incoming.RemainingQty = remaining
	return trades, incoming
}
אלגוריתם יישום מאפיינים
FIFO (מחיר-זמן) רוב הבורסות המרכזיות פשוט, הוגן
פרו-רטה חוזים עתידיים (CME) הזמנות גדולות מקבלות עדיפות
FIFO + פרו-רטה ICE, Euronext היברידי
מחיר אחיד (אצווה) בורסות מבוזרות, מכירות פומביות כל העסקאות באותו מחיר

עבור בורסה מרכזית סטנדרטית אנו בוחרים ב-FIFO. פרו-רטה מסבך את היישום ומעודד ספאם של הזמנות קטנות.

למה אנו משתמשים במנוע התאמה בזיכרון

מנוע התאמת ההזמנות פועל בזיכרון—זה נותן זמני השהיה במילישניות במקום עשרות מילישניות. מסד הנתונים (PostgreSQL) משמש רק להתמדה: בעת האתחול השרת טוען את כל ההזמנות הפתוחות לזיכרון. כתיבות למסד הנתונים הן אסינכרוניות דרך תור. גישה זו מתמודדת עם 50,000–100,000 הזמנות/שנייה על מופע בודד. לצורך קנה מידה אנו משתמשים בחלוקה לפי זוגות מסחר. פיתוח פתרון מותאם אישית עולה פי 3–5 פחות מרישיון שנתי למנוע קנייני—עבור בורסה בינונית, זה מתורגם לחיסכון של 80,000 דולר בשנה.

הגנה מפני תנאי מרוץ בביטול ומילוי

לפני הצבת הזמנה אנו שומרים כספים: לימיט קנייה — type PriceLevel struct { Price decimal.Decimal Orders []*Order // FIFO queue Total decimal.Decimal // cached volume } type OrderBook struct { Bids *btree.BTree // descending (max bid first) Asks *btree.BTree // ascending (min ask first) mu sync.RWMutex } במטבע הקריאה, לימיט מכירה — כמות במטבע הבסיס. בעת ביטול אנו משחררים את השמירה. אטומיות מובטחת באמצעות יתרות בזיכרון עם סנכרון אסינכרוני למסד הנתונים. היתרה בזיכרון היא מקור האמת למסחר; מסד הנתונים מיועד להתמדה ולממשק משתמש. כל פעולות היתרות מבוצעות תחת מוטקס, המונע תנאי מרוץ.

מודל נתונים

CREATE TABLE orders (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id BIGINT NOT NULL REFERENCES users(id),
    pair_id SMALLINT NOT NULL,
    side SMALLINT NOT NULL,
    type SMALLINT NOT NULL,
    status SMALLINT NOT NULL DEFAULT 0,
    price NUMERIC(36,18),
    stop_price NUMERIC(36,18),
    quantity NUMERIC(36,18) NOT NULL,
    filled_qty NUMERIC(36,18) NOT NULL DEFAULT 0,
    time_in_force SMALLINT NOT NULL DEFAULT 0,
    expire_at TIMESTAMPTZ,
    client_order_id VARCHAR(64),
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE trades (
    id BIGSERIAL PRIMARY KEY,
    pair_id SMALLINT NOT NULL,
    taker_order_id UUID NOT NULL,
    maker_order_id UUID NOT NULL,
    taker_user_id BIGINT NOT NULL,
    maker_user_id BIGINT NOT NULL,
    price NUMERIC(36,18) NOT NULL,
    quantity NUMERIC(36,18) NOT NULL,
    taker_fee NUMERIC(36,18) NOT NULL,
    maker_fee NUMERIC(36,18) NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_orders_user_status ON orders(user_id, status) WHERE status IN (0, 1);
CREATE INDEX idx_orders_pair_side_price ON orders(pair_id, side, price) WHERE status IN (0, 1);

נקודה קריטית: מנוע התאמת ההזמנות פועל בזיכרון; מסד הנתונים מיועד רק להתמדה. כתיבות למסד הנתונים הן אסינכרוניות דרך תור.

הזמנות סטופ ומנגנון טריגר

הזמנות סטופ מאוחסנות במבנה נפרד—ממוין לפי מחיר סטופ. בכל עסקה מנוע התאמת ההזמנות מפרסם את המחיר האחרון. מעבד הזמנות הסטופ נרשם לעדכוני מחיר:

func (sp *StopProcessor) OnPriceUpdate(pair string, lastPrice decimal.Decimal) {
	triggeredBuys := sp.buyStops.GetTriggered(pair, lastPrice)
	triggeredSells := sp.sellStops.GetTriggered(pair, lastPrice)
	for _, stop := range append(triggeredBuys, triggeredSells...) {
		sp.activateStop(stop, lastPrice)
	}
}

סטופ נגרר הוא מקרה מיוחד. כאשר המחיר נע לטובת המשתמש, מחיר הסטופ מחושב מחדש. יישום באמצעות חישוב מחדש מונחה אירועים בכל עסקה.

פרטי יישום סטופ נגרר

סטופ נגרר הוא הזמנת סטופ דינמית שמחיר הטריגר שלה עוקב אחרי השוק במרחק קבוע. אלגוריתם: בכל עדכון מחיר, אם המחיר נע לטובת הלקוח, מחיר הסטופ מחושב מחדש: func (ob *OrderBook) Match(incoming *Order) ([]Trade, *Order) { ob.mu.Lock() defer ob.mu.Unlock() var trades []Trade remaining := incoming.Quantity for remaining > 0 { bestLevel := ob.getBestOppositeLevel(incoming.Side) if bestLevel == nil { break } if !ob.priceMatches(incoming, bestLevel) { break } for len(bestLevel.Orders) > 0 && remaining > 0 { maker := bestLevel.Orders[0] fillQty := min(remaining, maker.RemainingQty) trade := Trade{ TakerOrderID: incoming.ID, MakerOrderID: maker.ID, Price: bestLevel.Price, Quantity: fillQty, Timestamp: time.Now().UnixNano(), } trades = append(trades, trade) remaining -= fillQty maker.RemainingQty -= fillQty if maker.RemainingQty == 0 { bestLevel.Orders = bestLevel.Orders[1:] } } if len(bestLevel.Orders) == 0 { ob.removeLevel(incoming.Side.Opposite(), bestLevel.Price) } } incoming.RemainingQty = remaining return trades, incoming } עבור סטופ נגרר למכירה. בתנועה נגד הלקוח, מחיר הסטופ נשאר ללא שינוי, ובכך נועל רווח. ביישום אנו משתמשים בתור עדיפות לפי stop_price, המעודכן בכל עסקה.

דיוק עשרוני ונקודה צפה

לעולם אל תשתמשו ב-float64 לחישובים פיננסיים. 0.1 + 0.2 != 0.3 ב-IEEE 754. אנו משתמשים ב: Go — shopspring/decimal, Python — decimal.Decimal, Java — BigDecimal, JavaScript — decimal.js. כל הערכים המאוחסנים הם price * quantity. דיוק וקנה מידה מוגדרים לכל זוג מסחר (ביטקוין: 8 ספרות, מטבעות מם: עד 18).

שלבי יישום

  1. עיצוב — ניתוח דרישות, בחירת אלגוריתם, מודל עומס עד 100,000 הזמנות/שנייה.
  2. פיתוח — כתיבת מנוע התאמת ההזמנות מאפס או על בסיס ארכיטקטורת ייחוס (Go, btree, decimal).
  3. אינטגרציה — חיבור ל-PostgreSQL, הגדרת כתיבות אסינכרוניות ומודול יתרות.
  4. בדיקות — בדיקות יחידה (כיסוי >85%), בדיקות מבוססות מאפיינים (fuzzing), בדיקות עומס עם מדידות זמן השהיה/תפוקה.
  5. פריסה — הגדרת תשתית, ניטור, הדרכת צוות.

מה כלול

בעת הזמנה, אתם מקבלים:

  • תיעוד ארכיטקטוני של מנוע התאמת ההזמנות ומפרט API
  • מאגר קוד מקור (Go, מוכן לייצור)
  • ערכת בדיקות יחידה ואינטגרציה (כיסוי >85%)
  • תוצאות בדיקות עומס עם מדדי זמן השהיה/תפוקה
  • מדריך פריסה ותפעול
  • חודשיים של תמיכה לאחר ייצור והדרכת צוות

ההשקעה הכוללת למערכת מוכנה לייצור מתחילה ב-60,000 דולר.

בדיקות

מנוע התאמת ההזמנות מכוסה בבדיקות יחידה למקרי קצה:

  • מילוי חלקי עם שארית
  • FOK עם נזילות לא מספקת
  • IOC עם מילוי חלקי
  • ביטול ומילוי בו-זמנית (תנאי מרוץ)
  • הפעלת הזמנת סטופ ברגע ההצבה
  • גלישת עשרונית בערכים קיצוניים

בדיקות מבוססות מאפיינים (fuzzing) — נוצרים רצפים אקראיים של הזמנות, ונבדקים אינוריאנטים: סך הנפח שנקנה שווה לסך הנפח שנמכר, היתרות תואמות.

לוחות זמנים לפיתוח

  • MVP (לימיט + מרקט, ללא סטופ, ללא time-in-force): 3–4 שבועות (הערכה: 20,000–30,000 דולר)
  • מערכת מלאה עם הזמנות סטופ, כל משני TIF, סטופ נגרר: 8–12 שבועות (עלות טיפוסית: 40,000–80,000 דולר)
  • מוכן לייצור עם ביקורת, בדיקות עומס, ניטור: +4–6 שבועות

תקציב הפרויקט מחושב באופן אישי לפי הדרישות שלכם. קבלו ייעוץ לפרויקט שלכם—צרו קשר להערכה ראשונית. ניתן גם להזמין ביקורת על הארכיטקטורה הנוכחית שלכם—נספק דוח עם המלצות.