#!/usr/bin/env python
"""
Patch sltp_engine.py VPS Charter - FIX Charter V3 trailing rules.

REGOLE Charter V3 unificate (UGUALI per VPTR3, RETTANGOLO_SIMPLE, MA_TRAILING):
  - PnL < 1%        -> HOLD: nessuna azione (no SL mosso). Default = hard cap Charter -3% o nessun SL
  - PnL >= 1%       -> pre_trailing ON. SL a BREAKEVEN (entry price)
  - PnL >= 1.5%     -> trigger trailing ON. Lock-in step list progressiva
  - HWM             -> SL non peggiora MAI (per LONG solo sale, per SHORT solo scende)
  - PnL <= -3%      -> CLOSE_FULL (hard cap Charter)
  - PnL >= +5%      -> CLOSE_PARTIAL 50% (TP1)
  - PnL >= +3% dopo TP1 -> CLOSE_FULL (TP finale)
  - PnL < 0% tra -3% e 0% -> no SL mosso (rischio controllato da hard cap -3%)

FIX BUG: il codice attuale applica "SL = current * 1.015" (per short) a QUALSIASI PnL > 0,
anche a PnL 0.5% (sotto pre-trailing 1%), generando SL in PERDITA sopra entry.
"""
import re
import sys
from pathlib import Path

FILE = "/opt/charter-live/live_deploy_v2/sltp_engine.py"

# === NUOVO BLOCCO TRAILING (sostituisce righe 562-598) ===
NEW_TRAILING_BLOCK = '''    # 4. === MATTIA 16/08 FIX Charter V3 unified trailing rules ===
    # REGOLE Charter V3 (uguali per VPTR3, RETTANGOLO_SIMPLE, MA_TRAILING):
    #   - PnL < 1%    -> HOLD (no SL mosso, mantieni default o hard cap Charter -3%)
    #   - PnL >= 1%   -> pre_trailing attivo, SL a BREAKEVEN (entry)
    #   - PnL >= 1.5% -> trigger trailing, lock-in step list progressiva (0.5/1/2/3%)
    #   - HWM         -> SL non peggiora MAI (LONG sale, SHORT scende)
    # FIX BUG precedente: applicava "SL = current * (1 +/- 0.015)" a QUALSIASI PnL > 0,
    # generando SL in PERDITA sopra entry quando PnL era tra 0% e 1% (sotto pre-trailing).
    if pnl_pct < 0.01:
        # Pre-trailing NON ancora attivo. NON muovere SL.
        return {
            "action": "hold",
            "sl_price": None,
            "close_size": None,
            "reason": f"VPTR3 Charter V3: PnL={pnl_pct*100:.2f}% < 1% pre-trailing, HOLD (no SL mosso)",
            "new_state": {},
        }

    # Pre-trailing 1% raggiunto: attiva flag (se non gia attivo)
    if not sym_state.get("pre_trailing_active", False):
        sym_state["pre_trailing_active"] = True

    # Calcola SL target in base al profitto
    if pnl_pct < VPTR3_TRAILING_TRIGGER_PCT:
        # PnL tra 1% e 1.5%: SL a breakeven (entry)
        sl_trailing = entry_price
        lock_label = "breakeven"
    else:
        # PnL >= 1.5%: lock-in step list progressiva
        if pnl_pct >= 0.05:  # >= 5%
            lock_in = 0.030  # +3% lock-in
        elif pnl_pct >= 0.03:  # >= 3%
            lock_in = 0.020  # +2% lock-in
        elif pnl_pct >= 0.02:  # >= 2%
            lock_in = 0.010  # +1% lock-in
        else:  # 1.5% - 2%
            lock_in = 0.005  # +0.5% lock-in

        if side == "Buy":
            sl_trailing = entry_price * (1 + lock_in)
        else:
            sl_trailing = entry_price * (1 - lock_in)
        lock_label = f"lock-in +{lock_in*100:.1f}%"

    # HWM: SL non peggiora MAI
    prev_sl = sym_state.get("trailing_sl")
    hwm_locked = False
    if prev_sl is not None:
        if side == "Buy" and sl_trailing < prev_sl:
            sl_trailing = prev_sl
            hwm_locked = True
        elif side == "Sell" and sl_trailing > prev_sl:
            sl_trailing = prev_sl
            hwm_locked = True

    new_trailing_state = {symbol: {**sym_state, "trailing_sl": sl_trailing}}
    return {
        "action": "update_sl",
        "sl_price": round(sl_trailing, 6),
        "close_size": None,
        "reason": f"VPTR3 Charter V3: PnL={pnl_pct*100:.2f}%, SL a {lock_label} {sl_trailing:.6f}" + (" (HWM lock)" if hwm_locked else ""),
        "new_state": new_trailing_state,
    }
'''

