
    @i;                        d Z ddlmZ ddlZddlmZ ddlmZ ddlmZ ddl	Z
ddlmZ ddlmZ dd	lmZ dd
lmZ ddlmZ  ed       G d d             ZddZddZdZ	 	 	 	 	 	 	 	 ddZddZddZ G d d      Zy)a8  
H4 bias pipeline.

Two layers:
  1. compute_algo_bias(df4) -> BiasData
     Deterministic EMA20/50 + HH/HL + RSI extremes (port V15
     bias_computer.compute_algo_bias). Pure function.
  2. compute_ai_bias(...) -> BiasData (async)
     AI override invoked ONLY when algo flags ambiguous=True
     (port V15 bias_computer.ai_override_bias). Silent fallback
     to algo on any error.

  3. BiasResolver
     Stateful facade: dispatches algo + AI, persists outcomes to
     SessionState.bias_cache (BiasEntry), respects TTL on cache hits.
     Used by the orchestrator. Brain modules consume the resolved
     BiasData via TechSnapshot.bias / allowed_direction populated upstream.

V16 layering rule: analysis/ does NOT import from brain/. The AI client
is injected; a tiny inline JSON-extract helper avoids the dependency.
    )annotationsN)	dataclass)datetime)Optional)calc_atr)calc_rsi)utc_now)extract_json_from_response)	BiasEntryT)frozenc                  V    e Zd ZU dZded<   ded<   ded<   ded<   dZd	ed
<   dZd	ed<   y)BiasDataaV  
    H4 bias output (typed).

    bias: "RIALZISTA" | "RIBASSISTA" | "NEUTRO"
    allowed_direction: "BUY" | "SELL" | "BOTH" | "NONE"
    h1_compatibility: 0.3..1.0
    h1_reason: human-readable
    ambiguous: True when algo is uncertain (can be fed to BrainBias later)
    rsi_h4_override: True when RSI H4 extreme triggered an override
    strbiasallowed_directionfloath1_compatibility	h1_reasonFbool	ambiguousrsi_h4_overrideN)__name__
__module____qualname____doc____annotations__r   r        $/home/work/apex_v16/analysis/bias.pyr   r   '   s3    	 INIt!OT!r   r   c                "    t        ddd| d      S )NNEUTRONONE      ?Fr   r   r   r   r   )r   )reasons    r   _neutror&   ;   s      r   c                   | t        |       dk  rt        d      S | d   j                  d      j                         }| d   j                  d      j                         }t	        | d   j
                  d         }t	        |j
                  d         }t	        |j
                  d         }	 t	        t        |       j
                  d         }|d	kD  rt        d
ddd|ddd      S |dk  rt        dddd|ddd      S ||kD  xr ||kD  }||k  xr ||k  }| d   j
                  dd j                  | d   j
                  dd j                  t        fdt        dt                    D              }	t        fdt        dt                    D              }
t        fdt        dt                    D              }t        fdt        dt                    D              }|	dk\  xr |
dk\  }|dk\  xr |dk\  }|r|rt        dddd|	 d |
 d!"      S |r|rt        d
ddd#| d$| d%"      S |r|st        ddd&d'|	 d(|
 d)| d*| d%	"      S |r|st        d
