
    i?                        d Z ddlmZ ddlZddlmc m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 ddlZej&                  j)                  d e ee      j/                         j0                  j0                               ddlmZmZmZmZmZmZm Z  ddl!m"Z" ddl#m$Z$ dd	l%m&Z&m'Z' d!d
Z( G d d      Z)d"dZ*d#dZ+d#dZ,d$dZ-d Z.d Z/d Z0d Z1d Z2d Z3d Z4d Z5d Z6d Z7d Z8d Z9d Z:d Z;d%dZ<e=d k(  r ej|                   e<              yy)&u  
Smoke tests for analysis/bias.py — AI override + BiasResolver.

Covers:
  - resolve() unambiguous algo path (no AI call)
  - resolve() ambiguous algo path with AI happy/parse-error/timeout/credit
  - cache TTL hit / miss (manipulate computed_at)
  - invalidate(symbol) and invalidate(all)
  - AI validation: garbage bias -> fallback algo, allowed BOTH coerced to NONE
                   when bias=NEUTRO (V14 coherence, V15 line 257-259),
                   h1_compatibility clamped to [0.3, 1.0]
  - persistence roundtrip BiasData -> BiasEntry -> BiasData
  - V15 router parity: df4=None -> NEUTRO BOTH 0.5

FakeAIClient implements ask() (BiasResolver calls .ask, not .ask_for_decision).

Run:
    cd ~/apex_v16
    python -m tests.test_bias_resolver
    )annotationsN)datetime	timedeltatimezone)Path)AI_BIAS_PROMPTBiasDataBiasResolver_bias_data_to_entry_entry_to_bias_datacompute_ai_biascompute_algo_bias)
AIResponse)utc_now)	BiasEntrySessionStatec                     t        d|         y )Nz  ok  )print)labels    //home/work/apex_v16/tests/test_bias_resolver.py_okr   0   s    	F5'
    c                  .    e Zd ZdZdddZ	 	 d	 	 	 ddZy)	FakeAIClientzG
    Mimics AIClient.ask: returns canned AIResponse, tracks calls.
    Nc                >    |xs t        d d      | _        g | _        y )Nunknown)text
error_kind)r   cannedcalls)selfr   s     r   __init__zFakeAIClient.__init__<   s    K
 K!#
r   c                d   K   | j                   j                  ||d d d       | j                  S w)N<   )temperatureprompt_preview)r    appendr   )r!   promptr%   
max_tokenss       r   askzFakeAIClient.ask@   s5     

&$Sbk
 	 {{s   .0N)r   zAIResponse | NonereturnNone)皙?N)r(   strr%   floatr)   z
int | Noner,   r   )__name__
__module____qualname____doc__r"   r*    r   r   r   r   8   s)    $ ;>+/(4>r   r   c                B    t        t        j                  |       d      S )N   r   attempts)r   jsondumps)payloads    r   make_ai_responser=   I   s    4::g.;;r   c            	         g d}} g ddz  }|D ]<  }|}||z   }| j                  |t        ||      dz   t        ||      dz
  |dd       |}> t        j                  |       S )z<Noisy uptrend: EMA20>EMA50, struct bullish, RSI in (50, 80).      Y@)
333333?r.   皙ٿ      ?r@   g      g?r@   rA   rB      皙?  openhighlowclosevolume)r'   maxminpd	DataFrame)rowsbasepatterndeltaocs         r   df4_unambiguous_bullishrV   Q   s{    U$DJQNG 5Ls1ay3q!9s?Q$
 	  <<r   c            
        t        d      D  cg c]
  } d| dz  z    c} t        dd      D  cg c]
  } d| dz  z
   c} z   }g }t        t        |            D ]Z  } |t        | dz   t        |      dz
           }|j                  ||    |t	        ||    |      dz   t        ||    |      dz
  d	d
       \ t        j                  |      S c c} w c c} w )zA20 bars up then 8 bars down: EMA bullish but recent struct LH/LL.   r?   rB   r7   	   g     [@333333?g?rE   )rG   rJ   rH   rI   rK   )rangelenrM   r'   rL   rN   rO   )iclosesrP   nxts       r   df4_ambiguousr`   `   s    ',Ry1!ea#go1',Q{3!ea#go34FD3v; SQFa011Iq	3'$.vay#&-	
 	 << 23s
   C	Cc                     t               S r+   )r   r5   r   r   fresh_staterb   p   s
    >r   c                     t               } t               }t        ||       }t               }t	        j
                  |j                  d|            }|j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        j                  d|j                   d	|j                         d
