217 lines
8.2 KiB
Markdown
217 lines
8.2 KiB
Markdown
# DLOB + L1…L10 — podstawy (co jest czym i gdzie to liczymy)
|
||
|
||
Ten dokument wyjaśnia pojęcia:
|
||
- **DLOB** (Drift Limit Order Book),
|
||
- **L1 / L2 / L3** oraz potoczne **L1…L10**,
|
||
- na jakich warstwach w naszym stacku powstają dane i metryki,
|
||
- gdzie “pracuje AI” (modele/strategie) vs gdzie jest execution (order placement).
|
||
|
||
## Co to jest DLOB
|
||
|
||
**DLOB** = *Decentralized Limit Order Book* w Drift.
|
||
|
||
W praktyce: to jest **księga zleceń** dla rynku (np. `SOL-PERP`):
|
||
- **bids** = zlecenia kupna (po stronie bid),
|
||
- **asks** = zlecenia sprzedaży (po stronie ask).
|
||
|
||
Księga ma wiele “poziomów” cenowych; przy każdej cenie stoi pewna ilość (size).
|
||
|
||
## L1 / L2 / L3 (format i sens)
|
||
|
||
### L1 (Top of Book)
|
||
L1 to skrót od “top of book”:
|
||
- **best bid** = najwyższa cena kupna (pierwszy poziom po stronie bid),
|
||
- **best ask** = najniższa cena sprzedaży (pierwszy poziom po stronie ask).
|
||
|
||
Z L1 najczęściej liczysz:
|
||
- **spread** = `best_ask - best_bid`,
|
||
- **mid** = `(best_bid + best_ask) / 2`.
|
||
|
||
### L2 (zagregowane poziomy)
|
||
L2 to lista poziomów (levels) po obu stronach:
|
||
- `bids: [{ price, size }, ...]` (zwykle posortowane malejąco po `price`)
|
||
- `asks: [{ price, size }, ...]` (zwykle posortowane rosnąco po `price`)
|
||
|
||
To jest najpopularniejszy “orderbook UI”: słupki/heat per poziom ceny.
|
||
|
||
### L3 (pojedyncze zlecenia)
|
||
L3 to “niezagregowane” dane: pojedyncze zlecenia (większy wolumen danych).
|
||
U nas pod UI i metryki zazwyczaj wystarcza L2.
|
||
|
||
## L1…L10 (co to znaczy w praktyce)
|
||
|
||
**L1…L10** to potoczne określenie:
|
||
> “pierwsze 10 poziomów z L2 najbliżej top of book”.
|
||
|
||
To nie jest osobny format; to po prostu wycinek L2.
|
||
|
||
W naszym stacku “ile leveli bierzemy” kontroluje:
|
||
- `DLOB_DEPTH` (np. 10 → “L1…L10”).
|
||
|
||
## Jak to działa w naszym stacku (warstwy)
|
||
|
||
Poniżej “łańcuch” od źródła do metryk:
|
||
|
||
### Warstwa A: On-chain → DLOB w pamięci (VPS/k3s)
|
||
Komponent: `dlob-publisher`.
|
||
|
||
- Łączy się do Solany przez `ENDPOINT` (HTTP RPC) i `WS_ENDPOINT` (WebSocket).
|
||
- Subskrybuje konta/zdarzenia i buduje DLOB (orderbook) w pamięci.
|
||
- Publikuje snapshoty do Redis (u nas: `dlob-redis`).
|
||
|
||
To jest najbliżej źródła i zwykle najbardziej “real-time”.
|
||
|
||
### Warstwa B: Cache + REST API (VPS/k3s)
|
||
Komponenty: `dlob-redis` + `dlob-server`.
|
||
|
||
- `dlob-redis` trzyma snapshoty/publish.
|
||
- `dlob-server` udostępnia HTTP:
|
||
- `GET /l2?marketName=SOL-PERP&depth=10` → L2 (bids/asks + best bid/ask itp.)
|
||
- `GET /l3?...` → L3 (jeśli potrzebujesz)
|
||
|
||
To jest warstwa dystrybucji danych “w klastrze”, żeby inne serwisy nie musiały gadać bezpośrednio z Solaną.
|
||
|
||
Uwaga o rynkach:
|
||
- `dlob-publisher` ładuje rynki wg `PERP_MARKETS_TO_LOAD` (indeksy) / `SPOT_MARKETS_TO_LOAD`.
|
||
- Jeśli rynek nie jest załadowany przez publisher, `dlob-server` nie rozpozna `marketName`.
|
||
|
||
### Warstwa C: Metryki w DB/Hasura (VPS/k3s)
|
||
Komponenty: `dlob-worker`, `dlob-depth-worker`, `dlob-slippage-worker`.
|
||
|
||
To są “workery pod UI/AI”, które liczą metryki i zapisują je do Postgresa (Hasura).
|
||
|
||
#### `dlob-worker` (collector + basic stats)
|
||
Wejście:
|
||
- odpytuje `dlob-server` po HTTP `/l2` (źródło L2),
|
||
- rynki: `DLOB_MARKETS`,
|
||
- głębokość (ile leveli): `DLOB_DEPTH`,
|
||
- częstotliwość: `DLOB_POLL_MS`.
|
||
|
||
Wyjście (upsert do DB):
|
||
- `dlob_l2_latest` = snapshot L2 “latest” per market,
|
||
- `dlob_stats_latest` = pochodne metryki liczone z top‑N leveli (N=`DLOB_DEPTH`), m.in.:
|
||
- `mid_price`, `spread_abs`, `spread_bps`,
|
||
- `depth_bid_*` / `depth_ask_*`,
|
||
- `imbalance`.
|
||
|
||
Czyli: jeśli pytasz “gdzie liczymy L1…L10 metryki” → tutaj (w `dlob-worker`), bo bierze top‑N leveli z L2.
|
||
|
||
#### `dlob-depth-worker` (depth w bandach bps)
|
||
Wejście:
|
||
- czyta z DB `dlob_l2_latest` (czyli już “przetworzone” L2).
|
||
|
||
Wyjście:
|
||
- `dlob_depth_bps_latest` = płynność w pasmach wokół mid (np. ±5/10/20/50/100/200 bps).
|
||
|
||
To nie jest “L1…L10”, tylko “ile płynności mieści się w oknie cenowym” wokół mid.
|
||
|
||
#### `dlob-slippage-worker` (slippage vs size)
|
||
Wejście:
|
||
- czyta z DB `dlob_l2_latest`.
|
||
|
||
Wyjście:
|
||
- `dlob_slippage_latest` = symulacja wykonania zlecenia (market) po L2 dla progów `DLOB_SLIPPAGE_SIZES_USD`.
|
||
|
||
To jest bardzo użyteczne jako feature do strategii (“ile kosztuje wejście/wyjście teraz dla X USD”).
|
||
|
||
## Gdzie “pracuje AI” (TFT itp.)
|
||
|
||
AI/strategia powinna pracować na warstwie “features”, a nie na surowych subskrypcjach Solany:
|
||
|
||
Najczęstszy zestaw wejść dla modelu:
|
||
- candles/ticki (np. `drift_ticks` + `get_drift_candles(...)`),
|
||
- bieżące statsy z DLOB:
|
||
- `dlob_stats_latest` (mid/spread/depth/imbalance),
|
||
- `dlob_depth_bps_latest` (depth w bandach),
|
||
- `dlob_slippage_latest` (slippage vs size),
|
||
- opcjonalnie pełny snapshot L2 (z `dlob_l2_latest`), jeśli model potrzebuje “kształtu” książki.
|
||
|
||
Kluczowa zasada bezpieczeństwa:
|
||
- **Model (np. na Vast)** może sugerować “desired state” (wejść/wyjść/parametry),
|
||
- **Executor na VPS** zawsze odpowiada za:
|
||
- risk checks,
|
||
- składanie/cancel/close,
|
||
- klucze prywatne i podpisywanie transakcji,
|
||
- kill switch.
|
||
|
||
## Szybki słownik (1-liner)
|
||
|
||
- **bid**: kupno, zielona strona książki
|
||
- **ask**: sprzedaż, czerwona strona książki
|
||
- **best bid / best ask (L1)**: top-of-book
|
||
- **spread**: koszt “wejścia/wyjścia natychmiast” (ask-bid)
|
||
- **mid**: punkt odniesienia między bid/ask
|
||
- **L2**: lista poziomów `{price,size}`
|
||
- **L1…L10**: top 10 poziomów z L2 (u nas kontrolowane przez `DLOB_DEPTH`)
|
||
|
||
## Jak liczymy “liquidity” i “kasa” (USD) w metrykach
|
||
|
||
W UI/DB słowo “liquidity” zwykle oznacza **depth**: “ile wolumenu stoi w orderbooku blisko ceny”.
|
||
U nas trzymamy to rozdzielnie dla bid/ask oraz w dwóch wariantach:
|
||
|
||
### A) Top‑N leveli (np. L1…L10) — `dlob_stats_latest`
|
||
Liczone w `dlob-worker` na podstawie L2 z `/l2`:
|
||
|
||
- Bierzemy pierwsze `N = DLOB_DEPTH` leveli z `bids` i `asks`.
|
||
- Każdy level ma:
|
||
- `price = price_int / PRICE_PRECISION`
|
||
- `size_base = size_int / BASE_PRECISION`
|
||
- “kasa” (notional) na tym levelu: `size_usd = size_base * price`
|
||
- Sumujemy po levelach:
|
||
- `depth_bid_base = Σ size_base` (po stronie bid),
|
||
- `depth_bid_usd = Σ (size_base * price)` (po stronie bid),
|
||
- analogicznie `depth_ask_base`, `depth_ask_usd` (po stronie ask).
|
||
|
||
To odpowiada intuicji “ile jest płynności na L1…LN”.
|
||
|
||
### B) Okno cenowe w bps od mid — `dlob_depth_bps_latest`
|
||
Liczone w `dlob-depth-worker` na podstawie `dlob_l2_latest`:
|
||
|
||
- Dla pasma `band_bps` wyznaczamy:
|
||
- `minBidPrice = mid * (1 - band_bps/10_000)`
|
||
- `maxAskPrice = mid * (1 + band_bps/10_000)`
|
||
- Sumujemy wszystkie levele, które mieszczą się w tym oknie:
|
||
- bids: `price >= minBidPrice`
|
||
- asks: `price <= maxAskPrice`
|
||
- Liczymy sumy:
|
||
- `bid_base`, `bid_usd`, `ask_base`, `ask_usd` tak jak wyżej (`usd = base * price`).
|
||
|
||
To odpowiada intuicji “ile płynności jest *blisko* ceny w ±X bps”.
|
||
|
||
### Ważne doprecyzowanie
|
||
|
||
Te liczby to **notional z orderbooka** (ile “stoi” na poziomach cenowych).
|
||
Nie są to “pieniądze w kontrakcie”, tylko przybliżenie kosztu/pojemności wykonania przy danej cenie i bez przesunięcia rynku.
|
||
|
||
## Spec: Orderbook UI (L1…L10 + “liquidity bars”)
|
||
|
||
Wizualizacja orderbooka (jak na screenach) jest oparta o L2 i pokazuje tylko top‑N leveli:
|
||
- `N` = liczba leveli wyświetlanych na stronę (np. 10 → “L1…L10”).
|
||
|
||
### Kolumny / wartości
|
||
|
||
Na każdym levelu liczymy:
|
||
- `size_usd = size_base * price`
|
||
|
||
W UI pokazujemy:
|
||
- `Size (USD)` = `size_usd` dla danego poziomu,
|
||
- `Total (USD)` = suma skumulowana od best‑price “w głąb” (cumulative):
|
||
- bids: kumulacja od best bid w dół,
|
||
- asks: kumulacja od best ask w górę (w UI zwykle best ask jest bliżej środka).
|
||
|
||
### “Liquidity bars” (znormalizowane słupki tła)
|
||
|
||
Żeby “na oko” widzieć gdzie stoi płynność:
|
||
|
||
1) **Level bar (per‑poziom)** — normalizacja do największego `size_usd` w widocznych levelach danej strony:
|
||
- `level_scale = size_usd / max(size_usd w widoku)`
|
||
2) **Total bar (cumulative)** — normalizacja do największego `total_usd` w widocznych levelach danej strony:
|
||
- `total_scale = total_usd / max(total_usd w widoku)`
|
||
|
||
Żeby duże “ściany” nie zabijały kontrastu, warto użyć krzywej:
|
||
- `scale_curved = sqrt(clamp01(scale))`
|
||
|
||
Interpretacja:
|
||
- **level bar** = “ile stoi na tym poziomie”,
|
||
- **total bar** = “ile stoi łącznie do tego poziomu”.
|