docs: add rpc/dlob/candles notes

This commit is contained in:
u1
2026-02-01 21:17:58 +01:00
parent 89415f6793
commit b06fe7f9a4
15 changed files with 2077 additions and 0 deletions

216
doc/dlob-basics.md Normal file
View File

@@ -0,0 +1,216 @@
# 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”.