
    iKE              
          d 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mZ ej                  j                  ej                  j                  e            Zej                  j                  ed      Zej                  j                  ed      ZdZdZdZd	Zd
ZdZg dZdZdZ ej4                  ej6                  dd        ej8                  d      ZdedefdZd+dede de!de e"z  dz  fdZ#de"e   fdZ$de"e   de fdZ%de"e   de fdZ&de"e   de fdZ'd e(de(fd!Z)d"e(d#e(de(fd$Z*de"e   de+e e f   fd%Z,de"e   de fd&Z-d'e ddfd(Z.d) Z/e0d*k(  r e/        yy),u=  
signal_aggregator.py — Module de signaux macro/on-chain (observation mode)
Processus séparé : lit les APIs, écrit un cache JSON toutes les 60s.
Démarrage : nohup python3 signal_aggregator.py > logs/signal_aggregator.log 2>&1 &

Adaptations vs version originale :
  - Symboles USDC → USDT pour tous les appels fapi (bot trade USDC, perps sont USDT)
  - NETFLOW_INFLOW_HIGH_USD = 500_000 (calibré mid-caps, pas large-caps)
  - unlock_proximity = 0 toujours (TokenUnlocks n'a pas d'API publique)
  - Watchlist dynamique : lit market_spy_watchlist.json si disponible
    N)datetimetimezonezsignal_cache.jsonzmarket_spy_watchlist.json<   i  i ig{Gz?g{Gzt)BIOUSDCWLFIUSDC
PENDLEUSDCBANANAS31USDCXVGUSDCDASHUSDCTSTUSDC	PARTIUSDCORCAUSDCAPEUSD
MANTRAUSDCORDIUSDCMEGAUSDC
GIGGLEUSDCzhttps://fapi.binance.comzhttps://api.binance.comz.%(asctime)s [SIGNAL] %(levelname)s %(message)sz%H:%M:%S)levelformatdatefmtsignal_aggregatorsymreturnc                 d    | j                  d      r| dd dz   S | j                  d      r| S | dz   S )u@   Convertit BIOUSDC → BIOUSDT pour les appels fapi (perps USDT).USDCNUSDT)endswith)r   s    signal_aggregator.pyusdt_symbolr    8   s:    
||F3Bx&  
||F
<    urlparamstimeoutc                     	 t        j                  | ||      }|j                          |j                         S # t        $ r%}t
        j                  d|  d|        Y d}~yd}~ww xY w)u*   GET avec timeout, retourne None si échec.)r#   r$   zGET failed : N)requestsgetraise_for_statusjson	Exceptionlogdebug)r"   r#   r$   res        r   _getr0   @   s\    LLVW=	vvx 		KuBqc*+s   7: 	A(A##A(c                  0   	 t         j                  j                  t              rMt	        t              5 } t        j                  |       }ddd       t        t              rt        |      dkD  r|S t        S # 1 sw Y   /xY w# t        $ r Y t        S w xY w)zECharge la watchlist depuis le fichier JSON du bot, fallback statique.Nr   )ospathexistsWATCHLIST_FILEopenr*   load
isinstancelistlenr+   STATIC_WATCHLIST)fdatas     r   load_watchlistr>   K   s}    77>>.)n% $yy|$$%#d)a- $ $  s(   2B A8
'B 8B=B 	BBsymbolsc                    d}d}d}| D ]  }t        |      }t        t         dd|i      }|rd|v r	 |t        |d         dz  z  }|dz  }t        t         dd|i      }t        t         d	d|i      }|sp|ss	 t        |d
         }	t        |d         }
||	|
z  z  } |dk(  rdddddS ||z  }|t        |d      z  }|t        k\  rd}n+|t        k  rd}n|t        z
  t        t        z
  z  }dd|z  z
  }|dkD  rd}n|dkD  rd}n
|dkD  rd}nd}d|z  d|z  z   }t        |d      t        |d      t        |d      |dS # t        t
        f$ r Y 
