"""EXIT-12 — partial_tp_trail: partial AL TP PIENO, poi il runner CORRE col trail.

Idea (diversa dal ladder 80/20 gia' SCARTATO, che metteva uno stop FISSO alla
soglia 80% del TP): qui il partial avviene AL TP PIENO (tp0, alla media), esce una
frazione q del trade, e il RESIDUO resta SENZA TP, protetto da un trailing
chandelier k*ATR ancorato all'estremo favorevole raggiunto DOPO il partial; il
floor dello stop e' tp0 -> il profitto al livello TP e' LOCKATO (il runner non
puo' mai chiudere peggio di tp0). horizon esteso a 3*mb (cap HARD_CAP) per dare
spazio al runner.

  Fase 1 (pre-partial): exit standard = (tp0, sl0). Al tocco di tp0 esce q.
  Fase 2 (post-partial): TP rimosso. Trail sul residuo:
    Long :  stop(j) = max( tp0, max(high[part..j-1]) - k*atr14[j-1] )  (solo sale)
    Short:  stop(j) = min( tp0, min(low [part..j-1]) + k*atr14[j-1] )  (solo scende)
  floor = tp0 -> profitto lockato al livello TP, MAI peggio.

ANTI-LOOK-AHEAD: levels(j) usa SOLO dati <= j-1:
  - estremo favorevole post-partial sullo slice [part .. j-1] (mantenuto
    incrementalmente, aggiornato col bar j-1 prima di calcolare lo stop in j);
  - atr14[j-1] (indice causale).
  on_partial(j) registra solo l'indice del bar di partial (j) e il prezzo (tp0):
  l'estremo della fase 2 parte da high/low del bar di partial j (<= j, ma il primo
  bar valutato in fase 2 e' j+1, slice [j..j] noto al poll di j+1 -> causale).

GRID: q in {0.5, 0.7} x k in {2.0, 3.0}  (4 celle). tp_frac=q.
"""
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

from exit_lab import ExitPolicy, evaluate, HARD_CAP  # noqa: E402


class PartialTpTrail(ExitPolicy):
    name = "partial_tp_trail"

    def __init__(self, ctx, i, d, entry, tp0, sl0, mb, q=0.5, k=3.0, **params):
        super().__init__(ctx, i, d, entry, tp0, sl0, mb, **params)
        self.q = float(q)
        self.k = float(k)
        self.horizon = min(3 * mb, HARD_CAP)
        self.high = ctx["high"]
        self.low = ctx["low"]
        self.atr = ctx["atr14"]
        # stato fase 2 (post-partial)
        self.partial_idx = None        # bar in cui e' avvenuto il partial (None = fase 1)
        self.fav_high = None           # estremo favorevole sullo slice [partial..j-1]
        self.fav_low = None
        self._last_seen = None         # ultimo indice incorporato nell'estremo
        self.cur_stop = None           # stop trailing fase 2, floor=tp0, monotono

    def levels(self, j):
        # ---- Fase 1: pre-partial -> exit standard (tp0, sl0), esce frazione q al TP
        if self.partial_idx is None:
            return self.tp0, self.sl0, self.q

        # ---- Fase 2: post-partial -> TP rimosso, trail chandelier floor=tp0
        h, l, atr, d = self.high, self.low, self.atr, self.d
        # incorpora i bar fino a j-1 (dati causali, gia' chiusi al poll del bar j)
        while self._last_seen < j - 1:
            self._last_seen += 1
            if h[self._last_seen] > self.fav_high:
                self.fav_high = h[self._last_seen]
            if l[self._last_seen] < self.fav_low:
                self.fav_low = l[self._last_seen]
        a = atr[j - 1]
        if a != a:                     # NaN -> resta sullo stop corrente
            return None, self.cur_stop, 1.0
        if d == 1:
            cand = self.fav_high - self.k * a
            if cand > self.cur_stop:   # lo stop long puo' solo SALIRE (stringersi)
                self.cur_stop = cand
        else:
            cand = self.fav_low + self.k * a
            if cand < self.cur_stop:   # lo stop short puo' solo SCENDERE
                self.cur_stop = cand
        return None, self.cur_stop, 1.0

    def on_partial(self, j, price, remaining):
        # entra in fase 2: ancora l'estremo al bar di partial j (high/low[j] sono
        # noti al poll del bar j+1, primo bar valutato in fase 2). floor=tp0.
        self.partial_idx = j
        self.fav_high = self.high[j]
        self.fav_low = self.low[j]
        self._last_seen = j
        self.cur_stop = self.tp0       # profitto lockato al livello TP, MAI peggio


GRID = [
    {"q": q, "k": k}
    for q in (0.5, 0.7)
    for k in (2.0, 3.0)
]


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