
    bjAm                    l   U 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m	Z	m
Z
mZ ddlmZmZmZmZ ddlZddlZ ej(                  d      ZdZdZd	Zd	Zej4                  j7                  ej4                  j9                  e            Zej4                  j?                  ed
d      Z e	 G d d             Z!e	 G d d             Z"dddddddZ#ddddddZ$dddZ%dddZ&d d!d"Z'd#d$d%d&ddd'd(d)d*d+d	dd'd,d-dd.d	dd	d/d0d1d	d	d	d2d3d(d)dd4d	dd	d5d	d	d6d	d2d7d8Z(dQd9Z)dRd:Z*dSdTd;Z+dUd<Z,dVd=Z-dVd>Z.dWd?Z/dXd@Z0dYdZdAZ1dBZ2 G dC dD      Z3da4dEe5dF<    ejl                         Z7d[dGZ8	 	 d\	 	 	 	 	 	 	 	 	 	 	 d]dHZ9dIdJdKdLdKdLd8dJdJdKdLdLdLd8dKdKdLdLdLdLd8dKdLdLdLdLdLd8dIdJdKdLdKdLd8dLdLdLdLdLdLd8dMZ:dNe5dO<   d^dPZ;y)_u  
Couche 7 — Détection de contexte de marché (per-token)
======================================================
Identifie le régime structurel d'un token donné parmi 6 régimes :
  - PHOENIX              : rebond depuis ATL récent
                           → neutralise partiellement RSI surachat
  - BULL_RUN             : tendance haussière saine
                           → grille standard
  - PARABOLIC_EXTENSION  : tendance verticale, pas encore de capitulation
                           → prudence accrue, pas de blocage
  - PUMP_TERMINAL        : pic terminal avec capitulation visible
                           → blocage long + suggestion profit-taking
  - CONSOLIDATION        : prix stable, faible volatilité, pas baissier
                           → grille standard, watch breakout
  - BEAR_RANGE           : tendance baissière nette
                           → blocage long

NOTE : couche per-token, complémentaire de `market_regime.py` (macro BTC+alts).

Cf. PREDICTIVE_SYSTEM_V2.md — section "COUCHE 7".

Calibration v2 (post-test 12/05/2026) :
 - PUMP_TERMINAL : trend_7d 100% → 40% (seuil initial inatteignable)
 - PHOENIX : scoring par points (≥3/4 conditions) au lieu de ET strict
 - Nouveaux régimes intermédiaires : PARABOLIC_EXTENSION, CONSOLIDATION
    )annotationsN)	dataclassfieldasdict)DictListOptionalTupleMarketContextzhttps://api.binance.com/api/v3FTdatazcouche7_decisions.logc                      e Zd ZU dZded<   ded<   ded<   ded<   ded<   ded	<   ded
<   ded<   ded<   ded<   ded<   ded<   y)ContextMetricsu2   Sous-indicateurs utilisés pour la classification.floatcurrent_priceatl_60dath_180ddistance_from_atl_60d_pctdistance_from_ath_180d_pctintdays_since_atl_60ddays_since_ath_180dtrend_7d_pcttrend_30d_pctvolume_ratio_7d_30dcapitulation_candles_3drealized_volatility_30dN)__name__
__module____qualname____doc____annotations__     1/home/ubuntu/crypto_trading_bot/market_context.pyr   r   @   sK    <NO$$ %%  ""r#   r   c                  ^    e Zd ZU dZded<   ded<   ded<   ded	<    ee
      Zded<   ddZy)ContextResultu)   Résultat de la détection pour un token.strregimer   
confidencer   metricsr   implications)default_factoryz	List[str]notesc                    t        |       S N)r   selfs    r$   to_dictzContextResult.to_dictZ   s    d|r#   Nreturnr   )	r   r   r   r    r!   r   listr-   r2   r"   r#   r$   r&   r&   Q   s0    3KT2E92r#   r&   g333333?g      @g?-      )dist_atl_mindist_atl_maxtrend_30d_minvolume_ratio_mindays_since_atl_maxmin_conditions_met      ?g?333333?   )dist_ath_mintrend_7d_minr:   capitulation_candles_minr=   皙?)r:   rB   333333?g?)r:   trend_30d_maxg{Gz?gQ?)trend_30d_abs_maxvolatility_maxU   neutralized      ?d   )rsi_overbought_thresholdstage_filtermax_position_size_multipliertarget_upside_pctuse_unlock_long_termblock_long_entriesK   standard      ?   F   cautious   )rM   rN   rO   rP   rQ   rR   suggest_tight_stop<   	amplified        )rM   rN   rR   suggest_profit_takingrQ   rO      )rM   rN   rO   rP   rQ   rR   watch_for_breakoutA   )rR   watch_for_phoenixrM   rQ   rO   )PHOENIXBULL_RUNPARABOLIC_EXTENSIONPUMP_TERMINALCONSOLIDATION
BEAR_RANGEc                N   | rt        |       dk  ry	 | dd D cg c]  }t        |d         t        |d         z
  ! }}|rt        t        j                  |            nd}|dk  ryd}| dd D ]  }t        |d	         t        |d         t        |d         t        |d
         f\  }}}}t	        ||z
        }	|t        ||      z
  }
