
    jLS                       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ZddlmZ ddlmZmZ ddlZej&                  j)                  d e ee      j/                         j0                  j0                               ddlmZmZ ddlmZ ddlmZ dd	l m!Z!m"Z"m#Z#m$Z$ dd
l%m&Z&m'Z'm(Z( ddl)m*Z*m+Z+m,Z, ddl-m.Z. ddl/m0Z0m1Z1 d-dZ2 G d d      Z3 G d d      Z4 G d d      Z5 G d d      Z6 G d d      Z7ddd	 	 	 d.dZ8d/dZ9d0dZ:ddl;m<Z< d Z=d Z>d  Z?d! Z@d" ZAd# ZBd$ ZCd% ZDd& ZEd' ZFd( ZGd) ZHd* ZId1d+ZJeKd,k(  r ej                   eJ              yy)2a_  
Phase A smoke tests for orchestrator.py + brain/brain_selector.py.

Phase A scope: orchestrator runs the loop, builds tech, resolves bias,
chooses brain, calls evaluate_entry / manage_exit, and LOGS proposals
without acting on them. No opener / closer / sizing / risk_manager
invoked yet (Phase B). state.active_trades is observed, never mutated.

Patterns: FakeMarketDataProvider (canned bars), FakeAIClient (canned
AIResponse), FakeStateStore (in-memory save count), and a fake
BrainBundle exposing evaluate_entry / manage_exit only.

Run:
    cd ~/apex_v16
    python -m tests.test_orchestrator_phase_a
    )annotationsN)Pathdatetimetimezone)BiasDataBiasResolver)TechSnapshot
AIResponse)INDICI_FUTURESMR_EXCLUDEDTF_ALLOWED_SYMBOLSchoose_brain)RuntimeConfigRunModeAccountKind)EntryDecisionBrainDecisionEntryEvalResult)Orchestrator)SessionState
StateStorec                     t        d|         y )Nz  ok  )print)labels    6/home/work/apex_v16/tests/test_orchestrator_phase_a.py_okr   +   s    	F5'
    c                  "    e Zd ZdZdddZddZy)FakeProviderz?Returns whatever DataFrame is supplied per (symbol, timeframe).Nc                (    |xs i | _         g | _        y N)barscalls)selfr$   s     r   __init__zFakeProvider.__init__5   s    JB	13
r   c                  K   | j                   j                  |||f       | j                  j                  ||f      }||j                  rt        j                  g d      S |j                  |      j                  d      S w)N)openhighlowclosevolume)columnsT)drop)	r%   appendr$   getemptypd	DataFrametailreset_index)r&   symbol	timeframendfs        r   get_barszFakeProvider.get_bars9   sm     

