
    .ؗjkT             	         d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlZddlZddlmZ ddlmZmZmZ ddlmZmZ  ed      Zedz  Zej/                  d	
       edz  Zedz  Zedz  Zedz  Z ej8                  ej:                  d ej<                  ed       ej>                         g        ej@                  d      Z! e	jD                         Z#d Z$dZ%ejL                  jO                  dd      jQ                         Z)e)r?e)jU                  d      D  ch c]#  } | jQ                         s| jQ                         % c} Z+ne%hZ+e%Z,ejL                  jO                  dd      jQ                         xs dZ-i Z.ejL                  jO                  dd      jQ                         Z/e/rQe/jU                  d      D ]=  Z0de0v se0jU                  dd      \  Z1Z2e2jQ                         e.e1jQ                         <   ? dZ3dZ4d Z5d!Z6 e7d" e8ejL                  jO                  d#d$                  Z9d	Z:d%Z;d&Z<d'Z=d(hZ>d(hZ?d) Z@e$d*        ZAe$dOdPd+       ZBe$dQdRd,       ZCe$dSd-       ZDe$dTdSd.       ZEd/ ZFe$e9fdUd0       ZGe$e9f	 	 	 dVd1       ZHe$d2        ZIe$d3        ZJe$dWd4       ZKe$d5        ZLdXd6ZMdYd7ZNdZd8ZOd[d9ZPdXd:ZQdXd;ZRe$	 d\	 	 	 	 	 d]d<       ZSe$d^d=       ZTe$d^d>       ZUe$d_d?       ZVe$d`dad@       ZWe$dA        ZXe$dbdB       ZYe$dC        ZZda[dD Z\dcdEZ]dddFZ^ G dG dHe      Z_dI Z`dedJZadK ZbdL ZcdM ZdeedNk(  r e`        yyc c} w )fu  
webhook_receiver.py — Webhook async per TV → Bybit V5.

ARCHITETTURA (fix del bug timeout):

    TradingView  ──HTTP POST──>  webhook
                                    │
                                    ├── validate secret + campi (~1ms)
                                    ├── enqueue su SQLite (persistente)
                                    ├── ritorna HTTP 200 IMMEDIATAMENTE  ← chiave!
                                    │
                                    └── background worker thread
                                            │
                                            ├── fetch prezzo Bybit
                                            ├── auto-bump qty
                                            ├── set leva
                                            ├── place order
                                            ├── log su orders.log UNICO
                                            ├── email notification
                                            └── mark completed su DB

PERCHÉ:
- TradingView timeout = 5-10s
- HTTP response deve tornare in <1s
- Tutto il resto (chiamate Bybit lente) avviene in background
- Se webhook crasha, la coda SQLite recupera gli ordini pending al restart
- PID file garantisce UNA SOLA istanza attiva (no race condition)

ENDPOINTS:
- POST /webhook   — riceve alert TV (fast, <100ms)
- GET  /health    — health check
- GET  /status    — stato coda (pending, completed, failed)
    )annotationsN)Path)datetimetimezone	timedelta)BaseHTTPRequestHandlerThreadingHTTPServerz /opt/charter-live/live_deploy_v2logsT)exist_okzwebhook_receiver.logz
orders.logzwebhook_queue.dbzwebhook_receiver.pidz1%(asctime)s [%(levelname)s] %(name)s: %(message)sutf-8encoding)levelformathandlerswebhookc                      fd}|S )Nc                 L    t         5   | i |cd d d        S # 1 sw Y   y xY wN)DB_LOCK)argskwargsfuncs     &/tmp/webhook_receiver.py.intent_matrixwrapperzdb_serialized.<locals>.wrapperP   s(     	)((	) 	) 	)s   # )r   r   s   ` r   db_serializedr   O   s    ) N    TV_2026_MATTIA_DEMOWEBHOOK_SECRETS ,WEBHOOK_DEFAULT_STRATEGYvptr3WEBHOOK_SECRET_STRATEGY_MAP:   z0.0.0.0i  g      @g333333?<   V2_PENDING_MAX_AGE_SECONDS6002   240d   VIRTUALUSDTc                    t         j                         rl	 t        t         j                         j	                               } ddl}|j                  |       r+t        j                  d|        t        j                  d       t         j                  t        t        j                                       t        j#                  dt        j                                 y# t        t        f$ r Y jw xY w)u>   Se c'è già un'istanza attiva, esci. Previene race condition.r   Nu/   ALTRO webhook_receiver già attivo PID=%d, escor'   zPID file scritto: %d)PID_FILEexistsint	read_textstrippsutil
pid_existslogerrorsysexitImportError
ValueError
write_textstrosgetpidinfo)old_pidr5   s     r   check_singletonrC      s    	(,,.4467G  )		KWU BIIK()HH#RYY[1 Z( 		s   A+C C,+C,c                 \   t        j                  t        dd      } | j                  d       | j                  d       	 | j                  d       | j                  d       | j                  d       | j                          t        j                  d	t               | S # t        $ r Y Yw xY w)
z.Inizializza DB SQLite per la coda persistente.F
   )check_same_threadtimeoutzPRAGMA journal_mode=WALah  
        CREATE TABLE IF NOT EXISTS queue (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            request_id TEXT UNIQUE NOT NULL,
            received_at TEXT NOT NULL,
            payload TEXT NOT NULL,
            status TEXT NOT NULL DEFAULT 'pending',
            error TEXT,
            order_id TEXT,
            processed_at TEXT
        )
    z=ALTER TABLE queue ADD COLUMN tunnel_pending INTEGER DEFAULT 0a  
        CREATE TABLE IF NOT EXISTS orders (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            request_id TEXT NOT NULL,
            order_id TEXT NOT NULL,
            symbol TEXT NOT NULL,
            side TEXT NOT NULL,
            qty REAL NOT NULL,
            price REAL,
            notional REAL,
            strategy TEXT,
            created_at TEXT NOT NULL
        )
    aL  
        CREATE TABLE IF NOT EXISTS position_ownership (
            symbol TEXT PRIMARY KEY,
            strategy TEXT NOT NULL,
            side TEXT NOT NULL,
            entry_request_id TEXT,
            entry_order_id TEXT,
            opened_at TEXT NOT NULL,
            source TEXT NOT NULL DEFAULT 'webhook'
        )
    zDB inizializzato: %s)sqlite3connectDB_PATHexecute	Exceptioncommitr7   rA   )conns    r   init_dbrO      s     ??7eRHDLL*+LL  	TU 	LL  	 	LL 
 
	 	KKMHH#W-K;  s    B 	B+*B+c                   t        t        j                               }| j                  d|t	        j
                  t        j                        j                         t        j                  |      |rdndf       | j                          |S )z/Inserisce richiesta nel DB. Ritorna request_id.zXINSERT INTO queue (request_id, received_at, payload, tunnel_pending) VALUES (?, ?, ?, ?)r'   r   )r>   uuiduuid4rK   r   nowr   utc	isoformatjsondumpsrM   )rN   payloadtunnel_pending
request_ids       r   enqueue_requestr[      sf     TZZ\"JLLb	X\\(,,/99;TZZ=P!	%
 	KKMr   c           	         |rdnd}| j                  d|||t        j                  t        j                        j                         |f       | j                          y)z5Marca richiesta come processata (completed o failed).	completedfailedzQUPDATE queue SET status=?, order_id=?, error=?, processed_at=? WHERE request_id=?NrK   r   rS   r   rT   rU   rM   )rN   rZ   order_idr8   statuss        r   mark_processedrb      sN     %[(FLL[	5	hll	#	-	-	/	=
 	KKMr   c                    | j                  d|t        j                  t        j                        j                         |f       | j                          y)z<Chiude definitivamente una richiesta filtrata o idempotente.zbUPDATE queue SET status='skipped', error=?, processed_at=? WHERE request_id=? AND status='pending'Nr_   rN   rZ   reasons      r   mark_skippedrf      @     	LL	2	hll+557D
 	KKMr   c                    | j                  d|t        j                  t        j                        j                         |f       | j                          y)zNMette in quarantena una richiesta troppo vecchia; non potra essere rieseguita.zbUPDATE queue SET status='expired', error=?, processed_at=? WHERE request_id=? AND status='pending'Nr_   rd   s      r   mark_expiredri      rg   r   c                    	 t        j                  t        |       j                  dd            }|j                  r|S |j                  t
        j                        S # t        t        f$ r Y y w xY w)NZz+00:00)tzinfo)	r   fromisoformatr>   replacerl   r   rT   	TypeErrorr<   )valueparseds     r   _parse_queue_timerr      s_    ''E
(:(:3(IJvOFNN(,,N,OOz" s   ;A A A0/A0c                   t        j                  t        j                        }g }| j	                  d      j                         }|D ]  \  }}t        |      }|||z
  j                         |kD  s*|dn$t        t        ||z
  j                                     }d| d| }	t        | ||	       |j                  |       t        j                  d||	        |S )zETerminalizza i pending scaduti. Mai rieseguire un vecchio segnale TV.z@SELECT request_id, received_at FROM queue WHERE status='pending'unknownz expired_pending_ttl age_seconds=z limit=z-PENDING EXPIRED/QUARANTINED: request_id=%s %s)r   rS   r   rT   rK   fetchallrr   total_secondsr>   r2   ri   appendr7   r8   )
rN   max_age_secondsrS   expiredrowsrZ   received_atreceivedagere   s
             r   expire_stale_pendingr~     s     ,,x||