||z
  }|
dt        |	d      z  kD  ss|d|z  kD  s||d	z  } |S c c}w # t        $ r"}t        j                  d|        Y d}~yd}~ww xY w)u   Compte les bougies de capitulation acheteuse sur les 3 derniers jours.

    Heuristique : upper_wick > 1.5× corps ET range > 1.5× moyenne 14j.
       r   iN   r7   r]   r@      rK   -q=zcapitulation detection error: )	lenr   npmeanabsmax	Exceptionloggerdebug)	klines_1dkranges	avg_rangecountohlcbody
upper_wick	day_rangees                r$   _detect_capitulation_candlesr      s;   
 I+5>st_E%!+ad+EE.4E"''&/*#	>23 	AqteAaDk51;adKJAq!Qq1u:DSAYJAIC#dE"222y3?7R
	  F  5aS9:s:   C9 $C4)C9 (A9C9 "C9 +C9 4C9 9	D$DD$c                    |dk  ry| |z
  |z  S )Nr   r]   r"   )endstarts     r$   _safe_pct_changer      s    z%K5  r#   c           	     2   t        |       dk  ryt        |t        |       dz
        }| |dz    d }t        j                  t        j                  t        j
                  |dd                  }t        |      rt        t        j                  |            S dS )z0Std des log-returns sur `window` derniers jours.rk   r]   r@   Nrn   )ro   minrp   difflogclipr   std)closeswindowwsubretss        r$   _realized_volatilityr      sx    
6{QFCK!O$A
1q5
C77266"''#ud345D"%d)544r#   c                   | rt        |       dk  rt        j                  d       y	 t        j                  | D cg c]  }t        |d          c}      }t        j                  | D cg c]  }t        |d          c}      }t        j                  | D cg c]  }t        |d          c}      }t        j                  | D cg c]  }t        |d          c}      }t        |d         }t        d	t        |             }t        d
t        |             }t        t        j                  || d             }	t        || d |	         }
|dz
  |	z
  }t        t        j                  || d             }t        || d |         }|dz
  |z
  }t        ||
      }|dkD  r||z  nd}t        |      dk\  rt        |t        |d               nd}t        |      dk\  rt        |t        |d               nd}t        |      dk\  r!t        t        j                  |dd             nd}t        |      dk\  r!t        t        j                  |dd             nd}|dkD  r||z  nd}t        |       }t        |d      }t        ||
||||||||||      S c c}w c c}w c c}w c c}w # t        $ r$}t        j!                  d| d       Y d}~yd}~ww xY w)uJ   Construit ContextMetrics depuis des klines 1d (>= 30 bougies, idéal 180).rV   z7Pas assez de klines pour compute_metrics (min 30 jours)Nrk   r7   rm      r[      r@   r   r]      i   i   iirU   )r   )r   r   r   r   r   r   r   r   r   r   r   r   zcompute_metrics error: T)exc_info)ro   ru   warningrp   arrayr   r   r   argminargmaxr   rq   r   r   r   rt   error)rw   rx   highslowsr   volumesr   win_60win_180atl_idxr   days_since_atlath_idxr   days_since_athdist_atl_pctdist_ath_pcttrend_7d	trend_30dvol_7dvol_30dvolume_ratiocap_candlesrvr   s                            r$   compute_metrics_from_klinesr      s   I+PQ/y9!%!+9:xxi8qt89	:151;:;((;AE!A$K;<fRj)RY(c3y>*biifWX/0fWXw/0 1*/biiwhi 012xy)'23!A+0'w?3;a<}x/SILVXYIY#M53DE_bKNv;Z\K\$]E&+4FGbe	14W1Brwwwrs|,-36w<23E%./3+2Q;v'C29=!&4'&2'3- .!# ,$/$&
 	