69a01YY]]FI./:<<(RSSwwqz%%4%00s   BBr#   )r$   zdict | NonereturnNone)r7   strr8   r>   r9   intr<   zpd.DataFrame)__name__
__module____qualname____doc__r'   r;    r   r   r!   r!   3   s    I41r   r!   c                  6    e Zd Z	 	 d	 	 	 ddZ	 d	 	 	 	 	 ddZy)FakeAINc                $   K   t        d d      S wNunknown)text
error_kindr   )r&   prompttemperature
max_tokenss       r   askz
FakeAI.askB   s     t	::   c                $   K   t        d d      S wrH   r   )r&   rL   rN   wheres       r   ask_for_decisionzFakeAI.ask_for_decisionF   s      t	::rP   )g?N)rL   r>   rM   floatrN   z
int | Noner<   r   )iX  N)rL   r>   rN   r?   r<   r   )r@   rA   rB   rO   rS   rD   r   r   rF   rF   A   s?    :=+/;(;4>;
 9=;;'*;	;r   rF   c                  :    e Zd ZdZ	 	 d	 	 	 	 	 d	dZdddZd Zy)
	FakeBrainzDRecords calls; returns canned EntryDecision / BrainDecision or None.Nc                Z    || _         |xs t        dd      | _        g | _        g | _        y )NHOLDdefault)actionreason)entry_decisionr   exit_decisionevaluate_callsmanage_calls)r&   r\   r]   s      r   r'   zFakeBrain.__init__N   s1    
 -*\m6R[.\>@')r           )last_entry_eval_timec                  K   | j                   j                  ||f       t        t        |dd      xs d      xs d }t	        | j
                  |      S w)Ncandle_timer   )decisionevaluated_candle_time)r^   r0   rT   getattrr   r\   )r&   r7   techra   cts        r   evaluate_entryzFakeBrain.evaluate_entryX   sV     ""FD>2 7427a8@D(("$
 	
s   AAc                X   K   | j                   j                  |       | j                  S wr#   )r_   r0   r]   )r&   ctxs     r   manage_exitzFakeBrain.manage_exitc   s&       %!!!s   (*)NN)r\   zEntryDecision | Noner]   zBrainDecision | Noner<   r=   )r@   rA   rB   rC   r'   ri   rl   rD   r   r   rV   rV   L   s?    N 04.2*,* ,* 
	* JM 	
"r   rV   c                       e Zd ZdZddZddZy)	JsonlSinkz.In-memory replacement for a JsonlLogger.write.c                    g | _         y r#   )events)r&   s    r   r'   zJsonlSink.__init__j   s	    "$r   c                B    | j                   j                  d|i|       y )Nevent)rp   r0   r&   rr   fieldss      r   writezJsonlSink.writem   s    GU5f56r   Nr<   r=   rr   r>   r<   r=   )r@   rA   rB   rC   r'   ru   rD   r   r   rn   rn   h   s    8%7r   rn   c                  (    e Zd ZdZddZddZddZy)	
FakeLoggerz7Mimics LoggerBundle minimally for Orchestrator's hooks.c                    dd l }t               | _        t               | _        t               | _        |j                  d      | _        y )Nr   z	test.orch)loggingrn   	brain_logsession_log	error_log	getLoggersystem)r&   r{   s     r   r'   zFakeLogger.__init__s   s3    "$;"''4r   c                >     | j                   j                  |fi | y r#   )r}   ru   rs   s      r   log_session_eventzFakeLogger.log_session_eventz   s    u//r   c                B     | j                   j                  d||d| y )N)rR   error)r   )r~   ru   )r&   rR   r   extras       r   	log_errorzFakeLogger.log_error}   s     HEH%Hr   Nrv   rw   )rR   r>   r   r>   r<   r=   )r@   rA   rB   rC   r'   r   r   rD   r   r   ry   ry   q   s    A50Ir   ry   )asset_filterloop_sleep_secondsc                    t        t        j                  t        j                        }| |_        ||_        d|_        d|_        d|_	        |S )N)modeaccountr`   r   )
r   r   DRYr   
INELIGIBLEr   r   scan_loop_phase_offset_secondsmanage_loop_interval_seconds!maintenance_loop_interval_seconds)r   r   cfgs      r   make_configr      sI    
 W[[+2H2H
IC#C/C *-C&'(C$,-C)Jr   c                    t        | dz        S )Nz
state.json)r   )tmps    r   
make_storer      s    cL())r   c                d
   t        d>i d| d   dt        | j                  dd            dt        | j                  d| j                  dd                  dt        | j                  dd      xs d      dt	        | j                  dd            d	t        | j                  d	d
            dt        | j                  dd            dt        | j                  d| j                  dd                  dt        | j                  dd            dt        | j                  dd            dt        | j                  dd            dt        | j                  dd            d| j                  dd      dt	        | j                  dd            d| j                  dd      dt	        | j                  dd            dt	        | j                  dd            dt        | j                  dd            d| j                  dd      d| j                  dd      dt        | j                  dg             d t        | j                  d d            d!| j                  d!d"      d#t	        | j                  d#d            d$t        | j                  d$d            d%t        | j                  d%d            d&t	        | j                  d&d            d't	        | j                  d'd            d(t	        | j                  d(d            d)t	        | j                  d)d            d*t	        | j                  d*d            d+| j                  d+      d,t	        | j                  d,d            d-t	        | j                  d-d            d.t	        | j                  d.d            d/t	        | j                  d/d            d0t	        | j                  d0d            d1t	        | j                  d1d            d2t	        | j                  d2d            d3t        | j                  d3| j                  dd                  d4t        | j                  d4d            d5| j                  d5d6      d7| j                  d7d"      d8t        | j                  d8d            d9| j                  d9d      d:t        | j                  d:i             d;t        | j                  d;d            d<t        | j                  d<d            d=t        | j                  d=d            S )?z6TechSnapshot factory (mirror of test_brain_tf helper).r7   pricer`   r)   rc   r   is_candle_closedTcandle_age_secondsg      $@rsi      I@rsi_prevrsi_h1rsi_h4atr_m5_points	atr_ratio      ?
vol_regimeNORMAL	vol_spikeFmarket_structureRANGINGh1_struct_bullh1_struct_beartrend_maturityregimeregime_reason regime_near_trendingdeviation_pct
divergenceNONEmacd_deceleratingmacd_hist_lastcandle_strengthhammershooting_starbull_engulfingbear_engulfingdoji	doji_typepiercing
dark_cloudmorning_starevening_starvolume_weakbuy_absorptionsell_absorptionvwapvwap_deviation_pctbiasNEUTROallowed_directionh1_compatibility	h1_reason
swing_dataconsecutive_sl_count	tick_size
tick_valuerD   )r
   rT   r1   r?   boollistdict)ds    r   _to_snapr      s&    2{2AEE'3'(2 155w!4562 mQ/415	2
 aee$6=>2 !';T!BC2 !%%t$%2 quuZud);<=2 QUU8T*+2 QUU8T*+2 AEE/3782 k3/02 55x02 quu[%012 19=2  AEE"2E:;!2" AEE"2E:;#2$ 155!1156%2& uuXy)'2( eeOR0)2* "!%%(>"CD+2, AEE/378-2. 55v./20 quu%8%@A122 QUU#3S9:324 aee$5s;<526 AEE(E*+728 155%8992: AEE"2E:;;2< AEE"2E:;=2> !%%&'?2@ %%$A2B aeeJ./C2D lE23E2F !%%67G2H !%%67I2J }e45K2L AEE"2E:;M2N QUU#4e<=O2P 155w!456Q2R !';S!ABS2T UU68$U2V %% 3V<W2X quu%7=>Y2Z %%R([2\ lB/0]2^ !'=q!AB_2` k3/0a2b |S12c2 2r   )Anyc            
     n   t        j                         5 } t        dg      }d|_        d|_        d|_        t               }t        t        |             }t               }t               }t               }t        ||||||d      }t        j                  |j                               }d}	||	k(  }