$CG<<Jhj 	 $( [
K$[1h==?/Q'/)ScHn=[=[=]9^5_C7uGOCTUFz62NN:&IIEzSYZ[ Nr   c                   | j                  d|f      j                         }|st        j                  d|       y|\  }}|dk7  rt        j	                  d||       yt        |      }|7t        j                  t        j                        |z
  j                         |kD  r#t        | |       t        j                  d|       yy)z@Difesa in profondita prima di qualsiasi chiamata ordine a Bybit.z8SELECT status, received_at FROM queue WHERE request_id=?z6QUEUE STATE MISSING: request_id=%s; processing blockedFpendingz,QUEUE TERMINAL SKIP: request_id=%s status=%sz1STALE REQUEST BLOCKED before Bybit: request_id=%sT)rK   fetchoner7   r8   warningrr   r   rS   r   rT   rv   ri   )rN   rZ   rx   rowra   r{   r|   s          r   request_is_fresh_pendingr     s     ,,BZMhj  		JJWFKBJPVW -HHLL6APPRUddT:&		EzRr   c                    | j                  d      j                         }|D cg c]  }|d   t        j                  |d         f! c}S c c}w )z4Ritorna richieste pending (per recovery al startup).zRSELECT request_id, payload FROM queue WHERE status='pending' ORDER BY id LIMIT 100r   r'   )rK   ru   rV   loadsrN   rz   rs      r   get_pendingr   /  sK     <<\hj 	 /33QqT4::ad#$333s   $Ac                    t        |        | j                  d      j                         }|D cg c]  }|d   t        j                  |d         f! c}S c c}w )a  FIX 2026-07-19 (Mavis handoff TAILSCALE_WATCHDOG): ritorna solo richieste
    pending NON bloccate da tunnel_pending=1. Il worker processa queste;
    le richieste con tunnel_pending=1 restano in coda finche' il tunnel
    non torna su (segnalato da set_tunnel_state).zgSELECT request_id, payload FROM queue WHERE status='pending' AND tunnel_pending=0 ORDER BY id LIMIT 100r   r'   )r~   rK   ru   rV   r   r   s      r   get_pending_unblockedr   8  sS     <<qhj 	 /33QqT4::ad#$333s   $Ac                R    | j                  d      j                         }|r|d   S dS )zAFIX 2026-07-19: conta richieste in attesa che il tunnel torni su.FSELECT COUNT(*) FROM queue WHERE status='pending' AND tunnel_pending=1r   rK   r   )rN   r   s     r   count_pending_tunnel_blockedr   E  s2     ,,Phj  3q6ar   c                   t        |        | j                  d      j                         }|D cg c]  \  }}|	 }}}d}|r.dj                  d |D              }| j                  d| d|      }| j	                          ||j
                  nd}|t        |      k7  r t        j                  dt        |      |       |D cg c]  \  }}|t        j                  |      f c}}S c c}}w c c}}w )	zGSblocca solo richieste fresche e le restituisce per la coda in memoria.zgSELECT request_id, payload FROM queue WHERE status='pending' AND tunnel_pending=1 ORDER BY id LIMIT 100Nr"   c              3      K   | ]  }d   yw)?Nr   ).0_s     r   	<genexpr>z-unblock_all_tunnel_pending.<locals>.<genexpr>Y  s     99s   zaUPDATE queue SET tunnel_pending=0 WHERE status='pending' AND tunnel_pending=1 AND request_id IN ()r   z2TUNNEL UNBLOCK count drift: selected=%d updated=%d)r~   rK   ru   joinrM   rowcountlenr7   r   rV   r   )	rN   rz   rZ   r   request_idscurplaceholdersupdatedrX   s	            r   unblock_all_tunnel_pendingr   N  s     <<	L hj 	 488-*a:8K8
Cxx9[99ll77CnAG

 	KKM!ocll1G#d)H#d)U\]IMN2E*gZG,-NN 9 Os   C)!C/c                    dj                  t        | xs d      j                         j                  dd      j                  dd      j	                               S )N r!   r   -)r   r>   lowerrn   split)rp   s    r   _normalized_markerr   f  sF    88C$**,44S#>FFsCPVVXYYr   c                4     dt         fddD              S )zBRiconosce in modo uniforme tutte le forme Pine note per le uscite.)zmax exitz	exit barszopposite signalz
exit shortz	exit longz
short exitz	long exitztrailing shortztrailing longztrailing exitzclose entryzstrategy closec              3  J   K   | ]  t        fd D                yw)c              3  V   K   | ]   }|t        j                              v  " y wr   r   get)r   markerfieldrX   s     r   r   z5payload_has_close_intent.<locals>.<genexpr>.<genexpr>r  s%     SF(U);<<Ss   &)Nany)r   r   markersrX   s    @r   r   z+payload_has_close_intent.<locals>.<genexpr>q  s$       	S7SSs   #)commentalert_messager`   r   )rX   r   s   `@r   payload_has_close_intentr   j  s&    G
  =  r   c                L   ddddddd}t        | j                  d            }t        | j                  d            }|j                  |      }|j                  |      }||
dd|d	|fS d
dd}dd
d}|dk(  r"||v r||   }	||	k(  rdd| fS dd| d|	 d| fS ||v r"|dk(  r||   }	||	k(  rd| dfS d| d|	 d| fS ||v r!||k(  r|||   k(  r
d| d| dfS d| d| dfS ||v r+||v r'||k7  r"||   }	||	k(  r	d| d| fS d| d| d|	 d| fS dd| d| d| fS )zIClassifica l'ordine TV dalla transizione, senza dedurlo dal testo libero.flatlongshort)r   r   r   zmarket position flatzmarket position longzmarket position shortmarket_positionprev_market_positionLEGACYz missing/invalid transition prev=z	 current=buysell)r   r   ENTRYzflat->INVALIDz requires side=z, got 	FULL_EXITz->flatz->flat requires side=PARTIAL_EXITz->z opposite-side orderSCALE_INz same-side orderREVERSALzunsupported transition z side=r   )
rX   sidealiasescurrent_rawprevious_rawcurrentprevious
entry_side	exit_sideexpecteds
             r   classify_position_intentr   w  s      &!(G
 %W[[1B%CDK%gkk2H&IJLkk+&G{{<(H(*;L;K9U`Tcddd&1J%0I6g3g&040@6'+, 	
yzvNG
 	
 9F!2X&59X5Ez01 	
(#8
&OL
 	
 9H!49X&&!hZr':N#OOOhZr'2BCCC9J!67h;Ng&9=9I
xj7)45 	
(2gYohZvdVTP
 	
 /zG9F4&QQQr   c                    	 t        t        | j                  d                  }||z   }||z  }d|cxk  rdk  r|S  dS # t        t        t
        f$ r Y yw xY w)z@Quota della posizione TV precedente chiusa dall'ordine parziale.position_size        r   r'   )absfloatr   ro   r<   ZeroDivisionError)rX   qty	remainingbeforefractions        r   partial_exit_fractionr     si    gkk/:;<	S< 8(q(81(c1 z#45 s   -? AAc                v    t        |       }|dv ry|dv ry|dv ry|dv ry|d	v ry
|j                  dd      xs dS )zBNome stabile usato per impedire chiusure incrociate tra strategie.)r$   zvptr v3r$   )zma trailingzma trailing closema_trailing)z	rsi swingzrsi swing breakout	rsi_swing)zrettangolo tv simplezrettangolo simplezrett simplerettangolo_simple)
rettangolozrettangolo v2r   r   r   rt   )r   rn   rp   raws     r   canonical_strategy_namer     s^    
U
#C
""
22
11
JJ"
--;;sC -I-r   c                n    t        | xs d      j                         j                         }|dv ry|dv ryy)Nr!   r   r   r   r   r   r   rt   )r>   r4   r   r   s     r   canonical_position_sider     s<    
ekr

 
 
"
(
(
*C
o
r   c                    | j                  d|t        |      t        |      ||t        j                  t
        j                        j                         |f       | j                          y )NaS  INSERT INTO position_ownership (symbol,strategy,side,entry_request_id,entry_order_id,opened_at,source) VALUES (?,?,?,?,?,?,?) ON CONFLICT(symbol) DO UPDATE SET strategy=excluded.strategy,side=excluded.side,entry_request_id=excluded.entry_request_id,entry_order_id=excluded.entry_order_id,opened_at=excluded.opened_at,source=excluded.source)	rK   r   r   r   rS   r   rT   rU   rM   )rN   symbolstrategyr   rZ   r`   sources          r   set_position_ownerr     sZ     	LL	> 
(24KD4Q	Xx||HLL9CCEv	O	 	KKMr   c                    | j                  d|f      j                         }|sy |d   |d   |d   |d   |d   |d   dS )	NzlSELECT strategy,side,entry_request_id,entry_order_id,opened_at,source FROM position_ownership WHERE symbol=?r   r'               )r   r   entry_request_identry_order_id	opened_atr   r   )rN   r   r   s      r   get_position_ownerr     s^    
,,	139) hj  FCFAa&s1vQ r   c                J    | j                  d|f       | j                          y )Nz-DELETE FROM position_ownership WHERE symbol=?rK   rM   )rN   r   s     r   clear_position_ownerr     s    LL@6)LKKMr   c                    t        | |      }t        |      }t        |      }|dd| dfS |d   |k7  rdd|d    d| |fS |d   |k7  rdd|d    d	| |fS d
d|fS )zFFail-closed: una strategia chiude solo la posizione che le appartiene.NFz position_owner_missing incoming=r   zposition_owner_mismatch owner=z
 incoming=r   zposition_side_mismatch owner=z bybit=Tposition_owner_match)r   r   r   )rN   r   r   position_sideownerincomingactual_sides          r   position_owner_allows_closer     s     tV,E&x0H)-8K}8