w xY w# t        t
        f$ r Y cw xY w)u   
    Score 0-10 basé sur funding rate + open interest agrégés.
    > 7 : momentum haussier fort
    < 3 : funding négatif / OI faible = pression baissière
            r   /fapi/v1/premiumIndexsymbollastFundingRated      /fapi/v1/ticker/price/fapi/v1/openInterestpriceopenInterest      @)scoreavg_funding_pcttotal_oi_usdvalid       @       @      @逖 @B       @g?ffffff?      )
r    r0   BINANCE_FAPIfloat
ValueError	TypeErrormaxFUNDING_BULL_THRESHOLDFUNDING_BEAR_THRESHOLDround)r?   total_fundingrN   valid_countr   fsymr=   
price_dataoi_datarI   oiavg_funding
avg_oi_usdfunding_scoretoi_scorecombineds                    r   compute_funding_oi_scorern   Z   s    MLK 3 |n$9:Xt<LM%-t,='>!?#!EEq 
 \N*?@8TBRS
\N*?@8TBRS'j12gn56
*'. acTUVV+-K[!!44J ,,	.	. 116LOe6efcAg J	j	 	i	m#dXo5Hx# a0lA.	 S 	*  	* s#   E=$EEEE-,E-c                    d}d}d}| D ]  }t        |      }t        t         d|ddd      }|rt        |t              s8t        t         dd|i      }|rt        |d	         nd
}	 |D ]H  }	t        |	j                  dd            }
t        |	j                  dd            }||
|z  z  }|||z  z  }J |dz  } |dk(  s||z   dk(  rdddddS ||z
  }|||z   z  }|t        k\  rd}n"|t        k\  rd}n|dk\  rd}n|t        k\  rd}nd}|dk\  rt        d|d
