10 KiB
Serwisy DLOB na VPS (k3s
/ trade-staging)
Ten dokument opisuje aktualny, live runtime DLOB na
mevnode_bot w namespace trade-staging.
Czy legacy read-path pracuje na VPS?
Nie. W aktywnym stacku nie ma już starej ścieżki REST + backend workers.
Obecny read-path jest oparty o:
dlob-publisher-hotdlob-publisher-alldlob-redisdlob-hot-redis-to-postgres-raw-writerdlob-hot-postgres-to-postgres-derived-writerdlob-all-redis-to-postgres-derived-writerpostgreshasura
Czy na VPS jest GraphQL/WS dla stats i orderbook?
Tak. GraphQL i websocket subscriptions wystawia Hasura, a źródłem danych są tabele pochodne w PostgreSQL.
Z zewnątrz korzystamy przez frontend (proxy) pod:
- HTTP:
https://trade.rv32i.pl/graphql - WS:
wss://trade.rv32i.pl/graphql
Najważniejsze tabele live dla UI:
dlob_hot_derived_latestdlob_hot_derived_tsdlob_all_derived_latestdlob_all_derived_tsdrift_ticksdla wykresów
TL;DR: kto co robi
dlob-publisher-hot
- Rola: utrzymuje “żywy” DLOB dla hot-setu marketów i publikuje raw
snapshoty do Redis pod
dlob-hot:*. - Wejście: Solana RPC/WS/gRPC z secreta
trade-dlob-rpc, Drift SDK,PERP_MARKETS_TO_LOAD. - Wyjście: bieżący stan orderbooka do
dlob-redis.
dlob-hot-redis-to-postgres-raw-writer
- Rola: zapisuje kanoniczny raw payload
hotz Redis do PostgreSQL. - Wejście:
dlob-hot:*. - Wyjście:
dlob_hot_snapshot_latestdlob_hot_snapshot_ts
dlob-hot-postgres-to-postgres-derived-writer
- Rola: normalizuje warstwę
hotna derived tables dla UI i downstream processing. - Wejście: raw hot z PostgreSQL.
- Wyjście:
dlob_hot_derived_latestdlob_hot_derived_ts
dlob-publisher-all
- Rola: utrzymuje szeroki, pełny feed rynku i publikuje go do Redis
pod
dlob-all:*. - Wejście: ten sam upstream Solana/Yellowstone co
hot, ale bez ograniczenia do shortlisty. - Wyjście: pełny stan rynku do
dlob-redis.
dlob-all-redis-to-postgres-derived-writer
- Rola: buduje tylko warstwę pochodną dla
all, bez przechowywania raw append-only. - Wejście:
dlob-all:*. - Wyjście:
dlob_all_derived_latestdlob_all_derived_ts
dlob-redis
- Rola: szybki cache i punkt wymiany danych między publisherami i writerami.
- Uwagi: to nie jest trwałe źródło prawdy; trwałość daje PostgreSQL.
postgres +
hasura
postgres: trwałe składowanie rawhot, derivedhot/all,drift_ticks, tokenów i danych aplikacyjnych.hasura: query + subscription plane dla UI.
trade-ingestor
- Rola: zasila
drift_ticksi ścieżkę wykresów. - Uwaga: to osobny app-ingest path, nie zamiennik dla DLOB publisherów.
Jak to się spina (przepływ danych)
dlob-publisher-hotidlob-publisher-allbudują live state z prywatnego RPC/WS/gRPC i publikują snapshoty dodlob-redis.dlob-hot-redis-to-postgres-raw-writerzapisuje rawhotdo PostgreSQL.dlob-hot-postgres-to-postgres-derived-writertworzy znormalizowaną warstwęhot.dlob-all-redis-to-postgres-derived-writertworzy znormalizowaną warstwęall.hasurawystawia derived tables jako GraphQL/WS dla UI.trade-apiitrade-ingestorobsługują osobno chart/ticks path.
Co UI i API biorą z tej warstwy
- DLOB / L2 / stats: z
dlob_hot_derived_*idlob_all_derived_*przez Hasurę. - Chart / ticks: z
drift_ticksprzeztrade-api. - Metryki depth/slippage nie są już osobnymi workerami w k3s; są liczone z danych L2/derived w aktualnej ścieżce aplikacji.
Co to jest L1 / L2 / L3 (orderbook)
L1: najlepszy bid i ask.L2: zagregowane poziomy cenowe{ price, size }.L3: pojedyncze zlecenia.
W obecnym stacku live warstwa aplikacyjna korzysta przede wszystkim z
pochodnej reprezentacji L2 zapisanej w PostgreSQL
(bids_norm, asks_norm i metryki top-of-book),
a nie z osobnego REST serwera DLOB.
Szybka diagnostyka na VPS
KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl -n trade-staging get deploy | grep -E 'trade-|dlob-|hasura'
KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl -n trade-staging logs deploy/dlob-publisher-hot --tail=80
KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl -n trade-staging logs deploy/dlob-publisher-all --tail=80
KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl -n trade-staging logs deploy/dlob-hot-redis-to-postgres-raw-writer --tail=80
KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl -n trade-staging logs deploy/dlob-hot-postgres-to-postgres-derived-writer --tail=80
KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl -n trade-staging logs deploy/dlob-all-redis-to-postgres-derived-writer --tail=80Słownik pojęć (bid/ask/spread i metryki)
Podstawy orderbooka
- Bid: zlecenia kupna (chęć kupna). W orderbooku “bid side”.
- Ask: zlecenia sprzedaży (chęć sprzedaży). W orderbooku “ask side”.
- Best bid / best ask: najlepsza (najwyższa) cena kupna i najlepsza (najniższa) cena sprzedaży na topie księgi (L1).
- Spread: różnica pomiędzy
best_askabest_bid. Im mniejszy spread, tym “taniej” wejść/wyjść (mniej kosztów natychmiastowej realizacji). - Mid price: cena “po środku”:
(best_bid + best_ask) / 2. Używana jako punkt odniesienia do bps i slippage. - Level: pojedynczy poziom cenowy w L2 (np.
price=100.00,size=12.3). - Size: ilość/płynność na poziomie (zwykle w jednostkach “base asset”).
- Base / Quote:
base= instrument bazowy (np. SOL),quote= waluta wyceny (często USD).
Kolory w UI (visualizer)
bid/ “buy side” = zielony (.pos,#22c55e)ask/ “sell side” = czerwony (.neg,#ef4444)- “flat” / brak zmiany = niebieski (
#60a5fa) — używany m.in. w “brick stack” pod świecami
Jednostki i skróty
- bps (basis points): 1 bps = 0.01% =
0.0001. Np. 25 bps = 0.25%. - USD: u nas wiele wartości jest przeliczanych do USD
(np.
size_base * price).
Metryki “stats” (np.
dlob_stats_latest)
spread_abs(USD):best_ask - best_bid.spread_bps(bps):(spread_abs / mid_price) * 10_000.depth_levels: ile leveli (top‑N) z każdej strony braliśmy do liczenia “depth”.depth_bid_base/depth_ask_base: sumasizepo top‑N levelach bid/ask (w base).depth_bid_usd/depth_ask_usd: sumasize_base * pricepo top‑N levelach (w USD).imbalance([-1..1]): miara asymetrii płynności:(depth_bid_usd - depth_ask_usd) / (depth_bid_usd + depth_ask_usd)0 = relatywnie więcej płynności po bid, <0 = po ask.
oracle_price: cena z oracla (np. Pyth) jako punkt odniesienia.mark_price: “mark” z rynku/perp (cena referencyjna dla rozliczeń); różni się od oracle/top-of-book.
Metryki “depth
bands” (np. dlob_depth_bps_latest)
band_bps: szerokość pasma wokółmid_price(np. 5/10/20/50/100/200 bps).bid_usd/ask_usd: płynność po danej stronie, ale tylko z poziomów mieszczących się w oknie ±band_bpswokół mid.imbalance: jak wyżej, ale liczony per band.
Metryki “slippage”
(np. dlob_slippage_latest)
To jest symulacja “gdybym teraz zrobił market order o rozmiarze X” na podstawie L2.
size_usd: docelowy rozmiar zlecenia w USD.vwap_price: średnia cena realizacji (Volume Weighted Average Price) dla symulowanego fill.impact_bps: koszt/odchylenie względemmid_pricewyrażone w bps (zwykle na bazievwapvsmid).worst_price: najgorsza cena dotknięta podczas “zjadania” kolejnych leveli.filled_usd/filled_base: ile realnie udało się wypełnić (może być < docelowego, jeśli brakuje płynności).fill_pct: procent wypełnienia (100% = pełny fill).levels_consumed: ile leveli zostało “zjedzonych” podczas fill.
Metadane czasu (“świeżość”)
ts: timestamp źródła (czas snapshotu).slot: slot Solany, z którego pochodzi snapshot (monotoniczny “numer czasu” chaina).updated_at: kiedy nasz pipeline zapisał/odświeżył rekord w DB (do oceny, czy dane są świeże).
Redis retention na poziomie k3s i Redisa
Dla mevnode-bot retencja Redis jest ustawiana na
poziomie k3s przez cleaner CronJob, a nie
przez natywny TTL kluczy.
Powod:
- publishery DLOB zapisują do Redis w modelu
latest-only - kolejne
SETnadpisuje poprzedni snapshot tego samego klucza - zwykly Redis
TTLbylby kasowany przez kolejne zapisy, wiec nie daje poprawnej retencji semantycznej
Ustawienie operacyjne:
hot(dlob-hot:): retencja15mall(dlob-all:): retencja1h- cleaner uruchamiany z
CronJobco5m
Sposob dzialania cleanera:
- skanuje klucze
dlob-hot:*orazdlob-all:* - odczytuje
ts, a jesli go nie ma toupdatedAtTs, z payloadu JSON - usuwa tylko klucze starsze niz zadany prog wieku
To oznacza:
- aktywne markety pozostaja stale obecne w Redisie
- osierocone klucze po zmianie konfiguracji znikaja automatycznie
- Redis pozostaje warstwa
raw latest snapshot, bez normalizacji payloadu
Aktualny stan Redis, istotny dla tej decyzji:
- klucze nie maja automatycznej expiracji (
TTL=-1) maxmemory-policy=noevictionaof_enabled=0
Aktualny stan publisherow na poziomie k3s:
hotiallsa obecnie rozrozniane przez markety,REDIS_CLIENT,DLOB_SOURCEi persistent store- maja osobne
priorityClassName resources.requests/limitssa ustawione dla writerowhot; publishery maja rozroznienie priorytetow na poziomie klastra
Priorytety na poziomie k3s
Na poziomie k3s rozroznienie hot i
all jest ustawione przez osobne
PriorityClass.
Parametry:
hot:- deployment:
dlob-publisher-hot priorityClassName: dlob-hot- wartosc
PriorityClass:100000
- deployment:
all:- deployment:
dlob-publisher-all priorityClassName: dlob-all- wartosc
PriorityClass:50000
- deployment:
Znaczenie operacyjne:
hotma wyzszy priorytet schedulera i preemption nizall- przy presji na node
hotpowinien byc traktowany jako sciezka krytyczna allpozostaje feedem nizszego priorytetu dla pelnego coverage rynku
Zakres tej parametryzacji:
- to jest tylko priorytet na poziomie schedulera
k3s - nie ustawia jeszcze
resources.requestsaniresources.limits - nie zmienia logiki publishera, tylko kolejnosc traktowania workloadu przez klaster