o
    &·jk(  ã                
   @   sN  d Z ddlZddlZddlZddlZddlZddlmZmZmZ ddl	m
Z
 ddlmZ e
eƒ ¡ jZej deejd ƒ¡ ddlmZ ddlZddlZddlZddlZddlZedƒZejZd	Z i d
diddidddœddidddœddiddiddidœ	Z!de"fdd„Z#dd„ Z$de"de%de"de%fd d!„Z&d"d#„ Z'e(d$kr¥e'ƒ  dS dS )%aé  
bar_backtest.py

[2026-09-25] 1-minute-bar portfolio backtest over the 20 opening-window days,
driving the bot's REAL decision code (smart_engine / setup_analyzer / exit /
exit_giveback_room / exit_resistance_stall / volatility / reentry) once per
minute instead of every 5 s from ticks.

Differences from the tick replay (simulate.py), applied identically to every
variant so comparisons between variants stay fair:
  - one check per minute (at the bar close); Stage 2's 18 s hold is waived
    (a minute already spans it)
  - no trade-flow imbalance (needs ticks): the imbalance exit and the
    context rule's imbalance check are skipped
  - no quotes: the spread check is skipped
  - hard stop checked against each bar's LOW (fills at the stop, or the bar
    open if it gapped through)
  - entries fill at the NEXT bar's open (market order after the signal)

Usage: python bar_backtest.py VARIANT [--scanner current|alt]
Writes results/<variant>_<scanner>.json with every trade and its context.
é    N)ÚdatetimeÚ	timedeltaÚtimezone)ÚPath)ÚZoneInfoé   )Ú
get_configzAmerica/New_Yorkg     0Á@Úlow_ceilingTÚmax_ext)r	   r
   Ú
trend_barsé   éZ   )r	   Ú
dead_moneyÚfive_dayÚcontextFÚreal_targets)	ÚAÚBÚCÚDÚEÚFÚGÚHÚIÚvc              	   C   sh  t ƒ }|d }d|d< i | di ¡¥ddi¥|d< i | di ¡¥ddd	d
œ¥|d< i | di ¡¥d|  dd¡i¥|d< d|d< d|d< |d }i | di ¡¥d|  dd¡i¥|d< |  dd¡|d< d|d< i | di ¡¥d|  dd¡i¥|d< |d }| dd|  dd¡|  dd¡dœ¡ |  dd¡|d d < |d!  d"d	d#œ¡ |d$ }| d%d&d'd(d'd)œd*œ¡ |S )+NÚsmart_engineTÚuse_setup_planÚentry_slope_ruleÚenabledFÚmin_stopÚkg      Ð?)r   Ústyler!   Úmax_extension_from_openr
   r   Úpersistence_secondsi@B Úmax_evaluation_gap_secondsÚ
setup_planÚcontext_rulesr   r   Úreal_level_targetsÚreclaim_breakoutsÚfive_day_levelsr   Úresistance_stallg     €F@r	   r   )r   Ústall_minutesÚlow_ceiling_enabledÚdead_money_minutesr   ÚexitÚdeterioration_trend_barsÚgiveback_roomg      .@)Úmin_peak_gain_centsÚgiveback_ratioÚtradingé   g      3@g      ð?Úabove_entry)ÚmodeÚbuffer_cents)Úmax_positionsÚ#max_position_notional_pct_of_equityÚaccount_risk_pct_per_tradeÚreentry_after_loss)r   ÚgetÚupdate)r   ÚcfgÚseÚspÚrsÚt© rD   ú;/var/www/screener/trade/reports/open_window/bar_backtest.pyÚapply_variant:   s0    $$$
ÿ
ÿrF   c                 C   s^   | | }|dkr
dS t  t|d  d | ¡}t|t  t|d  d |  ¡ƒ}|dkr-|S dS )Nr   r;   g      Y@r:   r   )ÚmathÚfloorÚEQUITYÚmin)ÚentryÚstoprC   ÚriskÚqrD   rD   rE   Úqty_forS   s    rO   ÚdayÚwatchr?   Úreturnc                    s  |d }| d }dd„ |  ¡ D ƒ}dd„ |  ¡ D ƒ}t | d ¡}t|j|j|jddtd	� t¡‰‡fd
d„t	ddƒD ƒ}dd„ |D ƒ}	i g i ‰‰}
i i i i f\‰‰‰ ‰‡ ‡‡‡‡‡fdd„}|D �]Ÿ}|t
dd� }|D ]‰||ˆ v r|ˆ | |	ˆ< qotˆƒD ]å‰|	ˆ dk r�q„|ˆ d |	ˆ d … }|d }ˆˆ }|d |kr¨q„t|d |d ƒ|d< t|d |d ƒ|d< | t¡ d¡dkrÒ|ˆ|d d|ƒ q„|d |d kré|ˆt|d |d ƒd|ƒ q„t ˆ||d  ˆ ˆi ¡¡}|jˆˆ< |j�r|ˆ|d! |j|ƒ q„tjˆ||d" |ˆ ˆi ¡tˆƒ|d# k ˆ ˆd$¡d%�}|jˆˆ< |j�r;|ˆ|d! |j|ƒ q„tjˆ||d  ˆ  ˆi ¡d |d d&�}|jˆ ˆ< |j d'¡ˆˆ< |j�ri|ˆ|d! |j|ƒ q„tˆƒD ]‰| t¡ d¡dk�rŠ|ˆ|ˆ |	ˆ  d! d|ƒ �qn| t¡ d¡d(k�r˜qc|D �]g‰tˆƒ|d# k�r© �nZˆˆv �sÂ|	ˆ d)k �sÂ|ˆ |	ˆ  d |k�rÄ�qš|ˆ d |	ˆ d … }‡fd*d„ˆD ƒ}t ˆ|d d! |�rè|d nd |d+ ¡\}}|�sõ�qši |ˆ d, ¥d-td.d/„ |D ƒƒi¥}tjˆ|td0d/„ |D ƒƒ|ˆ d1 ||
 ˆi ¡d d |d2�	}|j|
ˆ< |j�s0�qš|	ˆ d }|t|ˆ ƒk�rA�qš|ˆ | d }|j  d3i ¡ d4¡}|d u �s]||k�r_�qšt!|||ƒ}|dk�rl�qš|j  d5¡�pti }i d6ˆ“d7|“d |“d|“d"|ˆ | d  "¡ “d|“d|“d8| d8¡“d9| d:¡“d;| d;¡“d<| d<¡“d=|j  d=¡“d>| d?¡“d@| dA¡“dB|j  dC¡�pÇi  dD¡“dE|j  dC¡�pÔi  dE¡“dF|j  dG¡“|j  dH¡�rôt#||j dH  d dI d)ƒnd | $ˆ¡d dJœ¥ˆˆ< �qšqcˆS )KNr4   Úsymbolsc                 S   s$   i | ]\}}|d d„ |d D ƒ“qS )c              	   S   s>   g | ]}t  |d  t¡|d |d |d |d |d dœ‘qS )r   r   é   é   é   r5   )rC   ÚoÚhÚlÚcr   )r   ÚfromtimestampÚUTC©Ú.0ÚbrD   rD   rE   Ú
<listcomp>_   s    6ÿz&run_day.<locals>.<dictcomp>.<listcomp>ÚbarsrD   )r^   ÚsÚdrD   rD   rE   Ú
<dictcomp>_   s
    ÿ