CTIIZH$,U:->,?z(T 	 V}#+E&M?'+O 	 '..r   c                T    | j                  d|rdnd|f       | j                          y)zFIX 2026-07-19: marca una richiesta come bloccata (tunnel_pending=1)
    o sbloccata (tunnel_pending=0). Usato in do_POST quando tunnel e' giu'.z4UPDATE queue SET tunnel_pending=? WHERE request_id=?r'   r   Nr   )rN   rZ   blockeds      r   set_tunnel_block_on_requestr     s*     	LL>1j) 	KKMr   c	                    t        j                  t        j                        j	                         }	d|	 d| d| d| d| d| dt        |d       d| }
t        j                  d	|
       | j                  d
|||||||||	f	       | j                          	 t        t        dd      5 }|j                  |
dz          ddd       y# 1 sw Y   yxY w# t        $ r }t        j                  d|       Y d}~yd}~ww xY w)zBRegistra prima nel DB; il file testuale e' un audit non bloccante.[z
] orderId=r   z qty=z price=z
 notional=r   z
 strategy=z	ORDER: %szINSERT INTO orders (request_id, order_id, symbol, side, qty, price, notional, strategy, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)ar   r   
Nz1ORDERS_LOG append failed (DB order preserved): %s)r   rS   r   rT   rU   roundr7   rA   rK   rM   open
ORDERS_LOGwriteOSErrorr8   )rN   rZ   r`   r   r   r   pricenotionalr   tslinefexcs                r   log_order_unifiedr    s	    
hll	#	-	-	/Brd*XJaxqeC5wjY^_gijYkXllvw  wA  BDHH[$ 	LL 	Q	XvtS%8RP 	KKML*cG4 	!GGD4K 	! 	! 	! L		EsKKLs6   C *C?C CC C 	C=C88C=c                D    | j                  d|f      j                         S )Nz<SELECT status, order_id, error FROM queue WHERE request_id=?r   )rN   rZ   s     r   get_request_statusr    s$    <<F	 hjr   c                ^   dD ci c]'  }|| j                  d|f      j                         d   ) }}| j                  d      j                         D cg c]!  }|d   |d   |d   |d   |d   |d	   |d
   d# }}| j                  d      j                         d   |d<   ||fS c c}w c c}w )N)r   r]   r^   skippedry   z)SELECT COUNT(*) FROM queue WHERE status=?r   ztSELECT symbol,strategy,side,entry_request_id,entry_order_id,opened_at,source FROM position_ownership ORDER BY symbolr'   r   r   r   r      )r   r   r   r   r   r   r   r   tunnel_blocked)rK   r   ru   )rN   ra   countsr   ownerss        r   get_queue_status_snapshotr    s     O	  	7&

(*Q 	F  <<G
 (*
 	 !f#a&#a& #A#a&Q3q6	

F 
  $||P hj F 6>)
s   ,B%&B*c                 ^    t         "ddlm}  ddlm}  |t
        dz          |        a t         S )Nr   )BybitDemoClient)load_dotenvzAPI_KEY_BYBIT.env)_bybitbybit_demo_clientr  dotenvr  LIVE_DEPLOY)r  r  s     r   	get_bybitr  <  s(    ~5&K"556 "Mr   c                   t         j                  d       	 	 |j                         }|y|\  }}	 t        | ||       |j                          	 9# t        $ rI}t         j                  d||       	 t        | |t        |             n# t        $ r Y nw xY wY d}~_d}~ww xY w# |j                          w xY w# t        $ r5}t         j                  d|       t        j                  d       Y d}~d}~ww xY w)zPWorker che processa la coda in background. UN solo worker per evitare conflitti.zWORKER started (background)NzWorker error on %s: %s)r8   zWorker loop error: %sr'   )r7   rA   r   process_orderrL   	exceptionrb   r>   	task_donetimesleep)rN   in_queueitemrZ   rX   es         r   worker_loopr'  I  s    HH*+
	<<>D|"&J	%dJ8 ""$   6
AF"43q6B  	 ""$ 	MM115JJqMM	st   B: B: A B: 	B"B1B	B		BBBBB% B""B% %B77B: :	C8+C33C8c                :6  op t        | |      syt        j                  d||j                  d      |j                  d      |j                  d             t	        |j                  dd            j                         }|j                  dd      j                  dd      j                         ppj                  d	      s	d	pvrpd	z   pt	        |j                  dd            j                         j                         }|d
vrt        d|      	 t        |j                  dd            }t        |j                  dd      xs d      }|dk  s|dkD  rd}d } ||j                  d            } ||j                  d            }	 ||j                  d            }
|j                  d      }|Ft	        |      j                         dk(  s*t	        |      j                         j                         dk(  rdt	        |j                  dd            j                         }t        j                  |t              }t        j!                  dp|||r|dd nd       nt	        |      j                         }t#        |      }|j                         dk(  rt        j                  dp|       d}t	        |j                  dd            }t	        |j                  d d            }t%               }t'        ||      \  }}t        j                  d!|||       |d"v r>d#| d$| }t        j)                  d%|||j                  d&      |       t+        | ||       y|d'k(  }|d(k(  xs |d)k(  xr |dk  xs t-        |      }d*}|d+k(  r&|j/                         }t1        pfd,|D        d      }|t        j!                  d-p|       nt	        |j                  dd            j                         }||k(  r)d.}t        j!                  d/||p|       t+        | ||       yt3        | p|t	        |j                  dd                  \  }}}|s+d0| }t        j)                  d1|||       t+        | ||       yt        |j                  d2d      xs d      }t        j                  d3p|||       |r@|dkD  r;t        j                  d4||j                  d5d      |j                  d&d      |||       |s|r|dkD  rt        j                  d6p|||       t        j                  d7p||       	 |j/                         }t1        pfd9|D        d      }|Ft        j                  d:p       t9        | p       t;        | |d;p|ddd| d<	       t+        | |d;       yt        |d2         }t	        |j                  dd=            }t3        | p||      \  }}}|s,t        j)                  d>|p||||       t+        | |d?|        y|d=k(  rd@nd=} || j                         k7  r-dA| dB| }t        j)                  dC||       t+        | ||       y|rWt=        ||      }!d|!cxk  rdk  s:n dD}t        j)                  dE||||j                  dF             t+        | ||       y||!z  }"n|}"|j?                  p|"      }"|"dk  rt        dG| dH|       	 |jA                  p      }#t        j                  dJ|p||| |"       	 |jC                  dKdLdMp| dNt	        |"      dOdPdQdPR      }$|$xs i j                  dT      xs% |$xs i j                  dUi       j                  dT      }%|%st7        dV|$       |"|#z  }&t        j                  dW|||%| |"|       |rdXndY}'t;        | ||%p| j                         |"|#|&| dZ|' 	       |r|"|k\  rt9        | p       tE        | ||%[       ypd\k(  r	 |j/                         }(t1        pfd]|(D        d      })|)r)t	        |)j                  dd            j                         nd}*|)=|*|k7  r8t        j                  d^p||*t        |)j                  d2d      xs d             n{d*}+	 t        |jA                  p      xs d      }+tG        jH                  tJ        jL                        jO                         p||||t	        |j                  d5d            t	        |j                  d&d            ||d_	|+|)r1|)j                  d      t        |)j                  d2d      xs d      d`nddadb| dcdd},tP        dez  }-|-jR                  jU                  dPdPf       tW        |-dgdhi      5 }.|.jY                  t[        j\                  |,djk      dlz          ddd       t        j!                  dmp||||)       t;        | |dnp|||xs dd| do	       t+        | |dn       dpdqdrdsfS 	 	 |j/                         }(t1        pfdu|(D        d      })|)bt	        |)j                  dd            j                         dvk(  r6t        j                  dwpt        |)j                  d2d      xs d             nwd*}+	 t        |jA                  p      xs d      }+tG        jH                  tJ        jL                        jO                         p||||t	        |j                  d5d            t	        |j                  d&d            ||d_	|+|)r1|)j                  d      t        |)j                  d2d      xs d      d`nddadxdd},tP        dyz  }-|-jR                  jU                  dPdPf       tW        |-dgdhi      5 }.|.jY                  t[        j\                  |,djk      dlz          ddd       t        j!                  dzp||||)       t;        | |d{p|||xs dd| d|	       t+        | |d{       dpdqd}dsfS 	 	 |j/                         }t1        pfd|D        d      }/|/|d+k7  r|/j                  dd      j                         }*t        |/j                  d2d      xs d      }0t        |/j                  dd      xs |/j                  dd      xs d      }1t        j!                  dp|||*|0|1       t;        | |dp|||xs dd| d	       t+        | |d|*        dpdqdp d|* d|0 ddsfS |)	 |jA                  p      }t        j                  dp|       d}2d}3	 |jA                  p      }3dj}4|3rn|3dkD  rit_        ||2z
        dk  r*t_        |3|2z
        dkD  rdP}4t        j                  d|3       n.|dkD  r)||3dz  kD  s||3dz  k  rdP}4t        j!                  d||3       |4r|3r|3}||z  }5|5t`        k  rFt`        tb        z  |z  }6|j?                  p|6      }6t        j!                  d||6|5t`               |6}||z  }5	 |je                  p|       t        j                  dp|||||	|
