"""Helper condivisi sulle barre OHLCV live (lezione EXIT-16, 2026-06-05). La riga -1 di un df di candele e' la candela IN FORMAZIONE finche' non e' trascorsa la sua durata: valutare segnali/exit-confirm su quella riga reintroduce la wick-sensitivity che EXIT-16 elimina (audit live: 2 stop su 3 del crash ETH erano wick-stop sulla barra in corso). Questa e' l'UNICA implementazione della detection — prima era copiata in 4 punti (strategy_worker, basket_trend_worker, pairs_worker, runner._check_stale_feed) con una variante hardcoded a 1h: una correzione applicata a una copia e dimenticata nelle altre reintrodurrebbe il bug in silenzio. """ from __future__ import annotations import time import numpy as np def bar_ms_of(ts_ms, window: int = 50) -> int: """Durata stimata della barra: mediana dei diff degli ultimi `window` timestamp (ms). 0 se la serie e' troppo corta per stimarla.""" ts = np.asarray(ts_ms, dtype="int64") if len(ts) < 2: return 0 return int(np.median(np.diff(ts[-window:]))) def last_bar_is_forming(ts_ms, now_ms: int | None = None) -> bool: """True se la riga -1 e' la candela IN CORSO (now < ts[-1] + durata barra).""" ts = np.asarray(ts_ms, dtype="int64") if len(ts) == 0: return False bar = bar_ms_of(ts) if not bar: return False if now_ms is None: now_ms = int(time.time() * 1000) return now_ms < int(ts[-1]) + bar def last_settled_idx(ts_ms, now_ms: int | None = None) -> int: """Indice dell'ultima barra COMPLETATA: -1 se la riga -1 e' chiusa, -2 se e' in formazione.""" return -2 if last_bar_is_forming(ts_ms, now_ms) else -1