פיתוח ויזואליזציה של זרימת הזמנות (תרשים Footprint)
כאשר מערכת המסחר שלך רואה רק OHLCV, אתה עיוור לקרב שמתרחש בתוך כל נר. תרשימי Footprint חושפים את המיקרו-מבנה: בכל רמת מחיר, אתה רואה בדיוק כמה חוזים נקנו ונמכרו. בילינו למעלה מ-10 שנים בפיתוח כלי מסחר לקרנות ולסוחרים מקצועיים, ואנחנו יודעים להפוך נתוני tick גולמיים ל-Footprint ברור שמעניק לך יתרון אמיתי.
מה Footprint חושף ש-OHLCV מסתיר
נר רגיל מציג פתיחה, גבוה, נמוך, סגירה ונפח כולל. נר Footprint מציג מה קרה בכל רמת מחיר: לדוגמה, ב-$42,050 היו 8.5 BTC של קניות אגרסיביות ו-2.1 BTC של מכירות אגרסיביות, בעוד שב-$42,100 היו 3.2 קניות ו-12.4 מכירות. זהו ה-Footprint האמיתי של משתתפי השוק.
מושגי מפתח: נפח Ask (קניות אגרסיביות), נפח Bid (מכירות אגרסיביות), דלתא (Ask - Bid), חוסר איזון (צד אחד שולט בסף מסוים, בדרך כלל פי 3), ונקודת שליטה (POC — רמת המחיר עם הנפח הכולל המקסימלי).
כיצד אנו מסווגים עסקאות ל-Footprint מדויק
ה-Footprint בנוי מנתוני tick — כל עסקה בודדת. כל עסקה חייבת להיות מסווגת כקנייה או מכירה אגרסיבית. אנו משתמשים בכלל הציטוט כאשר bid/ask זמין, ונופלים חזרה לכלל ה-tick. בורסות רבות מספקות דגל כיוון ישירות (לדוגמה, type Trade struct { Price decimal.Decimal Quantity decimal.Decimal Timestamp int64 IsBuy bool // true = aggressive buy (executed at ask) } // Классификация по tick rule или quote rule type TradeClassifier struct { lastPrice decimal.Decimal lastBid decimal.Decimal lastAsk decimal.Decimal } // Quote rule: более точный метод (требует bid/ask в момент сделки) func (tc *TradeClassifier) ClassifyByQuote(trade RawTrade) bool { midPrice := tc.lastBid.Add(tc.lastAsk).Div(decimal.New(2, 0)) return trade.Price.GreaterThanOrEqual(midPrice) // >= mid = buy } // Tick rule: fallback когда bid/ask недоступны func (tc *TradeClassifier) ClassifyByTick(trade RawTrade) bool { if trade.Price.GreaterThan(tc.lastPrice) { return true // uptick = buy } if trade.Price.LessThan(tc.lastPrice) { return false // downtick = sell } // Zero tick — используем предыдущую классификацию return tc.lastWasBuy } של Binance).
type Trade struct {
Price decimal.Decimal
Quantity decimal.Decimal
Timestamp int64
IsBuy bool // true = aggressive buy (executed at ask)
}
// Классификация по tick rule или quote rule
type TradeClassifier struct {
lastPrice decimal.Decimal
lastBid decimal.Decimal
lastAsk decimal.Decimal
}
// Quote rule: более точный метод (требует bid/ask в момент сделки)
func (tc *TradeClassifier) ClassifyByQuote(trade RawTrade) bool {
midPrice := tc.lastBid.Add(tc.lastAsk).Div(decimal.New(2, 0))
return trade.Price.GreaterThanOrEqual(midPrice) // >= mid = buy
}
// Tick rule: fallback когда bid/ask недоступны
func (tc *TradeClassifier) ClassifyByTick(trade RawTrade) bool {
if trade.Price.GreaterThan(tc.lastPrice) {
return true // uptick = buy
}
if trade.Price.LessThan(tc.lastPrice) {
return false // downtick = sell
}
// Zero tick — используем предыдущую классификацию
return tc.lastWasBuy
}השוואת שיטות סיווג
| שיטה | דיוק | נתונים נדרשים |
|---|---|---|
| כלל הציטוט | גבוה | Bid/ask בזמן העסקה |
| כלל ה-tick | בינוני | מחיר בלבד |
| דגל הבורסה (isBuyerMaker) | גבוה | שדה מהבורסה |
למה Footprint נותן פי 10 יותר מידע מ-OHLCV
נר OHLCV אחד מכיל 4 מספרים. נר Footprint מכיל עשרות מספרים בכל רמת מחיר, וחושף את המיקרו-מבנה של השוק: מי שולט בכל tick. זהו ערך עצום למסחר אלגוריתמי. השוואה: OHLCV נותן נפח כולל; footprint נותן התפלגות נפח על פני מחירים.
צבירת נר Footprint
type FootprintLevel struct {
Price decimal.Decimal
BidVol decimal.Decimal // агрессивные продажи
AskVol decimal.Decimal // агрессивные покупки
Delta decimal.Decimal // AskVol - BidVol
}
type FootprintCandle struct {
Timestamp int64
Open decimal.Decimal
High decimal.Decimal
Low decimal.Decimal
Close decimal.Decimal
Volume decimal.Decimal
Delta decimal.Decimal // суммарная delta свечи
Levels map[string]*FootprintLevel // price -> level data
POC decimal.Decimal // уровень с макс. объёмом
BuyPOC decimal.Decimal // уровень с макс. ask volume
SellPOC decimal.Decimal // уровень с макс. bid volume
}
type FootprintBuilder struct {
tickSize decimal.Decimal // шаг цены для группировки (например 10 USD для BTC)
candles map[int64]*FootprintCandle // timestamp -> candle
mu sync.Mutex
}
func (fb *FootprintBuilder) AddTrade(trade Trade, timeframe time.Duration) {
fb.mu.Lock()
defer fb.mu.Unlock()
// Вычисляем bucket для временного таймфрейма
bucket := (trade.Timestamp / int64(timeframe)) * int64(timeframe)
candle := fb.getOrCreateCandle(bucket, trade.Price)
// Группируем цену по тик-размеру
priceBucket := trade.Price.Div(fb.tickSize).Floor().Mul(fb.tickSize)
level := fb.getOrCreateLevel(candle, priceBucket)
if trade.IsBuy {
level.AskVol = level.AskVol.Add(trade.Quantity)
} else {
level.BidVol = level.BidVol.Add(trade.Quantity)
}
level.Delta = level.AskVol.Sub(level.BidVol)
// Обновляем OHLCV
candle.Volume = candle.Volume.Add(trade.Quantity)
candle.Delta = candle.Delta.Add(trade.IsBuyDelta(trade.Quantity))
if trade.Price.GreaterThan(candle.High) {
candle.High = trade.Price
}
if trade.Price.LessThan(candle.Low) {
candle.Low = trade.Price
}
candle.Close = trade.Price
// Обновляем POC
candle.POC = fb.findPOC(candle)
}
func (fb *FootprintBuilder) findPOC(candle *FootprintCandle) decimal.Decimal {
var maxVol decimal.Decimal
var poc decimal.Decimal
for price, level := range candle.Levels {
total := level.AskVol.Add(level.BidVol)
if total.GreaterThan(maxVol) {
maxVol = total
poc, _ = decimal.NewFromString(price)
}
}
return poc
} זיהוי חוסר איזון: האות המרכזי
חוסר איזון הוא דפוס ה-Footprint המרכזי. רמה שבה נפח ask הוא פי 3 מנפח bid היא "רצפת זכוכית" (קונים שלטו). רמה שבה נפח bid הוא פי 3 מנפח ask היא "תקרת זכוכית".
type ImbalanceDetector struct {
threshold decimal.Decimal // обычно 300% (3x)
}
type Imbalance struct {
Price decimal.Decimal
Type string // "bid" или "ask"
Ratio decimal.Decimal
Volume decimal.Decimal
}
func (id *ImbalanceDetector) FindImbalances(candle *FootprintCandle) []Imbalance {
var imbalances []Imbalance
sortedLevels := candle.SortedLevels() // по цене ascending
for i, level := range sortedLevels {
if i == 0 {
continue
}
below := sortedLevels[i-1]
// Сравниваем ask текущего уровня с bid уровня ниже
// "Stacked imbalance" — несколько подряд
if level.AskVol.IsPositive() && below.BidVol.IsPositive() {
ratio := level.AskVol.Div(below.BidVol).Mul(decimal.New(100, 0))
if ratio.GreaterThan(id.threshold) {
imbalances = append(imbalances, Imbalance{
Price: level.Price,
Type: "ask",
Ratio: ratio,
Volume: level.AskVol,
})
}
}
}
return imbalances
} עיבוד חזיתי: Canvas בקנה מידה גדול
Footprint מורכב יותר מנרות רגילים: כל רמת מחיר מכילה מספרים. HTML Canvas הוא הדרך היחידה היעילה לעיבוד מאות נרות עם פירוט מלא.
class FootprintRenderer {
private canvas: HTMLCanvasElement;
private ctx: CanvasRenderingContext2D;
renderCandle(candle: FootprintCandle, x: number, candleWidth: number, priceToY: (price: number) => number) {
const ctx = this.ctx;
const levels = candle.getSortedLevels();
const levelHeight = Math.abs(priceToY(levels[0].price) - priceToY(levels[1]?.price || levels[0].price - candle.tickSize));
for (const level of levels) {
const y = priceToY(level.price);
const maxLevelVol = candle.maxLevelVolume;
const askWidth = (level.askVol / maxLevelVol) * (candleWidth * 0.45);
const bidWidth = (level.bidVol / maxLevelVol) * (candleWidth * 0.45);
ctx.fillStyle = 'rgba(0, 177, 94, 0.3)';
ctx.fillRect(x + candleWidth/2, y, askWidth, levelHeight - 1);
ctx.fillStyle = 'rgba(232, 66, 66, 0.3)';
ctx.fillRect(x + candleWidth/2 - bidWidth, y, bidWidth, levelHeight - 1);
if (level.price === candle.poc) {
ctx.strokeStyle = '#FFD700';
ctx.lineWidth = 1;
ctx.strokeRect(x, y, candleWidth, levelHeight - 1);
}
if (levelHeight > 12) {
ctx.fillStyle = '#6b7087';
ctx.font = `${Math.min(levelHeight - 2, 10)}px JetBrains Mono`;
ctx.textAlign = 'left';
ctx.fillText(formatVol(level.bidVol), x + 2, y + levelHeight - 3);
ctx.textAlign = 'right';
ctx.fillText(formatVol(level.askVol), x + candleWidth - 2, y + levelHeight - 3);
}
if (level.imbalanceType === 'ask') {
ctx.fillStyle = 'rgba(0, 177, 94, 0.8)';
ctx.fillRect(x, y, 3, levelHeight);
} else if (level.imbalanceType === 'bid') {
ctx.fillStyle = 'rgba(232, 66, 66, 0.8)';
ctx.fillRect(x, y, 3, levelHeight);
}
}
}
renderDeltaBar(candle: FootprintCandle, x: number, candleWidth: number, baseY: number) {
const ctx = this.ctx;
const delta = candle.delta;
const maxDelta = this.maxAbsDelta;
const barWidth = Math.abs(delta / maxDelta) * (candleWidth / 2);
const color = delta >= 0 ? '#00B15E' : '#E84242';
ctx.fillStyle = color;
if (delta >= 0) {
ctx.fillRect(x + candleWidth / 2, baseY, barWidth, 8);
} else {
ctx.fillRect(x + candleWidth / 2 - barWidth, baseY, barWidth, 8);
}
}
} פרופיל דלתא בתוך נר
דלתא מצטברת על פני פסים פנימיים מראה את המאבק בין קונים למוכרים:
function calculateCumulativeDelta(trades: Trade[], bucketSize: number): CumDeltaPoint[] {
const points: CumDeltaPoint[] = [];
let cumDelta = 0;
for (const trade of trades) {
cumDelta += trade.isBuy ? trade.quantity : -trade.quantity;
points.push({ ts: trade.timestamp, price: trade.price, cumDelta });
}
return points;
} אחסון נתונים וביצועים
נתוני Footprint גדולים בהרבה מ-OHLCV. עבור BTC/USDT 1m עם tick של $10: כ-15 רמות לכל נר. זה 1,440 נרות × 15 רמות × 2 ערכים = 43,200 רשומות ליום עבור מסגרת זמן אחת. אחסון אופטימלי הוא TimescaleDB עם דחיסה, שמקטין את הגודל פי 5–20. המערכות היצרניות שלנו מטפלות ב-8,000 עסקאות בשנייה לכל ליבה, ומאפשרות עיבוד בזמן אמת של מסגרות זמן מרובות.
פירוט: בחירת גודל ה-tick
גודל ה-tick קובע את צעד המחיר לקיבוץ רמות. עבור BTC/USDT אנו משתמשים בדרך כלל ב-$10; עבור אלטקוינים, $0.01. הבחירה תלויה בתנודתיות ובנזילות.התהליך שלנו לבניית מערכת Footprint
- ניתוח דרישות וזיהוי מקורות נתונים (API של בורסות)
- עיצוב סכמת אחסון וארכיטקטורת מסווג
- יישום ליבה: מסווג עסקאות, בונה, מזהה חוסר איזון
- חזית: רנדרר Canvas עם זום וגלילה
- אינטגרציה עם נתונים בזמן אמת דרך WebSocket
- בדיקות היסטוריות ואימות
- פריסה עם ניטור והתראות
מה אתה מקבל
- קוד מקור מלא עם הערות
- תיעוד API וארכיטקטורה
- דוגמאות שימוש עם נתוני בדיקה
- גישה לריפוזיטורי עם CI/CD
- הדרכת צוות
- 3 חודשי תמיכה לאחר מסירה
לוחות זמנים ועלות
לוחות הזמנים תלויים בנפח הנתונים ובמורכבות. הערכות:
- תרשים Footprint בסיסי לזוג אחד: 2–3 חודשים
- פלטפורמה מלאה עם מסגרות זמן מרובות, התראות ובדיקות היסטוריות: 4–6 חודשים
העלות מחושבת באופן אישי. למהנדסים שלנו יש ניסיון של למעלה מ-10 שנים בפיתוח מערכות מסחר, עם מסירה של למעלה מ-15 פרויקטים לקרנות ולסוחרים מקצועיים. צור קשר להערכת פרויקט חינמית — נספק הערכה וייעוץ.







