Skip to content

Poprawki przed live: S-5, S-8, precyzja cen, potwierdzenie usuwania - #37

Merged
mbeczynski merged 7 commits into
mainfrom
trader/poprawki-przed-live
Oct 18, 2026
Merged

mbeczynski merged 7 commits into
mainfrom
trader/poprawki-przed-live

Conversation

@mbeczynski

@mbeczynski mbeczynski commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Cztery znaleziska z audytu kodu 09-10.10, które blokują live (renacode-infra → docs/projekty/trader/08-przed-go-live.md, sekcja „Znaleziska audytu kodu”). N-2 (TOTP przy przepływach), S-6 i S-7 świadomie pominięte — to osobne decyzje.

Każda poprawka ma test regresyjny, który sprawdzono na czerwono: po cofnięciu poprawki test pada, po przywróceniu przechodzi.

S-5 — idempotencja zleceń sprawdza symbol, stronę i ilość

Wada: gdy giełda znała zlecenie o naszym orderLinkId, kod przyjmował je jako złożone (juz_istnialo=True). Nie sprawdzał, czy to w ogóle zlecenie z planu.

Zmiana:

  • StanZlecenia dostaje pola symbol i strona, wypełniane z tej samej odpowiedzi giełdy (Bybit symbol/side, Alpaca symbol/side).
  • Nowa funkcja base.sprawdz_zastane. Zastane zlecenie jest uznane za nasze tylko wtedy, gdy symbol i strona są równe planowi, a ilość mieści się w 0 < ilość ≤ plan. Każdy rozjazd, a także brak symbolu lub strony, daje ExchangeError(code="tozsamosc-zlecenia", wymaga_czlowieka=True) bez POST-a i bez zapisu.
  • Sprawdzenie stoi w zloz_plan zaraz po odczycie stanu, więc obejmuje obie gałęzie: juz_istnialo i „giełda zna to zlecenie mimo odmowy”. Jest też w obu adapterach (zloz_zlecenie bez istnienie_sprawdzone, w Alpace także w ścieżce 422).
  • Ilość jest sprawdzana jako „nie więcej niż w planie”, a nie „równa”. Pod tym samym id może leżeć zlecenie zastępcze na mniejszą ilość, które odmowa_gieldy składa, gdy rachunek potwierdza mniej.

Testy: tests/test_egzekucja.py (adapter Bybita: inna strona, inny symbol, większa ilość, brak tożsamości oraz kanarek na mniejszą ilość) i tests/test_gielda_exec.py (zloz_plan, 8 przypadków oraz kanarek zgodnego zlecenia). Bez poprawki pada 12 z 14 testów; przechodzą tylko 2 kanarki. Atrapy w 3 istniejących testach dostały symbol i stronę.

S-8 — funding z limitem wieku

Wada: compute_funding przenosił ostatnią stawkę na każdą kolejną świecę bez względu na jej wiek. Po zatrzymaniu pobierania funding_rate i funding_z wyglądały na świeże.

Zmiana:

  • features.FUNDING_WIEK_MAX = 16 h (dwa interwały ogłoszenia Bybita). Starsza stawka nie trafia ani do cechy, ani do okna z-score: świeca dostaje brak wartości.
  • Potok cech wypisuje jedną linię UWAGA … funding PRZETERMINOWANY na N świecach (najstarsza stawka X h, limit 16 h).
  • Czujka dozor.py --co swiezosc pyta też o market.funding_rate, z progiem równym temu samemu limitowi. Dotyczy tylko silników, które mają instrumenty z meta->>'funding_from', więc equity nie dostaje „brak danych”. ocen_swiezosc przyjmuje opcjonalny prog_min w wierszu. Zapytanie sprawdzone na prawdziwym Postgresie (timescaledb-ha:pg17).

Testy: tests/test_funding_wiek.py: stojące pobieranie, kanarek ze świeżymi stawkami, granica 16/17 h, czujka (próg i zapytanie). Bez poprawki padają oba testy cechy; kanarek przechodzi.

Do zrobienia po scaleniu: zmiana dotyka cech, więc wynik trenera trzeba zmierzyć przed i po (kryterium zamknięcia w 08-przed-go-live.md). Przy zdrowym pobieraniu limit nie zabiera żadnej świecy (kanarek). Zmienić się może tylko historia z lukami w fundingu powyżej 16 h, jeśli ktoś przeliczy cechy wstecz.