|
st        j                   d|
fd||	f      d	t#        j$                         v st        j&                  |      rt        j(                  |      nd	t        j(                  |	      d
z  }dd|iz  }t+        t        j,                  |            d 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}}d d d        t1        d       y # 1 sw Y   xY w)NMESr   r`   r      config	ai_clientmarket_data_providerstatestoreloggermax_iterations==z%(py0)s == %(py3)srcpy0py3assert %(py5)spy5)z2%(py2)s
{%(py2)s = %(py0)s._iteration
} == %(py5)sorchr   py2r   assert %(py7)spy7z9run(): max_iterations=3 -> exit code 0, 3 scan iterations)tempfileTemporaryDirectoryr   r   r   r   r   r   r   r!   rF   ry   r   asynciorun
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanation
_iterationr   )r   r   r   r   providerair   r   r   @py_assert2@py_assert1@py_format4@py_format6@py_assert4@py_assert3@py_format8s                   r   !test_run_completes_max_iterationsr     s]   		$	$	& $#w/-0*+,(01-49%>X"8uV

 [[$rQwrQrrQ#!#!####!######t###t######!########$$ CD%$ $s   HH++H4c                 X   t        j                         5 } t        dg      }t               }t	        t        |             }t        t        ddddddd	
            }t        |t               t               ||t               ||dd      }t        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(                  |      dz  }	t        j*                  d      dz   d|	iz  }