|	       d}7d}8||z  }9t_        |9|7z
        |7z  |8kD  r8|7|z  }6|j?                  p|6      }6t        j!                  dp||6|9|7       |6}||z  }5|j                  dd      j                         }:|:dv rrpj                         tf        vr\|};ti        |;p||:      \  }<}=t        j                  dp||=       |<s/t        j!                  dp||=       t+        | |d|=        dpdq|=dsfS |:dv r$pj                         tj        vr	 ddl6}>|>jn                  jq                  dt	        ts        tt              jw                         jR                  jR                               ddl<m=}? |:j}                  d      rdnd}@ |?|p|@      }A|Aj                  d      Ad   dnd}Bt        j                  dp|@Ad   |Ad   |B|Ad          |Ad   rJt        j!                  dp|Ad   |Ad   B|Ad          d|Ad    d|B d|Ad    d}Ct+        | ||C       dpdq|CdsfS 	 	 d}Dd}E	 |j                  d      }F|Ft        F      }E|j                         dv rdnd}G	 EEdkD  rGdk(  reEdDz   z  }H||HkD  r>t        j)                  dp|HE|ED       d|dd|Hdd|Edd}Ct+        | ||C       dpdq|CdsfS t        j                  dp|HE       n|EdDz
  z  }I||Ik  r>t        j)                  dp|IE|ED       d|dd|Idd|Edd}Ct+        | ||C       dpdq|CdsfS t        j                  dp|IE       nt        j                  dp|:       |}J|j?                  p|      }|dk  rt        dĉp dJ d|       t_        |Jz
        dkD  rt        j!                  dȉpJ|       ||z  }5|d+k(  rN|}K|j?                  p||Kz         }||k  rt        d| dK d|       t        j                  d̉p|K|       ||z  }5|j                  p||ͫ      }$|$xs i j                  dT      xs% |$xs i j                  dUi       j                  dT      }%|%st7        d|$       t        | p||||%       |}Ld}M|}N|j                         dv rdvndϊot        dЫ      D ]  }O	 |j/                  p      }Pt1        opfdф|PD        d      }Q|Qrwt        Qj                  dd      xs Qj                  dd      xs |      }Lt        Qj                  dd      xs d      xs d}Mt        Qj                  d2d      xs |      }N n	 t        j                  dԫ        M	 t        |j                  p            }Mt        j                  d։p|%LMN       d}R|j                         dv r|	|
	 t	        t        jR                        }S|Stl        jn                  vr tl        jn                  jq                  dS       ddlFmG}T |Tj                  p      }U|U Ud   }V|Ud   }Wd|Ud    d|Ud    d|Ud    d}Xnd}Vd}Wd}Xt        j!                  dp       |j                         dv rdnd}YL}Z|	ZdYVz  z   z  }	|
ZdYWz  z   z  }
t        j                  dp|Z|	|
XVW	       |	WLU|j                         dv rdnd}G|G|	Lz
  z  |Lz  }[|[Rdz
  k  r,|	}\LdGRz  z   z  }	t        j)                  dp[|[dz  |\|	|L|       d}]|	|
-	 |j                  p|	|
       t        j                  dp|	|
       d}e	 t        |$t              r|$ni }ft        |fj                  dd      xs d      }gt        fj                  dd      xs d      }ht        fj                  dd      xs d      }igdk  ridkD  r
hdkD  rihz  }ggdkD  rX|d+k7  rSt_        g|7z
        |7z  }j|jekD  r"t        j)                  dpgedz  jdz  hi       nt        j                  dpgjdz         t        j                  d||%       Lxs |}k||kz  }l|d+k(  r| dn|}mt;        | ||%p||kl|m	       tE        | ||%]       	 ddlLmM}n  |n|%p||||t        |5dЫ      d||xs d|	|
||d       y# t        t        f$ r t        d|j                  d            w xY w# t4        $ r}t7        d8p d$|       d}~ww xY w# t4        $ r}t7        dIp d$|       d}~ww xY w# t4        $ r}t7        dS|       d}~ww xY w# t4        $ r Y w xY w# 1 sw Y   xY w# t4        $ r#}t        j)                  dtp||       Y d}~wd}~ww xY w# t4        $ r Y w xY w# 1 sw Y   xY w# t4        $ r#}t        j)                  d~p||       Y d}~d}~ww xY w# t4        $ r#}t        j)                  dp||       Y d}~
d}~ww xY w# t4        $ r}t7        dp d$|       d}~ww xY w# t4        $ r!}t        j!                  d|       Y d}~	d}~ww xY w# t4        $ r!}t        j!                  d|       Y d}~
Wd}~ww xY w# t4        $ r#}t        j)                  dp||       Y d}~Ud}~ww xY w# t        t        f$ r d}EY Mw xY w# t4        $ r<}t        j)                  dp||       d| dÝ}Ct+        | ||C       dpdq|CdsfcY d}~S d}~ww xY w# t4        $ r&}t        j!                  dOdz   p|       Y d}~d}~ww xY w# t4        $ r"}t        j!                  dՉp|       Y d}~d}~ww xY w# t4        $ r#}t        j)                  dp||       Y d}~d}~ww xY w# t4        $ r7}^t        j!                  dp^       	 Mxs t        |j                  p            }_|j                         dv rdnd}Y|	}`|
}aLxs |xs _}b`1Y`_z
  z  dk\  r&t        t_        `bz
        |bz  d      }c_dY|cz  z
  z  }`a1Ya_z
  z  dk  r&t        t_        abz
        |bz  d      }c_dY|cz  z   z  }a|j                  p`a       |`|a}
}	t        j!                  dp_|	|
       n:# t4        $ r.}dd^dddd }]t        j)                  d||%p|]       Y d}d~dnd}d~dww xY wY d}^~^d}^~^ww xY w# t4        $ r!}t        j!                  d|       Y d}~d}~ww xY w# t4        $ r }t        j!                  d|       Y d}~yd}~ww xY w)u<   Processa un singolo ordine. Può richiedere diversi secondi.NzProcessing %s: %s %s qty=%sr   r   r   r!   z.Pz.PZUSDT)r   r   zInvalid side: r   zInvalid qty: leverager   r'   r-   c                L    	 | t        |       S d S # t        t        f$ r Y y w xY wr   )r   ro   r<   )vs    r   
safe_floatz!process_order.<locals>.safe_float{  s1    	 }586$6:& 		s     ##r  sl_pricetp_pricer   rt   secretuQ   STRATEGY missing/unknown in payload for %s %s → using default=%s (secret=***%s)nonersi_swing_breakoutzCSTRATEGY mapping: RSI_SWING_BREAKOUT -> rettangolo_simple for %s %sr   tfr   z4INTENT CLASSIFIED: request_id=%s intent=%s reason=%s)r   r   zPROTOCOL_REJECTED intent=z: z/%s request_id=%s payload_order_id=%r comment=%rr`   r   r   r   r   r   c              3     K   | ]>  }|j                  d       k(  st        |j                  dd      xs d      dkD  s;| @ ywr   sizer   Nr   r   r   pr   s     r   r   z process_order.<locals>.<genexpr>  C     k!AEE(Ov,E%PQPUPUV\^_P`PedeJfijJjAk   A"A Az=REVERSAL SOURCE POSITION ABSENT: %s; resync to target side=%sREVERSAL_ALREADY_APPLIEDz"%s request_id=%s symbol=%s side=%sz%PROTOCOL_REJECTED REVERSAL_OWNERSHIP z%s request_id=%s owner=%sr7  z8REVERSAL VERIFIED: %s old_side=%s old_qty=%s new_side=%szqCLOSE INTENT detected via normalized marker: comment=%r alert_message=%r order_id=%r (qty=%s side=%s strategy=%s)r   uW   CLOSE INTENT (comment-based): %s side=%s qty=%s comment=%r → recupero posizione Bybitu<   CLOSE INTENT: %s side=%s qty=%s → recupero posizione Bybitz)CLOSE INTENT: cannot fetch positions for c              3     K   | ]>  }|j                  d       k(  st        |j                  dd      xs d      dkD  s;| @ ywr6  r8  r9  s     r   r   z process_order.<locals>.<genexpr>  r;  r<  uD   CLOSE INTENT: no open position for %s → skip idempotent (HTTP 200)no_position_close_no_posBuyz]CLOSE BLOCKED OWNERSHIP: request_id=%s symbol=%s strategy=%s bybit_side=%s reason=%s owner=%szCLOSE_BLOCKED_OWNERSHIP SellzPROTOCOL_REJECTED exit side=z does not reduce Bybit side=z%s request_id=%sz4PROTOCOL_REJECTED PARTIAL_EXIT_INVALID_POSITION_SIZEz(%s request_id=%s qty=%s position_size=%rr   z*Exit qty invalid after normalization: raw=z
 position=z%CLOSE INTENT: cannot fetch price for uQ   %s: %s current pos side=%s size=%s → placing %s qty=%s reduceOnly=True @ marketPOSTz/v5/order/createlinearMarketGTCT)categoryr   r   	orderTyper   timeInForce
