docs(snapshot): sync workspace documentation

This commit is contained in:
u1
2026-03-29 13:14:30 +02:00
parent 93a6e4aa2f
commit 0d3d110026
37 changed files with 8500 additions and 123 deletions

240
observer-sol-v1.md Normal file
View File

@@ -0,0 +1,240 @@
# SOL-PERP Observer V1
Ten dokument definiuje pierwszy działający observer bota dla `SOL-PERP`.
Celem nie jest jeszcze trading, tylko:
- stabilny loop `1s`
- powtarzalny snapshot feature'ów
- decyzja `long|flat|short`
- heartbeat i audyt w `bot_state` / `bot_events`
## Zakres
- rynek: `SOL-PERP`
- tryb: `observe`
- źródło danych v1: `Hasura`
- bez kluczy prywatnych
- bez składania transakcji
## Architektura
- `bot_config` pozostaje desired state i źródłem parametrów
- `bot-observer` czyta `bot_config`, pobiera snapshot rynku, liczy feature'y i zapisuje wynik
- `bot_state` przechowuje heartbeat, ostatni błąd i ostatni wynik loopa
- `bot_events` przechowuje append-only audyt decyzji i błędów
## Źródła danych
Observer czyta z Hasury:
- `get_drift_candles(...)`
- `dlob_hot_derived_latest` dla hot marketów z fallbackiem do `dlob_all_derived_latest`
Depth bands i slippage nie są już czytane z osobnych legacy tabel.
Observer liczy je lokalnie z `bids_norm` / `asks_norm` nowego derived read-model.
Wersja v1 zakłada małe query tylko dla jednego rynku.
## Parametry w `bot_config.params`
Minimalny kontrakt:
```json
{
"strategy": {
"type": "predictive_observer_v1"
},
"loop": {
"decision_interval_ms": 1000,
"candle_bucket_seconds": 1,
"candles_limit": 64
},
"features": {
"mom_fast_s": 3,
"mom_mid_s": 10,
"mom_slow_s": 30,
"vol_window_s": 30,
"depth_band_bps": 10,
"slippage_size_usd": 500
},
"gates": {
"freshness_max_ms": 800,
"spread_max_bps": 8,
"slippage_max_bps": 12,
"depth_band_min_usd": 3000
},
"sizing": {
"target_notional_usd": 500
},
"decision": {
"threshold": 1.2,
"horizon_s": 60
},
"scoring": {
"weights": {
"mom_fast": 0.9,
"mom_mid": 0.35,
"mom_slow_reversal": 0.55,
"imbalance": 5.0,
"mark_vs_oracle": 0.08,
"spread": 0.15,
"slippage": 0.12
}
}
}
```
## Feature Snapshot
Na każdy tick observer liczy:
- `mark_price`
- `oracle_price`
- `mid_price`
- `spread_bps`
- `stats_imbalance`
- `depth_bid_usd`
- `depth_ask_usd`
- `depth_imbalance`
- `buy_slippage_bps`
- `sell_slippage_bps`
- `mark_vs_oracle_bps`
- `mom_3s`
- `mom_10s`
- `mom_30s`
- `vol_30s`
- `data_age_ms`
## Gates
Jeśli którykolwiek gate nie przechodzi, observer nie produkuje sygnału kierunkowego i zapisuje event `skip`.
Wersja v1:
- `data_age_ms <= freshness_max_ms`
- `spread_bps <= spread_max_bps`
- `max(buy_slippage_bps, sell_slippage_bps) <= slippage_max_bps`
- `min(depth_bid_usd, depth_ask_usd) >= depth_band_min_usd`
## Model decyzji
V1 nie używa ML. To baseline scoring do zebrania danych i benchmarku.
Long side:
```text
long_score =
mom_fast * w_mom_fast
+ mom_mid * w_mom_mid
- mom_slow * w_mom_slow_reversal
+ depth_imbalance * w_imbalance
- mark_vs_oracle_bps * w_mark_vs_oracle
- spread_bps * w_spread
- buy_slippage_bps * w_slippage
```
Short side jest lustrzanym odbiciem long side.
Decyzja:
- jeśli gates fail -> `flat`
- jeśli `max(abs(long_score), abs(short_score)) < threshold` -> `flat`
- w przeciwnym razie `long` albo `short`
## Event `decision`
```json
{
"version": "observer-sol-v1",
"bot": {
"id": "uuid",
"name": "sol-observer",
"mode": "observe",
"market_name": "SOL-PERP",
"kill_switch": false
},
"runtime": {
"service": "bot-observer",
"observe_only": true,
"loop_ms": 1000,
"query_latency_ms": 82,
"loop_latency_ms": 96
},
"snapshot": {
"data_age_ms": 214,
"stats_updated_at": "2026-03-23T20:12:31.112Z",
"depth_updated_at": "2026-03-23T20:12:31.112Z",
"buy_slippage_updated_at": "2026-03-23T20:12:31.112Z",
"sell_slippage_updated_at": "2026-03-23T20:12:31.112Z"
},
"features": {
"mom_3s": 3.8,
"mom_10s": 6.1,
"mom_30s": -12.5,
"vol_30s": 5.9,
"spread_bps": 2.1,
"depth_imbalance": 0.18,
"buy_slippage_bps": 4.2,
"sell_slippage_bps": 4.0,
"mark_vs_oracle_bps": -1.3
},
"gates": {
"fresh": true,
"spread_ok": true,
"slippage_ok": true,
"depth_ok": true
},
"decision": {
"side": "long",
"confidence": 0.72,
"long_score": 1.91,
"short_score": -2.34,
"target_notional_usd": 500,
"horizon_s": 60
}
}
```
## Event `skip`
Observer zapisuje `skip`, gdy:
- dane są stale
- brakuje wymaganych metryk
- gates nie przechodzą
- bot jest w trybie innym niż aktywny observe loop
Payload `skip` ma ten sam nagłówek co `decision`, ale z polem:
```json
{
"skip_reason": "gate_failed:freshness"
}
```
## `bot_state.state`
V1 zapisuje w `bot_state.state` znormalizowany stan loopa:
- `service`
- `version`
- `observe_only`
- `market_name`
- `mode`
- `status`
- `last_decision`
- `last_features`
- `last_gates`
- `last_snapshot`
- `counters`
## Zmienne środowiskowe
Najważniejsze:
- `BOT_ID` albo `BOT_NAME`
- `HASURA_GRAPHQL_URL`
- `HASURA_ADMIN_SECRET`
- `DLOB_SOURCE` opcjonalnie, jeśli chcesz zawęzić derived read-model do konkretnego `source`
- `CANDLES_SOURCE` opcjonalnie, jeśli chcesz zawęzić candles do konkretnego źródła ticków
- `OBSERVER_PORT` opcjonalnie, domyślnie `8791`
## Cel wersji v1
V1 ma odpowiedzieć na trzy pytania:
- czy Hasura daje stabilny loop `1s` dla jednego rynku
- czy feature snapshot jest wystarczająco świeży
- czy baseline scoring daje sensowny materiał do dalszego strojenia i późniejszego ML