t-        t        j.                  |
            dx}x}}ddd       t1        d       y# 1 sw Y   xY w)zJEven when brain returns an EntryDecision, Phase A must NOT create a trade.r   r   BUYg     @g    @g     @      ?P   test)	directionentry_pricesl_pricetp_pricerr_multiplier
confidence	rationaler\   TFMR   r   r   r   r   r   r   brain_dispatchr   r   )z5%(py2)s
{%(py2)s = %(py0)s.active_trades
} == %(py5)sr   r   z%Phase A must NOT mutate active_tradesz
>assert %(py7)sr   NzHPhase A dry-run: brain returns Entry but state.active_trades stays empty)r   r   r   r   r   r   rV   r   r   rF   r!   ry   r   r   active_tradesr   r   r   r   r   r   _format_assertmsgr   r   r   )r   r   r   r   brainr   r   r   r   r   r  s              r   *test_dry_run_does_not_mutate_active_tradesr     s3   		$	$	& R#w/49%&4B&*
  &(uZ\"'u5	
 	DHHJ""QbQ"b(QQQ"bQQQQQQuQQQuQQQ"QQQbQQQ*QQQQQQQQ!R" RS#R Rs   E7F  F)c                 ,   t        j                         5 } t        dg      }t               }dg|_        t        d      }t               }t        |t               t               |t        t        |             |||dd      }t        j                  |j                                |j                  j                  D cg c]  }|d	   	 }}d
}||v}	|	st!        j"                  d|	fd||f      t!        j$                  |      dt'        j(                         v st!        j*                  |      rt!        j$                  |      nddz  }
dd|
iz  }t-        t!        j.                  |            dx}}	ddd       t1        d       yc c}w # 1 sw Y   xY w)zLWhen evaluate_entry returns a decision, brain_log gets a 'dry_run_proposal'.r   r   6ENr  r     r  rr   dry_run_proposal)not in)z%(py1)s not in %(py3)srp   py1r   r   r   z>Phase A: empty-bars path skips evaluate_entry without crashing)r   r   r   r   r   rV   ry   r   rF   r!   r   r   r   r   r|   rp   r   r   r   r   r   r   r   r   r   )r   r   r   r  r   r   erp   @py_assert0r   r   r   s               r   +test_dry_run_logs_proposal_when_brain_emitsr#     s,   		$	$	& 0#w/ !6 .&(z$s)4V"'u5	
 	DHHJ&,&6&6&=&=>!G*>> "/!////!///!////////////////102 HI ?)0 0s   B)F
>F
B'F
F

Fc            
        t        j                         5 } t        dg      }d|_        d|_        d|_        t               }t        t        |             }t        |t               t               ||t               d      }ddi|j                  fd}||_        t        j                  |j                                d   }d	}||k\  }|st!        j"                  d
|fd||f      t!        j$                  |      t!        j$                  |      dz  }	t!        j&                  dd          dz   d|	iz  }
t)        t!        j*                  |
            dx}x}}ddd       t-        d       y# 1 sw Y   xY w)uG   save() runs in maintenance_loop + final save in finally — at least 1.r   r   r`   r      r   r9   c                0    dxx   dz  cc<    |        y )Nr9   r  rD   )soriginal_save
save_counts    r   counting_savez>test_iteration_order_and_save_each_tick.<locals>.counting_save.  s    sOq O!r   r  )>=)z%(py1)s >= %(py4)s)r   py4u   expected ≥1 saves, got z
>assert %(py6)spy6Nz8save(): runs in maintenance_loop + final save in finally)r   r   r   r   r   r   r   r   r   r   rF   r!   ry   saver   r   r   r   r   r  r   r   r   )r   r   r   r   r   r*  r"  r   r   @py_format5@py_format7r(  r)  s              @@r   'test_iteration_order_and_save_each_tickr1    s)   		$	$	& S#w/-0*+,(01-49%&(uZ\

 1X


	 #