reduceOnly)signedzCLOSE INTENT: order failed: orderIdresultz-CLOSE INTENT: order returned but no orderId: zE%s OK %s: orderId=%s side=%s qty=%s reduceOnly=True (position was %s)partial_exitcloser   )r`   
RENDERUSDTc              3     K   | ]>  }|j                  d       k(  st        |j                  dd      xs d      dkD  s;| @ ywr6  r8  r9  s     r   r   z process_order.<locals>.<genexpr>  D     za155?f;TY^_`_d_dekmn_o_tstYuxyYyzr<  uh   RENDER TOTAL-BLOCK: %s side=%s ma c'e' posizione %s esistente size=%s → PASSA (chiusura pos esistente))	r   r   r   r  r   r   r`   r   r4  )r   r7  )markopen_positionz0RENDER DEFINITIVELY blocked (2026-08-10, no new z% positions, only close intent passes))r  alertbybit_statere   zrender_blocked.jsonl)parentsr   r   r   r   F)ensure_asciir   u]   RENDER BLOCKED + LOGGED: %s side=%s qty=%s price=%s pos=%s → vedi logs/render_blocked.jsonlrender_blocked_render_blocked   r  z;RENDER definitively blocked, logged to render_blocked.jsonl)ra   re   zJRENDER TOTAL-BLOCK check exception for %s %s: %s (FAIL-OPEN, ordine passa)c              3     K   | ]>  }|j                  d       k(  st        |j                  dd      xs d      dkD  s;| @ ywr6  r8  r9  s     r   r   z process_order.<locals>.<genexpr>F  rR  r<  r   uc   RENDER SHORT-BLOCK: %s side=Sell ma c'e' posizione LONG esistente size=%s → PASSA (chiusura long)z:RENDER SHORT blocked (Long-only test 2026-08-09, log only)zrender_shorts_skipped.jsonluj   RENDER SHORT BLOCKED + LOGGED: %s side=%s qty=%s price=%s pos=%s → vedi logs/render_shorts_skipped.jsonlrender_short_blocked_render_short_blockedzLRENDER SHORT blocked (Long-only test), logged to render_shorts_skipped.jsonlzJRENDER SHORT-BLOCK check exception for %s %s: %s (FAIL-OPEN, ordine passa)c              3     K   | ]>  }|j                  d       k(  st        |j                  dd      xs d      dkD  s;| @ ywr6  r8  r9  s     r   r   z process_order.<locals>.<genexpr>v  sC     pqxF1JuUVUZUZ[acdUeUjijOknoOopr<  avgPrice
entryPriceus   ANTI-DOPPIA SKIP: %s side=%s qty=%s ma c'è GIA' posizione %s size=%s entry=%s (no pyramiding, no chiusura manuale)anti_dup_skip_anti_dup_skipanti_dup_skip_existing_z&ANTI-DOPPIA: posizione gia' aperta su z (side=z size=z2). Pine ha mandato alert a raffica, no pyramiding.zCANTI-DOPPIA check exception for %s %s: %s (FAIL-OPEN, ordine passa)z!Price fetched from Bybit: %s = %szCannot fetch price for g      ?z4Bybit market price fetch fallito (non bloccante): %sg&.>zQPine price=1.0 placeholder rilevato, uso Bybit market price=%.6f per sizing/SL/TPr   g      ?zUPine price=%.6f sospetto (Bybit=%.6f, ratio fuori 0.5-2x), uso Bybit per sizing/SL/TPu1   Auto-bump qty: %s → %s (notional %.2f$ < %.2f$)u8   set_leverage failed (potrebbe essere già impostata): %suS   ORDER → symbol=%s side=%s qty=%.4f price=%.4f leverage=%d SL=%s TP=%s strategy=%sg     p@g?zRSIZING ENFORCEMENT: %s qty=%.4f -> %.4f (nozionale %.2f -> %.2f USDT, target 1500)r$   vptr_v3zP007 regime check %s %s: %suH   P007 SKIP-REGIME ATTIVATO per %s %s: %s — ordine NON inoltrato a BybitzP007 SKIP-REGIME: )r$   rf  r   )classify_regimevptrr,   30adxz.1fr   z;P007b CHOP check %s %s tf=%s: regime=%s chop=%s adx=%s (%s)regimeis_chopre   ug   P007b SKIP-CHOP ATTIVATO per %s %s: regime=%s chop=%s adx=%s reason=%s — ordine NON inoltrato a BybitzP007b CHOP: regime=z adx=z (r   zBP007b CHOP check exception for %s %s: %s (FAIL-OPEN, ordine passa)g{Gz?sl_refr   zZONA-MID SKIP: %s LONG entry=%.6f > bottom_pattern*1.015 = %.6f (entry OLTRE 1.5%% dal bottom %.6f, NON si entra in zona mid). sl_ref=%.6f ZONA_ENTRY_PCT=%.3fzZONA-MID LONG: entry .6fz > bottom_pattern*1.015 (u   ) — OLTRE 1.5% dal bottom z, NO entry in zona midzJZONA-MID LONG OK: %s entry=%.6f <= %.6f (1.5%% UP dal bottom_pattern %.6f)zZONA-MID SKIP: %s SHORT entry=%.6f < top_pattern*0.985 = %.6f (entry OLTRE 1.5%% dal top %.6f, NON si entra in zona mid). sl_ref=%.6f ZONA_ENTRY_PCT=%.3fzZONA-MID SHORT: entry z < top_pattern*0.985 (u   ) — OLTRE 1.5% dal top zJZONA-MID SHORT OK: %s entry=%.6f >= %.6f (1.5%% DOWN dal top_pattern %.6f)zZONA-MID: sl_ref assente per %s (strategy=%s). ZONA-MID si applica SOLO al Rettangolo. VPTR3 (alert TV) non rientra. Lascio passare al filtro successivo.uv   ZONA-MID check FAILED for %s %s: %s — ordine SKIPPATO (fail-closed Mattia: meglio perdere occasione che aprire male)zZONA-MID check failed: z' (fail-closed, no entry in zona dubbia)zNormalized qty invalid for z: raw=z normalized=g-q=z,QTY-STEP NORMALIZED: %s raw=%s normalized=%sz(REVERSAL combined qty invalid: existing=z target=z
 combined=zGREVERSAL COMBINED ORDER: %s close_qty=%s target_new_qty=%s order_qty=%s)r   r   r   zOrder returned but no orderId: r   r   c              3    K   | ]  }t        |j                  d d            j                         k(  rRt        |j                  dd            j                         k(  r&t	        |j                  dd      xs d      dkD  r|  yw)r   r!   r   r7  r   N)r>   r   upperr   r   )r   r:  expected_position_sider   s     r   r   z process_order.<locals>.<genexpr>h  sx      7qh+,224>vr*+1137MM155+0q1A5  7s   BB		markPricez6POST-FILL position lookup attempt %d failed for %s: %sg      ?z+POST-FILL mark price unavailable for %s: %sz:POST-FILL REFERENCE %s: orderId=%s entry=%s mark=%s qty=%sgQ)SETUPS_BY_SYMBOLsl_clamp_maxtp2_pctzCharter P006 namez (clamp_min=sl_clamp_minz, clamp_max=g{Gzz3fallback -2%SL/+5%TP (symbol non in SETUPS Charter)uO   AUTO-FILL SL/TP: symbol %s NON in SETUPS Charter → fallback -2%% SL / +5%% TPz[AUTO-FILL SL/TP per %s %s @ entry=%.6f: SL=%.6f TP=%.6f source=%s (sl_pct=%.4f tp_pct=%.4f)u^   AUTO-FILL SL/TP FAILED for %s %s: %s — proceeding senza SL/TP (sltp_engine coprira' in 5min)gư>z{CHARTER P006 OVERRIDE: %s sl_loss_pct=%.4f (loss %.2f%%) > -3%% Charter hard cap, clampato SL da %s a %s (entry=%s side=%s))r.  r/  zTRADING STOP OK %s: SL=%s TP=%sz0set_trading_stop first attempt failed for %s: %sg~jth?z.TRADING STOP RECOVERED %s: mark=%s SL=%s TP=%szORDER_OPEN_UNPROTECTED first=z retry=i  z=ORDER_OPEN_UNPROTECTED request_id=%s orderId=%s symbol=%s: %sg?cumExecValue
cumExecQtyu   POST-ORDER SIZING MISMATCH: %s nozionale_effettiva=%.2f USDT (target 1500±%.0f%%), drift=%.1f%%, qty=%s, avgPrice=%s. POSSIBILE Pine price placeholder non rilevato o slippage estremo. CHARTER 500x3=1500 USDT VIOLATO.zTPOST-ORDER SIZING OK %s: nozionale_effettiva=%.2f USDT (drift=%.2f%%, in tolleranza)z)POST-ORDER check fallito (non blocca): %szORDER OK %s: orderId=%s	_reversal)r`   r8   )send_trade_email)rL  r   r   r   r  r`  notional_usdz
TV webhook)r   re   stoptpr*  r4  )ordersignalz*Email notification failed (non blocca): %s)Or   r7   rA   r   r>   r4   rn   rq  endswithr   r<   r   ro   r2   SECRET_STRATEGY_MAPDEFAULT_STRATEGYr   r   r  r   r8   rf   r   fetch_positionsnextr   rL   RuntimeErrorr   r  r   	round_qtyget_last_price_requestrb   r   rS   r   rT   rU   LOG_DIRparentmkdirr  r  rV   rW   r   MIN_NOTIONAL_USDNOTIONAL_SAFETY_MARGINset_leverageEXCLUDED_FROM_REGIME_CHECKcheck_regime_ema50EXCLUDED_FROM_CHOP_CHECKr9   pathinsertr   __file__resolvecharter_core.market_regimerg  
startswithcreate_market_orderr   ranger"  r#  get_mark_pricer  charter_corert  set_trading_stopmax
isinstancedictemail_notifierr|  r  )qrN   rZ   rX   
symbol_rawr   r   r*  r-  r  r.  r/  strategy_rawsecr   strategy_ownerr4  r   bybitposition_intentintent_reasonre   is_partial_exitis_close_intentreversal_existing_size	positionspospos_sideowner_okowner_reasonr   r&  pos_size
close_sider   	close_qtycurrent_pricer  r`   notional_closesuffixpositions_rbexisting_rbexisting_sidemark_rblog_entry_rbrb_log_pathr
  existingexisting_sizeexisting_entryPINE_PLACEHOLDER_PRICEbybit_market_pricepine_price_suspiciousr  new_qtyTARGET_NOTIONAL	TOLERANCEcurrent_notionalstrategy_namebybit_for_regimeregime_passregime_reason_sysrg  tf_chopregime_infoadx_strskip_reasonZONA_ENTRY_PCT_sl_ref_sl_ref_raw
