Files
trade-frontend/doc/dlob-basics.md
2026-02-01 21:44:45 +01:00

8.2 KiB
Raw Blame History

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 topN 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 topN 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) TopN 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 topN 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 bestprice “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 (perpoziom) — 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”.