# 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”.