"""EXIT-13 — hurst_exit: TP condizionato al REGIME (rolling-Hurst causale).

IDEA. Le fade puntano alla MEDIA: in regime ANTI-PERSISTENTE (Hurst basso) la
reversione tende a completarsi -> ha senso tenere il TP pieno tp0. In regime
PERSISTENTE/trending (Hurst alto) la reversione spesso NON arriva fino in fondo
(il movimento continua, lo stop-loss si concentra li': vedi loss-guard Hurst) ->
conviene "prendere quel che c'e'": TP anticipato a meta' strada (entry+tp0)/2.

  - H[j-1] <  h_lo  (anti-persistente): tp(j) = tp0    (reversione completa)
  - H[j-1] >= h_lo  (persistente):      tp(j) = (entry+tp0)/2  (TP a meta')

SL FISSO (sl0) e horizon (max_bars) invariati.

ANTI-LOOK-AHEAD. prepare() precalcola H = rolling_hurst(close, window=100, step=6)
una volta sullo sleeve. rolling_hurst e' causale: H[k] usa returns[k-window:k] e
returns[m]=diff(log(close))[m] dipende da close[m+1], quindi H[k] dipende solo da
close <= k. In levels(j) leggo H[j-1] -> solo dati <= j-1. SL/horizon invariati.
La decisione del regime e' fissata col bar precedente, il bar j tocca i livelli.

NB sul TP a meta'. Lo "step=6" significa che H e' costante a tratti di 6 barre; il
regime e' ri-letto ogni bar (j-1) ma cambia valore ogni 6. Il TP a meta' strada
e' SEMPRE >= breakeven per costruzione (e' fra entry e tp0, e tp0 e' gia' oltre il
margine fee per come la fade lo fissa), quindi non maschera uscite in perdita.

GRID: h_lo in {0.45, 0.50, 0.55} (3 celle).
"""
import sys
from pathlib import Path

import numpy as np

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from exit_lab import ExitPolicy, evaluate  # noqa: E402

sys.path.insert(0, str(Path(__file__).resolve().parents[3]))
from src.fractal.indicators import rolling_hurst  # noqa: E402


class HurstExit(ExitPolicy):
    name = "hurst_exit"

    @classmethod
    def prepare(cls, ctx, **params):
        if "hurst100" not in ctx:
            c = ctx["close"]
            ctx["hurst100"] = rolling_hurst(c, window=100, step=6)

    def __init__(self, ctx, i, d, entry, tp0, sl0, mb, **params):
        super().__init__(ctx, i, d, entry, tp0, sl0, mb, **params)
        self.h = ctx["hurst100"]
        self.h_lo = float(params.get("h_lo", 0.50))
        self.tp_half = (entry + tp0) / 2.0  # TP a meta' strada (persistente)

    def levels(self, j: int):
        # SOLO dati <= j-1
        hv = self.h[j - 1]
        if not np.isfinite(hv):
            return self.tp0, self.sl0, 1.0
        if hv < self.h_lo:
            tp = self.tp0          # anti-persistente: reversione completa
        else:
            tp = self.tp_half      # persistente: prendi la meta'
        return tp, self.sl0, 1.0


GRID = [
    {"h_lo": 0.45},
    {"h_lo": 0.50},
    {"h_lo": 0.55},
]


if __name__ == "__main__":
    evaluate(HurstExit, GRID)