= :8:;R  .qc2TBsM   J- JJ- *J?J- J#.J- J(F;J- J- -	K6KKc                    t         }|d   | j                  cxk  xr |d   k  nc | j                  |d   k\  | j                  |d   k\  | j                  |d   k  d}t        |j                               |fS )Nr8   r9   r:   r;   r<   )dist_atl_in_rangetrend_30d_strongvolume_surge
atl_recent)PHOENIX_THRESHOLDSr   r   r   r   sumvalues)mphcondss      r$   _phoenix_scorer   %  s~    	B/13N3NdRTUcRddOOr//BB--4F1GG**b1E.FF	E u||~%%r#   c                    t         }| j                  |d   k\  | j                  |d   k\  | j                  |d   k\  | j                  |d   k\  d}t        |j                               |fS )NrA   rB   r:   rC   )near_athtrend_7d_verticaltrend_30d_extendedcapitulation_visible)PUMP_TERMINAL_THRESHOLDSr   r   r   r   r   r   )r   ptr   s      r$   _pump_terminal_scorer   0  so    	!B00B~4FF^^r./AAooO1DD ! 9 9R@Z=[ [	E u||~%%r#   c                   g }t        |       \  }}t        |       \  }}|t        d   k\  r{|d   rvt        t	        j
                  dd|z  z   dd            }|j                  d| d| j                  dd	| j                  d
d| j                  d
d| j                   
       d||fS |t        d   k\  r|d   rdd|z  z   }|dt        d| j                  t        d   z
  dz        z  z  }|dt        d| j                  dz
  dz        z  z  }t        t	        j
                  |dd            }|j                  d| d| j                  d
d| j                   d| j                  d
d| j                  d
       d||fS t         }| j                  |d   k\  r| j                  |d   k\  r| j                  dk(  rpt        t	        j
                  ddt        d| j                  dz        z  z   dd             }|j                  d!| j                  d
d	| j                  d
d"       d#||fS |t        d   k\  rjdd|z  z   }t        t	        j
                  |dd$            }|j                  d%| d&| j                   d'| j                  d
d| j                  d       d||fS t"        }|d   | j                  cxk  r|d(   k  rzn nw| j                  dk(  rht        t	        j
                  dd)t        d| j                  |d   z
  dz        z  z   dd             }|j                  d*| j                  d
d+       d,||fS t$        }	t'        | j                        |	d-   k  rD| j(                  |	d.   k  r2|j                  d/| j                  d
d0| j(                  d1       d2d3|fS |j                  d4| j                  d
d	| j                  d
       d5d6|fS )7u   Classifie le régime à partir des métriques.

    Ordre : PUMP_TERMINAL > PHOENIX (si rebond ATL récent fort) > PARABOLIC_EXTENSION
            > BULL_RUN > CONSOLIDATION > BEAR_RANGE
    r=   r   g?g?gffffff?zPT z/4 + capitulation: dist_ATH=z.0%z
, trend7d=z+.0%z, trend30d=z, cap_candles=rf   r   rU   r:   g      ?g?g       @zPhoenix z/4: ATL+z (zd), trend30d=u   , vol×z.2frc   rB   r   r?   rE   rK   g?zParabolic: trend30d=z, no capitulation yetre   r>   zPhoenix weak z/4 (ATL too old zd): trend30d=rF   rD   zBull steady: trend30d=z, no capitulationrd   rG   rH   zConsolidation: trend30d=z, vol=z.2%rg   g?zBear/range: trend30d=rh   gffffff?)r   r   r   r   rp   r   appendr   r   r   r   r   r   r   r   r   PARABOLIC_EXTENSION_THRESHOLDSBULL_RUN_THRESHOLDSCONSOLIDATION_THRESHOLDSrr   r   )
r   r-   pt_count	pt_detailph_count	ph_detailconfpebrcss
             r$   classify_regimer   ;  sE    E.q1Hi(+Hi 	,-ABB,-RWWTD8O3T4@A(78T8TUX7Y Z~~d+;qt6L M4457	

 e++ %&:;;	,@WdXo%s33Eo3V!VZ] ]^^^s3!6!6!< CDDDRWWT4./xj)D)DT(J K$$%]1??42H I))#.0	

 $%% 