_direction	_zona_max	_zona_minraw_qtytarget_position_qtyactual_entry_priceactual_mark_priceactual_position_qtyattemptpositions_afterposition_afterCHARTER_P006_SL_CAP_WS_ROOTrt  setupsl_pcttp_pct	sl_source	directionentry_price_sl_loss_pct_old_slprotection_errorfirst_errormark_referenceretry_slretry_tp	referencedistanceretry_errorPOST_ORDER_TOLERANCE
order_respcum_exec_valuecum_exec_qtyavg_price_execdriftlogged_pricelogged_notionalexecution_strategyr|  rr  r   sq                                                                                                                  @@r   r  r  a  s   #D*5HH*X.F0CW[[QVEWY W[[2./557Jb)11%<BBDF??6"vV';&w{{62&'--/557D?">$233AGKKq)* 7;;z1-23H!|x#~ w{{7+,E'++j12H'++j12H ;;z*Ls<0668B>#lBSBYBYB[BaBaBcgpBp'++h+,224&**30@AgD(CH	I |$**,,X6N ~~//VX^`de&	W[[y)	*B'++i,-GKE &>gt%L"O]HHCZQ`bop11,_,=RO		CVZ++j)7	4T:v.%7O;& 	]x'[SAX-Z9QRY9Z  !*$))+	kykmqr;KKWY_aef37762./557H43@&*V\^bcT:v6,Gfnc#''&"2E.F-)HlE @O		5vz5QT:v6%*37761+=+B%C"HHOQWYacy{  A37 018'++oWY:ZZ,c4	C /7HHnT31OQWY]_bc	Z--/I kykmqr;HH[]cd v.dJvtQPQSTYaXbboVpqz=9V%swwvu-.(C&.()
%, II3FNHlE
 z-El^+TU'50Ve