Poprawka z recenzji: czujka liczy wiek fundingu dla każdego instrumentu osobno (tylko aktywne) i alarmuje według najstarszego, z listą nieświeżych instrumentów w treści. Wcześniej max(event_time) po wszystkich perpach przykrywało przestój jednego: BTC sprzed 2 h zasłaniał SOL sprzed 3 dni. Alarm jest jeden, a nie jeden na instrument, bo padnięty zbieracz to jedna awaria. Test na prawdziwej bazie z dwoma instrumentami (test_czujka_fundingu_liczy_wiek_PER_INSTRUMENT_na_prawdziwej_bazie) na starym zapytaniu pada: wiek 120 min zamiast ~72 h.

Znana luka trening–produkcja (bez naprawy w tym PR): wiersze feat.feature_value (funding_rate, funding_z) policzone przed tą zmianą zostają w historii z wypełnieniem w przód, także przez przerwy w fundingu dłuższe niż 16 h. Trener uczy się więc na wartościach, których produkcja w tej samej sytuacji już nie dostanie (dostanie brak). Domknięcie wymaga przeliczenia cech wstecz (--backfill) i pomiaru trenera przed i po.

Zapytanie dla właściciela (tylko SELECT): przerwy dłuższe niż 16 h w market.funding_rate, czyli miejsca, gdzie stara historia cech ma przeterminowane wartości:

SELECT i.symbol, x.instrument_id,
       x.poprzedni, x.event_time AS nastepny,
       x.event_time - x.poprzedni AS przerwa
  FROM (SELECT instrument_id, event_time,
               lag(event_time) OVER (PARTITION BY instrument_id ORDER BY event_time) AS poprzedni
          FROM market.funding_rate) x
  JOIN ref.instrument i ON i.id = x.instrument_id
 WHERE x.event_time - x.poprzedni > interval '16 hours'
 ORDER BY przerwa DESC
 LIMIT 100;

Pusty wynik znaczy, że stara historia cech nie ma przeterminowanych wartości i luka jest czysto teoretyczna. Nie widać tu natomiast przerwy po OSTATNIEJ stawce, ale tę zgłasza teraz czujka.

Ceny ucięte do 2 miejsc

Wada: limit, cel i średnia cena szły przez fmtMoney (2 miejsca), więc 0.012345 wyświetlało się jako „0,01”.

Zmiana: fmtPrice w web/src/utils/format.js: co najmniej 6 cyfr znaczących, nie mniej niż 2 miejsca po przecinku. Użyty w Moves.jsx (limit, cel, średnia cena) i Overview.jsx (planowane zlecenia, pozycje). API nie podaje tu kroku ceny instrumentu, więc zastosowano wariant „co najmniej 6 cyfr znaczących” z kryterium zamknięcia.

Testy: stend UI, scenariusz 7 (0.012345 i 0.4567). Na buildzie z origin/main 4 błędy, na tej gałęzi zielono.

Ilości (dołożone): fmtNum(qty, 4) ucinało małe ilości drogich instrumentów (0.000123 BTC → „0.0001”). fmtQty używa tej samej reguły 6 cyfr znaczących, bez minimum miejsc po przecinku, i ucina zamiast zaokrąglać (roundingMode: 'trunc'): 0.9999996 pokazuje się jako 0,999999, a nie „1”. Zmiana obejmuje planowane zlecenia, pozycje, plany i transakcje. W scenariuszu 7 stendu są 2 nowe asercje, które na poprzednim buildzie padają.

„Usuń” bez potwierdzenia

Wada: jedno kliknięcie wysyłało DELETE przy kluczu giełdy, kluczu usługi i portfelu.

Zmiana: nowy komponent web/src/components/Potwierdzenie.jsx, okno w aplikacji, a nie natywne confirm():

  • portal do <body>, bo .glass-card ma backdrop-filter i przycinałby position: fixed;
  • role="alertdialog", fokus ustawiany na „Anuluj” tylko przy otwarciu, a Escape i kliknięcie w tło zamykają okno;
  • okno nazywa usuwany obiekt;
  • style dopisane w index.css.

DELETE idzie dopiero po kliknięciu „Usuń klucz” / „Usuń portfel”.

