fix: LabelGenerator _classify_bar — simulation LONG/SHORT indépendante

Bug : quand le SL LONG était touché, la fonction retournait 0 (NEUTRAL)
immédiatement sans évaluer les conditions SHORT. Résultat : 0 labels SHORT
sur 12230 barres, modèle inutilisable pour signaux SHORT.

Fix : deux boucles indépendantes (LONG et SHORT) qui évaluent chacune
leur propre TP/SL. Si les deux gagnent, priorité au premier résolu.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Tika
2026-03-08 22:22:27 +00:00
parent 8f3b026f82
commit daea333555

View File

@@ -140,30 +140,59 @@ class LabelGenerator:
sl_short: float, sl_short: float,
) -> int: ) -> int:
""" """
Parcourt les barres futures bar par bar et retourne le label. Simule LONG et SHORT de façon indépendante sur les barres futures.
Vérifie HIGH pour TP LONG et LOW pour SL LONG (et inversement pour SHORT).
LONG et SHORT sont deux trades hypothétiques distincts : le SL du LONG
(prix baisse) ne signifie pas que le SL du SHORT (prix monte) est touché.
Les deux simulations sont donc parcourues séparément pour éviter de
manquer les signaux SHORT quand le prix descend.
Retourne le label du trade gagnant qui se résout en premier :
1 (LONG), -1 (SHORT) ou 0 (NEUTRAL).
""" """
for _, bar in future.iterrows(): # --- Simulation LONG indépendante ---
# LONG : TP atteint ? long_win_idx = None
if bar['high'] >= tp_long and bar['low'] > sl_long: long_lose_idx = None
for idx, (_, bar) in enumerate(future.iterrows()):
tp_hit = bar['high'] >= tp_long
sl_hit = bar['low'] <= sl_long
if tp_hit and sl_hit:
long_lose_idx = idx # Barre ambiguë → perte
break
if tp_hit:
long_win_idx = idx
break
if sl_hit:
long_lose_idx = idx
break
# --- Simulation SHORT indépendante ---
short_win_idx = None
short_lose_idx = None
for idx, (_, bar) in enumerate(future.iterrows()):
tp_hit = bar['low'] <= tp_short
sl_hit = bar['high'] >= sl_short
if tp_hit and sl_hit:
short_lose_idx = idx # Barre ambiguë → perte
break
if tp_hit:
short_win_idx = idx
break
if sl_hit:
short_lose_idx = idx
break
long_won = long_win_idx is not None
short_won = short_win_idx is not None
if long_won and not short_won:
return 1 return 1
# LONG : SL atteint en premier ? if short_won and not long_won:
if bar['low'] <= sl_long:
# Vérifie si TP atteint le même bar (candle ambiguë)
if bar['high'] >= tp_long:
return 0 # Ambigu → neutre
return 0 # SL touché → pas de LONG
# SHORT : TP atteint ?
if bar['low'] <= tp_short and bar['high'] < sl_short:
return -1 return -1
# SHORT : SL atteint en premier ? if long_won and short_won:
if bar['high'] >= sl_short: # Les deux trades seraient gagnants : prendre celui qui se résout en premier
if bar['low'] <= tp_short: return 1 if long_win_idx <= short_win_idx else -1
return 0 return 0 # Aucun TP atteint dans l'horizon
return 0
return 0 # Ni TP ni SL atteint dans l'horizon
@staticmethod @staticmethod
def _log_distribution(labels: pd.Series) -> None: def _log_distribution(labels: pd.Series) -> None: