o
    [†j£'  ã                
   @   sÀ   d Z ddlZddlm  mZ ddlZddlZddl	Z	ddl
Z
ddlZddlZddlmZmZmZ ej dej ej ej e¡¡¡¡ ddd„ZG dd„ dejƒZedkr^ejd	d
� dS dS )aQ  
test_simulation_e2e.py

Exercises the full decision pipeline (scoring -> entry confirmation ->
trailing stop -> exit -> replacement search) against synthetic bar data,
with NO Alpaca API calls at all. This lets you validate strategy logic
end-to-end any time, not just during market hours.

Run:
    python tests/test_simulation_e2e.py
é    N)ÚdatetimeÚ	timedeltaÚtimezonec              
   C   sl   |pt dddddtjd�}g }tt| |ƒƒD ]\}\}}| |t|d� |d |d	 |d	 ||d
œ¡ q|S )Néê  é   é   é   é   ©Útzinfo)Úminutesç{®Gáz„?g¸…ëQ¸ž?)ÚtÚoÚhÚlÚcÚv)r   r   ÚutcÚ	enumerateÚzipÚappendr   )ÚpricesÚvolumesÚstartÚbarsÚiÚpr   © r   ú8/var/www/screener/premarket/tests/test_simulation_e2e.pyÚ	make_bars   s   
þr    c                   @   s<   e Zd Zdd„ Zdd„ Zdd„ Zdd„ Zd	d
„ Zdd„ ZdS )ÚTestSimulationEndToEndc              	      s°   t  ¡ ˆ_t ¡ ˆ_t tj tj tj 	t
¡¡¡¡ dd l}|j ¡  dd ladd ladd ladd ladd ladd latjt_tjˆ_‡ ‡fdd„}dd„ ‰ |t_t ¡  d S )Nr   Fc                     s,   ˆj } ˆ | dƒˆ | dƒˆ | dƒˆ | dƒdœS )NÚ	premarketÚintradayÚtradesÚstate)r"   r#   r$   r%   )Útmpdir)Úbase©Úos_pathÚselfr   r   Ú	fake_dirs:   s   üz/TestSimulationEndToEnd.setUp.<locals>.fake_dirsc                 S   s*   ddl m} || ƒ| }|jddd� |S )Nr   )ÚPathT)ÚparentsÚexist_ok)Úpathlibr,   Úmkdir)r'   Únamer,   r   r   r   r   r)   C   s   z-TestSimulationEndToEnd.setUp.<locals>.os_path)ÚtempfileÚmkdtempr&   ÚosÚgetcwdÚ	_orig_cwdÚchdirÚpathÚdirnameÚabspathÚ__file__Úconfig_loaderÚ
get_configÚcache_clearÚ
data_storeÚposition_managerÚentry_engineÚscorerÚrisk_managerÚintraday_healthÚBASE_DIRÚ_dirsÚ
_orig_dirsÚensure_dirs)r*   r<   r+   r   r(   r   ÚsetUp$   s"   

"
	zTestSimulationEndToEnd.setUpc                 C   s   t j| jdd� | jt_d S )NT)Úignore_errors)ÚshutilÚrmtreer&   rG   r?   rF   )r*   r   r   r   ÚtearDownL   s   zTestSimulationEndToEnd.tearDownc                 C   s  t g d¢g d¢tdddddtjd�d	�}tjd
|dddddd�}d|d< |  |d d¡ t g d¢g d¢tdddddtjd�d	�}ddt tj¡f}tj	d
|dd�}|  
|jdd|j› d|j› d�¡ tjd
|||d|d �}|  |jd!|j› �¡ tjd"d#�}|d$ d% }|jd
|d&|d' |j¡d(�}	|  |	¡ |  |	d) d*¡ |	d+ }
|  |
|¡ g d,¢}|
}|D ]}|jd
|d-d.�}|  |¡ |jd
 d+ }|  ||¡ |}q±|jd
 d/ }|jd
 d+ }|jd
|d0 d-d.�}|  |d1¡ | d
|d0 |¡}|  |d) d2¡ |  |d3 d¡ d-S )4a9  
        Simulates a stock that:
          1. develops healthy premarket momentum (scores well)
          2. confirms at the open (breakout holds, VWAP rising, volume expansion)
          3. gets entered
          4. runs up, trailing stop follows
          5. reverses and gets stopped out with a profit
        )g×£p=
× @g…ëQ¸!@ç)\�Âõ(!@g¸…ëQ8!@)iÐÝ iàu	 i�” é`� r   r   r   é	   r   r
   ©r   ÚDEVXçÍÌÌÌÌL!@gÍÌÌÌÌÌ @é€„ ç333333!@ç=
×£p=!@©Úpm_highÚpm_lowÚavg_vol_baselineÚbidÚaskrX   Útotal_score)rU   gR¸…ëQ!@gffffff!@g     €!@ç�Âõ(\�!@)é † iðI i ¿ i@ iP4 r   r	   g
×£p=Š!@r^   i ¡ ©rZ   )ÚHEALTHYÚWATCHzIExpected DEVX's rising, volume-expanding open to read HEALTHY/WATCH, got ú (score=ú)rO   ©Úopening_baseline_volumeÚhealth_readingz$Expected entry, got reasons_against=T©Ú
simulationéÿÿÿÿr   r_   z; ©Úaccount_equityr   ÚreasonÚstatusÚopenÚcurrent_stop)ç33333³!@gfffffæ!@gš™™™™"@gÍÌÌÌÌL"@N)r   Úhighest_pricer   ÚTRAILING_STOPÚclosedÚ
current_pl)r    r   r   r   rB   Úscore_premarket_candidateÚassertGreaterÚnowrD   Úcompute_healthÚassertInÚ	raw_stateÚhealth_scorerA   Úevaluate_entryÚ
assertTrueÚshould_enterÚreasons_againstr@   ÚPositionManagerÚenter_positionÚjoinÚreasons_forÚassertIsNotNoneÚassertEqualÚ
assertLessÚupdate_positionÚassertIsNoneÚ	positionsÚassertGreaterEqualÚexit_position)r*   Úpm_barsÚ	pm_resultÚ	open_barsÚquoterg   ÚdecisionÚpmÚentry_priceÚposÚinitial_stopÚ
run_pricesÚ	last_stopr   Úexit_signalrp   ÚhighestÚ
stop_pricert   r   r   r   Ú5test_full_pipeline_developing_stock_enters_and_trailsQ   sl   
ýþý	
ÿÿÿ
þ
ÿ

zLTestSimulationEndToEnd.test_full_pipeline_developing_stock_enters_and_trailsc                 C   sÚ   t g d¢g d¢tdddddtjd�d	�}tjd
|dddddd�}d|d< t g d¢g d¢tdddddtjd�d	�}ddt tj¡f}tjd
|dd�}|  	|j
dd|j
› d|j› d�¡ tjd
|||d|d�}|  |j¡ dS ) zhA stock that fades immediately after open should NOT enter,
        even if it looked fine in premarket.)gš™™™™™!@g…ëQ¸…!@rS   rU   )i€O iÐ éð` rœ   r   r   r   rP   r   r
   rQ   ÚFADEXrq   çš™™™™!@rT   rN   rV   rW   rX   )rž   g      !@gáz®Gá @gÃõ(\�Â @gš™™™™™ @)i€8 i`ê  i@œ  i0u  i N  r   r	   g®Gáz” @g…ëQ¸ž @r`   Ú	UNHEALTHYz4Expected FADEX's fading open to read UNHEALTHY, got rc   rd   rœ   re   N)r    r   r   r   rB   rv   rx   rD   ry   r†   r{   r|   rA   r}   ÚassertFalser   )r*   r�   rŽ   r�   r�   rg   r‘   r   r   r   Ú(test_fading_stock_does_not_confirm_entry¢   s:   ýþý

ÿÿÿ
þz?TestSimulationEndToEnd.test_fading_stock_does_not_confirm_entryc                 C   sn   dd l }t g ddiddigdœ¡ t dt tj¡ ¡ i¡ t 	¡ }|  
| d|¡¡ |  | d|¡¡ d S )Nr   ÚsymbolÚAAAÚBBB)Úpremarket_20Úfinal_10)Ú
top_stocksr?   Úsave_watchlistÚsave_cooldownsr   rx   r   r   Ú	isoformatÚload_cooldownsr~   Ú_is_on_cooldownr    )r*   r§   Ú	cooldownsr   r   r   Ú8test_slot_replacement_excludes_open_and_cooldown_symbolsË   s   þÿzOTestSimulationEndToEnd.test_slot_replacement_excludes_open_and_cooldown_symbolsc                 C   s²   dd l }| ¡ }|d d }tjdd�}t|ƒD ]}d|› �}tg d¢g d¢ƒ}|j|d	d
|dd�}|  |¡ q|  | 	¡ ¡ tg d¢g d¢ƒ}	|jdd	d
|	dd�}
|  
|
¡ d S )Nr   ÚtradingÚmax_positionsTrh   ÚSYM)ç      $@r²   r²   )éè  r³   r³   r²   r_   Útestrk   ÚOVERFLOW)r<   r=   r@   r�   Úranger    r‚   r…   r    Úhas_available_slotr‰   )r*   r<   Úcfgr°   r’   r   r¢   r   r”   Úoverflow_barsÚoverflowr   r   r   Útest_max_positions_enforcedØ   s   

ÿz2TestSimulationEndToEnd.test_max_positions_enforcedN)	Ú__name__Ú
__module__Ú__qualname__rI   rM   r›   r¡   r®   r»   r   r   r   r   r!   #   s    (Q)r!   Ú__main__é   )Ú	verbosity)N)Ú__doc__ÚbuiltinsÚ@py_builtinsÚ_pytest.assertion.rewriteÚ	assertionÚrewriteÚ
@pytest_arÚsysr4   ÚjsonrK   r2   Úunittestr   r   r   r8   Úinsertr9   r:   r;   r    ÚTestCaser!   r¼   Úmainr   r   r   r   Ú<module>   s    "&
 Jÿ