# Vecchio blocco da sostituire (esatto, con rientri e newlines come nel file)
OLD_TRAILING_BLOCK = '''    # 4. Altrimenti: imposta SL trailing a -1.5% dal current (con high water mark)
    # FIX 07/08/2026: aggiunto HWM per evitare che lo SL scenda quando il prezzo oscilla.
    # Prima: SL = current * (1 - 1.5%) sempre. Se il prezzo scendeva, lo SL scendeva con lui
    # e si perdevano i lock-in di profitto. Adesso state[symbol]['trailing_sl'] tiene il
    # massimo raggiunto (Buy) o il minimo (Sell) e lo SL non peggiora mai.
    # Mattia 14/08: pre-trailing 1% \u2192 SL a breakeven (entry)
    if not sym_state.get("pre_trailing_active", False) and pnl_pct >= 0.01:
        sym_state["pre_trailing_active"] = True
    breakeven_sl = entry_price if sym_state.get("pre_trailing_active", False) else None

    if side == "Buy":
        sl_trailing = current_price * (1 - VPTR3_TRAILING_TRIGGER_PCT)
    else:
        sl_trailing = current_price * (1 + VPTR3_TRAILING_TRIGGER_PCT)

    # Applica breakeven se pre-trailing attivo e migliore del trailing
    if breakeven_sl is not None:
        if side == "Buy" and breakeven_sl > sl_trailing:
            sl_trailing = breakeven_sl
        elif side == "Sell" and breakeven_sl < sl_trailing:
            sl_trailing = breakeven_sl

    # High water mark: se il nuovo SL trailing e' PEGGIORE del precedente, mantieni precedente
    prev_sl = sym_state.get("trailing_sl")
    hwm_locked = False
    if prev_sl is not None:
        if side == "Buy" and sl_trailing < prev_sl:
            sl_trailing = prev_sl
            hwm_locked = True
        elif side == "Sell" and sl_trailing > prev_sl:
            sl_trailing = prev_sl
            hwm_locked = True

    # IMPORTANTE: wrap in {symbol: ...} perche' main_loop fa new_state.update(result["new_state"])
    # e si aspetta la chiave del symbol al top level
    new_trailing_state = {symbol: {**sym_state, "trailing_sl": sl_trailing}}
    return {
        "action": "update_sl",
        "sl_price": round(sl_trailing, 6),
        "close_size": None,
        "reason": f"VPTR3 trailing: PnL={pnl_pct*100:.2f}%, SL a -1.5% dal current" + (" (HWM lock)" if hwm_locked else ""),
        "new_state": new_trailing_state,
    }
'''

# === Main loop: aggiungi 'hold' handling ===
OLD_MAIN_LOOP = '''                        log(f"  {symbol} [{strat_label}] {result['reason']}")

                        if result["new_state"]:
                            new_state.update(result["new_state"])
                        elif symbol in new_state and result["action"] == "close_full":
                            new_state.pop(symbol, None)  # reset

                        if result["action"] == "close_full":'''
NEW_MAIN_LOOP = '''                        log(f"  {symbol} [{strat_label}] {result['reason']}")

                        if result["new_state"]:
                            new_state.update(result["new_state"])
                        elif symbol in new_state and result["action"] == "close_full":
                            new_state.pop(symbol, None)  # reset

                        if result["action"] == "hold":
                            log(f"  {symbol} [{strat_label}] HOLD: nessuna azione (PnL sotto pre-trailing 1%)")
                            continue
                        elif result["action"] == "close_full":'''

def main():
    p = Path(FILE)
    if not p.exists():
        print(f"ERRORE: {FILE} non esiste")
        sys.exit(1)
    content = p.read_text(encoding="utf-8")

    if NEW_TRAILING_BLOCK in content:
        print("PATCH: trailing block gia' applicato, skip")
    else:
        if OLD_TRAILING_BLOCK not in content:
            print("ERRORE: OLD_TRAILING_BLOCK non trovato. Patch non applicabile.")
            print("Cerco pattern simili...")
            # Cerca linee chiave per debug
            for kw in ["# 4. Altrimenti: imposta SL trailing", "breakeven_sl = entry_price"]:
                for i, line in enumerate(content.split("\n"), 1):
                    if kw in line:
                        print(f"  riga {i}: {line}")
            sys.exit(1)
        content = content.replace(OLD_TRAILING_BLOCK, NEW_TRAILING_BLOCK)
        print("PATCH: trailing block aggiornato")

    if NEW_MAIN_LOOP in content:
        print("PATCH: main_loop 'hold' gia' applicato, skip")
    else:
        if OLD_MAIN_LOOP not in content:
            print("ERRORE: OLD_MAIN_LOOP non trovato")
            sys.exit(1)
        content = content.replace(OLD_MAIN_LOOP, NEW_MAIN_LOOP)
        print("PATCH: main_loop 'hold' handling aggiunto")

    p.write_text(content, encoding="utf-8")
    print(f"OK: {FILE} aggiornato ({len(content)} bytes)")

if __name__ == "__main__":
    main()
