docs(snapshot): sync workspace documentation
This commit is contained in:
359
bots.md
Normal file
359
bots.md
Normal file
@@ -0,0 +1,359 @@
|
||||
# Boty (auto-trading) na Drift PERP — architektura VPS + Vast
|
||||
|
||||
Ten dokument opisuje docelową architekturę “botów”, które:
|
||||
- potrafią otworzyć pozycję na wybranym rynku PERP,
|
||||
- mierzą i publikują statsy (m.in. DLOB/L2, spread, depth, slippage),
|
||||
- mogą zostać **zatrzymane / zmodyfikowane / natychmiast spłaszczone** (exit) w każdej chwili,
|
||||
- działają na VPS (k3s) bez ręcznych zmian “na żywo” (snapshot deploy; patrz `doc/workflow.md`).
|
||||
|
||||
## Założenia i cele
|
||||
|
||||
### Cele
|
||||
- **Separation of concerns:** model/strategia nie ma dostępu do kluczy; wykonanie transakcji jest tylko na VPS.
|
||||
- **Kill switch:** jeden przełącznik ma natychmiast zatrzymać bota i wyjść z pozycji (cancel + close/reduce-only).
|
||||
- **Desired-state loop:** bot nie “wydaje komend” ad‑hoc; utrzymuje stan docelowy (np. target exposure) i zbiega do niego.
|
||||
- **Obserwowalność:** każda decyzja i akcja ma log/metrykę/event w DB.
|
||||
|
||||
Słownik pojęć (DLOB/L1…L10): `doc/dlob-basics.md`.
|
||||
Strategia MVP (sekundy → minuty): `doc/bot-strategy-reversal.md`.
|
||||
|
||||
### Non-goals (na start)
|
||||
- HFT / ultra-low-latency (na początku stawiamy na stabilność i kontrolę).
|
||||
- Multi-venue routing i arbitraż między giełdami.
|
||||
- Zarządzanie portfelem wielu subkont per user (to może wejść później).
|
||||
|
||||
## Warstwy systemu (docelowo)
|
||||
|
||||
### 1) Data plane (VPS/k3s): market data + statsy
|
||||
Cel: zapewnić botom i UI spójne, “lokalne” źródła danych.
|
||||
|
||||
Aktualne elementy (już istnieją w repo/stacku):
|
||||
- `dlob-publisher-hot` → buduje hot-path DLOB z Solany i publikuje `dlob-hot:*` do `dlob-redis`.
|
||||
- `dlob-publisher-all` → buduje pełny feed rynku i publikuje `dlob-all:*` do `dlob-redis`.
|
||||
- `dlob-hot-redis-to-postgres-raw-writer` → zapisuje raw `hot` do PostgreSQL.
|
||||
- `dlob-hot-postgres-to-postgres-derived-writer` → buduje derived `hot`.
|
||||
- `dlob-all-redis-to-postgres-derived-writer` → buduje derived `all`.
|
||||
- `Hasura` → wystawia te dane jako GraphQL + subscriptions dla UI i botów.
|
||||
|
||||
Konfiguracja rynków:
|
||||
- `DLOB_MARKETS` (symbole: np. `SOL-PERP`, `BTC-PERP`, `1MBONK-PERP`).
|
||||
- `PERP_MARKETS_TO_LOAD` (indeksy rynków ładowane przez `dlob-publisher-hot` / `dlob-publisher-all`).
|
||||
|
||||
### 2) Execution plane (VPS/k3s): bot executor (klucze + transakcje)
|
||||
Cel: jedyne miejsce, gdzie występują klucze prywatne i podpisywanie transakcji.
|
||||
|
||||
Warianty uruchomienia:
|
||||
- **A) 1 Deployment na bota**: proste izolowanie i rollout per bot.
|
||||
- **B) 1 Deployment “executor” obsługujący wiele botów**: łatwiejszy pooling połączeń/RPC, ale większy blast radius.
|
||||
|
||||
Minimalne wymagania executora:
|
||||
- Czyta konfigurację bota (desired state) z DB (Hasura/Postgres).
|
||||
- Subskrybuje statsy/telemetrię z Hasury (GraphQL + subscriptions) lub odpytuje okresowo.
|
||||
- Wykonuje akcje tradingowe (place/modify/cancel/close) z ograniczeniami ryzyka.
|
||||
- Ma **hard kill switch** (patrz niżej).
|
||||
|
||||
## Maszyneria bota (to jest kluczowe)
|
||||
|
||||
To, czy bot “działa bezpiecznie”, zależy mniej od samego modelu, a bardziej od maszynerii: pętli kontrolnej, zarządzania orderami i bezpiecznego wyjścia.
|
||||
|
||||
Poniżej minimalny podział odpowiedzialności wewnątrz executora (nawet jeśli to jest jeden proces):
|
||||
|
||||
- **Config store**: odczyt `bot_config` (desired state) + zapis `bot_state`.
|
||||
- **Market data adapter**: pobiera features z Hasury (np. `dlob_stats_latest`, `dlob_depth_bps_latest`, `dlob_slippage_latest`, candles).
|
||||
- **Strategy adapter**: tworzy propozycję “desired” (lokalnie lub z Vast). Nie robi transakcji.
|
||||
- **Risk engine**: weryfikuje limity (max pozycja, max slippage, max order rate, timeouts, freshness danych).
|
||||
- **Order manager**: mapuje “desired” → konkretne akcje: place/modify/cancel; pilnuje idempotencji i retry.
|
||||
- **Position reconciler**: porównuje stan konta (pozycje/ordery) z DB i “desired”; wykrywa rozjazdy.
|
||||
- **Kill switch**: zawsze dostępny; wymusza cancel + close i przejście do `mode=off`.
|
||||
|
||||
### Stan bota jako state machine (proponowane)
|
||||
|
||||
Traktuj to jak automat stanów, żeby uniknąć chaosu “ifów”:
|
||||
- `off`: bot nie wysyła transakcji (może tylko heartbeat).
|
||||
- `observe`: liczy/loguje decyzje, ale nie handluje.
|
||||
- `trade`: aktywny, utrzymuje desired-state.
|
||||
- `panic`: tryb awaryjny; cancel + close; potem `off`.
|
||||
|
||||
Opcjonalne stany pośrednie (jeśli potrzebne): `entering`, `managing`, `exiting`.
|
||||
|
||||
### Minimalny model danych (MVP)
|
||||
|
||||
Docelowo boty muszą być “sterowane” i audytowalne z DB:
|
||||
|
||||
- `bot_config` (sterowanie):
|
||||
- `mode`: `off|observe|trade`
|
||||
- `market_name` (np. `SOL-PERP`)
|
||||
- `target_exposure_usd` / `target_base`
|
||||
- limity: `max_position_usd`, `max_slippage_bps`, `max_orders_per_min`, `cooldown_ms`
|
||||
- `kill_switch` (bool)
|
||||
- `strategy`: `{ type, params, model_endpoint, model_version }`
|
||||
- `bot_state` (status runtime):
|
||||
- `last_heartbeat_at`, `last_error`, `last_action_at`
|
||||
- `position_snapshot` (opcjonalnie)
|
||||
- `bot_events` (audit log):
|
||||
- `decision` (wejście modelu/strategii + features hash + wersja)
|
||||
- `order_sent`, `order_ack`, `order_filled`, `order_canceled`
|
||||
- `panic_exit`, `error`
|
||||
|
||||
W MVP możesz zacząć od: `bot_config` + `bot_events`, a `bot_state` dodać po pierwszym działającym loopie.
|
||||
|
||||
### 3) Model plane (Vast): sygnały i inference (bez kluczy)
|
||||
Cel: trenowanie/inference modeli na Vast, ale bez dostępu do środków.
|
||||
|
||||
Kontrakt między VPS a Vast:
|
||||
- VPS wysyła “features snapshot” (np. statsy DLOB + candles + pozycja).
|
||||
- Vast zwraca “decision” (np. `target_base`, `target_notional_usd`, `side`, `confidence`, `params`).
|
||||
- VPS decyduje o wykonaniu i podpisuje transakcje.
|
||||
|
||||
Zasada: Vast **nigdy** nie dostaje sekretów (RPC keys, prywatne klucze, tokeny).
|
||||
|
||||
### 4) RPC: dedykowany Solana RPC/WS (VPS)
|
||||
Cel: stabilność + mniejsze opóźnienia dla `dlob-publisher` i executora.
|
||||
|
||||
Założenia:
|
||||
- `dlob-publisher` i executor używają tego samego RPC/WS (lub dwóch endpointów tej samej infrastruktury).
|
||||
- W przyszłości możliwy fallback na publiczny endpoint (z ograniczeniami).
|
||||
|
||||
## Desired-state loop (core idea)
|
||||
|
||||
Bot działa jako pętla kontrolna:
|
||||
1) Odczytaj `desired_state` (z DB) + bieżący stan (pozycja, ordery, rynek).
|
||||
2) Policz różnicę (`delta`) i oceń ryzyko/limity.
|
||||
3) Wykonaj minimalny zestaw akcji, żeby przybliżyć się do `desired_state`.
|
||||
4) Zapisz eventy (co zrobił i dlaczego).
|
||||
|
||||
Przykładowe `desired_state` (na start):
|
||||
- `mode`: `off | observe | trade`
|
||||
- `market`: `SOL-PERP` (lub lista)
|
||||
- `target_exposure_usd` albo `target_base`
|
||||
- `max_position_usd`, `max_leverage`, `max_slippage_bps`
|
||||
- `order_policy`: `market | limit | post_only | chase`
|
||||
- `cooldown_ms`, `max_orders_per_min`
|
||||
|
||||
## Kill switch (must-have)
|
||||
|
||||
Kill switch ma działać bez udziału modelu i bez UI.
|
||||
|
||||
Źródła kill switch (kolejność priorytetu):
|
||||
1) **Env var** executora (np. `BOT_KILL_SWITCH=1`) → natychmiastowy “panic mode”.
|
||||
2) Flaga w DB (np. `bot_config.kill_switch=true`).
|
||||
3) Warunek bezpieczeństwa (np. RPC lag, brak danych, brak świeżości L2, zbyt duży drawdown).
|
||||
|
||||
Akcja kill switch (docelowy runbook):
|
||||
- `cancel_all` na rynku (lub subkoncie),
|
||||
- `close_position` (preferowane: reduce-only, kontrola slippage),
|
||||
- przejście bota do `mode=off` + zapis eventu “panic_exit”.
|
||||
|
||||
## Awaria VPS: co się dzieje z transakcjami i jak się zabezpieczyć
|
||||
|
||||
Kluczowe fakty:
|
||||
- **Nie da się “ubić” wysłanej transakcji** na Solanie. Jeśli tx została przyjęta do sieci i wyląduje w bloku, jej efekt jest on-chain.
|
||||
- To, co można zrobić po awarii, to **kolejne transakcje korygujące**: `cancel` / `close` / `reduce-only`.
|
||||
|
||||
W praktyce awarie dzielą się na 3 przypadki:
|
||||
|
||||
1) **Tx nie weszła (dropped/expired)** → po restarcie robisz reconcile i jedziesz dalej.
|
||||
2) **Tx weszła i postawiła order** → po restarcie reconcile zobaczy otwarte ordery i może je utrzymać/zmodyfikować/cancel.
|
||||
3) **Tx weszła i zmieniła pozycję** → po restarcie reconcile zobaczy pozycję i może ją domknąć lub dopasować do desired-state.
|
||||
|
||||
### Reconcile po starcie (must-have)
|
||||
|
||||
Każdy bot po restarcie powinien:
|
||||
- wczytać `bot_config` (desired state),
|
||||
- pobrać stan on-chain (pozycje + otwarte ordery dla rynku/subkonta),
|
||||
- porównać “observed” vs “desired” i wykonać minimalne akcje korekcyjne.
|
||||
|
||||
To chroni przed sytuacją: “wysłaliśmy akcję, VPS padł przed zapisaniem eventu”.
|
||||
|
||||
## Guardian / Safety bot (żeby wyjść mimo padnięcia executora)
|
||||
|
||||
Jeśli wymaganie brzmi: “w każdej chwili możemy wyjść”, to kill switch nie może mieszkać tylko w tym samym VPS/klastrze co executor.
|
||||
|
||||
Proponowany wzorzec:
|
||||
- Osobny, mały serwis **`bot-guardian`** uruchomiony **poza** głównym VPS (drugi VPS / inny klaster / inna VM).
|
||||
- Guardian ma dostęp do:
|
||||
- RPC/WS,
|
||||
- klucza lub uprawnień do cancel/close (patrz uwagi bezpieczeństwa),
|
||||
- sygnału “czy executor żyje” (np. `bot_state.last_heartbeat_at` w DB albo endpoint health).
|
||||
|
||||
Zachowanie:
|
||||
- Jeśli heartbeat executora jest starszy niż `T_fail` (np. 10–30s) → guardian włącza “panic mode”:
|
||||
- `cancel_all_orders`,
|
||||
- `close_position` (reduce-only; z limitem slippage),
|
||||
- zapis eventu `panic_exit` (jeśli ma dostęp do DB).
|
||||
|
||||
Uwaga: guardian nie potrzebuje modelu/AI. Ma być “głupi i niezawodny”.
|
||||
|
||||
### Minimalne bezpieczeństwo guardiana
|
||||
|
||||
- Klucze: trzymane jako secret tylko na hoście guardiana; nie w repo.
|
||||
- Zakres: jeśli to możliwe, ogranicz klucz do subkonta / minimalnych uprawnień (w zależności od możliwości Drift/Solana).
|
||||
- Rate-limit: zabezpieczenie przed pętlą panic (np. `panic_cooldown_ms`).
|
||||
|
||||
## HA executora (opcjonalnie): leader election
|
||||
|
||||
Jeśli chcesz 2 repliki executora (na jednym lub wielu węzłach), potrzebujesz single-writer:
|
||||
- tylko **leader** składa transakcje,
|
||||
- **follower** obserwuje i jest gotowy przejąć.
|
||||
|
||||
Wzorzec:
|
||||
- lease w DB/Redis (np. rekord “lock” z TTL),
|
||||
- leader odnawia lease co N sekund,
|
||||
- brak odnowienia → follower przejmuje i robi reconcile.
|
||||
|
||||
To ogranicza ryzyko “double-trading”, ale nie zastępuje guardiana (bo oba mogą paść razem).
|
||||
|
||||
## Model kosztów (expected vs realized) — entry/exit/modify
|
||||
|
||||
Bot powinien umieć policzyć:
|
||||
- **expected costs** (przed wejściem/wyjściem): czy trade ma sens i czy mieści się w limitach,
|
||||
- **realized costs** (po fakcie): ile faktycznie kosztowało wejście/wyjście + ile “spaliły” modyfikacje.
|
||||
|
||||
W praktyce koszty dzielą się na 3 kubełki:
|
||||
1) koszt mikrostruktury (spread/slippage/market impact),
|
||||
2) fee maker/taker + funding (opcjonalnie),
|
||||
3) koszt tx na Solanie (network fee + priority) oraz koszt modyfikacji (cancel+place).
|
||||
|
||||
### 1) Koszt wejścia/wyjścia z orderbooka (spread/slippage)
|
||||
|
||||
Źródła danych:
|
||||
- `dlob_stats_latest`: `mid_price`, `spread_bps`, `best_bid_price`, `best_ask_price`
|
||||
- `dlob_slippage_latest`: `impact_bps`, `vwap_price`, `fill_pct` dla `size_usd` i `side`
|
||||
|
||||
Warianty:
|
||||
- **Taker/market (lub agresywny limit krzyżujący)**: używaj `impact_bps` z `dlob_slippage_latest` jako “expected execution cost vs mid”.
|
||||
- **Maker/post-only**: policz 2 scenariusze:
|
||||
- “maker-fill”: koszt vs mid wynikający z `limit_price` (zwykle lepszy niż taker),
|
||||
- “fallback-taker”: jeśli nie fill → koszt = `impact_bps` (jak taker).
|
||||
|
||||
Przykładowa definicja kosztu vs mid (bps), jeśli liczysz z cen:
|
||||
- buy: `cost_bps = (fill_price / mid_at_submit - 1) * 10_000`
|
||||
- sell: `cost_bps = (1 - fill_price / mid_at_submit) * 10_000`
|
||||
|
||||
Uwaga: `mid` się rusza, więc to jest miara “kosztu wykonania względem chwilowego środka rynku” (przydatna do strojenia), a nie pełne PnL.
|
||||
|
||||
### 2) Fee maker/taker i funding (opcjonalnie)
|
||||
|
||||
Na MVP trzymaj fee jako parametry konfiguracyjne:
|
||||
- `fee_maker_bps`
|
||||
- `fee_taker_bps`
|
||||
|
||||
Realized fee licz po fill:
|
||||
- `fee_usd = fill_notional_usd * fee_rate_bps / 10_000`
|
||||
|
||||
Funding dla holdów “minuty” zwykle jest mały; można dodać później.
|
||||
|
||||
### 3) Koszt transakcyjny (Solana fee + priority) i koszt modyfikacji
|
||||
|
||||
Fakty:
|
||||
- “modyfikacja” ordera to zazwyczaj **cancel + place** → zwykle 2 transakcje.
|
||||
- Nie da się “ubić” wysłanej transakcji; można tylko wysłać korektę (cancel/close) kolejną tx.
|
||||
|
||||
Definicje:
|
||||
- `tx_fee_usd` = koszt jednej transakcji w USD (network fee + priority fee).
|
||||
- `tx_fee_bps = (tx_fee_usd / notional_usd) * 10_000`
|
||||
|
||||
Koszt modyfikacji (expected):
|
||||
- `expected_modify_cost_usd ≈ expected_reprices * (tx_fee_cancel_usd + tx_fee_place_usd)`
|
||||
- `expected_modify_cost_bps = (expected_modify_cost_usd / entry_notional_usd) * 10_000`
|
||||
|
||||
Źródło `tx_fee_usd`:
|
||||
- na start: stałe estymaty w `bot_config`,
|
||||
- później: estymata “priority fee” z feedu/endpointu + przelicznik SOL→USD.
|
||||
|
||||
### Expected total cost (proponowana metryka bramkująca)
|
||||
|
||||
Na ticku decyzji licz:
|
||||
- `expected_total_cost_bps = execution_bps + fee_bps + tx_bps + expected_modify_bps (+ funding_bps)`
|
||||
|
||||
I bramkuj wejście/wyjście:
|
||||
- `expected_total_cost_bps <= max_expected_total_cost_bps`
|
||||
|
||||
## Parametry kosztów (do `bot_config`) — propozycja
|
||||
|
||||
Minimalny zestaw:
|
||||
- `fee_maker_bps`, `fee_taker_bps`
|
||||
- `max_expected_total_cost_bps`
|
||||
- `max_expected_modify_bps` (opcjonalnie osobny limit na “cancel+place spam”)
|
||||
- `tx_fee_usd_est` (albo `tx_fee_sol_est` + `sol_price_usd_est`)
|
||||
- `expected_reprices_per_entry`
|
||||
|
||||
To działa zarówno dla strategii regułowych, jak i ML (model może dać `urgency`, ale executor i tak liczy koszty).
|
||||
|
||||
## Logowanie kosztów (do `bot_events`) — propozycja
|
||||
|
||||
Żeby stroić progi i ocenić jakość wejść, zapisuj:
|
||||
|
||||
### Na decyzji (przed wysłaniem orderów)
|
||||
- `mid_at_submit`, `spread_bps_at_submit`
|
||||
- `slippage_quote`: `impact_bps`, `vwap_price`, `fill_pct` (dla danego `size_usd`)
|
||||
- `expected_cost_bps_breakdown`: `{ execution_bps, fee_bps, tx_bps, expected_modify_bps, total_bps }`
|
||||
- `order_intent`: `{ policy, limit_price, size_usd, side }`
|
||||
|
||||
### Po fill / wyjściu
|
||||
- `realized_execution_bps` (vs `mid_at_submit`), osobno entry i exit
|
||||
- `realized_fee_usd`, `realized_tx_usd`
|
||||
- `modify_count`: liczba cancel+place w trakcie wejścia/zarządzania
|
||||
- `time_to_fill_ms`, `hold_time_s`
|
||||
|
||||
To daje realną pętlę feedbacku: “koszt vs jakość sygnału” i “czy chase zjada edge”.
|
||||
|
||||
## Obserwowalność i audyt (DB + logi)
|
||||
|
||||
W DB trzymamy:
|
||||
- Konfigurację (`bot_config`) — desired state i limity.
|
||||
- Stan (`bot_state`) — ostatnia decyzja, last heartbeat, ostatni błąd.
|
||||
- Eventy (`bot_events`) — “decision”, “order_sent”, “order_filled”, “panic_exit”, “error”.
|
||||
|
||||
MVP może zacząć od samego `bot_events` + `bot_config`, a resztę dodać po pierwszym działającym loopie.
|
||||
|
||||
## Interfejsy (kontrakty)
|
||||
|
||||
### VPS ↔ Hasura
|
||||
- Executor czyta: `dlob_*_latest` + `drift_ticks`/candles + własne tabele botów.
|
||||
- Executor pisze: `bot_events`, `bot_state` (oraz ewentualnie `bot_orders`).
|
||||
|
||||
### VPS ↔ Vast
|
||||
- HTTP endpoint (lub gRPC) “predict”.
|
||||
- Kontrakty muszą być wersjonowane (`model_version`) i logowane w `bot_events`.
|
||||
|
||||
## Bezpieczeństwo (sekrety)
|
||||
|
||||
Zasady:
|
||||
- Prywatne klucze i tokeny są tylko w K8s Secret (na VPS) i nigdy nie trafiają do repo.
|
||||
- Vast nie dostaje sekretów.
|
||||
- Logi nie mogą wypisywać URL z api-key ani payloadów z sekretami.
|
||||
|
||||
## Deployment (k3s + GitOps)
|
||||
|
||||
Zasady deployu: snapshoty, brak ręcznych zmian na VPS (`doc/workflow.md`).
|
||||
|
||||
Docelowe komponenty w `trade-deploy` (do dopięcia):
|
||||
- `Deployment/bot-executor` (albo `Deployment/bot-<name>`),
|
||||
- `Secret/bot-executor-keys` (klucze/konfiguracja),
|
||||
- NetworkPolicy (opcjonalnie) ograniczająca ruch do Vast i RPC.
|
||||
|
||||
## Plan budowy (iteracyjnie)
|
||||
|
||||
### Etap 0: Observe-only (bez handlu)
|
||||
- `bot_config` + `bot_events`.
|
||||
- Executor subskrybuje `dlob_stats_latest` i loguje “decision” bez transakcji.
|
||||
- Kill switch działa (przełącza tryb i loguje).
|
||||
|
||||
### Etap 1: Paper trading / dry-run
|
||||
- Executor wylicza ordery i zapisuje je do DB, ale nie wysyła na chain.
|
||||
- UI pokazuje “co by zrobił”.
|
||||
|
||||
### Etap 2: Live trading (minimal)
|
||||
- Wejście/wyjście market/limit z restrykcjami ryzyka.
|
||||
- Panic exit: cancel + close.
|
||||
|
||||
### Etap 3: Integracja z modelem (Vast)
|
||||
- Predictor endpoint, wersjonowanie modeli, fallback (jeśli Vast down → observe/off).
|
||||
|
||||
## Otwarte pytania (do uzupełnienia)
|
||||
|
||||
- Czy bot ma działać na jednym rynku PERP czy wielu jednocześnie?
|
||||
- Jaki tryb wejścia/wyjścia: market vs limit vs post-only/chase?
|
||||
- Jakie KPI/limity: max drawdown, max slippage, max position, timeouts?
|
||||
- Jak rozdzielamy konta/subkonta (1 bot = 1 subkonto?) i jak to audytujemy?
|
||||
Reference in New Issue
Block a user