:##%%3D69UV^U_`FII(&*=z62,Wc:Hx$1$O		DfjZ]_f_j_jkz_{|T:v6 8+I IOOFI6	>I#jYaXbcdd	V!008M 	d &(Hj)	U	CNN6+=$
%c)n$D@ 	 # E KR$$Y/aEKR3D3DXr3R3V3VW`3a!NugVWW"]2X *h
Ix	Y#2$
Hfj>N>N>PR[]jlz  @H  I  IJ  KQ  JR  }S  	T)x"7 v.tZ(; (	u 002Lz<z  }A  BKITS!<=CCEZ^M&=D+@  D}eKOOFTU<V<[Z[6\^ #E$8$8$@$EAFG #,,x||4>>@"($sU#*S_^`Aa=b$'J(C$D$,B	 !( @K;??63JTYZeZiZijprsZtZyxyTz*{  QU$ !QQUPVV{|  &(>>""(((E+sW= QGGDJJ|%H4OPQ{"D#ukC!$
4DfdTWY^Ycbcefksjt  uD  iE  FT:/?@y>y{{{?^F'	u 002Lz<z  }A  BK&3{vr/J+K+Q+Q+SW\+\~{vq'A'FQ!GI #E$8$8$@$EAFG #,,x||4>>@"($sU#*S_^`Aa=b$'J(C$D$,B	 !( @K;??63JTYZeZiZijprsZtZyxyTz*{  QU$ [  &(EE""(((E+sW= QGGDJJ|%H4OPQ  I"D#ukC!$
4JFTXZ]_d_ihiklqypz  {P  oQ  RT:/EFy  =K  L  L  L?IVj))+	pIprvwOz$A$LL4::<M!(,,vq"9">Q?M"8<<
A#>#d(,,|]^B_#dcdeNKK  Nc=-YdJsTYT^]^`afneoo}c~z-D]O+TU9:`ag`hhop}o~  E  FS  ET  TF  9G  H  H  H }	H((0EHH8&%H !O"11&9 "014u--.5#>PSi>i:jmq:q$(!HHhj|}QYE$6$::eFX[^F^>^$(!KKo13!3" U{H""#&<<E//&'2G(,<	>;S68,
 HHbT3x8XO OIU{
o-.@9L!E)//&'2hC*:O	M; KK
I6<<>M,,Ga1a %78H&RVXe%f"].mLKKbdjlpr  Az-?+OP9FFF ::v||~Um?m	mIIQDN$:$:$<$C$C$J$J KLB,77?eTG)%AK5@__U5K5WU+C0]`GHHRT7K,A;yCYk(35 9%  F"D+h*?YAWY`bmnvbwy 3K4I3J%PWyXZ[fgo[pZqqrsT:{;yKHHH & 	 NGkk(+"K(G jjlo52J)A7Q;Q#q>'9:	9$II @ %eY.Z %:%D]^ghk]l  mI  JQ  RU  IV  Vl  #mK z;?9 LLLe	7< $q>'9:	9$II @ %eY.Z %;5+E[\efi[j  kD  EL  MP  DQ  Qg  #hK z;?9 LLLe	7< HH  q], G
//&#
&C
ax6vhfWI\Z]Y^_``
3=E!BFGUXYU{H*$!oof&<?R&RS((:;Q:RRZ[nZooyz}y~  	Z/1Dc	K;%%V$C%HE  +]/@/@2/N/R/RS\/]H<UGDEEtV^T:xP &*jjlo&EU68 	j#33F;O!7O 7 N %*"&&z15 %)),:&"
 %*.*<*<[!*L*QPQ$R$ZVZ!&+N,>,>vq,I,PS&Q#  	

4+,  	R %e&:&:6&B C HHIX13DFY[B  ~~//X5EIY%	';--.Hsxx'8,5$((0E  ~.y),U6]O <++0+@*AeTbNcMddeg	
 Q	mouv "ZZ\_<"I,K&!i&.@*@A&!i&.@*@AHHrT;(IvW]_  2 >**,/9Qr
 "X0B%BCFXX-44G)Q>Q1Q-QRHII BlL3,>Segkm x3	J""6Hx"PHH6(S>  D(5U2
z~~na@EAFZ^^L!<ABz~~j!<ABQ>A#5,:J+l:NA/Z"?89OKE++		  v .2F2LeVYk[giwy oqw  zH  JO  RU  JU  V HH&
H= &.LL(O3Bj3PH:Y/V^dJ&$\Sbdvw4h>NO	E3&&$s!ueHVWFXZ (G4K|$H(RTV	
Y z" A=U);(>?@@A|  	Z!J6(RTUVTWXYY	ZV  	V!FvhbQRPSTUU	V  	C!=aSABB	CB ! $Q Q  	uIIbdjlprstt	u ! $Q Q  	uIIbdjlprstt	u.  j 			WY_aeghiij  	H!81#FGG	H  OJANNO6  SNPQRRSF  	mIIZ\bdhjkll	m< z" N  A		  K$	#/s2YZT:{3yK@@@Az  	jKKPRY\]R]_eghii	j  	RKKEvqQQ	RP  	'IIvdA' '	'<  	JKKJFT_`J!2!YeE<P<PQW<X6Y!%!@Ab	##.I%I>	'IN9R,SWX,X"3x)';#<y#H%PH-Y5I1IJH'IN9R,SWX,X"3x)';#<y#H%PH-Y5I1IJH&&v8&T%-x(L"NHhH J3K?'+Y4$  		Y$h8HJ J	J%	J\  D?CCD*  E@!DDEs3  AZ (AZ< A[ 8&A\  BA\< 3A\ C A\< 1*A\/AA\< -BA^ 9A]+ CA^ 3*A];AA^ /C3A^7 &(A_& A` 1A`5 (D
Aa" @:Ab A/AAb) CA$Ab) D+1Ab) J%BAc1M Ad# N-C/Ae S<,Af  T+C"Ak Y-Ak1 Z-AZ9Z<	A[[A[[A[[	A[=['A[8[8A[=\ 	A\\	A\\A\\	A\,\(A\< \+A\,\,A\< \/A\9\4A\< \<	A](]A]#]#A](]+	A]8]4A^ ]7A]8]8A^ ];A^^ A^ ^	A^4^A^/^/A^4^7	A_#_ A__A_#_&	A`_/A` ` A``	A`2`A`-`-A`2`5	Aa`>AaaAaa"	Aba+Ab	b	AbbAb&b%Ab&b)	Ac.b21Ac)c#Ac.c)Ac.c1	Ad c:AddAd d#	Aed,Ae	e	Aee	Ae=eAe8e8Ae=f 
Akf
Aj<f"CAi<i;Aj<i<	Aj3j$Aj.j)Aj<j.Aj3j3Aj<j<Akk	Ak.kAk)k)Ak.k1	Alk:AllAlc                  ,    e Zd ZdZdZd Zd Zd Zd Zy)WebhookHandlerNc                     y r   r   )selfr   r   s      r   log_messagezWebhookHandler.log_message1  s    r   c                   t        j                  |      j                  d      }	 | j                  |       | j	                  dd       | j	                  dt        t        |                   | j	                  dd       | j                          | j                  j                  |       y # t        $ r }t        j                  d|       Y d }~y d }~ww xY w)Nr   zContent-Typezapplication/jsonContent-Length
ConnectionrO  zSend failed: %s)rV   rW   encodesend_responsesend_headerr>   r   end_headerswfiler  rL   r7   r   )r  coderX   bodyr&  s        r   
_send_jsonzWebhookHandler._send_json4  s    zz'"))'2	.t$^-?@-s3t9~>\73JJT" 	.KK)1--	.s   BB+ +	C4CCc                   | j                   j                  d      d   }|j                  d      r|j                  dd      j	                         }|s| j                  dddi      S 	 t        | j                  |      }|s| j                  dd	|d
      S | j                  d||d   xs d|d   |d   d      S |dk(  rE| j                  dddt        j                  t        j                        j                         d      S |dk(  r_	 t        | j                        \  }}| j                  dd|d   |d   |d   |d   |d   |d   | j                   j#                         |d	      S | j                  dddi      S # t        $ r'}| j                  ddt        |      i      cY d }~S d }~ww xY w# t        $ r'}| j                  ddt        |      i      cY d }~S d }~ww xY w)Nr   r   z/webhook/status/r!     r8   zmissing request_id  zrequest_id not found)r8   rZ   r[  r   r'   r   )rZ   ra   r`   r8     z/healthTzbybit-webhook)okservicer  z/statusr]   r^   r  ry   r  )	r  queue_pendingqueue_completedqueue_failedqueue_skippedqueue_expiredqueue_tunnel_blockedqueue_size_in_memoryposition_owners	not found)r  r   r  rn   r4   r	  r  dbrL   r>   r   rS   r   rT   rU   r  r$  qsize)r  r  rZ   r   r&  r  r  s          r   do_GETzWebhookHandler.do_GET@  s   yys#A& ??-.&8"=CCEJsW6J,KLL?(*=??3:P`j0klls",!!f1	 #A V	-   9??3*ll8<<0::<)  
 9?!:477!Cs%+I%6'-k':$*8$4%+I%6%+I%6,23C,D,0MM,?,?,A'-
- 
 
 sWk$:;;1  ?sWc!f,=>>?,  ?sWc!f,=>>?sC   ',E: #E: AF- :	F*F%F*%F*-	G6GGGc                f   | j                   j                  d      d   }|dk(  r	 t        | j                  j	                  dd      xs d      }|r,| j
                  j                  |      j                  dd      nd	}t        j                  |      }t        |j	                  d
d            j                         j                         }|dvr| j                  dddi      S 	 t        dz  j                  |d       |dk(  rnt#        | j$                        }|D ]  }| j&                  j)                  |        t+        |      }t,        j/                  d|       | j                  ddd|d      S t1        | j$                        }	t,        j3                  d|	       | j                  ddd|	d      S |dk7  r| j                  dddi      S 	 t        | j                  j	                  dd      xs d      }|r,| j
                  j                  |      j                  dd      nd}	 t        j                  |      }t        |j	                  d!d            j                         }|t4        vrxt,        j3                  d"|j	                  d#      |j	                  d$      |j	                  d%      t7        d& |j9                         D                     | j                  d'dd(i      S |j	                  d#      s| j                  ddd)i      S |j	                  d$      s| j                  ddd*i      S |j	                  d+      |j	                  d+      dk(  r| j                  ddd,i      S d-}	 t        dz  j;                  d      j                         j                         }|dk(  }	 t?        | j$                  ||/      }|s| j&                  j)                  ||f       |rt,        j3                  d2|       t,        j/                  d3||j	                  d#      |j	                  d$      |j	                  d+      |rd4nd       | j                  dd|tC        jD                  tF        jH                        jK                         d5|d6      S # t         $ r Y w xY w# t         $ r'}
| j                  ddt        |
      i      cY d }
~
S d }
~
ww xY w# t         $ r(}
| j                  ddt        |
      d      cY d }
~
S d }
~
ww xY w# t         $ r(}
| j                  dd t        |
      d      cY d }
~
S d }
~
ww xY w# t<        $ r Y t         $ r!}
t,        j3                  d.|
       Y d }
~
d }
~
ww xY w# t         $ r>}
t,        jA                  d0|
       | j                  dd1t        |
      d      cY d }
~
S d }
~
ww xY w)7Nr   r   z/tunnel-stater   0r   rn   )errorsz{}stater!   )updownr  r8   zstate must be 'up' or 'down'ztunnel_state.txtr   r   z4TUNNEL UP: sbloccate e accodate %d richieste frescher[  T)r  r  	unblockedz\TUNNEL DOWN: %d richieste gia' bloccate, nuove richieste saranno accettate ma non processater!  )r  r  already_blockedr  z/webhookr  r  zread failed)r8   detailzinvalid JSONr0  z>REJECTED WRONG_SECRET: symbol=%r side=%r strategy=%r fields=%sr   r   r   c              3  l   K   | ],  }t        |      j                         d k7  s t        |       . yw)r0  N)r>   r   )r   keys     r   r   z)WebhookHandler.do_POST.<locals>.<genexpr>  s&     WC#c(..:Jh:Vs3xWs   !44i  WRONG_SECRETzmissing symbolzmissing sider   zmissing qtyFz'Marker tunnel_state.txt read failed: %s)rY   zQueue/persist failed: %szqueue failedzQTUNNEL DOWN: richiesta %s persistita ma NON accodata; attende tunnel UP entro TTLz$ACCEPTED %s: %s %s qty=%s (queued%s)z, tunnel_blockedz-Order queued, will be processed in background)r  rZ   	queued_atmessager  )&r  r   r2   headersr   rfilereaddecoderV   r   r>   r4   r   r	  r  r=   rL   r   r  r$  putr   r7   rA   r   r   ALLOWED_SECRETSsortedkeysr3   FileNotFoundErrorr[   r   r   rS   r   rT   rU   )r  r  lengthr   datar  unblocked_itemsr%  r"  nr&  received_secretr  r   rZ   s                  r   do_POSTzWebhookHandler.do_POSTl  s   yys#A& ?"?T\\--.>DIJSYdjjoof-44WY4O_czz#DHHWb1288:@@B.??3:X0YZZ11==eg=V D=&@&IO / 0))$/0 #O 4IHHSU^_??3tdYb0cdd4TWW=AKK ~  AB  C??3tfab0cdd :??3+(>??
	T))*:C@EAFFOU$**//&)000K[]C
	U::c?D
 dhhx45;;=/1 KKP"DHHV$4dhhz6JW499;WW
 ??3.(ABB xx!??32B(CDDxx??3.(ABB 88E?"dhhuo&;??3-(@AA 	F 22==w=OUUW]]_F#v-N	U($~VJ!!!:t"45
 KKk"$ 	7TXXh/&1A488E?'5#2	? s$!hll3==?F,%
  	i !   ?sWc!f,=>>?  	T??3-3q6(RSS	T  	U??3.CPQF(STT	U> ! 	 	FKKA1EE	F  	UMM4a8??3.CPQF(STT	Us   B<R $R >A2R 1A R AS	 &S= *:T1 %7U) 	RR RR 	SS;SS		S:S5/S:5S:=	T.T)#T.)T.1	U&=U&U!!U&)	V023V+%V0+V0)	__name__
__module____qualname__r  r$  r  r	  r  r8  r   r   r   r  r  -  s"    	BH
.*<Xjr   r  c                    t                t               } t        |       }t        j                         }|r8t
        j                  dt        |             |D ]  }|j                  |        nt
        j                  d       t        j                  t        | |fd      }|j                          | t        _        |t        _        t#        t$        t&        ft              t
        j                  d       t
        j                  d       t
        j                  dt$        t&               t
        j                  d       t
        j                  d	       t
        j                  d       fd
}t)        j(                  t(        j*                  |       t)        j(                  t(        j,                  |       	 j/                          	 t0        j3                          t
        j                  d       y # t4        $ r Y !w xY w# 	 t0        j3                          n# t4        $ r Y nw xY wt
        j                  d       w xY w)Nz8RECOVERY: %d pending items from previous run, re-queuingzNo pending items to recoverT)targetr   daemonz<============================================================zWEBHOOK READY (async pattern)zListening on %s:%dz,Fast path: <100ms (validation + queue + 200)z3Background worker: process order in separate threadc                P    t         j                  d       j                          y )NzShutdown signal received)r7   rA   shutdown)signumframeservers     r   r@  zmain.<locals>.shutdown  s    +,r   zServer stopped)rC   rO   r   queueQueuer7   r   r   r.  rA   	threadingThreadr'  startr  r  r$  r	   LISTEN_HOSTLISTEN_PORTr  SIGINTSIGTERMserve_foreverr0   unlinkrL   )r  r   r$  r%  workerr@  rC  s         @r   mainrP    s    