DHHJ#R!R!#RRR!RRRRRR!RRR'@C@Q%RRRRRRRR)S* BC+S Ss   EE22E;c                 0   t        j                         5 } t        dg      }t               }t	               }t        |t               t               |t        t        |             |i d      }t        j                  |j                               }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                   |      rt        j"                  |      ndt        j"                  |      d	z  }d
d|iz  }	t%        t        j&                  |	            dx}}d |j(                  j*                  D        }t-        |      }
|
sddt        j                         v st        j                   t,              rt        j"                  t,              ndt        j"                  |      t        j"                  |
      dz  }t%        t        j&                  |            dx}}
ddd       t/        d       y# 1 sw Y   xY w)z=No brains wired -> log scan_skip with brain_dispatch_missing.r   r   r  r  r   r   r   r   r   r   r   Nc              3  ,   K   | ]  }|d    dk7    yw)rr   r  NrD   ).0r!  s     r   	<genexpr>zAtest_orchestrator_skips_when_no_brain_dispatch.<locals>.<genexpr>J  s     U1W:!33Us   z,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}all)r   r   r,  z>orchestrator: empty brain_dispatch -> no proposals, clean exit)r   r   r   r   ry   r   rF   r!   r   r   r   r   r   r   r   r   r   r   r   r   r|   rp   r6  r   )r   r   r   r   r   r   r   r   r   r   r   r/  s               r   .test_orchestrator_skips_when_no_brain_dispatchr7  8  s=   
 
	$	$	& V#w/&(z$s)4V	
 [[$rQwrQrrQUV=M=M=T=TUUsUUUUUUUUUsUUUsUUUUUUUUUUUUUUV HIV Vs   G#HHc            	        t        dddd      } t        dddd	d
d	d      }t        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 }||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 )N	RIALZISTAr  r   r   r   r   r   r   r  g      <@      >@r   r   r7   r   r   r   r   r   r7   rg   r   r  r   z.%(py2)s
{%(py2)s = %(py0)s.chosen
} == %(py5)sresr   r   r   isz5%(py2)s
{%(py2)s = %(py0)s.reject_reason
} is %(py5)sz7brain_selector: RANGING + RSI<32 + 6E (non-index) -> MRr   r   r   chosenr   r   r   r   r   r   r   r   reject_reasonr   r   rg   r?  r   r   r   r   r  s           r   0test_brain_selector_ranging_extreme_rsi_picks_mrrG  R  s+   %(B8Dtq% D
 dD
9C::::33:$$$$$$$$$$$$3$$$3$$$$$$$$$$$$$ABr   c            	        t        dddd      } t        dddd	d
dd      }t        dd
ddddt        j                        }t        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 }||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 )Nr9  r  r   r   r:  r  r         H@TRENDINGr%  BULLISH_EXPANSIONr<          r   tzinfo)r7   rg   r   now_utcr  r   r>  r?  r   r   r   r@  rB  z?brain_selector: TRENDING + RSI 42-58 bouncing + non-index -> TF)r   r   r   r   utcr   rD  r   r   r   r   r   r   r   r   rE  r   )	r   rg   daytimer?  r   r   r   r   r  s	            r   .test_brain_selector_trending_pullback_picks_tfrT  `  sG   %(B8Dt/ D tQB1X\\BG
dD'
JC::::33:$$$$$$$$$$$$3$$$3$$$$$$$$$$$$$IJr   c                    t        dddd      } t        dddd	d
d      }t        d||       }|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}||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        d       y )Nr9  r  r   r   r:  r   r   rI  BREAKOUTr%  r7   r   r   r   r   r=  r@  z.%(py2)s
{%(py2)s = %(py0)s.chosen
} is %(py5)sr?  r   r   r   REGIME_BREAKOUTr   z5%(py2)s
{%(py2)s = %(py0)s.reject_reason
} == %(py5)sz2brain_selector: BREAKOUT regime -> REGIME_BREAKOUTrC  rF  s           r   )test_brain_selector_breakout_returns_noner[  p  s,   %(B8D$ D e$T
:C::::33:1 11 11111 111111131113111111 11111111<=r   c                    t        dddd      } t        dddd	d
d      }t        d||       }|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}||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        d       y )Nr   r   r  r   r:  r        9@r;  r   r   rW  r=  r@  rX  r?  r   r   r   	BIAS_NONEr   rZ  z.brain_selector: bias allowed=NONE -> BIAS_NONErC  rF  s           r   *test_brain_selector_bias_none_returns_noner_  }  s(   V%(B8Dtq D dD
9C::::33:++++++++++++3+++3+++++++++++++89r   c            	        d} | t         v }|st        j                  d|fd| t         f      t        j                  |       dt	        j
                         v st        j                  t               rt        j                  t               nddz  }dd|iz  }t        t        j                  |            dx} }t        d	d
dd      }t        ddddddd      }d} | t        v }|st        j                  d|fd| t        f      t        j                  |       dt	        j
                         v st        j                  t              rt        j                  t              nddz  }dd|iz  }t        t        j                  |            dx} }t        d||      }|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}||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} |	|      }d }||u }|st        j                  d|fd!||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |	      t        j                  |      t        j                  |      t        j                  |      d"z  }d#d$|iz  }t        t        j                  |            dx}x}	x}x}x}}t#        d%       y)&z3MYM in MR_EXCLUDED -> never MR even if RSI extreme.MYMin)z%(py1)s in %(py3)sr   r  r   r   Nr9  r  r   r   r:  r;  g      ?@TRENDING_SOFTr%  rK  r<  r   r=  r@  rX  r?  r   r   r   INDICES_NO_PRO_TREND_SETUPr   rZ  mr_excludedT)zg%(py8)s
{%(py8)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.details
}.get
}(%(py6)s)
} is %(py11)s)r   r   r,  r-  py8py11zassert %(py13)spy13z;brain_selector: MR_EXCLUDED symbol (MYM) never routes to MR)r   r   r   r   r   r   r   r   r   r   r   r   r   rD  rE  detailsr1   r   )r"  r   r   r   r   rg   r?  r   r   r   r  @py_assert5@py_assert7@py_assert10@py_assert9@py_format12@py_format14s                    r   )test_brain_selector_mr_excluded_blocks_mrrq    s   5K5K5KK%(B8D $!Q/ D "5N""""5N"""5""""""N"""N"""""""
e$T
:C::::33: < << <<<<< <<<<<<<3<<<3<<<<<< <<<<<<<<;;1;??1=1?=)1T1)T1111)T11111131113111;111?111=111)111T11111111EFr   c                    t        dddd      } t        dddd	d
d      }t        d||       }|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}||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        d       y)z+V15 patch #20: indices SKIP RANGING regime.r9  r  r   r   r:  r   r]  r;  r   r   rW  r=  Nr@  rX  r?  r   r   r   RANGING_INDEX_BLOCKEDr   rZ  z;brain_selector: indices in RANGING -> RANGING_INDEX_BLOCKEDrC  rF  s           r   +test_brain_selector_indices_ranging_skippedrt    s,   %(B8D$q D e$T
:C::::33:7 77 77777 777777737773777777 77777777EFr   c                    ddl m } m}  | dddddd|j                        t        j                         5 }t        dd	g
      }t               }t               }t        |t               ||t        t        |            t               fdd      }t        j                  |j                               }d}||k(  }	|	st        j                   d|	fd||f      dt#        j$                         v st        j&                  |      rt        j(                  |      ndt        j(                  |      dz  }
dd|
iz  }t+        t        j,                  |            dx}	}d}|j.                  }|j0                  }||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.                  }|j0                  }||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D ]  }|j.                  j0                  |   }|j2                  }d}||k(  }|st        j                   d|fd||f      t        j(                  |      t        j(                  |      t        j(                  |      dz  }dd |iz  }t+        t        j,                  |            dx}x}x}} 	 ddd       t5        d!       y# 1 sw Y   xY w)"a6  Empty H4 -> resolver returns NEUTRO BOTH; tech build returns None;
    loop just skips the symbol and survives the tick.

    Uses a fixed UTC clock at 14:00 so the trading-hours gate passes for
    MES/6E (otherwise the gate skips the symbol BEFORE the bias resolver
    runs and bias_cache stays empty).
    r   r   rL  r%        rO  r   r  r   c                      S r#   rD   )fixeds   r   <lambda>z:test_no_h4_data_path_does_not_crash_loop.<locals>.<lambda>  s    U r   r  )r   r   r   r   r   r   now_utc_providerr   r   r   r   r   r   r   Nrb  )zO%(py1)s in %(py7)s
{%(py7)s = %(py5)s
{%(py5)s = %(py3)s.bias_cache
}.entries
}r   )r   r   r   r   zassert %(py9)spy9)r   r  BOTH)z1%(py3)s
{%(py3)s = %(py1)s.direction
} == %(py6)s)r   r   r-  zassert %(py8)srg  zDno-h4-data: loop survives, BiasResolver caches NEUTRO BOTH per asset)r   r   rR  r   r   r   r   r!   r   rF   r   r   ry   r   r   r   r   r   r   r   r   r   r   
bias_cacheentriesr  r   )r   _tzr   r   r   r   r   r   r   r   r   r   r"  r   @py_assert6r  @py_format10symrk  r0  @py_format9ry  s                        @r   (test_no_h4_data_path_does_not_crash_loopr    s    3T1b"a377;E		$	$	& E#t}5>&(z$s)4Z\*	
 [[$rQwrQrrQ0((0(000u00000u0000u000000000000(00000000000/u''/'///t/////t////t//////u///u///'///////////  	EC##++C0D0::DfD:fDDDD:fDDD0DDD:DDDfDDDDDDD	EE" NO#E Es   N3O??Pc                 T    d } t        j                   |               t        d       y)z@Calling orch.stop() makes run() exit at the next sleep boundary.c                   K   t        j                         5 } t        dg      }d|_        d|_        d|_        t               }t        |t               t               |t        t        |             t               d      fd}t        j                  j                          |              d {    d d d        y 7 # 1 sw Y   y xY ww)Nr   r   r`   r  i  r   c                 l   K   t        j                  d       d {     j                          y 7 w)Ng?)r   sleepstop)r   s   r   stopperzBtest_stop_event_breaks_loop_early.<locals>.runner.<locals>.stopper  s'     mmC(((		 )s   424)r   r   r   r   r   r   r   r   rF   r!   r   r   ry   r   gatherr   )r   r   r   r  r   s       @r   runnerz1test_stop_event_breaks_loop_early.<locals>.runner  s     ((* 	8cE73C14C./0C,45C1 NEfh\^:d3i#8#D
 ..WY777	8 	8 8	8 	8s/   CBC2C 3C7	C CCCz1stop(): graceful exit interrupts the sleep windowN)r   r   r   )r  s    r   !test_stop_event_breaks_loop_earlyr    s     8  KK;<r   c                 4   t        d       t                t                t                t	                t                t                t                t                t                t                t                t                t                t        d       y)Nztest_orchestrator_phase_a.pyzALL 13 TESTS PASSEDr   )r   r  r  r#  r1  r7  rG  rT  r[  r_  rq  rt  r  r  rD   r   r   mainr    se    	
()%'.0/1+-244624-/.0-//1,.%'	
 r   __main__)r   r>   r<   r=   )r   r?   r<   r   )r   r   r<   r   )r   r   r<   r
   )r<   r?   )MrC   
__future__r   builtinsr   _pytest.assertion.rewrite	assertionrewriter   r   jsonsysr   pathlibr   r   r   pandasr3   pathinsertr>   __file__resolveparentanalysis.biasr   r	   analysis.tech_snapshotr
   brain.ai_clientr   brain.brain_selectorr   r   r   r   core.configr   r   r   core.contractsr   r   r   orchestratorr   persistence.state_storer   r   r   r!   rF   rV   rn   ry   r   r   r   typingr   r  r  r#  r1  r7  rG  rT  r[  r_  rq  rt  r  r  r  r@   exitrD   r   r   <module>r     sX  " #     
   '  3tH~--/66==> ? 0 / &  < ; H H % <1 1; ;" "87 7I I$   	 *4p E,T,J<D4J4CK 
>
:G2G$P<=4& zCHHTV r   