"""EXIT-07 — TP dinamico che DECADE col tempo (verso breakeven).

PRIOR (vincolante, ieri SCARTATO un ladder 80/20): il TP delle fade sta alla
MEDIA, e li' il movimento e' esaurito -> il prezzo spesso NON arriva al TP e il
trade muore a max_bars (close) o sullo SL. Idea di questa famiglia: invece di
tenere il TP fisso al target ambizioso (la media), farlo DECADERE col passare
delle barre verso un target piu' modesto (breakeven che copre le fee, o meta'
strada). Cosi' un trade che s'e' avvicinato ma non ha toccato il TP pieno puo'
essere chiuso in profitto/pareggio prima di scadere a max_bars potenzialmente
in perdita. SL FISSO (sl0) invariato. horizon = max_bars invariato.

TP attivo nel bar j:
    frac(j) = clamp( ((j - i) / mb) * speed , 0, 1 )
    tp(j)   = tp0 + frac(j) * (target_fin - tp0)
  - frac dipende SOLO da j (indice di tempo) e da costanti note all'entrata
    (i, mb, speed) -> NESSUN dato > j-1 -> anti-look-ahead OK per costruzione.
  - long  (d=+1): tp0 > entry; target_fin <= tp0 -> il TP SCENDE verso entry.
  - short (d=-1): tp0 < entry; target_fin >= tp0 -> il TP SALE verso entry.
  - speed>1: il TP raggiunge il target finale prima della fine (clamp a 1).

target_fin (param "target"):
  - "breakeven": entry*(1 + d*0.003) -> copre 0.3% (~ fee 0.10%RT + margine).
    Per long sta sopra entry, per short sotto: chiudere li' NON e' in perdita.
  - "halfway"  : (entry + tp0)/2 -> meta' strada fra entry e il TP pieno.

GRID: speed in {0.5, 1.0, 1.5} x target in {breakeven, halfway} (6 celle).
"""
import sys
from pathlib import Path

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


class TpDecay(ExitPolicy):
    name = "tp_decay"

    def __init__(self, ctx, i, d, entry, tp0, sl0, mb, **params):
        super().__init__(ctx, i, d, entry, tp0, sl0, mb, **params)
        self.speed = float(params.get("speed", 1.0))
        target = str(params.get("target", "breakeven"))
        if target == "breakeven":
            self.target_fin = entry * (1.0 + d * 0.003)
        elif target == "halfway":
            self.target_fin = 0.5 * (entry + tp0)
        else:
            raise ValueError(f"target sconosciuto: {target}")
        # mb puo' essere 0 in teoria; protezione
        self.mb_eff = max(int(mb), 1)

    def levels(self, j: int):
        # frac dipende SOLO dal tempo (j - i), mb, speed: costanti note all'entrata
        frac = ((j - self.i) / self.mb_eff) * self.speed
        if frac < 0.0:
            frac = 0.0
        elif frac > 1.0:
            frac = 1.0
        tp = self.tp0 + frac * (self.target_fin - self.tp0)
        return tp, self.sl0, 1.0


GRID = [
    {"speed": 0.5, "target": "breakeven"},
    {"speed": 1.0, "target": "breakeven"},
    {"speed": 1.5, "target": "breakeven"},
    {"speed": 0.5, "target": "halfway"},
    {"speed": 1.0, "target": "halfway"},
    {"speed": 1.5, "target": "halfway"},
]


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