ÿzrun_day.<locals>.<dictcomp>c                 S   s$   i | ]\}}|d d„ t |ƒD ƒ“qS )c                 S   s   i | ]	\}}|d  |“qS )rC   rD   )r^   Úir_   rD   rD   rE   rd   a   ó    z&run_day.<locals>.<dictcomp>.<dictcomp>)Ú	enumerate)r^   rb   ÚblrD   rD   rE   rd   a   s   $ Údateé	   r   )Útzinfoc                    s   g | ]	}ˆ t |d � ‘qS ))Úminutes)r   )r^   re   )ÚstartrD   rE   r`   d   rf   zrun_day.<locals>.<listcomp>r   i‚  c                 S   s   i | ]}|d “qS )éÿÿÿÿrD   )r^   rb   rD   rD   rE   rd   e   s    c                    sº   ˆ  | ¡}t|d |ƒ}ˆ i |¥t|dƒ| ¡ |t||d  |d  dƒt||d  |d  dƒt||d  d dƒt|d |d  d dƒd	œ¥¡ ˆˆˆ ˆfD ]}|  | d ¡ qRd S )
NÚpeakrV   Úentry_priceÚqtyrT   éd   r   Úlow)Ú
exit_priceÚ	exit_timeÚexit_reasonÚplÚ
pl_dollarsÚmfe_cÚmae_c)ÚpopÚmaxÚappendÚroundÚ	isoformat)ÚsymÚpxÚreasonÚtsÚpro   Úst)Ú
conf_stateÚgb_stateÚhealthyÚ	positionsÚstall_stateÚtradesrD   rE   Úclosei   s   

üÿzrun_day.<locals>.closeé;   )Úsecondsr   rn   rC   ro   rX   rs   rY   z%H:%Mz15:55rW   Ú
END_OF_DAYÚ
stop_pricezhard stop (intrabar)rp   rZ   Ú
entry_timer9   T)Ú
slots_freerˆ   )Útrade_imbalancer�   rˆ   z15:15rT   c                    s   g | ]
}|d  ˆ kr|‘qS )ÚsymbolrD   ©r^   rC   )rb   rD   rE   r`   £   s    r<   ÚlevelsÚsession_highc                 s   ó   � | ]}|d  V  qdS )rX   NrD   r]   rD   rD   rE   Ú	<genexpr>§   ó   € zrun_day.<locals>.<genexpr>c                 s   r˜   )r   NrD   r]   rD   rD   rE   r™   ¨   rš   Úbaseline)Úquoter“   Úas_ofÚstage3rL   Úplanr”   rq   ÚsetupÚrrÚreward_riskÚtargetÚtarget_labelÚext_from_open_pctÚ	pos_in_5dÚpos_in_5d_range_pctÚroom_5d_atrÚroom_to_5d_high_atrÚ	vol_classÚ
volatilityÚclassÚdaily_atr_pctÚpaceÚvolume_pace_ratioÚvwaprr   )Úvwap_dist_pctÚ
watch_rank)%Úitemsr   ÚfromisoformatÚyearÚmonthrP   ÚETÚ
astimezoner\   Úranger   Úlistr|   rJ   ÚstrftimeÚexit_giveback_roomÚevaluater=   Ú	state_outÚshould_exitr‚   Úexit_resistance_stallÚlenÚexit_engineÚconfirmationÚ
startswithÚreentryÚcheckr   ÚsumÚpersistenceÚshould_enterÚmetricsrO   r   r~   Úindex)rP   rQ   r?   Út_cfgÚsymsra   ÚidxÚd0rl   ÚuptoÚpersistrŒ   ÚmÚnowrh   r_   r„   ÚgÚrÚeÚpriorÚokÚ_ÚlvÚdcsÚnxtrK   rL   rN   rŸ   rD   )r†   r‡   rˆ   r‰   rb   rŠ   rm   r‹   rE   Úrun_day\   sú   ÿ 
€,

þ
ÿ
€€
2.&$ÿ

ÿÿÿÿþþþ
ý
ý
ý
üû
ú
úùø	÷,
õ€rÝ   c            
      C   s|  t  ¡ } | jdttƒd� | jdddgdd� |  ¡ }tt|j ƒ}|j|ji dœ}tt	d  
d	¡ƒD ];}t t |d
¡¡}||jdkrFdnd }t|||ƒ}||d |d < t|d t|ƒdttdd„ |D ƒƒdƒdd� q3t	d }|jdd� ||j› d|j› d�  t |¡¡ dd„ |d  ¡ D ƒ}	td|j› d|j› dt|	ƒ› dtdd„ |	D ƒƒ› d td!d„ |	D ƒƒd"›�
ƒ d S )#NÚvariant)Úchoicesz	--scannerÚcurrentÚalt)rß   Údefault)rÞ   ÚscannerÚdaysra   zbars_*.json.gzÚrtÚtop30_currentÚ	top30_alträ   ri   r‹   c                 s   r˜   ©rw   NrD   r•   rD   rD   rE   r™   Ô   rš   zmain.<locals>.<genexpr>rT   T)ÚflushÚresults)Úexist_okrÙ   z.jsonc                 S   s   g | ]	}|D ]}|‘qqS rD   rD   )r^   ÚtrrC   rD   rD   rE   r`   Ø   rf   zmain.<locals>.<listcomp>zTOTAL ú/z: z	 trades, c                 s   s   � | ]	}|d  dkV  qdS )rw   r   NrD   r•   rD   rD   rE   r™   Ù   s   € z wins, $c                 s   r˜   rè   rD   r•   rD   rD   rE   r™   Ú   rš   z+.2f)ÚargparseÚArgumentParserÚadd_argumentÚsortedÚVARIANTSÚ
parse_argsrF   rÞ   rã   ÚHEREÚglobÚjsonÚloadÚgzipÚopenrÝ   ÚprintrÁ   r~   rÇ   ÚmkdirÚ
write_textÚdumpsÚvalues)
ÚapÚar?   ÚoutÚfrP   rQ   rì   ÚresÚalltrD   rD   rE   ÚmainÈ   s&   0$2ÿr  Ú__main__))Ú__doc__rî   rø   rö   rG   Úsysr   r   r   Úpathlibr   Úzoneinfor   Ú__file__ÚresolveÚparentrô   ÚpathÚinsertÚstrÚparentsÚconfig_loaderr   r   r/   rÂ   r¼   rÀ   rÅ   r·   Úutcr\   rI   rò   ÚdictrF   rO   rº   rÝ   r  Ú__name__rD   rD   rD   rE   Ú<module>   sJ    ÷	l
ÿ