(B	2o..NNb00%%*RWWTD3sAOOc4I+J$JJDRVWX"1??4"8
1>>RVBW X" #	
 %dE11 %&:;;dXo%RWWT4./H:%5a6J6J5K L-WQ5J5J34OQ	
 $%% 
B
?qE"_2EE%%*RWW4#cAOOb6I$IS#PQQQ$
  	-aood-CCTUV4&& 
"BAOO#6 77%%,<)==&qt&< =,,S13	
 e++ 
LL
5Zt?TU u$$r#   c                ~    t        |       }|yt        |      \  }}}t        |t        |d      |t        |   |      S )u/   Pipeline complet : klines 1d → ContextResult.Nr7   )r(   r)   r*   r+   r-   )r   r   r&   roundIMPLICATIONS)rw   r*   r(   r)   r-   s        r$   detect_market_contextr     sM    ))4G / 8FJQ'!&) r#   c           
     ~   	 t        j                  t         d| d|d|      }|j                  dk(  r$|j	                         }t        |t              r|S dS t        j                  d|  d|j                   d	|j                  dd
         y# t        $ r%}t        j                  d|  d|        Y d}~yd}~ww xY w)u<   Récupère `limit` bougies 1d depuis l'API publique Binance.z/klines1d)symbolintervallimit)paramstimeout   NzBinance z HTTP z: x   zfetch_daily_klines(z	) error: )requestsgetBINANCE_API_URLstatus_codejson
isinstancer5   ru   rv   textrt   )r   r   r   rr   r   s         r$   fetch_daily_klinesr     s    LLw'$$G

 ==C668D%dD14;t;xxvamm_Bqvvds|nMN *6()A3?@s$   AB B 5B 	B<B77B<i  c                  D    e Zd ZdZddZed	d       Zd
ddZddZddZ	y)MarketContextDetectoru  Singleton qui maintient un cache per-symbole de ContextResult (TTL 1h).

    Usage :
        from market_context import get_market_context_detector
        detector = get_market_context_detector()
        result = detector.get_context('SAGAUSDC')  # → ContextResult | None
    c                D    i | _         t        j                         | _        y r/   )_cache	threadingLock_lockr0   s    r$   __init__zMarketContextDetector.__init__  s    (*^^%
r#   c                    t        | d      }|rt        |      dk\  r| S | j                  d      rB| dd dz   }t        |d      }|r+t        |      dk\  rt        j	                  d|  d|        |S y)	u   Essaie le symbole tel quel, puis le fallback USDT si USDC absent.

        Ex : SAGAUSDC → essaie SAGAUSDC d'abord, puis SAGAUSDT.
        rV   r   USDCNUSDTzCouche7: fallback u    → )r   ro   endswithru   rv   )r   klinesfallbackklines2s       r$   _resolve_symbolz%MarketContextDetector._resolve_symbol  s{     $F"5c&kR'M??6"cr{V+H(<G3w<2-1&xjIJr#   c                `   t        j                         }| j                  5  |s7|| j                  v r)| j                  |   \  }}||z
  t        k  r|cddd       S ddd       	 | j                  |      }|t        j                  d| d       yt        |d      }|rt        |      dk  ryt        |      }|(| j                  5  ||f| j                  |<   ddd       |S |S # 1 sw Y   xY w# 1 sw Y   |S xY w# t        $ r%}t        j                  d| d|        Y d}~yd}~ww xY w)	u   Retourne le ContextResult pour le symbole, depuis le cache si frais.

        - Si données insuffisantes (< 30j) → retourne None (régime UNKNOWN)
        - Ne lève jamais d'exception — toujours fail-safe.
        Nz	Couche7: u$    non résolvable (< 30j de données)r   r   rV   zCouche7 get_context(z): )time	monotonicr   r   
