# 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 aktualny łańcuch od źródła do metryk w live runtime: ### Warstwa A: On-chain → DLOB w pamięci (VPS/k3s) Komponenty: `dlob-publisher-hot` i `dlob-publisher-all`. - Łączą się do Solany przez prywatny RPC/WS/gRPC. - Budują DLOB w pamięci. - Publikują snapshoty do Redis: - `dlob-hot:*` - `dlob-all:*` To jest najbliżej źródła i najbardziej “real-time”. ### Warstwa B: Redis → PostgreSQL (VPS/k3s) Komponenty: - `dlob-hot-redis-to-postgres-raw-writer` - `dlob-hot-postgres-to-postgres-derived-writer` - `dlob-all-redis-to-postgres-derived-writer` Ten etap: - utrwala raw `hot` w PostgreSQL, - buduje derived layer `hot` i `all`, - zapisuje znormalizowane poziomy orderbooka oraz metryki top-of-book do tabel `dlob_*_derived_*`. ### Warstwa C: Hasura + aplikacja - `Hasura` wystawia derived tables jako GraphQL + subscriptions. - UI i narzędzia aplikacyjne czytają z `dlob_hot_derived_latest` oraz `dlob_all_derived_latest`. - Metryki depth/slippage nie są już liczone przez osobne workery k3s; bieżąca ścieżka aplikacji wylicza je z danych L2/derived. ## 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_*_derived_latest` Zapisywane w writerach warstwy pochodnej na podstawie live snapshotów orderbooka: - Bierzemy znormalizowane top level’e z `bids_norm` i `asks_norm`. - Każdy level ma: - `price` - `size_base` - “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 W aktualnej ścieżce aplikacji to liczenie jest robione z danych L2/derived, a nie przez osobny backend worker: - 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”.