Testy: stend UI, scenariusz 8, dla każdego z trzech obiektów:

  • pierwsze kliknięcie nie wysyła DELETE;
  • okno nazywa obiekt;
  • fokus jest na „Anuluj”;
  • Escape i „Anuluj” zamykają okno bez DELETE;
  • potwierdzenie wysyła dokładnie jedno DELETE na właściwy adres;
  • nie pojawia się żadne natywne okno dialogowe.

Na buildzie z origin/main 5 błędów, a dalej stend przerywa się na braku okna.

Wyniki

  • ruff check .: czysto.
  • python -B -m pytest tests/ z TEST_DB_URL (lokalny timescaledb-ha:pg17 z migracjami): 3791 passed, 0 skipped (po poprawkach z recenzji).
  • npm ci && npm run build (web): OK.
  • node tests/stend_ui.spec.mjs: PRZESZEDŁ (wszystkie scenariusze 1-8).

Uwaga: stend i test dymny chodzą tylko w docker-publish.yml, czyli dopiero po pushu na main. Na PR uruchamia się wyłącznie pytest.

🤖 Generated with Claude Code

https://claude.ai/code/session_017S4D5yYFQmJsfcLx14gnjE

mbeczynski and others added 7 commits October 18, 2026 12:28
Idempotencja po samym orderLinkId przyjmowala JAKIEKOLWIEK zlecenie o tym id
jako zlozone. StanZlecenia niesie teraz symbol i strone z odpowiedzi gieldy,
a `sprawdz_zastane` porownuje symbol, strone i ilosc (nie wiecej niz plan)
w zloz_plan oraz w obu adapterach. Rozjazd albo brak tozsamosci daje
ExchangeError(wymaga_czlowieka=True), bez POST-u i bez zapisu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017S4D5yYFQmJsfcLx14gnjE
…a w przod (S-8)

compute_funding przenosil ostatnia stawke na kazda swiece bez wzgledu na wiek,
wiec po zatrzymaniu pobierania funding_rate/funding_z wygladaly na swieze.
Stawka starsza niz FUNDING_WIEK_MAX (2 interwaly ogloszenia) nie trafia ani do
cechy, ani do okna z-score; potok wypisuje UWAGA z liczba swiec i wiekiem.
Czujka `dozor --co swiezosc` dostaje market.funding_rate z tym samym progiem
(tylko dla silnikow z funding_from).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017S4D5yYFQmJsfcLx14gnjE
Limit, cel i srednia cena szly przez fmtMoney (2 miejsca), wiec instrument
ponizej 1 USDC tracil wiekszosc cyfr: 0.012345 -> "0,01". Nowy fmtPrice daje
co najmniej 6 cyfr znaczacych i nie mniej niz 2 miejsca. Stend UI, scenariusz 7.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017S4D5yYFQmJsfcLx14gnjE
…erdzenia

Jedno klikniecie wysylalo DELETE. Nowe okno aplikacji (Potwierdzenie.jsx,
portal do body, alertdialog, fokus na "Anuluj", Escape i klik w tlo zamykaja)
nazywa usuwany obiekt; DELETE idzie dopiero po jawnym potwierdzeniu. Bez
natywnego confirm(). Stend UI, scenariusz 8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017S4D5yYFQmJsfcLx14gnjE
`fmtNum(qty, 4)` ucinalo male ilosci drogich instrumentow: 0.000123 BTC
-> "0.0001". fmtQty dzieli z fmtPrice regule 6 cyfr znaczacych, bez minimum
miejsc. Planowane zlecenia, pozycje, plany i transakcje. Stend UI, scenariusz 7.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017S4D5yYFQmJsfcLx14gnjE
- dozor: wiek fundingu PER INSTRUMENT, alarm z najstarszego z lista
  nieswiezych (aktywne instrumenty); max() po wszystkich perpach przykrywal
  przestoj jednego. Test na prawdziwej bazie z dwoma instrumentami.
- fmtQty: roundingMode 'trunc' - 0.9999996 nie pokazuje „1".
- Potwierdzenie: fokus tylko przy otwarciu, osobno od obslugi Escape.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017S4D5yYFQmJsfcLx14gnjE
@mbeczynski
mbeczynski merged commit 57cf09d into main Oct 18, 2026
1 check passed
@mbeczynski
mbeczynski deleted the trader/poprawki-przed-live branch October 18, 2026 11:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant