#!/usr/bin/env python
"""
TEST DRY-RUN della nuova logica Charter V3 in sltp_engine.py.
Importa la funzione process_vptr3_position dal file modificato, esegue casi sintetici.
NON chiama Bybit. NON modifica stato reale.
"""
import sys
import importlib.util
from pathlib import Path

# Carica bybit_demo_client come modulo stub per evitare dipendenze
sys.path.insert(0, '/opt/charter-live/live_deploy_v2')

# Mock bybit_demo_client prima dell'import
class _StubBybit:
    def __init__(self, *args, **kwargs): pass
    def fetch_positions(self): return []
    def set_trading_stop(self, *a, **kw): return {"retCode": 0, "retMsg": "OK (STUB)"}
    def create_market_order(self, *a, **kw): return {"retCode": 0, "retMsg": "OK (STUB)"}

sys.modules['bybit_demo_client'] = type(sys)('bybit_demo_client')
sys.modules['bybit_demo_client'].BybitDemoClient = _StubBybit

# Stub rettangolo_strategy
sys.modules['rettangolo_strategy'] = type(sys)('rettangolo_strategy')
sys.modules['rettangolo_strategy'].compute_signal = lambda *a, **kw: None
sys.modules['rettangolo_strategy'].is_doji = lambda *a, **kw: False
sys.modules['rettangolo_strategy'].has_hammer_bullish = lambda *a, **kw: False
sys.modules['rettangolo_strategy'].has_hammer_bearish = lambda *a, **kw: False

sys.modules['rettangolo_config'] = type(sys)('rettangolo_config')
sys.modules['rettangolo_config'].load_assets = lambda: []

# Carica sltp_engine
spec = importlib.util.spec_from_file_location("sltp_engine", "/opt/charter-live/live_deploy_v2/sltp_engine.py")
sltp = importlib.util.module_from_spec(spec)
spec.loader.exec_module(sltp)

print('='*80)
print('TEST DRY-RUN Charter V3 unified trailing rules')
print('='*80)

# === TEST 1: REPLICA ESATTA del caso bug 16/08 18:01 ===
# entry=0.366, side=Sell, current=0.363, PnL=+0.82%
# VECCHIO CODICE: generava SL=0.368445 (in perdita)
# NUOVO CODICE: deve ritornare action='hold' (no SL mosso)
print('\n[TEST 1] Caso BUG originale 16/08 18:01 - PnL=+0.82% sotto pre-trailing')
print('  entry=0.366 side=Sell current=0.363 size=4077 PnL=+0.82%')
result = sltp.process_vptr3_position(None, {}, 'BEATUSDT', 'Sell', 0.366, 0.363, 4077)
print(f'  -> action={result["action"]} sl={result.get("sl_price")} reason="{result["reason"]}"')
assert result['action'] == 'hold', f"ATTESO action=hold, ottenuto {result['action']}"
print('  PASS: hold corretto, no SL mosso a 0.368445 (bug risolto)')

# === TEST 2: PnL = 1% esatto -> pre-trailing attivo, SL a breakeven ===
print('\n[TEST 2] PnL=+1.00% esatto -> pre-trailing attivo, SL a breakeven')
print('  entry=0.366 side=Sell current=0.36234 PnL=+1.00%')
result = sltp.process_vptr3_position(None, {}, 'BEATUSDT', 'Sell', 0.366, 0.36234, 4077)
print(f'  -> action={result["action"]} sl={result.get("sl_price")} reason="{result["reason"]}"')
assert result['action'] == 'update_sl', f"ATTESO action=update_sl, ottenuto {result['action']}"
assert abs(result['sl_price'] - 0.366) < 1e-6, f"ATTESO sl=0.366 (breakeven), ottenuto {result['sl_price']}"
print('  PASS: SL a breakeven 0.366 corretto')

# === TEST 3: PnL = 1.5% -> trigger ON, lock-in +0.5% ===
print('\n[TEST 3] PnL=+1.50% -> trigger ON, lock-in +0.5% (SL=entry*0.995 per Sell)')
print('  entry=0.366 side=Sell current=0.3605 PnL=+1.50%')
result = sltp.process_vptr3_position(None, {}, 'BEATUSDT', 'Sell', 0.366, 0.3605, 4077)
print(f'  -> action={result["action"]} sl={result.get("sl_price")} reason="{result["reason"]}"')
expected = 0.366 * (1 - 0.005)
assert result['action'] == 'update_sl'
assert abs(result['sl_price'] - expected) < 1e-6, f"ATTESO sl={expected}, ottenuto {result['sl_price']}"
print(f'  PASS: SL a lock-in +0.5% = {result["sl_price"]} corretto')

# === TEST 4: PnL = 2% -> lock-in +1% ===
print('\n[TEST 4] PnL=+2.00% -> lock-in +1% (SL=entry*0.99)')
print('  entry=0.366 side=Sell current=0.3586 PnL=+2.04%')
result = sltp.process_vptr3_position(None, {}, 'BEATUSDT', 'Sell', 0.366, 0.3586, 4077)
print(f'  -> action={result["action"]} sl={result.get("sl_price")} reason="{result["reason"]}"')
expected = 0.366 * (1 - 0.010)
assert abs(result['sl_price'] - expected) < 1e-6, f"ATTESO sl={expected}, ottenuto {result['sl_price']}"
print(f'  PASS: SL a lock-in +1% = {result["sl_price"]} corretto')

# === TEST 5: PnL = 3% -> lock-in +2% ===
print('\n[TEST 5] PnL=+3.00% -> lock-in +2% (SL=entry*0.98)')
print('  entry=0.366 side=Sell current=0.355 PnL=+3.01%')
result = sltp.process_vptr3_position(None, {}, 'BEATUSDT', 'Sell', 0.366, 0.355, 4077)
print(f'  -> action={result["action"]} sl={result.get("sl_price")} reason="{result["reason"]}"')
expected = 0.366 * (1 - 0.020)
assert abs(result['sl_price'] - expected) < 1e-6, f"ATTESO sl={expected}, ottenuto {result['sl_price']}"
print(f'  PASS: SL a lock-in +2% = {result["sl_price"]} corretto')

# === TEST 6: PnL = 4.9% (sotto TP1 5%) -> lock-in +2% (ultimo step prima di TP1) ===
print('\n[TEST 6] PnL=+4.90% -> lock-in +2% (sotto TP1 5%, ultimo step)')
print('  entry=0.366 side=Sell current=0.348 PnL=+4.92%')
result = sltp.process_vptr3_position(None, {}, 'BEATUSDT', 'Sell', 0.366, 0.348, 4077)
print(f'  -> action={result["action"]} sl={result.get("sl_price")} reason="{result["reason"]}"')
expected = 0.366 * (1 - 0.020)
assert result['action'] == 'update_sl', f"ATTESO update_sl, ottenuto {result['action']}"
assert abs(result['sl_price'] - expected) < 1e-6, f"ATTESO sl={expected}, ottenuto {result['sl_price']}"
print(f'  PASS: SL a lock-in +2% = {result["sl_price"]} corretto (PnL 4.9% < TP1 5%)')

# === TEST 6B: PnL = 5% esatto -> close_partial (TP1) ===
print('\n[TEST 6B] PnL=+5.00% -> CLOSE_PARTIAL 50% (TP1)')
print('  entry=0.366 side=Sell current=0.3477 PnL=+5.01%')
result = sltp.process_vptr3_position(None, {}, 'BEATUSDT', 'Sell', 0.366, 0.3477, 4077)
print(f'  -> action={result["action"]} close_size={result.get("close_size")} reason="{result["reason"]}"')
assert result['action'] == 'close_partial', f"ATTESO close_partial, ottenuto {result['action']}"
assert result['close_size'] == 4077 / 2
print(f'  PASS: TP parziale 50% = {result["close_size"]} corretto')

# === TEST 7: HWM lock - prezzo scende dopo step 2 ===
print('\n[TEST 7] HWM: prezzo scende da 3% a 2% ma SL non deve peggiorare')
print('  state={"trailing_sl": 0.358} (precedente lock-in +2% per Sell)')
print('  entry=0.366 side=Sell current=0.3586 PnL=+2.04%')
result = sltp.process_vptr3_position(None, {'BEATUSDT': {'trailing_sl': 0.35868}}, 'BEATUSDT', 'Sell', 0.366, 0.3586, 4077)
print(f'  -> action={result["action"]} sl={result.get("sl_price")} reason="{result["reason"]}"')
assert 'HWM lock' in result['reason'], f"ATTESO HWM lock in reason, ottenuto {result['reason']}"
# Per Sell, HWM blocca se nuovo SL > precedente. lock-in +1% = 0.36234 > 0.35868? SI, ma HWM per Sell blocca se nuovo PEGGIORE = piU' alto
# Quindi 0.36234 > 0.35868 -> HWM blocca -> SL resta 0.35868
assert abs(result['sl_price'] - 0.35868) < 1e-6, f"ATTESO HWM sl=0.35868, ottenuto {result['sl_price']}"
print('  PASS: HWM blocca, SL resta al livello precedente')

# === TEST 8: PnL = -4% (hard cap Charter V3 hard -4% per VPTR3) -> close_full ===
print('\n[TEST 8] PnL=-4.00% (Charter hard cap VPTR3_SL_HARD_PCT=-0.04) -> CLOSE_FULL')
print('  entry=0.366 side=Sell current=0.38064 PnL=-4.00%')
result = sltp.process_vptr3_position(None, {}, 'BEATUSDT', 'Sell', 0.366, 0.38064, 4077)
print(f'  -> action={result["action"]} close_size={result.get("close_size")} reason="{result["reason"]}"')
assert result['action'] == 'close_full', f"ATTESO close_full, ottenuto {result['action']}"
assert result['close_size'] == 4077
print('  PASS: close_full Charter hard cap VPTR3 (-4%)')

# === TEST 9: PnL = +5% -> TP parziale 50% (duplicato di 6B per copertura) ===
print('\n[TEST 9] PnL=+5.00% -> TP_PARTIAL 50% (close_size=size/2)')
print('  entry=0.366 side=Sell current=0.3477 PnL=+5.01%')
result = sltp.process_vptr3_position(None, {}, 'BEATUSDT', 'Sell', 0.366, 0.3477, 4077)
print(f'  -> action={result["action"]} close_size={result.get("close_size")} reason="{result["reason"]}"')
assert result['action'] == 'close_partial', f"ATTESO close_partial, ottenuto {result['action']}"
assert result['close_size'] == 4077 / 2
print('  PASS: TP parziale 50% corretto (TP1)')

# === TEST 10: DOPO TP1, PnL +3% addizionale -> TP finale ===
print('\n[TEST 10] Dopo TP1, PnL=+8% (TP1_price=0.3477) -> CLOSE_FULL')
print('  entry=0.366 side=Sell current=0.337 (TP1=0.3477 * 0.97 = 0.33727, target <= 0.33727)')
state_after_tp1 = {'BEATUSDT': {'tp1_done': True, 'tp1_price': 0.3477, 'entry_price': 0.366, 'side': 'Sell'}}
result = sltp.process_vptr3_position(None, state_after_tp1, 'BEATUSDT', 'Sell', 0.366, 0.337, 4077)
print(f'  -> action={result["action"]} close_size={result.get("close_size")} reason="{result["reason"]}"')
assert result['action'] == 'close_full', f"ATTESO close_full, ottenuto {result['action']}"
print('  PASS: TP finale dopo TP1+3% addizionale')

# === TEST 11: PnL negativo tra 0% e -3% -> HOLD (no SL mosso) ===
print('\n[TEST 11] PnL=-1.5% (loss moderata, no close) -> HOLD')
print('  entry=0.366 side=Sell current=0.3715 PnL=-1.50%')
result = sltp.process_vptr3_position(None, {}, 'BEATUSDT', 'Sell', 0.366, 0.3715, 4077)
print(f'  -> action={result["action"]} reason="{result["reason"]}"')
assert result['action'] == 'hold', f"ATTESO hold, ottenuto {result['action']}"
print('  PASS: HOLD su loss moderata (no SL mosso, hard cap -3% non ancora raggiunto)')

# === TEST 12: LONG side test ===
print('\n[TEST 12] LONG: entry=0.5 current=0.5025 PnL=+0.5% -> HOLD')
result = sltp.process_vptr3_position(None, {}, 'BTCUSDT', 'Buy', 0.5, 0.5025, 100)
print(f'  -> action={result["action"]} reason="{result["reason"]}"')
assert result['action'] == 'hold'
print('  PASS: HOLD su LONG PnL 0.5%')

print('\n[TEST 13] LONG: entry=0.5 current=0.505 PnL=+1% -> SL=0.5 breakeven')
result = sltp.process_vptr3_position(None, {}, 'BTCUSDT', 'Buy', 0.5, 0.505, 100)
print(f'  -> action={result["action"]} sl={result.get("sl_price")} reason="{result["reason"]}"')
assert result['action'] == 'update_sl'
assert abs(result['sl_price'] - 0.5) < 1e-6
print('  PASS: LONG breakeven corretto')

print('\n[TEST 14] LONG: entry=0.5 current=0.5245 PnL=+4.9% -> SL=0.5*1.02=0.51 lock-in +2% (sotto TP1 5%)')
result = sltp.process_vptr3_position(None, {}, 'BTCUSDT', 'Buy', 0.5, 0.5245, 100)
print(f'  -> action={result["action"]} sl={result.get("sl_price")} reason="{result["reason"]}"')
assert result['action'] == 'update_sl'
assert abs(result['sl_price'] - 0.51) < 1e-6, f"ATTESO sl=0.51, ottenuto {result['sl_price']}"
print('  PASS: LONG lock-in +2% (PnL 4.9% < TP1 5%) corretto')

print('\n' + '='*80)
print('TUTTI I TEST PASSATI - Charter V3 trailing funziona correttamente')
print('='*80)