_CACHE_TTLr   ru   rv   r   ro   r   rt   r   )	r1   r   force_refreshnowresulttsresolvedr   r   s	            r$   get_contextz!MarketContextDetector.get_context  s)    nnZZ 	" Vt{{%:![[0
8j(!		" 	" 	"	++F3Hy0TUV'<FS[2-*62F!ZZ 8+13-DKK'8M6M)	" 	"$8M 	NN1&QC@A	sS   0C&$,C? C? /C? C2	C? $C? &C/2C<7C? <C? ?	D-D((D-c                ~    | j                   5  | j                  j                  |d        d d d        y # 1 sw Y   y xY wr/   )r   r   pop)r1   r   s     r$   
invalidatez MarketContextDetector.invalidate  s0    ZZ 	*KKOOFD)	* 	* 	*s   3<c                ~    | j                   5  t        | j                        t        dcd d d        S # 1 sw Y   y xY w)N)cached_symbolsttl_seconds)r   ro   r   r  r0   s    r$   cache_statsz!MarketContextDetector.cache_stats	  s3    ZZ 	S&)$++&6zR	S 	S 	Ss   3<N)r4   None)r   r'   r4   zOptional[str])F)r   r'   r  boolr4   Optional[ContextResult])r   r'   r4   r  r3   )
r   r   r   r    r   staticmethodr   r  r  r  r"   r#   r$   r   r     s1    &  $B*Sr#   r   zOptional[MarketContextDetector]_detector_instancec                     t         %t        5  t         
t               a ddd       t         S t         S # 1 sw Y   t         S xY w)uT   Retourne le singleton MarketContextDetector (pattern identique à market_regime.py).N)r  _detector_lockr   r"   r#   r$   get_market_context_detectorr    sB     ! 	=!)%:%<"	= 	= s	   2A c                   t         sy	 t        j                  t        j                  j	                  t
              d       t        j                         t        j                  dt        j                               | |||r|j                  nd|r|j                  nd|r|j                  ni |r|j                  ng |rt        |j                        ni t        t         d}|r|j#                  |       t%        t
        dd	
      5 }|j'                  t)        j*                  |d      dz          ddd       y# 1 sw Y   yxY w# t,        $ r"}t.        j1                  d|        Y d}~yd}~ww xY w)u   Enregistre une entrée JSONL dans data/couche7_decisions.log.

    Appelé depuis market_spy.py à chaque évaluation d'un surge.
    Permet de rejouer offline et comparer décision actuelle vs Couche7.
    NT)exist_okz%Y-%m-%dT%H:%M:%SZzN/Ar]   )r  dtr   bot_decisionreasoncouche7_regimecouche7_confidencecouche7_implicationscouche7_notesr*   shadow_modeenabledazutf-8)encodingF)ensure_ascii
zlog_couche7_decision error: )COUCHE7_LOG_ENABLEDosmakedirspathdirnameCOUCHE7_LOG_FILEr   strftimegmtimer(   r)   r+   r-   r   r*   COUCHE7_SHADOW_MODECOUCHE7_ENABLEDupdateopenwriter   dumpsrt   ru   rv   )r   contextr  r  extraentryfr   s           r$   log_couche7_decisionr9  !  s    9
BGGOO$45E))+-- 4dkkmD(07gnnU8?'"4"4S<CG$8$8.5W]]229vgoo.r.&
 LL"C': 	BaGGDJJu59D@A	B 	B 	B 93A37889s6   C;E *D7.E 7E <E  E 	E.E))E.	LONG_PLUSLONGWATCHAVOID)BULL_STRONG	BULL_WEAKNEUTRAL
CORRECTIONEARLY_RECOVERYBEARzDict[str, Dict[str, str]]MACRO_CONTEXT_MATRIXc                V    t         j                  |       }|y|j                  |d      S )u   Retourne le signal combiné macro × per-token depuis la matrice.

    Retourne 'LONG' si macro_regime inconnu (fail-safe — ne pas bloquer si données manquantes).
    r;  r<  )rD  r   )macro_regimetoken_regimerows      r$   get_combined_signalrI  h  s-    
 
"
"<
0C
{77<))r#   )rw   
List[List]r4   r   )r   r   r   r   r4   r   )rV   )r   z
np.ndarrayr   r   r4   r   )rw   rJ  r4   zOptional[ContextMetrics])r   r   r4   zTuple[int, Dict[str, bool]])r   r   r4   zTuple[str, float, List[str]])rw   rJ  r4   r  )r   
   )r   r'   r   r   r   r   r4   zOptional[List[List]])r4   r   ) N)r   r'   r5  r  r  r'   r  r'   r6  zOptional[Dict]r4   r  )rF  r'   rG  r'   r4   r'   )<r    
__future__r   r   loggingr(  r   r   dataclassesr   r   r   typingr   r   r	   r
   numpyrp   r   	getLoggerru   r   r0  r/  r'  r*  r+  abspath__file___SCRIPT_DIRjoinr,  r   r&   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r   r  r!   r   r  r  r9  rD  rI  r"   r#   r$   <module>rW     sH  6 #   	   0 0 . .  			?	+2   ggoobggooh7877<<V5LM  # # #  	 	 	"    !  "      %'%(+  %# %'"(+ $# %'"(+ $#" %'#"!% $(+ %'"(+ $#" #!$& $(+W2r4!55x&&Y%x&, 
IS ISZ 7; 3 :!$  #9#9$#9 #9 	#9
 #9 
#9| $/F\cv}  QX  ho  p#)F\cv}  QX  ho  p#*G\cv}  QX  ho  p#*G\cv}  QX  ho  p#.F\cv}  QX  ho  p#*G\cv}  QX  ho  p3 / *r#   