Files
trade-doc/dlob-basics.md

185 lines
6.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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) TopN leveli (np. L1…L10) — `dlob_*_derived_latest`
Zapisywane w writerach warstwy pochodnej na podstawie live snapshotów orderbooka:
- Bierzemy znormalizowane top levele 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 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”.