z         }n|dk  rt        d|d
z
        }t        |d      t        |d      t        |d      |dS # t        t        t        f$ r Y hw xY w)u   
    Score 0-10 basé sur taker buy/sell ratio (proxy netflow).
    Seuil adapté mid-caps : 500K$ / 100K$ au lieu de 5M$ / 1M$.
    rA   r   !/futures/data/takerlongshortRatio15m   rC   periodlimitrG   rC   rI         ?buyVolsellVolrF   rK         ?)rL   net_flow_usd	buy_ratiorO         "@      @      @      @      ?333333?
   皙?rX   )r    r0   rZ   r8   r9   r[   r(   r\   r]   KeyErrorNETFLOW_INFLOW_HIGH_USDNETFLOW_INFLOW_MED_USDNETFLOW_OUTFLOW_WARN_USDminr^   ra   )r?   total_buy_voltotal_sell_volrc   r   rd   r=   re   rI   barbuy_volsell_volnet_flowr{   rL   s                  r   compute_netflow_pressurer      s   
 MNK 3 n=>uq9
 :dD1 \N*?@8TBRS
.8j)*c	 3 1!56 A!67'E/1(U"22	3
 1K+2 aMN:q@caPP/H-."@AI **	+	+	Q	-	- DB$	d	Aus{# uah*9a(	 7 Ix0 		s   )AEE21E2_symbolsc                     ddddS )uz   
    DÉSACTIVÉ — TokenUnlocks n'a pas d'API publique.
    Retourne toujours 0 (signal neutre, pas de pénalité).
    rA   zdisabled - no public APIr   )rL   noterO    )r   s    r   compute_unlock_proximityr      s    
 "<qIIr!   funding_pctc                 h    | t         k\  ry| t        k  ry| t        z
  t         t        z
  z  }dd|z  z
  S )zQConvertit un funding (%) en score 0-10 (plus haut = plus favorable long squeeze).rP   rQ   rR   )r_   r`   )r   rk   s     r   _funding_to_scorer      s?    ,,,,	-	-2HKa2abAq=r!   rz   r{   c                     | t         k\  rd}n"| t        k\  rd}n| dk\  rd}n| t        k\  rd}nd}|dk\  rt        d|d	z         }|S |d
k  rt	        d|d	z
        }|S )z,Convertit netflow + buy_ratio en score 0-10.r|   r}   r   r~   r   r   r   r   rv   r   )r   r   r   r   r^   )rz   r{   rL   s      r   _netflow_to_scorer      s|    ..	/	/			1	1DB$ L 
d	Aus{#Lr!   c                    i }| D ]  }t        |      }d}t        t         dd|i      }|rd|v r	 t        |d         dz  }d}d}d}t        t         d	|d
dd      }	t        t         dd|i      }
|	rt        |	t              rt|
rr	 t        |
d         }d}d}|	D ]D  }|t        |j                  dd            |z  z  }|t        |j                  dd            |z  z  }F ||z   }|dkD  r||z
  }||z  }d}d}t        t         dd|i      }|r |
r	 t        |d         t        |
d         z  }||s5t        ||nd      }t        ||      }d}|dkD  rd}n|dkD  rd}n|dkD  rd}d|z  d|z  z   d|z  z   }|dk\  rd}n|dk\  rd }n
|d!k\  rd"}nd#}d$}||t        k\  r|dk  rd%}n	||d&k\  rd'}||t        ||ndd(      t        |d      t        |d      t        |d      t        |d)      t        |d)      t        |d)      ||d*||<    t        |j                         d+ d,      }|dd( }d}|rt!        d- |D              t#        |      z  }t        |d)      t#        |      ||D cg c]  }|d.   d%k(  s|d    c}d/}||fS # t        t
        f$ r d}Y bw xY w# t        t
        t        f$ r Y w xY w# t        t
        t        f$ r d}Y w xY wc c}w )0z
    Construit les signaux par symbole pour identifier des setups 'explosifs' minutes.
    Retourne (symbol_map, explosive_summary).
    NrB   rC   rD   rE   rA   ry   Frp   rq   rr   rs   rG   rI   rw   r   rx   TrH   rJ   rP   rS   rQ   rT   rR   rU   rV   rW   g333333?g      @
SETUP_FORTSETUP_MOYENg      @MIXTEFAIBLELOWHIGHg~jtx?MEDIUMrY   rX   )rC   fapi_symbolr   rz   r{   oi_usdrj   netflow_scoreexplosive_scoresetup	trap_riskc                     | d   S )Nr   r   )xs    r   <lambda>z+compute_symbol_signal_map.<locals>.<lambda>k  s    q9J7K r!   )keyreversec              3   &   K   | ]	  }|d      yw)r   Nr   ).0r   s     r   	<genexpr>z,compute_symbol_signal_map.<locals>.<genexpr>p  s     AQA/0As   r   )avg_explosive_scoresymbols_rankedtop_candidatesbull_trap_alerts)r    r0   rZ   r[   r\   r]   r8   r9   r(   r   r   r   r_   ra   sortedvaluessumr:   )r?   
per_symbolr   rd   r   r=   rz   r{   taker_oktakerre   rI   buy_sumsell_sumr   totalr   rf   f_scoren_scorerl   r   r   r   rankedr   avg_explosiver   summarys                                r   compute_symbol_signal_mapr     s   
 J \
3 |n$9:Xt<LM%-##D):$;<sB
 	n=>uq9
 \N*?@8TBRS
Zt,j12  ECuSWWXq%9:UBBGcggi&; <u DDHE  (*19#*X#5L '%I#H
 ,'<=$?OPzw~67%
7@S:TT x#;3JKPST#L)< JHj HiH'>dWn=Q c! E#!E#EE 	#7M(MS_bcScI$)= I  0GSRST!,2y!,FA&"7A."7A.$_a8"

3a\
| J%%'-KUYZFBQZNMA&AACKO  %]A6f+(28UQAkNf<TQx[U	G wE 	* #"#2 	84  	84 | VsB   J
A1J)K3K!
K!
J&%J&)K KKKc                    t        |       }t        |       }t        |       }t        |       \  }}|d   |d   z   }|dk\  rd}n|dk\  rd}n|dk\  rd}n
|dk\  rd	}nd
}d}d}	|dk  r	d}d|dd}	|dk\  rd}
n|dk\  rd}
n
|dk\  rd}
nd}
t	        j
                  t        j                        j                         t        |       |||t        |d      d|||	t        |
d      d||dS )uJ   Calcule les 3 composantes et le score agrégé (0-20, unlock désactivé).rL      BULLISH_STRONG   BULLISH	   NEUTRAL   BEARISHBEARISH_STRONGF rY   zScore .1fu   /20 < 5 — serait veto en prod   rv   r   g333333?   gffffff?ry   rX      T)	timestampsymbols_count
funding_oinetflowunlockscore_total	score_maxinterpretationveto_activeveto_reasonsize_modulatorobs_modesymbol_signals	explosive)rn   r   r   r   r   nowr   utc	isoformatr:   ra   )r?   r   r   r   
symbol_mapr   	raw_scorer   r   r   size_mods              r   compute_all_signalsr   }  s.   )'2J)'2G)'2F5g>J	 G$ww'77I B)	b"	a"	a") KK1}yo-LM B	b	a \\(,,/99;W!Y*(""!,$ r!   payloadc                    t         dz   }	 t        |d      5 }t        j                  | |d       ddd       t	        j
                  |t                t        j                  d| d   dd	| d
    d| d   d   dd| d   d   dd| d   d   dd       y# 1 sw Y   exY w# t        $ rM}t        j                  d|        	 t	        j                  |       n# t        $ r Y nw xY wY d}~yY d}~yd}~ww xY w)u5   Écriture atomique du cache JSON (tempfile + rename).z.tmpwrX   )indentNu   Cache écrit — score=r   r   z/20 (r   z) | funding=r   rM   z+.5fz% | netflow=r   rz   z+.0fz	$ (ratio=r{   z.3f)u   Échec écriture cache: )
CACHE_FILEr6   r*   dumpr2   replacer,   infor+   errorremoveOSError)r   tmpr<   r/   s       r   write_cacher     s   
v
C#s^ 	,qIIgq+	,


3
#%gm&<S%A B()* +|,->?E Fy).9$? @i(5c:!	=	
	, 	,  		,QC01	IIcN 		 sR   B  BA#B  BB   	C6)C1CC1	C$!C1#C$$C11C6c                     t         j                  d       t         j                  dt                t         j                  dt         d       t         j                  d       d} 	 	 t	               }t         j                  dt        |       d       t        |      }t        |       d} t        j                  t               _# t        $ r t         j                  d	       Y y t        $ r[}| d
z  } t         j                  d|  d|        | dk\  r,t         j                  d       t        j                  d       d} Y d }~d }~ww xY w)Nu6   === signal_aggregator démarré (observation mode) ===u
   Cache → u   Mise à jour toutes les suH   VETO désactivé, size_modulator ignoré — mode observation uniquementr   zWatchlist: z	 symbolesu   Arrêt demandérF   zErreur cycle #r&   rY   u&   5 erreurs consécutives — pause 5mini,  )r,   r   r   UPDATE_INTERVALr>   r-   r:   r   r   KeyboardInterruptr+   r   timesleep)consecutive_errorsr?   r   r/   s       r   mainr     s   HHEFHHz*&'HH''8:;HHWX
	'$&GIICL>;<)'2G !" 	

?#)  ! 	HH&' 	'!#II'9&:"QC@A!Q&		BC

3%&"	's    (AC E	&E	.AEE	__main__)N   )1__doc__r2   r*   r   loggingtempfiler'   r   r   r3   dirnameabspath__file__
SCRIPT_DIRjoinr   r5   r   r   r   r   r_   r`   r;   rZ   BINANCE_SPOTbasicConfigINFO	getLoggerr,   strr    dictintr9   r0   r>   rn   r   r   r[   r   r   tupler   r   r   r   __name__r   r!   r   <module>r     s  
 
      ' GGOOBGGOOH$=>
GGLL-@A
GGLL-HI # " #    !   *(   
,,;
 g+,
S S c 4  TD[4=O 
S	 
Ad3i AD AH?d3i ?D ?DJtCy JT J5 U E e  (rtCy rU4:5F rn6c 6t 6r $ 0$> zF r!   