z   d|iz  }	t!        t        j"                  |	            d x}x}}|j$                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }	t!        t        j"                  |	            d x}x}}|j&                  }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }	t!        t        j"                  |	            d x}x}}|j(                  }
t+        |
      }d}||k(  }|s t        j                  d|fd||f      dt        j                         v st        j                  t*              rt        j                  t*              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |
      t        j                  |      t        j                  |      dz  }t        j                  d      dz   d|iz  }t!        t        j"                  |            d x}
x}x}}| j,                  }|j.                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      t        j                  |      dz  }	dd|	iz  }t!        t        j"                  |            d x}x}x}}d}| j0                  }|j2                  }||v }
|
st        j                  d|
fd ||f      t        j                  |      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      d!z  }	dd|	iz  }t!        t        j"                  |            d x}x}
x}}t5        d"       y )#N	ai_clientstateMES	RIALZISTA==z,%(py2)s
{%(py2)s = %(py0)s.bias
} == %(py5)sbdpy0py2py5got z | 
>assert %(py7)spy7BUYz9%(py2)s
{%(py2)s = %(py0)s.allowed_direction
} == %(py5)sassert %(py7)sFisz1%(py2)s
{%(py2)s = %(py0)s.ambiguous
} is %(py5)sr   zK%(py5)s
{%(py5)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.calls
})
} == %(py8)sr\   airn   py1py3rp   py8z)AI must NOT be called on unambiguous algoz
>assert %(py10)spy10zS%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.brain
}.bias_calls_count
} == %(py7)srf   rn   ro   py4rs   assert %(py9)spy9inzO%(py1)s in %(py7)s
{%(py7)s = %(py5)s
{%(py5)s = %(py3)s.bias_cache
}.entries
}r}   r~   rp   rs   z8resolve: unambiguous algo -> no AI call, cache populated)rb   r   r
   rV   asynciorunresolvebias
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_saferepr_format_assertmsg	h1_reasonAssertionError_format_explanationallowed_direction	ambiguousr    r\   brainbias_calls_count
bias_cacheentriesr   )rf   r{   resolverdf4rl   @py_assert1@py_assert4@py_assert3@py_format6@py_format8@py_assert2@py_assert7@py_assert6@py_format9@py_format11@py_assert5@py_format10@py_assert0s                     r   !test_resolve_unambiguous_skips_air   x   sp   ME	Bb6H
!
#C	X%%eS1	2B77DkD7k!DDD7kDDDDDD2DDD2DDD7DDDkDDDT"''#bll^#DDDDDDDD(5(5((((5((((((2(((2((((((5(((((((<< 5 <5    <5      2   2   <   5       xxJ3x=JAJ=AJJJ=AJJJJJJ3JJJ3JJJJJJrJJJrJJJxJJJ=JJJAJJJJJJJJJJJ;;,;'',1,'1,,,,'1,,,,,,5,,,5,,,;,,,',,,1,,,,,,,,E$$,$,,,5,,,,,5,,,,5,,,,,,E,,,E,,,$,,,,,,,,,,,BCr   c            	        t               } t        t        ddddd            }t        ||       }t	        j
                  |j                  dt                           }|j                  }d}||k(  }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                   |            d x}x}}|j"                  }d}||k(  }|st        j                  d	|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                   |            d x}x}}|j$                  }d}||k(  }|st        j                  d	|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                   |            d x}x}}|j&                  }|j(                  }d}	 ||	      }
|
st        j*                  d|j&                         dz   dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |	      t        j                  |
      dz  }t        t        j                   |            d x}x}x}	}
|j,                  }t/        |      }d}
||
k(  }|s
t        j                  d	|fd||
f      dt        j                         v st        j                  t.              rt        j                  t.              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |
      dz  }dd|iz  }t        t        j                   |            d x}x}x}}
|j,                  d   d   }d}||k(  }|slt        j                  d	|fd||f      t        j                  |      t        j                  |      d z  }d!d"|iz  }t        t        j                   |            d x}x}}| j0                  }|j2                  }d}||k(  }	|	st        j                  d	|	fd#||f      d$t        j                         v st        j                  |       rt        j                  |       nd$t        j                  |      t        j                  |      t        j                  |      d%z  }d&d'|iz  }t        t        j                   |            d x}x}x}	}t5        d(       y ))Nrh   rt   g333333?zAI sees bullish continuationr   r   h1_compatibilityreasonr   rd   rg   ri   rk   rl   rm   rv   rs   ru   z8%(py2)s
{%(py2)s = %(py0)s.h1_compatibility
} == %(py5)sAI: zreason=zm
>assert %(py8)s
{%(py8)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.h1_reason
}.startswith
}(%(py6)s)
}rn   ro   r   py6r   r7   rz   r\   r{   r|   assert %(py10)sr   r   r%   gffffff?)z%(py1)s == %(py4)s)r}   r   zassert %(py6)sr   r   rf   r   r   r   zFresolve: ambiguous + AI valid -> override applied, counter=1, temp=0.7)rb   r   r=   r
   r   r   r   r`   r   r   r   r   r   r   r   r   r   r   r   r   
startswithr   r    r\   r   r   r   )rf   r{   r   rl   r   r   r   r   r   r   r   r   r   r   r   r   @py_format5@py_format7r   s                      r   %test_resolve_ambiguous_calls_ai_happyr      s   ME	-" 0	/  
B b6H	X%%e]_=	>B77!k!7k!!!!7k!!!!!!2!!!2!!!7!!!k!!!!!!!(5(5((((5((((((2(((2((((((5(((((((&$&$&&&&$&&&&&&2&&&2&&&&&&$&&&&&&&<<D<""D6D"6*D*DDgbll^,DDDDDDD2DDD2DDD<DDD"DDD6DDD*DDDDDDxx3x=A=A=A33rrx=A88A;}%,,%,,,,%,,,%,,,,,,,,,,;;,;'',1,'1,,,,'1,,,,,,5,,,5,,,;,,,',,,1,,,,,,,PQr   c                 ,   t               } t        t        dd            }t        ||       }t	        j
                  |j                  dt                           }|j                  }d}||k(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                   |            d x}x}}|j"                  }|j$                  }d}	 ||	      }
|
 }|sdd
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      t        j                  |	      t        j                  |
      dz  }t        t        j                   |            d x}x}x}	x}
}| j&                  }|j(                  }d}||k(  }	|	st        j                  d|	fd||f      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      t        j                  |      dz  }t        j*                  d      dz   d|iz  }t        t        j                   |            d x}x}x}	}t-        d       y )Nzthis is not JSON at allr7   r8   r   rd   rg   NEUTROri   rk   rl   rm   rv   rs   r   oassert not %(py8)s
{%(py8)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.h1_reason
}.startswith
}(%(py6)s)
}r   r   rf   r   z%counter must bump even on parse errorz
>assert %(py9)sr   z>resolve: AI parse error -> fallback algo, counter still bumped)rb   r   r   r
   r   r   r   r`   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   )rf   r{   r   rl   r   r   r   r   r   r   r   @py_assert9r   r   s                 r   .test_resolve_ambiguous_ai_parse_error_fallbackr      s   ME	Z-FQRS	TBb6H	X%%e]_=	>B77h7h7h227h||.|&&.v.&v...........r...r...|...&...v...........;;U;''U1U'1,UUU'1UUUUUU5UUU5UUU;UUU'UUU1UUU.UUUUUUUUHIr   c                 6   t               } t        t        d dd            }t        ||       }t	        j
                  |j                  dt                           }|j                  }d}||k(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                   |            d x}x}}| j"                  }|j$                  }d}	||	k(  }