dd&d+| d*| d,|	 d(|
 d!	"      S |r|s|r|rt        d-d.d/d0| d1| d2d3      S t        d-d.d4d5|d6d7|d6d8|	 d9|
 d3      S # t        $ r d}Y w xY w):a{  
    Algorithmic H4 bias from EMA(20/50) + structural HH/HL on last 8 bars.

    V15-parity overrides (priority over EMA/struct):
      RSI H4 > 80 -> RIBASSISTA, allowed SELL (overbought MR)
      RSI H4 < 20 -> RIALZISTA, allowed BUY (oversold MR)

    Returns:
        BiasData. ambiguous=True when EMA and structure disagree
        (a downstream BrainBias may resolve).
    N   insufficient_h4_barsclosespan2   g      I@P   
RIBASSISTASELLg333333?zOVERRIDE: RSI H4 z.1fz > 80 (overbought extreme)T)r   r   r   r   r   	RIALZISTABUYz < 20 (oversold extreme)highlowc              3  @   K   | ]  }|   |d z
     kD  sd   yw   Nr   .0ihighss     r   	<genexpr>z$compute_algo_bias.<locals>.<genexpr>w   $     H1a5Q<0GQH   r9   c              3  @   K   | ]  }|   |d z
     kD  sd   ywr8   r   r;   r<   lowss     r   r>   z$compute_algo_bias.<locals>.<genexpr>x   $     E1tAwa!e/DQEr@   c              3  @   K   | ]  }|   |d z
     k  sd   ywr8   r   r:   s     r   r>   z$compute_algo_bias.<locals>.<genexpr>y   r?   r@   c              3  @   K   | ]  }|   |d z
     k  sd   ywr8   r   rB   s     r   r>   z$compute_algo_bias.<locals>.<genexpr>z   rD   r@            ?zEMA20>EMA50 H4 + HH/HL (z HH, z HL))r   r   r   r   zEMA20<EMA50 H4 + LH/LL (z LH, z LL)ffffff?zEMA bullish, struct mixed (z HH/z HL vs z LH/zEMA bearish, struct mixed (z LL vs r!   r"   r#   zEMA/struct conflict (ema_bull=z, struct_bear=)r$   g?zRange/consolidation - EMA z.2f/z, HH z HL )lenr&   ewmmeanr   ilocr   	Exceptionr   valuessumrange)df4ema20ema50
last_close
last_ema20
last_ema50rsi_h4ema_bullishema_bearishhhhllhllstruct_bullishstruct_bearishr=   rC   s                  @@r   compute_algo_biasrc   E   ss    {c#hm-..L"%**,EL"%**,Es7|((,-Juzz"~&Juzz"~&Jx}))"-. {$!)&5OP 
 	
 {#!)&5MN 
 	
 z)Ej:.EKz)Ej:.EKKRS!((Eu:??23&&D	HaU,H	HB	EaT+E	EB	HaU,H	HB	EaT+E	EB1W(qN1W(qN~# 0E"TB	
 	
 ~$ 0E"TB	
 	
 ># 3B4tB4wrd$rdRVW	
 	
 >$ 3B4tB4wrd$rdRVW	
 	
 	KN$ 6{m>R`Qaabc
 	
  .z#.>a
3?OuUWTXX\]_\`a S  s   0!J: :K	K	u
  Sei un analista di mercato. Analizza questo contesto H4 e determina il bias.

SYMBOL: {symbol}
H4 Close recenti: {h4_closes}
H4 EMA20: {ema20:.4f}  EMA50: {ema50:.4f}
H4 RSI: {rsi_h4:.1f}
ATR H4: {atr_h4:.4f}

Il calcolo algoritmico ha rilevato ambiguità ({algo_reason}).
Decidi il bias finale. Restituisci SOLO JSON valido, zero testo fuori dal JSON:

{{
    "bias": "RIALZISTA" | "RIBASSISTA" | "NEUTRO",
    "allowed_direction": "BUY" | "SELL" | "NONE",
    "h1_compatibility": 0.3-1.0,
    "reason": "max 15 words"
}}c           
     6  K   |t        |      dk  r|S 	 t        |d   j                  d      j                         j                  d         }t        |d   j                  d      j                         j                  d         }t        t        |      j                  d         }t        t        |      j                  d         }|d   j                  dd j                         D cg c]  }t        t        |      d	       }	}t        j                  ||	|||||j                  xs d
      }
	 | j                  |
d       d{   }|j                  |S 	 t        j                   t#        |j                              }t)        |t*              rd|vr|S |j-                  d      }|dvr|S |j-                  dd      }|dvrd}|dk(  rd}	 t        |j-                  dd            }t1        dt3        d|            }t5        |j-                  dd            dd }t7        |||d| dd      S c c}w # t        $ r |cY S w xY w7 # t        $ r |cY S w xY w# t        j$                  t&        f$ r |cY S w xY w# t.        t&        f$ r d}Y w xY ww)a<  
    AI override on ambiguous algo bias. Pure function (no state).

    Silent fallback to algo_result on:
      - df4 None or shorter than 10 bars
      - feature-extraction exception
      - AIResponse.text is None (any error_kind: credit / overload / invalid / unknown)
      - JSON parse failure
      - missing 'bias' field in AI response
      - bias not in {RIALZISTA, RIBASSISTA, NEUTRO} (V16 stricter than V15
        which silently coerced unknown bias to NEUTRO; V16 prefers algo)

    Validation rules (V15-parity, see V15 bias_computer.py:254-262):
      - allowed_direction whitelist {BUY, SELL, NONE}; anything else
        (incl. "BOTH") -> coerced to NONE
      - bias=NEUTRO  ->  allowed_direction forced to NONE
        (V14 coherence rule preserved by V15)
      - h1_compatibility clamped to [0.3, 1.0]
    N
   r*   r(   r+   r.   r-   r5   rG   r   )symbol	h4_closesrU   rV   rZ   atr_h4algo_reasonrI   )temperaturer   )r2   r0   r!   r   r"   )r3   r1   r"   r!   r   r#   333333?rH   r%   override   zAI: Fr   r   r   r   r   r   )rL   r   rM   rN   rO   r   r   tolistroundrP   AI_BIAS_PROMPTformatr   asktextjsonloadsr
   JSONDecodeError
ValueError
isinstancedictget	TypeErrormaxminr   r   )	ai_clientrf   rT   algo_resultema20_vema50_vrsi_h4_vatr_h4_vcrg   promptrespai_datar   allowedh1_compr%   s                    r   compute_ai_biasr      s    4 {c#hmG((b(1668==bABG((b(1668==bAB#++B/0#++B/014W1B1B231G1N1N1PQAU58Q'Q	Q ""W))8[ # F]]6s];; yy**7		BC gt$g(=;;vD88kk-v6G--x$6<= #s3()GXz23DS9F! / ] R  <    *- $ z" s   JCH< ,H7H< 
*J5I II J(I! AJJ /AJ7H< <I
J	I

JI IJIJ!I?<J>I??JJJJJc                    t        dt        dt        t        | j                  dz                          }t        | j                  || j                  xs ddd t               j                               S )z
    Persist subset of BiasData. ambiguous and rsi_h4_override are
    process flags, not output: intentionally NOT persisted.
       d    Nrm   )	direction
confidence	rationalecomputed_at)
r}   r~   intrp   r   r   r   r   r	   	isoformat)bdr   s     r   _bias_data_to_entryr   )  sf    
 RS#eB,?,?#,E&F"GHIJ&&<<%2t,I'')	 r   c                    dddj                  | j                  d      }t        dt        d| j                  dz              }t        || j                  || j                  dd	      S )
z
    Reconstruct a BiasData from a persisted BiasEntry. The bias label
    is deterministically derived from allowed_direction (V15 mapping is
    1:1 in compute_algo_bias and ai-override). Process flags reset.
    r2   r0   )r3   r1   r!   rk   rH   g      Y@Frn   )r{   r   r}   r~   r   r   r   )bebias_for_dirr   s      r   _entry_to_bias_datar   7  sk      
c",,!  #s3 567G,, ,, r   c                  \    e Zd ZdZdZded	 	 	 ddZddZdddZddZdd	Z	dd
Z
ddZy)BiasResolverab  
    H4 bias dispatcher with AI override + per-symbol cache.

    Coordinates:
      1. compute_algo_bias  (deterministic algo)
      2. compute_ai_bias    (AI override on ambiguous; silent fallback)
      3. SessionState.bias_cache (TTL persistence for cold start)

    Cache semantics (V15 parity, TTL=3600s):
      - resolve(symbol, df4) returns cached BiasData if BiasEntry.computed_at
        is within ttl_seconds; otherwise recomputes.
      - Cache writes happen for BOTH algo-only and AI-resolved decisions, so
        the next cold start finds them all (state v2 BiasCache).
      - resolve() does NOT save SessionState; the orchestrator owns the
        save after each tick (V15-BUG-9 discipline preserved).

    AI failure policy (silent fallback):
      - AIResponse.text is None / parse error / schema violation -> use algo.
      - state.brain.bias_calls_count is incremented when an AI call is
        ATTEMPTED (algo-ambiguous path), regardless of success. Failure
        rate is greppable from system.log via "[BIAS AI] {symbol} fallback".
        A dedicated bias_ai_failures_count is on BACKLOG.
    i  N)loggerttl_secondsc               <    || _         || _        || _        || _        y N)r   stater   r   )selfr   r   r   r   s        r   __init__zBiasResolver.__init__g  s!     #
&r   c                  K   | j                  |      }||S |t        |      dk  r$t        ddddd      }| j                  ||       |S t	        |      }|j
                  r}| j                  j                  xj                  dz  c_        t        | j                  |||	       d {   }||u s|j                  |j                  k(  r| j                  d
| d       n|}| j                  ||       |S 7 Mw)Nr(   r!   BOTHr#   
no_h4_dataFr$   r9   )r   rf   rT   r   z
[BIAS AI] z%: fallback algo (no override applied))
_cache_getrL   r   
_cache_putrc   r   r   brainbias_calls_countr   r   r   _log_warning)r   rf   rT   cachedr   algofinals          r   resolvezBiasResolver.resolvet  s     (M ;#c(R-"(!$&B OOFB'I %>>JJ--2-).. 	 E }4>> A!! (MN E&s   B&C8(C6)AC8c                    |/| j                   j                  j                  j                          y| j                   j                  j                  j	                  |d       y)z<Drop cached BiasEntry for symbol, or clear the entire cache.N)r   
bias_cacheentriesclearpopr   rf   s     r   
invalidatezBiasResolver.invalidate  sC    >JJ!!))//1JJ!!))--fd;r   c                (    | j                  |      duS )z
        True iff there is a non-expired BiasEntry for symbol.
        Used by the orchestrator to skip the H4 REST fetch when resolve()
        will return the cached value anyway (df4 is unused on cache hit).
        N)r   r   s     r   has_fresh_cachezBiasResolver.has_fresh_cache  s     v&d22r   c                <   | j                   j                  j                  j                  |      }|y 	 t	        j
                  |j                        }t               |z
  j                         }|| j                  k\  ry t        |      S # t        t        f$ r Y y w xY wr   )r   r   r   r{   r   fromisoformatr   r|   rx   r	   total_secondsr   r   )r   rf   entryr   ages        r   r   zBiasResolver._cache_get  s    

%%--11&9=	"001B1BCK y;&557$""""5)) :& 		s   B	 	BBc                \    t        |      | j                  j                  j                  |<   y r   )r   r   r   r   )r   rf   r   s      r   r   zBiasResolver._cache_put  s!    0CB0G

%%f-r   c                    | j                   y 	 | j                   j                  j                  |       y # t        $ r Y y w xY wr   )r   systemwarningrP   )r   msgs     r   r   zBiasResolver._log_warning  s>    ;;	KK&&s+ 		s   %5 	A A)r   r   returnNone)rf   r   rT   zOptional[pd.DataFrame]r   r   r   )rf   zOptional[str]r   r   )rf   r   r   r   )rf   r   r   zOptional[BiasData])rf   r   r   r   r   r   )r   r   r   r   )r   r   r   r   DEFAULT_TTL_SECONDSr   r   r   r   r   r   r   r   r   r   r   r   L  sP    0  .' ' 
''R<3*Hr   r   )r%   r   r   r   )rT   pd.DataFramer   r   )rf   r   rT   r   r   r   r   r   )r   r   r   r   )r   r   r   r   )r   
__future__r   ru   dataclassesr   r   typingr   pandaspdanalysis.indicators.atrr   analysis.indicators.rsir   core.contractsr	   core.json_parsingr
   persistence.state_storer   r   r&   rc   rq   r   r   r   r   r   r   r   <module>r      s   , #  !    , , " 8 - $" " "&hf&W W 
	W
 W W|*z zr   