B $B'G{{}HNPST[P\] 	DLL	 	./ [H~dSF
LLN N&N !+{!;^LFHHXHH,-HH!;<HH;<HHBCHHX MM&--*
MM&..(+#	OO 	!"  			OO 		!"sB   8H 	G3 3	G?>G?H>HH>	H%"H>$H%%H>c                    | rt        |       |k  ryd|dz   z  }t        | d|       |z  }| |d D ]  }||z  |d|z
  z  z   } |S )zCalcola EMA 50 su una lista di close. Ritorna float o None.
    Usa la formula standard EMA = close * k + prev_ema * (1-k), k=2/(period+1).
    Seed: SMA delle prime `period` candele. Gestione input insufficienti: None.Ng       @r'   )r   sum)closesperiodkemacs        r   compute_ema50rX    sh     S[6)vzA
fWfo

'CFG_ $!ecQUm#$Jr   c                "   t        |       j                         j                         } | st        S | j	                  d      r| dd S | j	                  d      r	 t        t        | dd       dz        S | xs t        S # t        $ r	 t        cY S w xY w)zConverte '30m' -> '30', '4H' -> '240', '1h' -> '60', '3h' -> '180' per API Bybit.
    Gestisce case-insensitive. Default fallback: EMA50_DEFAULT_TIMEFRAME.mNrn  hr(   )r>   r4   r   EMA50_DEFAULT_TIMEFRAMEr  r2   r<   )tf_strs    r   _tf_string_to_bybitr^  #  s     [ &&(F&&scr{s	+s6#2;'",-- ,,,  	+**	+s   A< <BBc                v   	 | dv rM	 ddl m}  |       D ];  }|d   |j                         k(  s|j                  dd      s-t        |d         c S  n| d	k(  rM	 dd
l m	}  |       D ];  }|d   |j                         k(  s|j                  dd      s-t        |d         c S  nQ| dk(  rL	 ddl m
}  |       D ];  }|d   |j                         k(  s|j                  dd      s-t        |d         c S  	 ddlm}  |       D ];  }|d   |j                         k(  s|j                  dd      s-t        |d         c S  	 t
        S # t        $ r# t        j	                  dt
               t
        cY S w xY w# t        $ r# t        j	                  dt
               t
        cY S w xY w# t        $ r# t        j	                  dt
               t
        cY S w xY w# t        $ r# t        j	                  dt
               t
        cY S w xY w# t        $ r*}t        j	                  d|t
               Y d}~t
        S d}~ww xY w)a  Ritorna il timeframe Bybit ('60'=1H, '240'=4H, '30'=30m, '180'=3H, '5'=5m, '15'=15m) per la strategia.
    - vptr3: legge da VPTR_V3_ASSETS.csv colonna timeframe
    - rettangolo: legge da rettangolo_assets.csv
    - rettangolo_simple: legge da RETTANGOLO_SIMPLE_ASSETS.csv
    - ma_trailing: legge da MA_TRAILING_ASSETS.csv
    - fallback: 4H (240) se read fallisce o simbolo non in lista.
    NOTA Coder 19/07 13:46: load_assets() per rettangolo NON esiste in sltp_engine.py
    (esiste solo load_vptr3_assets), quindi importiamo da rettangolo_config.py
    che e' il modulo canonico per la lettura di rettangolo_assets.csv.
    FIX 2026-08-05 (Mattia 4 strategie): supporto RETTANGOLO_SIMPLE + MA_TRAILING.
    re  r   )load_vptr3_assetszWP007 get_strategy_timeframe: sltp_engine.load_vptr3_assets non importabile, fallback %sr   enabledT	timeframer   )load_rettangolo_simple_assetszWP007 get_strategy_timeframe: load_rettangolo_simple_assets non importabile, fallback %sr   )load_ma_trailing_assetszQP007 get_strategy_timeframe: load_ma_trailing_assets non importabile, fallback %s)load_assetszWP007 get_strategy_timeframe: rettangolo_config.load_assets non importabile, fallback %sz6P007 get_strategy_timeframe exception: %s, fallback %sN)sltp_enginer`  r;   r7   r   r\  rq  r   r^  rc  rd  rettangolo_configre  rL   )r   r   r`  assetrc  rd  _load_rett_assetsr&  s           r   get_strategy_timeframerj  4  s0   *j++/9
 +, C?flln49d9S.u[/ABBC ,,/E
 78 C?flln49d9S.u[/ABBC &/?
 12 C?flln49d9S.u[/ABBC	+J
 '( 	?EX&,,.0UYYy$5O*5+=>>	?
 #"O  /u35../  /u35../  /o35../  	+KKq/1**	+  jLaQhii""js   H E	  H H H H E8  H  H H #H +F' 1 H H %H 5H 7G = H H 1H H 	)E52H 4E55H 8)F$!H #F$$H ')GH GH )H?H HH 	H8H33H8c                   t         sy	 t        ||      }| j                  ||t              }|rt	        |      t
        dz   k  rBt        j                  d|||rt	        |      ndt
        dz          dd|rt	        |      nd dfS |dd	 D cg c]  }t        |d
          }}t	        |      t
        dz   k  ryt        |t
              }|d   }	|y|j                         dv r|	|k  r/dd|	dd|dfS |j                         dv r|	|kD  rdd|	dd|dfS dd|	dd|dfS c c}w # t        $ r'}
t        j                  d|
       dd|
 fcY d}
~
S d}
~
ww xY w)a  Controlla P007 regime EMA 50.
    Ritorna (pass, reason).
    - pass=True: regime OK, ordine puo' procedere
    - pass=False: regime KO, SKIP-REGIME
    - Se fetch klines fallisce o dati insufficienti: pass=True con reason warn
      (regola Mattia "MAI bloccare").
    Usa candela [-2] (P010 Charter, esclude candela in formazione).
    fetch_ohlcv ritorna lista [ts, o, h, l, c, v]; escludiamo l'ultima (live)
    e poi prendiamo close[-2] dalle klines chiuse.)TzP007 DISABLED via kill switchr   zKP007 SKIP-CHECK: klines insufficienti per %s tf=%s (%d<%d), regime IGNORATOr   Tz,P007 skip-check: dati insufficienti (klines=r   Nrn  r   r'   )Tz6P007 skip-check: chiuse insufficienti post-filtro live)TzP007 skip-check: ema50 Noner   Fz!P007 SKIP-REGIME LONG: close[-2]=ro  z < EMA50[-2]=r   z"P007 SKIP-REGIME SHORT: close[-2]=z > EMA50[-2]=zP007 OK: close[-2]=z vs EMA50[-2]=z8P007 check exception: %s, regime IGNORATO (MAI bloccare)zP007 skip-check: exception )EMA50_REGIME_ENABLEDrj  fetch_ohlcvEMA50_LOOKBACK_CANDLESr   EMA50_PERIODr7   r   r   rX  r   rL   )clientr   r   r   r4  klinesrW  rS  ema50
last_closer&  s              r   r  r  n  s     69#Hf5##FB0FGV|a'77KKe6CKq,QRBRTHX^VdeHffghii'-cr{3!%!+33v;))Sfl3BZ
=8::<?*E!!B:cBRR_`efi_jkllZZ\..E!!CJsCSS`afgj`klmm+Js+;>%PSUVV 4  9NPQR3A37889sN   A;D( D( D#!D( 9D( "D( 4"D( D( #D( (	E1EEE__main__)F)rX   r  rY   boolreturnr>   )NN)rZ   r>   r`   r>   r8   r>   )rZ   r>   re   r>   )expired_pending_ttl)rx   r2   )rZ   r>   rx   r2   rw  rv  )rw  r2   )rw  r>   )rX   r  rw  rv  )rX   r  r   r>   rw  ztuple[str, str])rX   r  r   r   rw  r   )r   )r   r>   r   r>   r   r>   rZ   r>   r`   r>   r   r>   )r   r>   )r   r>   r   r>   r   r>   )T)rZ   r>   r   rv  )rZ   r>   )r$  zqueue.Queue)rZ   r>   rX   r  )r+   )f__doc__
__future__r   r?   r9   rV   r"  r  loggingrF  rD  rH   rQ   pathlibr   r   r   r   http.serverr   r	   r  r  r  LOG_FILEr  rJ   r0   basicConfigINFOFileHandlerStreamHandler	getLoggerr7   RLockr   r   _EXPECTED_DEFAULTenvironr   r4   _webhook_secrets_envr   r/  EXPECTED_SECRETr  r  _map_envpairr  stratrI  rJ  r  r  r  r2   PENDING_MAX_AGE_SECONDSrm  rp  r\  ro  r  r  rC   rO   r[   rb   rf   ri   rr   r~   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r  r'  r  r  rP  rX  r^  rj  r  r9  )ss   0r   <module>r     s   D # 	 
          2 2 C
 56

 t ++|#

&
&++   
,,>Hw7 g	" )//
 * zz~~&7<BBD *>*D*DS*IWQQWWYqwwyWO()O#
 ::>>"<gFLLNYRY  ::>>7<BBDs# =$;C+JC/4{{}		,=
   b#bjjnn5QSX&Y"Z[ 
     ,_ )? 2$ 1 1h 	 	       6M  $ 4K.1PT * 4 4 	4 	4     O O.Z
#RL2.  EN#&25?B  
 
  
 / /$   L L&    6 
0FEXi+ i^4#t
-"7#t$9N zF s1 Xs   %M;M