|
st        j                  d|
fd||	f      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                   |            d x}x}x}
}	t'        d       y )NoverloadrC   )r   r   r9   r   rd   rg   r   ri   rk   rl   rm   rv   rs   r7   r   rf   r   r   r   z1resolve: AI overload (text=None) -> fallback algorb   r   r   r
   r   r   r   r`   r   r   r   r   r   r   r   r   r   r   r   r   rf   r{   r   rl   r   r   r   r   r   r   r   r   s               r   *test_resolve_ambiguous_ai_timeout_fallbackr      sB   ME	ZTjSTU	VBb6H	X%%e]_=	>B77h7h7h227h;;,;'',1,'1,,,,'1,,,,,,5,,,5,,,;,,,',,,1,,,,,,,;<r   c                 8   t               } t        t        d ddd            }t        ||       }t	        j
                  |j                  dt                           }|j                  }d}||k(  }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                   |            d x}x}}| j"                  }|j$                  }d}	||	k(  }
|
st        j                  d	|
fd||	f      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                   |            d x}x}x}
}	t'        d       y )Ncreditr7   credit_balance_too_low)r   r   r9   error_detailr   rd   rg   r   ri   rk   rl   rm   rv   rs   r   rf   r   r   r   z8resolve: AI credit-exhausted -> fallback algo (no raise)r   r   s               r   )test_resolve_ambiguous_ai_credit_fallbackr      sK   ME	Zh- 
B b6H	X%%e]_=	>B77h7h7h227h;;,;'',1,'1,,,,'1,,,,,,5,,,5,,,;,,,',,,1,,,,,,,BCr   c                    t               } t               }t        ||       }t        j                  |j                  dt                            | j                  j                  d   }t        j                  |j                  dt                           }|j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd	|iz  }	t!        t        j"                  |	            d x}x}}| j                  j                  d   }
|
|u }|st        j                  d
|fd|
|f      t        j                  |
      dt        j                         v st        j                  |      rt        j                  |      nddz  }t        j$                  d      dz   d|iz  }t!        t        j"                  |            d x}
}|j&                  }t)        |      }d}||k(  }|s
t        j                  d|fd||f      dt        j                         v st        j                  t(              rt        j                  t(              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t!        t        j"                  |            d x}x}x}}t+        d       y )Nrd   rg   rt   ri   ru   bd2rm   rv   rs   rw   )z%(py1)s is %(py3)scached_entry)r}   r~   z BiasEntry replaced -> cache missz
>assert %(py5)srp   r   rz   r\   r{   r|   r   r   zKresolve: cache hit within TTL -> no recompute, BiasEntry identity preserved)rb   r   r
   r   r   r   rV   r   r   r   r   r   r   r   r   r   r   r   r   r    r\   r   )rf   r{   r   r   r   r   r   r   r   r   r   r   @py_format4r   r   r   r   s                    r   test_cache_hit_within_ttlr      s   ME	Bb6HKK  (?(ABC##++E2L
++h&&u.E.GH
IC  )E) E)))) E))))))3)))3))) )))E)))))))##E*^*l:^^^*l^^^*^^^^^^l^^^l^^^^<^^^^^^^xx3x=A=A=A33rrx=AUVr   c                    t               } t               }t        || d      }t        j                  |j                  dt                            t               t        d      z
  j                         }| j                  j                  d   }t        |j                  |j                  |j                  |      | j                  j                  d<   t        j                  |j                  dt                           }|j                   }d}||k(  }|st#        j$                  d|fd	||f      d
t'        j(                         v st#        j*                  |      rt#        j,                  |      nd
t#        j,                  |      t#        j,                  |      dz  }	dd|	iz  }
t/        t#        j0                  |
            d x}x}}| j                  j                  d   }|j2                  }||k7  }|st#        j$                  d|fd||f      dt'        j(                         v st#        j*                  |      rt#        j,                  |      ndt#        j,                  |      dt'        j(                         v st#        j*                  |      rt#        j,                  |      nddz  }t#        j4                  d      dz   d|iz  }t/        t#        j0                  |            d x}}t7        d       y )Ni  )re   rf   ttl_secondsrg      )hours)	direction
confidence	rationalecomputed_atrt   ri   ru   r   rm   rv   rs   )!=)z3%(py2)s
{%(py2)s = %(py0)s.computed_at
} != %(py4)s	new_entryold)rn   ro   r   z)TTL expiry must trigger fresh computed_atz
>assert %(py6)sr   z7resolve: TTL expired -> recompute and refresh BiasEntry)rb   r   r
   r   r   r   rV   r   r   	isoformatr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   )rf   r{   r   r   er   r   r   r   r   r   r   r   r   s                 r   test_cache_miss_after_ttlr      s   ME	Bb4HHKK  (?(ABC9yq))
4
4
6C  'A&/++!,,++3'EU# ++h&&u.E.GH
IC  )E) E)))) E))))))3)))3))) )))E)))))))  ((/I  T C'TTT CTTTTTT9TTT9TTT TTTTTTCTTTCTTTT)TTTTTTTABr   c                    t               } t               }t        ||       }t        j                  |j                  dt                            t        j                  |j                  dt                            |j                  d       d}| j                  }|j                  }||v}|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      dz  }dd	|iz  }t!        t        j"                  |            d x}x}x}}d}| j                  }|j                  }||v }|st        j                  d
|fd||f      t        j                  |      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      dz  }dd	|iz  }t!        t        j"                  |            d x}x}x}}t        j                  |j                  dt                            d}| j                  }|j                  }||v }|st        j                  d
|fd||f      t        j                  |      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      dz  }dd	|iz  }t!        t        j"                  |            d x}x}x}}t%        d       y )Nrd   rg   MNQ)not in)zS%(py1)s not in %(py7)s
{%(py7)s = %(py5)s
{%(py5)s = %(py3)s.bias_cache
}.entries
}rf   r   r   r   r   r   zEinvalidate(symbol): drops only that symbol, recompute on next resolve)rb   r   r
   r   r   r   rV   
invalidater   r   r   r   r   r   r   r   r   r   r   )	rf   r{   r   r   r   r   r   r   r   s	            r   'test_invalidate_symbol_forces_recomputer      s(   ME	Bb6HKK  (?(ABCKK  (?(ABC0((0(000500000500005000000000000(00000000000,E$$,$,,,5,,,,,5,,,,5,,,,,,E,,,E,,,$,,,,,,,,,,,KK  (?(ABC,E$$,$,,,5,,,,,5,,,,5,,,,,,E,,,E,,,$,,,,,,,,,,,OPr   c            	        t               } t               }t        ||       }t        j                  |j                  dt                            t        j                  |j                  dt                            | j                  }|j                  }t        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      d	z  }d
d|iz  }	t!        t        j"                  |	            d x}x}x}x}}|j%                          | j                  }
|
j                  }i }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |
      t        j                  |      t        j                  |      dz  }dd|iz  }t!        t        j"                  |            d x}
x}x}}t'        d       y )Nrd   rg   r   r   ri   )zn%(py7)s
{%(py7)s = %(py0)s(%(py5)s
{%(py5)s = %(py3)s
{%(py3)s = %(py1)s.bias_cache
}.entries
})
} == %(py10)sr\   rf   )rn   r}   r~   rp   rs   r   zassert %(py12)spy12)zO%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.bias_cache
}.entries
} == %(py7)sr   r   r   z0invalidate() with no arg -> entire cache cleared)rb   r   r
   r   r   r   rV   r   r   r\   r   r   r   r   r   r   r   r   r   r   )rf   r{   r   r   r   r   r   @py_assert8r   @py_format13r   r   r   r   r   s                  r    test_invalidate_all_clears_cacher     s   ME	Bb6HKK  (?(ABCKK  (?(ABC-''-3'(-A-(A----(A------3---3------u---u------'---(---A-------)##)r)#r))))#r))))))5)))5))))))#)))r))))))):;r   c            	     x   t               } t        t        ddddd            }t        ||       }t	        j
                  |j                  dt                           }|j                  }d	}||k(  }|st        j                  d
|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        j                  d|j                         dz   d|iz  }t!        t        j"                  |            d x}x}}|j$                  }|j&                  }d}	 ||	      }
|
 }|sddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |	      t        j                  |
      dz  }t!        t        j"                  |            d x}x}x}	x}
}t)        d       y )NGARBAGErt   g?noiser   r   rd   rg   r   ri   rk   rl   rm   rq   rr   rs   r   r   r   zOresolve: AI garbage bias -> fallback algo (V16 stricter than V15 silent-coerce))rb   r   r=   r
   r   r   r   r`   r   r   r   r   r   r   r   r   r   r   r   r   r   )rf   r{   r   rl   r   r   r   r   r   r   r   r   r   s                r   test_ai_garbage_bias_fallbackr     ss   ME	-"	/  
B b6H	X%%e]_=	>B770h07h0007h000000200020007000h000$rwwi 00000000||.|&&.v.&v...........r...r...|...&...v...........YZr   c            	     <   t               } t        t        ddddd            }t        ||       }t	        j
                  |j                  dt                           }|j                  }d}||k(  }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                   |            dx}x}}|j"                  }d}||k(  }|st        j                  d	|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        j$                  d|j"                         dz   d|iz  }t        t        j                   |            dx}x}}t'        d       y)z
    V15 bias_computer.py:254-259:
      - allowed not in {BUY,SELL,NONE} -> coerced to NONE
      - bias=NEUTRO -> SEMPRE allowed=NONE
    V15 prompt didn't even list BOTH (line 204), but defensive coercion
    handles a hallucinated AI response.
    r   BOTHrZ   	uncertainr   r   rd   rg   ri   rk   rl   rm   rv   rs   NNONEru   z+V14 coherence: NEUTRO must force NONE, got rr   zBresolve: AI NEUTRO+BOTH -> allowed coerced to NONE (V14 coherence))rb   r   r=   r
   r   r   r   r`   r   r   r   r   r   r   r   r   r   r   r   r   )	rf   r{   r   rl   r   r   r   r   r   s	            r   (test_ai_neutro_with_both_coerced_to_noner   2  s    ME	-#	/  
B b6H	X%%e]_=	>B77h7h7h227h M6 M6) M;L;LM6 M MFLfM M4L4L  M MCL9  M MCL9   M MCL9 $* M M;L;L
5b6J6J5KLM M M9L9LM MLMr   c            	     6   t               } t        t        ddddd            }t        j                  t        ||       j                  dt                           }|j                  }d	}||k(  }|st        j                  d
|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        j                  d|j                         dz   d|iz  }t!        t        j"                  |            d x}x}}t               }t        t        ddddd            }	t        j                  t        |	|      j                  dt                           }
|
j                  }d}||k(  }|st        j                  d
|fd||f      dt        j                         v st        j                  |
      rt        j                  |
      ndt        j                  |      t        j                  |      dz  }t        j                  d|
j                         dz   d|iz  }t!        t        j"                  |            d x}x}}t%        d       y )Nrh   rt   g      @rH   r   r   rd   rg   g      ?ri   r   rl   rm   rq   rr   rs   rD   rI   r@   r   zCresolve: h1_compatibility 2.5 -> 1.0, 0.1 -> 0.3 (clamp [0.3, 1.0]))rb   r   r=   r   r   r
   r   r`   r   r   r   r   r   r   r   r   r   r   r   )rf   r{   rl   r   r   r   r   r   state2ai2r   s              r    test_ai_h1_compatibility_clampedr   M  s   ME	-%6/  
B 
\Be<DDUMO\	]BC#C#%CCC#CCCCCC2CCC2CCCCCC#CCCb.A.A-B'CCCCCCCC ]F
.%50  C ++lS?GG}_
`CE3E3&EEE3EEEEEE3EEE3EEEEEE3EEE$s/C/C.D(EEEEEEEEMNr   c                 t   t        dddddd      } t        |       }|j                  }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            d x}x}}|j                  }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            d x}x}}|j                  }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            d x}x}}t        |      }|j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            d x}x}}|j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            d x}x}}|j                   }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            d x}x}}|j"                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            d x}x}}|j$                  }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            d x}x}}|j&                  }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            d x}x}}t)        d       y )Nrh   rt   g?zAI: bullish continuationF)r   r   r   r   r   rsi_h4_overrideri   )z1%(py2)s
{%(py2)s = %(py0)s.direction
} == %(py5)sr   rm   rv   rs   P   )z2%(py2)s
{%(py2)s = %(py0)s.confidence
} == %(py5)s)z1%(py2)s
{%(py2)s = %(py0)s.rationale
} == %(py5)sru   r   rk   r   z1%(py2)s
{%(py2)s = %(py0)s.h1_reason
} == %(py5)srw   ry   )z7%(py2)s
{%(py2)s = %(py0)s.rsi_h4_override
} is %(py5)szPpersist: BiasData<->BiasEntry roundtrip preserves persisted fields, resets flags)r	   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   )rl   r   r   r   r   r   r   r   s           r   !test_bias_data_to_entry_roundtripr   f  s'   	E(B
B
 	BA;;%;%;%11;%<<2<2<211<2;;444;44444;444444414441444;44444444444
a
 C  )E) E)))) E))))))3)))3))) )))E)))))))88"{"8{""""8{""""""3"""3"""8"""{"""""""&3&3&&&&3&&&&&&3&&&3&&&&&&3&&&&&&&==666=66666=666666636663666=66666666666==!E!=E!!!!=E!!!!!!3!!!3!!!=!!!E!!!!!!!'%'%''''%''''''3'''3''''''%'''''''Z[r   c                 |   t               } t               }t        ||       }t        j                  |j                  dd             }|j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd	|iz  }t        t        j                  |            d x}x}}|j                  }d
}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        j                   d|j                         dz   d	|iz  }t        t        j                  |            d x}x}}|j"                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd	|iz  }t        t        j                  |            d x}x}}|j$                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd	|iz  }t        t        j                  |            d x}x}}|j&                  }	t)        |	      }d}
||
k(  }|s
t        j                  d|fd||
f      dt        j                         v st        j                  t(              rt        j                  t(              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      t        j                  |      t        j                  |
      dz  }dd|iz  }t        t        j                  |            d x}	x}x}}
d}| j*                  }|j,                  }||v }	|	st        j                  d|	fd||f      t        j                  |      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}	x}}t/        j0                  dddddd gd!z        }t        j                  |j                  d"|            }|j                  }d
}||k(  }|st        j                  d|fd||f      d#t        j                         v st        j                  |      rt        j                  |      nd#t        j                  |      t        j                  |      dz  }dd	|iz  }t        t        j                  |            d x}x}}t3        d$       y )%Nrd   rg   r   ri   rk   rl   rm   rv   rs   r   ru   z@V15 router parity: no_h4_data -> BOTH (permissive default), got rr   rB   r   
no_h4_datar   r   rz   r\   r{   r|   r   r   r   r   rf   r   r   r   r7   rF      r   r   z9resolve: df4 None/short -> NEUTRO BOTH 0.5, cached, no AI)rb   r   r
   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r    r\   r   r   rN   rO   r   )rf   r{   r   rl   r   r   r   r   r   r   r   r   r   r   r   r   shortr   s                     r   #test_no_h4_data_returns_neutro_bothr     sS   ME	Bb6H	X%%eT2	3B77h7h7h227h b6 b6) bPaPab6 b b[a[ab bIaIa  b bXaXa  b bXaXa   b bXaXa $* b bPaPa
J2K_K_J`ab b bNaNab b%#%#%%%%#%%%%%%2%%%2%%%%%%#%%%%%%%<<'<'<<''''<<''''''2'''2'''<'''<'''''''xx3x=A=A=A33rrx=A,E$$,$,,,5,,,,,5,,,,5,,,,,,E,,,E,,,$,,,,,,,,,,, LL1aAQRSTWXXYE
++h&&ue4
5C  *F* F**** F******3***3*** ***F*******CDr   c                 H   t        d       t                t                t                t	                t                t                t                t                t                t                t                t                t                t                t        d       y)Nztest_bias_resolver.pyzALL 14 TESTS PASSEDr   )r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r5   r   r   mainr     sk    	
!"%')+24.0-/+-$&!#,.$&%'')	
 r   __main__)r   r/   r,   r-   )r<   dictr,   r   )r,   zpd.DataFrame)r,   r   )r,   int)?r4   
__future__r   builtinsr   _pytest.assertion.rewrite	assertionrewriter   r   r:   sysr   r   r   pathlibr   pandasrN   pathinsertr/   __file__r   parentanalysis.biasr   r	   r
   r   r   r   r   brain.ai_clientr   core.contractsr   persistence.state_storer   r   r   r   r=   rV   r`   rb   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r1   exitr5   r   r   <module>r     s  * #     
 2 2   3tH~--/66==> ?   ' " ; "< D&R0	J =
D"W&C.Q$	< [(N6O2\2E4( zCHHTV r   