o
    ÄVºjÇ”  ã                   @  sØ  U d Z 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ddlZddl	Z	ddl
mZ ddlmZ ddlmZmZmZ ddlmZmZmZmZmZmZ ddlmZ e d	¡Zed
ƒZ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#d$“d%d“d&d'd(g“d)g d*¢“d+d“d,d-gd.d/gd0dgd"d1gd2d3gddggd4d-gd5d/gddgd6d1gd7d3gddggd4d-gd5d/gddgd6d1gdd3ggd8dd.d9d:ddd;d:d<œ	d=œ¥Zd>ed?< eG d@dA„ dAƒƒZd�dFdG„Z dHdI„ Z!dŽdMdN„Z"d�dQdR„Z#d�dXdY„Z$d‘d\d]„Z%d’d^d_„Z&d“dcdd„Z'd”dhdi„Z(d•dldm„Z)G dndo„ doƒZ*G dpdq„ dqƒZ+ddrl,m-Z-m.Z. d	Z/d1dd.d9ddddddsœ	Z0da1dtedu< i Z2dvedw< d–dxdy„Z3d—dzd{„Z4d|d}„ Z5d˜d~d„Z6d€d�„ Z7d‚dƒ„ Z8d™d…d†„Z9dšd‹dŒ„Z:dS )›a”  
entry_rules.py -- trade1 ENTRY module built on the entry_rules engine
======================================================================

[2026-09-27] Installed in trade1 as a real entry module (rules_api
contract -- see the adapter at the bottom of this file): it BUYS when
the engine's verdict is PASS. The engine below is the same code that runs
as the shadow recorder in screener/trade (copied unchanged); its original
notes follow.

----------------------------------------------------------------------

[2026-09-26] Adapted from the sip_bot version the user supplied, for this
bot (stream.py / monitor.py / simulate.py). Changes, per the review:
  * record() builds the snapshot under the lock but writes the file AFTER
    releasing it, so the stream thread's on_trade/on_quote are never blocked
    by disk I/O (they share the lock).
  * track_outcomes (default True here, False in the live config): live, the
    outcomes are labeled after the close from Alpaca 1-min bars by
    shadow_outcomes.py (longer horizons, target-before-stop, and no memory
    held during the day or lost when a rescan unsubscribes a symbol).
  * seed_bars(): load a symbol's bars since the open in one go -- stocks added
    by a mid-day rescan otherwise start VWAP/HOD/EMA from the time they were
    added.
  * The caller (monitor.py) records only on decision CHANGES, not every
    5-second REJECTION.

Purpose
-------
Every time the bot evaluates an entry (ENTRY, REJECTION, CONFIRM_START, ...)
this module records a full feature snapshot, scores it with the *proposed*
entry rules (Volume Acceleration Score, order-flow pressure, relative
strength, spread, 3-bar volume trend), and later attaches what price actually
did afterwards (+1 / +5 / +15 min returns, MFE, MAE).

SHADOW ONLY:
  * never places orders and never changes a bot decision
  * never raises into the caller -- every public method is wrapped, internal
    errors are logged to logger "entry_rules" and swallowed
  * delete the hooks and the bot behaves exactly as before

Output
------
<log_dir>/shadow_entry_YYYY-MM-DD.jsonl  (one JSON object per line, ET date)
  {"type": "snapshot", "id": ..., ...}   written at decision time
  {"type": "outcome",  "id": ..., ...}   written once all horizons elapse
Join the two on "id" for analysis.

Integration (4 hooks)
---------------------
    from entry_rules import ShadowEntryRules, Bar

    shadow = ShadowEntryRules(config.get("shadow_entry_rules", {}))

    # stream handlers -- feed universe symbols AND benchmarks (SPY, IWM)
    shadow.on_bar(sym, Bar(ts, o, h, l, c, v))   # COMPLETED bar, ts = bar START
    shadow.on_quote(sym, ts, bid, ask, bid_size, ask_size)
    shadow.on_trade(sym, ts, price, size)

    # decision points (entry_engine)
    shadow.record(sym, "ENTRY", bot_context={"health": h, "momentum": m,
                                             "reason": r, "setup": s})
    shadow.record(sym, "REJECTION", bot_context={"reason": "rvol_floor"})

    # end of day (writes partial outcomes for anything still pending)
    shadow.flush()

All timestamps must be timezone-aware datetimes. Pass `now=` to record()
when replaying historical data; live, it defaults to the wall clock.

Notes
-----
* VWAP / HOD / EMAs / ATR are computed from the bars you feed. Feed RTH bars
  only if you want RTH values (premarket bars would move HOD and VWAP).
* Trade side uses the Lee-Ready rule (quote rule, then tick rule) against the
  most recent quote, so quotes and trades must be fed in arrival order.
* Outcome horizons are measured at the close of the first completed bar that
  ends at or after t0 + horizon. Bars that started before the snapshot are
  excluded, so MFE/MAE never include pre-entry prices.
é    )ÚannotationsN)Údeque)Ú	dataclass)ÚdateÚdatetimeÚ	timedelta)ÚAnyÚDequeÚDictÚListÚOptionalÚTuple)ÚZoneInfoÚentry_ruleszAmerica/New_YorkÚenabledTÚlog_dirzlogs/shadowÚbar_secondsé<   Úmax_barsiX  Úrvol_lookback_barsé   Úmin_baseline_barsé   Úaccel_window_barsé   Úraw_accel_floor_fracg      Ð?Úmomentum_thresholdgš™™™™™¹?Úmin_projected_elapsed_sé   Úimbalance_window_sé   Úmax_quote_age_sç       @Ú
atr_periodé   Úrs_lookback_barsÚbenchmark_symbolsÚSPYÚIWMÚoutcome_horizons_s)r   i,  i„  Útrack_outcomesg      è?éþÿÿÿç      ð?éÿÿÿÿg      ø?é   ç      @é   g333333Ó¿gš™™™™™¹¿g333333Ó?g333333ã?g      @g      à?ç        Úslope)	Úmin_vasÚmin_imbalanceÚmin_bar_rvolÚmax_spread_pctÚmin_rel_strength_pctÚblock_deceleratingÚvolume_rising_barsÚvolume_rising_modeÚvolume_rising_min_slope)Úrvol_bucketsÚaccel_bucketsÚimbalance_bucketsÚrulesúDict[str, Any]ÚDEFAULT_CONFIGc                   @  s>   e Zd ZU ded< ded< ded< ded< ded< ded< d	S )
ÚBarr   ÚtsÚfloatÚopenÚhighÚlowÚcloseÚvolumeN)Ú__name__Ú
__module__Ú__qualname__Ú__annotations__© rN   rN   ú)/var/www/screener/trade1/./entry_rules.pyrB   ‘   s   
 rB   ÚbaseÚdictÚoverrideÚreturnc                 C  sT   t | ƒ}| ¡ D ]\}}t|t ƒr#t| |¡t ƒr#t|| |ƒ||< q|||< q|S ©N)rQ   ÚitemsÚ
isinstanceÚgetÚ_merge)rP   rR   ÚoutÚkÚvrN   rN   rO   rX   ž   s   
rX   c                   s   t  ˆ ¡‡ fdd„ƒ}|S )Nc                   sN   | j  dd¡s	d S zˆ | g|¢R i |¤ŽW S  ty&   t dˆ j¡ Y d S w )Nr   Tz"entry_rules: error in %s (ignored))ÚcfgrW   Ú	ExceptionÚlogÚ	exceptionrJ   )ÚselfÚargsÚkwargs©ÚfnrN   rO   Úwrapper©   s   þz_safe.<locals>.wrapper)Ú	functoolsÚwraps)rd   re   rN   rc   rO   Ú_safe¨   s   rh   rC   r   r   c                 C  s   |   t¡ ¡ S rT   )Ú
astimezoneÚETr   )rC   rN   rN   rO   Ú_et_dateµ   s   rk   ÚsecondsÚintc                 C  s    |   ¡ }tj|||  | jd�S )N)Útz)Ú	timestampr   ÚfromtimestampÚtzinfo)rC   rl   ÚepochrN   rN   rO   Ú_floor¹   s   rs   ÚprevúOptional[float]ÚvaluerD   Únc                 C  s(   | d u r|S d|d  }| |||    S )Nr"   r.   rN   )rt   rv   rw   ÚarN   rN   rO   Ú_ema¾   s   ry   rx   Úbc                 C  s   |dkr
| | d S d S )Nr   r,   rN   ©rx   rz   rN   rN   rO   Ú_ratio_minus_oneÅ   ó   r|   c                 C  s,   | d u s|d u s|dkrd S | | d d S )Nr   r,   ç      Y@rN   r{   rN   rN   rO   Ú_pctÉ   s   r   Útableú
List[list]úOptional[int]c                 C  s>   | d u rd S |D ]\}}|d u s| |k r|  S q|d d S )Nr-   r.   rN   )rv   r€   ÚupperÚscorerN   rN   rO   Ú_bucketÏ   s   ÿr…   Úbarsú
Deque[Bar]rZ   c                 C  s2   | d u s
t | ƒ|krd S t| d j| d|  jƒS )Nr-   )Úlenr   rH   )r†   rZ   rN   rN   rO   Ú_change_pctØ   s   r‰   Úobjr   c                 C  s¢   t | tƒrdd„ |  ¡ D ƒS t | ttfƒrdd„ | D ƒS t | tƒr%|  ¡ S t | tƒs5| du s5t | tt	fƒr7| S t | t
ƒrMt | ¡sFt | ¡rHdS t| dƒS t	| ƒS )zBJSON-safe copy: datetimes -> ISO, NaN/inf -> None, floats rounded.c                 S  s   i | ]\}}t |ƒt|ƒ“qS rN   )ÚstrÚ_clean)Ú.0rZ   r[   rN   rN   rO   Ú
<dictcomp>á   ó    z_clean.<locals>.<dictcomp>c                 S  ó   g | ]}t |ƒ‘qS rN   )rŒ   )r�   r[   rN   rN   rO   Ú
<listcomp>ã   ó    z_clean.<locals>.<listcomp>Né   )rV   rQ   rU   ÚlistÚtupler   Ú	isoformatÚboolrm   r‹   rD   ÚmathÚisnanÚisinfÚround)rŠ   rN   rN   rO   rŒ   Þ   s   

 
"rŒ   c                   @  s   e Zd Zddd„ZdS )	Ú_SymbolStateÚdayr   r   rm   c                 C  s~   || _ t|d�| _tƒ | _d | _d | _d| _d | _d| _d| _	d| _
d | _d | _d | _d | _d | _d | _d| _d| _i | _d S )N)Úmaxlenr   r1   )r   r   r†   ÚtradesÚquoteÚ
last_priceÚ	last_sideÚcur_bar_startÚcur_bar_volumeÚcum_pvÚcum_vÚhodÚema9Úema20ÚatrÚ
prev_closeÚfirst_trade_tsÚcum_buyÚcum_sellÚmin_flow)r`   r�   r   rN   rN   rO   Ú__init__î   s&   
z_SymbolState.__init__N)r�   r   r   rm   )rJ   rK   rL   r°   rN   rN   rN   rO   rœ   í   s    rœ   c                   @  sø   e Zd ZdPdQdd„ZdRdd„ZedSdd„ƒZedTdd„ƒZe	dUdVdd„ƒZedWd!d"„ƒZ	dXd%d&„Z
e		dYdZd+d,„ƒZd[d.d/„Zd\d1d2„Zd]d3d4„Zd\d5d6„Zd^d8d9„Zd_d:d;„Zd`d>d?„ZdadBdC„ZdTdDdE„ZdbdIdJ„ZedcdKdL„ƒZdddNdO„ZdS )eÚShadowEntryRulesNÚconfigúOptional[dict]c                 C  s<   t t|pi ƒ| _t ¡ | _i | _i | _t| jd d�| _	d S )Nr   ©rl   )
rX   rA   r\   Ú	threadingÚRLockÚ_lockÚ_statesÚ_pendingr   Ú_bar_td)r`   r²   rN   rN   rO   r°   
  s
   
zShadowEntryRules.__init__Úsymbolr‹   rC   r   rS   rœ   c                 C  sD   t |ƒ}| j |¡}|du s|j|kr t|| jd ƒ}|| j|< |S )z7Per-symbol state, reset automatically on a new ET date.Nr   )rk   r¸   rW   r   rœ   r\   )r`   r»   rC   r�   ÚstrN   rN   rO   Ú_state  s   
zShadowEntryRules._stater†   ú	List[Bar]ÚNonec                 C  sV   |sdS | j � | j |d¡ W d  ƒ n1 sw   Y  |D ]}|  ||¡ q dS )z}Replace today's state for symbol with its bars since the open
        (oldest first), e.g. when a rescan adds it mid-session.N)r·   r¸   ÚpopÚon_bar)r`   r»   r†   rz   rN   rN   rO   Ú	seed_bars  s   ÿÿzShadowEntryRules.seed_barsÚbarrB   c                 C  sJ  | j �˜ |  ||j¡}|j |¡ |j|j |j d }| j||j	 7  _| j
|j	7  _
|jd u r6|jnt|j|jƒ|_t|j|jdƒ|_t|j|jdƒ|_|jd u r\|j|j }nt|j|j t|j|j ƒt|j|j ƒƒ}| jd }|jd u r~|n	|j||j |  |_|j|_|  ||¡ W d   ƒ d S 1 sžw   Y  d S )Nr/   é	   r   r#   )r·   r½   rC   r†   ÚappendrF   rG   rH   r¥   rI   r¦   r§   Úmaxry   r¨   r©   r«   Úabsr\   rª   Ú_update_outcomes)r`   r»   rÃ   r¼   ÚtypicalÚtrrw   rN   rN   rO   rÁ   '  s(    
þ
$"îzShadowEntryRules.on_barr1   ÚbidrD   ÚaskÚbid_sizeÚask_sizec                 C  sX   | j � |  ||¡}|t|ƒt|ƒt|ƒt|ƒf|_W d   ƒ d S 1 s%w   Y  d S rT   )r·   r½   rD   r    )r`   r»   rC   rË   rÌ   rÍ   rÎ   r¼   rN   rN   rO   Úon_quote=  s   ""þzShadowEntryRules.on_quoteÚpriceÚsizec                 C  sž  | j �Â |  ||¡}t|ƒt|ƒ}}|  |||¡}|||_|_|j ||||f¡ t| j	d | j	d ƒ}|t
|d� }|jrZ|jd d |k rZ|j ¡  |jrZ|jd d |k sIt|| j	d ƒ}	|j|	krn|	d|_|_| j|7  _|jd u r}||_|j |	ddg¡}
|dkrš| j|7  _|
d  |7  < n#|dk rµ| j|7  _|
d  |7  < W d   ƒ d S W d   ƒ d S W d   ƒ d S 1 sÈw   Y  d S )Nr   r   r´   r   r1   r.   )r·   r½   rD   Ú	_classifyr¡   r¢   rŸ   rÅ   rÆ   r\   r   Úpopleftrs   r£   r¤   r¬   r¯   Ú
setdefaultr­   r®   )r`   r»   rC   rÐ   rÑ   r¼   ÚsideÚkeep_sÚcutoffÚstartÚmfrN   rN   rO   Úon_tradeD  s:   
ÿ

êì"ízShadowEntryRules.on_trader¼   rm   c           	      C  s®   |j }|durA||d   ¡ | jd krA|\}}}}}|dkrA||krA||kr)dS ||kr/dS || d }||kr;dS ||k rAdS |jdurT||jkrMdS ||jk rTdS |jS )z9Lee-Ready: quote rule (ask/bid/midpoint), then tick rule.Nr   r!   r.   r-   r"   )r    Útotal_secondsr\   r¡   r¢   )	r`   r¼   rC   rÐ   ÚqÚ_rË   rÌ   ÚmidrN   rN   rO   rÒ   ^  s&   "


zShadowEntryRules._classifyÚeventÚnowúOptional[datetime]Úbot_contextc                 C  s2   |pt  t¡}|  ||||¡}|  ||¡ t|ƒS )zwSnapshot all features at a decision point. Returns the record
        (for logging only -- the bot must NOT act on it).)r   rà   rj   Ú_build_snapshotÚ_writerŒ   )r`   r»   rß   rà   râ   ÚsnaprN   rN   rO   Úrecordu  s   zShadowEntryRules.recordr@   c                 C  sX  | j �Ÿ dt ¡ jd d… ||t|ƒ ¡ |pi dœ}| j |¡}|d u s.|jr.|j	t
|ƒkr;d|d< |W  d   ƒ S |jd urC|jn|jd j}|  ||¡}|  |¡|d< |  ||¡}	|  |||¡}
|  ||	¡}|  ||	|
|¡}|j|||	|
||d� | jd	 r™| j |g ¡ |d
 ||d ||i d d ddœ	¡ |W  d   ƒ S 1 s¥w   Y  d S )NÚsnapshoté   )ÚtypeÚidrC   r»   rß   ÚbotÚno_bars_todayÚerrorr-   Úrising)rÐ   rI   ÚflowÚcontextÚvasÚshadowr*   rê   rß   r   )	rê   r»   rß   Út0Úp0ÚreturnsÚmfeÚmaer†   )r·   ÚuuidÚuuid4Úhexr‹   rƒ   r¸   rW   r†   r   rk   r¡   rH   Ú_volume_featuresÚ_volume_risingÚ_flow_featuresÚ_context_featuresÚ_vasÚ_rulesÚupdater\   r¹   rÔ   rÅ   )r`   r»   rß   rà   râ   rå   r¼   rÐ   Úvolrï   Úctxrñ   rò   rN   rN   rO   rã     s>   
úóÿ
ý$âz ShadowEntryRules._build_snapshotrQ   c                 C  sÈ  | j }dd„ |jD ƒ}t|ƒ}|d |d |d }}}||r$|d nd d d d d d d dd dœ
}	||d	 k r8|	S ||d	  d… }
t|
ƒt|
ƒ }||	d
< |dkrS|	S |d | |	d< |d t|d |d | ƒ d |	d< |d| kr‹tt|| d … ƒt|d| | … ƒƒ|	d< |d| d	 krÀ|	d d urÀtt|| d	 d… ƒt|d| d	 | d	 … ƒƒ}|d urÀ|	d | |	d< ||d k�r3g }dD ](}|| }|td|| ƒ|… }t|ƒt|ƒ }| |dkrð|| | nd ¡ qËd |v�r3||	d< |d |d	   k �r|d k �rn nd|	d< n|d |d	   k�r(|d k�r/n nd|	d< nd|	d< |jd u�rb||j  ¡ }|d |  k�rP|d k �rbn |	S |j	|d  | }|| |	d< |	S )Nc                 S  ó   g | ]}|j ‘qS rN   ©rI   ©r�   rz   rN   rN   rO   r‘   ¤  ó    z5ShadowEntryRules._volume_features.<locals>.<listcomp>r   r   r   r-   ÚINSUFFICIENT)
r†   Úlast_bar_volumeÚbaseline_avgÚbar_rvolÚ	raw_accelÚsmooth_accelÚaccel_momentumÚ	rvol_3barÚ
trend_3barÚprojected_rvolr.   r
  r   r  r+   r   r,   r  r0   r  r  r   )r   r0   r.   r  ÚACCELERATINGr  ÚDECELERATINGÚMIXEDr   r   r  )
r\   r†   rˆ   ÚsumrÆ   r|   rÅ   r£   rÛ   r¤   )r`   r¼   rà   Úcr[   rw   ÚLÚmbÚwrY   rP   Úavgrt   ÚrvrZ   Úidxrz   rx   ÚelapsedÚ	projectedrN   rN   rO   rû   ¢  s\   û&.6 
(
(
"þz!ShadowEntryRules._volume_featuresc                   s,  | j d }t| d¡pdƒ}|d d d d dœ}dd„ |jD ƒ}|r&t|ƒ|k r(|S || d … ‰ tˆ ƒ| ‰|d d ‰t‡‡fd	d
„tˆ ƒD ƒƒt‡fdd
„t|ƒD ƒƒ }|jˆ ˆdkra|ˆ nd t	‡ fdd
„td|ƒD ƒƒd� | dd¡dkr‚|d |d< |S |d d ur”|d | dd¡k|d< |S )Nr?   r9   r   )r†   ÚvolumesÚ
slope_normÚstrictÚokc                 S  r  rN   r  r  rN   rN   rO   r‘   á  r  z3ShadowEntryRules._volume_rising.<locals>.<listcomp>r.   r"   c                 3  s$   � | ]\}}|ˆ |ˆ   V  qd S rT   rN   ©r�   ÚiÚy)ÚmeanÚxmrN   rO   Ú	<genexpr>ç  ó   €" z2ShadowEntryRules._volume_rising.<locals>.<genexpr>c                 3  s   � | ]	}|ˆ  d  V  qdS ©r0   NrN   ©r�   r$  )r'  rN   rO   r(  ç  s   € c                 3  s$   � | ]}ˆ | ˆ |d   kV  qdS ©r.   NrN   r+  )ÚlastrN   rO   r(  é  r)  )r  r   r!  r:   r2   r!  r"  r   r;   r1   )
r\   rm   rW   r†   rˆ   r  Ú	enumerateÚranger  Úall)r`   r¼   ÚrÚNrY   r[   r2   rN   )r-  r&  r'  rO   rü   Ý  s&   
6ÿþzShadowEntryRules._volume_risingc                 C  sR  | j d }|t|d� }d } }}d}|jD ])\}	}
}}|	|k s%|	|kr&q|d7 }|dkr3||7 }q|dk r<||7 }q||7 }q|| }|||| ||||dkrW|| | nd d d d d d dœ}|jr�|j\}}}}}|j||||  ¡ d� |dkr�||kr�|| || d  d	 |d
< || dkr�|| ||  |d< | |  |||¡¡ |S )Nr   r´   r1   r   r.   )Úwindow_srŸ   Ú
trade_rateÚbuy_volÚsell_volÚunclassified_volÚ	imbalancerË   rÌ   Ú
spread_pctÚquote_size_imbalanceÚquote_age_s)rË   rÌ   r;  r"   r~   r9  r:  )r\   r   rŸ   r    r  rÛ   Ú_buy_volume_features)r`   r¼   rà   Úwinr×   ÚbuyÚsellÚunkÚcountrC   Ú_prÑ   rÕ   ÚtotalrY   ÚqtsrË   rÌ   ÚbszÚaszrN   rN   rO   rý   ð  s:   




ûzShadowEntryRules._flow_featuresÚbuy_30sc           	   
     sT  ˆj ˆjˆj ˆj dddddddœ	}ˆj ˆj }|dkr&ˆj ˆj | |d< ˆjdurI|ˆj  ¡ | jd  }|dkrIˆj dkrI|ˆj |  |d< t|| jd ƒ‰ ‡ ‡fd	d
„tˆjƒD ƒdd… }t|ƒdkr¨dd
„ |D ƒ}t	|ƒt	dd„ |D ƒƒ|d< |d< |d |d  |d< t	|ƒd ‰ˆdkr¨t	‡fdd„t
|ƒD ƒƒd ˆ |d< |S )ay  [2026-09-28 trade1] Buy volume, not just the buy/sell ratio:
          cum_buy / cum_sell / cum_delta   since the open (or since the stock was added)
          cum_delta_pct                    (buy - sell) / (buy + sell) since then
          buy_vol_rel                      last-30 s buy volume vs the stock's average
                                           buy volume per 30 s so far today
          buy_5min / sell_5min / delta_5min  last 5 finished 1-min bars
          buy_trend_5                      slope of buy volume over those 5 bars,
                                           as a share of their average (+ = growing)N)	r­   r®   Ú	cum_deltaÚcum_delta_pctÚbuy_vol_relÚbuy_5minÚ	sell_5minÚ
delta_5minÚbuy_trend_5r   rI  r   r0   rJ  r   c                   s   g | ]}|ˆ k rˆj | ‘qS rN   )r¯   )r�   rZ   )Úcurr¼   rN   rO   r‘   &  r�   z9ShadowEntryRules._buy_volume_features.<locals>.<listcomp>éûÿÿÿr   c                 S  ó   g | ]}|d  ‘qS )r   rN   ©r�   ÚxrN   rN   rO   r‘   (  r’   c                 s  s   � | ]}|d  V  qdS r,  rN   rR  rN   rN   rO   r(  )  ó   € z8ShadowEntryRules._buy_volume_features.<locals>.<genexpr>rK  rL  rM  c                 3  s$   � | ]\}}|d  |ˆ   V  qdS r*  rN   r#  )r&  rN   rO   r(  -  r)  é
   rN  )r­   r®   r¬   rÛ   r\   rs   Úsortedr¯   rˆ   r  r.  )	r`   r¼   rà   rG  rY   ÚtotÚwindowsÚdonerz   rN   )rO  r&  r¼   rO   r<    s,   	ý
"$&z%ShadowEntryRules._buy_volume_featuresc                 C  sf  | j }|jdkr|j|j nd }|jr|jdkr|jnd }d }|jd ur7|jd ur7||j  ko4|jkn  }d }|jrJ|rJ|jd |jd  | }|d }	t|j|	ƒ}
i }|d D ]-}||kraqZ| j	 
|¡}|ru|j|jkrut|j|	ƒnd }|
d urƒ|d urƒ|
| nd ||< qZ|t||ƒ|j|j|t|jƒdk|jt||jƒ|r«|jr«|j| | nd |||
|dœS )Nr   r0   r.   r%   r&   r   )ÚvwapÚprice_vs_vwap_pctr¨   r©   Ú	ema_stackÚema_warmr§   Údist_to_hod_pctÚdist_to_hod_atrrª   Ú
spread_atrÚown_change_pctÚrel_strength_pct)r\   r¦   r¥   rª   r¨   r©   r    r‰   r†   r¸   rW   r   r   rˆ   r§   )r`   r»   r¼   rÐ   r  rZ  rª   r\  r`  rZ   ÚownÚrsÚbmÚbstÚbchgrN   rN   rO   rþ   0  s4   
 "

ùz"ShadowEntryRules._context_featuresr  rï   c                 C  s‚  | j }|d |d }}t|d |d ƒt|d |d ƒ|d u r"d n||kr(dn|| k r/dnd	t|d
 |d ƒdœ}d }|d
 d urQ|d d urQ|d
 |d  }tdd„ |d D ƒƒtdd„ |d D ƒƒ d tdd„ |d D ƒƒ }tdd„ |d D ƒƒtdd„ |d D ƒƒ d tdd„ |d D ƒƒ }	dd„ | ¡ D ƒ}
d  }}|
s¹t| ¡ ƒ}t|| |	|  d dƒ}||||
|dœS )Nr  r   r  r<   r  r=   r.   r-   r   r8  r>   )ÚrvolÚaccelÚmomentumr8  c                 s  ó   � | ]\}}|V  qd S rT   rN   ©r�   rÝ   ÚsrN   rN   rO   r(  _  rT  z(ShadowEntryRules._vas.<locals>.<genexpr>c                 s  rk  rT   rN   rl  rN   rN   rO   r(  `  rT  c                 s  rk  rT   rN   rl  rN   rN   rO   r(  a  rT  c                 s  rk  rT   rN   rl  rN   rN   rO   r(  b  rT  c                 S  ó   g | ]
\}}|d u r|‘qS rT   rN   )r�   rZ   rm  rN   rN   rO   r‘   c  ó    z)ShadowEntryRules._vas.<locals>.<listcomp>g      $@)Úrawr„   Ú
componentsÚmissingÚvolume_pressure)r\   r…   ÚminrÆ   rU   r  Úvaluesr›   )r`   r  rï   r  ÚmÚthÚcompsÚpressureÚloÚhirr  rp  r„   rN   rN   rO   rÿ   P  s8   (ü*ÿÿ*ÿÿÿzShadowEntryRules._vasr  rñ   c              	     s&  | j d ‰ dd„ }dd„ |d  ¡ D ƒ}|d }||d ‡ fd	d
„ƒ||d ‡ fdd
„ƒ||d ‡ fdd
„ƒ||d ‡ fdd
„ƒ||rGt|ƒnd ‡ fdd
„ƒ|dkrTd n	ˆ d r\|dknddœ}ˆ  d¡rp| di ¡ d¡|d< dd„ | ¡ D ƒ}	dd„ | ¡ D ƒ}
|	r†dn|
rŠdnd}||	|
|d œS )!Nr?   c                 S  s   | d u rd S t || ƒƒS rT   )r—   )Úvalrd   rN   rN   rO   Úchkn  r}   z$ShadowEntryRules._rules.<locals>.chkc                 S  s   g | ]}|d ur|‘qS rT   rN   rR  rN   rN   rO   r‘   q  s    z+ShadowEntryRules._rules.<locals>.<listcomp>rb  r  r„   c                   ó   | ˆ d kS )Nr3   rN   ©rS  ©r1  rN   rO   Ú<lambda>t  ó    z)ShadowEntryRules._rules.<locals>.<lambda>r8  c                   r~  )Nr4   rN   r  r€  rN   rO   r�  u  r‚  r  c                   r~  )Nr5   rN   r  r€  rN   rO   r�  v  r‚  r9  c                   s   | ˆ d kS )Nr6   rN   r  r€  rN   rO   r�  w  r‚  c                   r~  )Nr7   rN   r  r€  rN   rO   r�  y  r‚  r  r8   r  T)rñ   r8  r  ÚspreadÚrel_strengthÚnot_deceleratingr9   rî   r"  Úvolume_risingc                 S  rn  )FrN   ©r�   rZ   r"  rN   rN   rO   r‘     ro  c                 S  rn  rT   rN   r‡  rN   rN   rO   r‘   €  ro  ÚFAILÚ
INCOMPLETEÚPASS)ÚdecisionÚfailedrr  r?   )r\   ru  rt  rW   rU   )r`   r  rï   r  rñ   r}  Úrs_valsÚtrendr?   rŒ  rr  r‹  rN   r€  rO   r   k  s(   

ÿø

zShadowEntryRules._rulesc                 C  s”  | j  |¡}|s
d S | jd }tt|ƒd�}|j| j }g }|D ]¢}t|jƒt|d ƒkr5| j|dd� q |j|d k rB| 	|¡ q |j|d  |k r…t
|j|d ƒ}	t
|j|d ƒ}
|d d u rc|	nt|d |	ƒ|d< |d d u rt|
nt|d |
ƒ|d< |d	  d
7  < |D ]#}t|ƒ|d vrª||d t|d� krªt
|j|d ƒ|d t|ƒ< q‡t|d ƒt|ƒkr½| j|dd� q | 	|¡ q || j |< d S )Nr)   r´   ró   T©Úpartialrô   rö   r÷   r†   r.   rõ   F)r¹   rW   r\   r   rÆ   rC   rº   rk   Ú_emit_outcomerÅ   r   rF   rG   rt  r‹   rH   rˆ   )r`   r»   rÃ   ÚpendÚhorizonsÚwindowÚbar_endÚkeepÚor{  rz  ÚhrN   rN   rO   rÈ   …  s8   

""&€z!ShadowEntryRules._update_outcomesr—  r�  r—   c                 C  sP   |   d|d |d |d |d |d |d |d |d	 |d
 |dœ|d ¡ d S )NÚoutcomerê   r»   rß   ró   rô   rõ   rö   r÷   r†   )ré   rê   r»   rß   ró   rô   Úreturns_pctÚmfe_pctÚmae_pctÚbars_observedr�  )rä   )r`   r—  r�  rN   rN   rO   r‘  £  s   üûzShadowEntryRules._emit_outcomec                 C  s^   | j �" | j ¡ D ]}|D ]	}| j|dd� qq	| j ¡  W d  ƒ dS 1 s(w   Y  dS )zBCall at end of day: writes partial outcomes for pending snapshots.Tr�  N)r·   r¹   ru  r‘  Úclear)r`   r’  r—  rN   rN   rO   Úflush«  s   ÿ"üzShadowEntryRules.flushræ   c                 C  s†   | j d }tj|dd� tj |d| t¡d›d�¡}t|ddd	��}| t	j
t|ƒd
d�d ¡ W d   ƒ d S 1 s<w   Y  d S )Nr   T)Úexist_okÚshadow_entry_z%Y-%m-%dz.jsonlrx   zutf-8)Úencoding)ú,ú:)Ú
separatorsÚ
)r\   ÚosÚmakedirsÚpathÚjoinri   rj   rE   ÚwriteÚjsonÚdumpsrŒ   )r`   ræ   rC   Údr©  ÚfrN   rN   rO   rä   µ  s   
"ÿzShadowEntryRules._writerT   )r²   r³   )r»   r‹   rC   r   rS   rœ   )r»   r‹   r†   r¾   rS   r¿   )r»   r‹   rÃ   rB   rS   r¿   ©r1   r1   )r»   r‹   rC   r   rË   rD   rÌ   rD   rÍ   rD   rÎ   rD   rS   r¿   )
r»   r‹   rC   r   rÐ   rD   rÑ   rD   rS   r¿   )r¼   rœ   rC   r   rÐ   rD   rS   rm   )NN)
r»   r‹   rß   r‹   rà   rá   râ   r³   rS   r³   )
r»   r‹   rß   r‹   rà   r   râ   r³   rS   r@   )r¼   rœ   rà   r   rS   rQ   )r¼   rœ   rS   rQ   )r¼   rœ   rà   r   rG  rD   rS   rQ   )r»   r‹   r¼   rœ   rÐ   rD   rS   rQ   )r  rQ   rï   rQ   rS   rQ   )
r  rQ   rï   rQ   r  rQ   rñ   rQ   rS   rQ   )r—  rQ   r�  r—   rS   r¿   )rS   r¿   )ræ   rQ   rC   r   rS   r¿   )rJ   rK   rL   r°   r½   rh   rÂ   rÁ   rÏ   rÚ   rÒ   ræ   rã   rû   rü   rý   r<  rþ   rÿ   r   rÈ   r‘  rŸ  rä   rN   rN   rN   rO   r±   	  s<    


ÿ
ÿ
	
#
;

!

 


	r±   )ÚEntryDecisionÚ
MarketView)	Úmax_entries_per_symbolÚstop_lookback_barsÚstop_buffer_centsÚmin_stop_pctÚearliest_minuteÚmin_barsÚmax_vwap_ext_pctÚmax_up_from_open_pctÚmarket_filterzOptional[ShadowEntryRules]Ú_enginezDict[Tuple[str, str], int]Ú_entriesc                  C  s@   t d u rddlm}  t| ƒ  di ¡ƒ}|jddd� t|ƒa t S )Nr   )Ú
get_configr   TF)r   r*   )r¼  Úconfig_loaderr¾  rQ   rW   r  r±   )r¾  r\   rN   rN   rO   Ú_get_engineß  s   rÀ  c                 C  s*   t | d | d | d | d | d | d ƒS )NÚtr—  r˜  Úlr  r[   )rB   )rz   rN   rN   rO   Ú_to_baré  s   *rÃ  c                 C  s   t ƒ  | |||¡ d S rT   )rÀ  rÚ   )r»   rC   rÐ   rÑ   rN   rN   rO   rÚ   í  ó   rÚ   c                 C  s   t ƒ  | |||||¡ d S rT   )rÀ  rÏ   )r»   rC   rË   rÌ   rÍ   rÎ   rN   rN   rO   rÏ   ñ  s   rÏ   c                 C  s   t ƒ  | t|ƒ¡ d S rT   )rÀ  rÁ   rÃ  )r»   rÃ   rN   rN   rO   rÁ   õ  rÄ  rÁ   c                 C  s   t ƒ  | dd„ |D ƒ¡ d S )Nc                 S  r�   rN   )rÃ  r  rN   rN   rO   r‘   ú  r’   zseed_bars.<locals>.<listcomp>)rÀ  rÂ   )r»   r†   rN   rN   rO   rÂ   ù  s   rÂ   rå   c                 C  s–  |   di ¡|   di ¡|   di ¡|   di ¡f\}}}}ti d|   di ¡  d¡“d|   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¡“d|  d¡“d|  d¡“|  d¡|  d¡|  d¡|  d ¡|  d!¡|  d"¡|  d#¡|  d$¡|  d%¡|  d&¡|  d'¡|  d(¡|  d)¡|  d*¡d+œ¥ƒS ),z7The values worth keeping on the trade for later review.rI   rï   rð   rñ   Úverdictrò   r‹  rŒ  rr  r„   Ú	vas_partsrq  rs  r  r  r  r  r  r†  rî   r8  Ú
trades_30srŸ   r9  Úbuy_vol_30sr5  Úsell_vol_30sr6  rJ  r­   r®   rH  rI  rK  rL  rM  rN  r[  r^  r\  ra  rb  )rJ  r­   r®   rH  rI  rK  rL  rM  rN  r[  r^  r\  Úown_change_5bar_pctrb  )rW   rŒ   )rå   r[   r¯  r  rm  rN   rN   rO   Ú_indicatorsý  sX   4ÿÿþ
ý
ý
ý
ü
ü
ü
û
û
ú
ù
ù
ù
ø
ø
òrË  Úviewr²  Ústater±  c              	   C  s˜  i t ¥| jpi ¥}| j t¡ ¡  ¡ | jf}t 	|d¡|d kr&t
ddd�S | j|d k r3t
ddd�S tdt| jƒd	 ƒ}||d
 k rQt
d|› d|d
 › d�d�S tƒ  | jd| jd ¡}| 	d¡rit
d|d d�S t|ƒ}| jrw| jd d nd }|r„| j| d	 d nd |d< |d }|dkrµ|d r�dd |d ¡ n
dd |d p¥g ¡ }	t
|p¬d|	d|d id�S g }
|d d urã|d d u sË|d |d krã|
 |d d uràd|d d›d|d › �nd ¡ |d! d u�r|d d u sù|d |d! k�r|
 |d d u�rd"|d d›d|d! › �nd#¡ |
�r%t
d$d%d& |
¡ d|d id�S |d' }|�r| j�p1i  	|d( ¡�p9g }|�rM|d) d* |d d  d	 d nd }||d+< |d u �sa|| 	d,d-¡k �rt
d.d%|d( › d/�|d u�ru|d›d0�nd1 d|d id�S d2d3„ | jt|d4 ƒ d … D ƒ}tt|ƒ|d5 d6  | jd	|d7 d6   ƒ}t 	|d¡d	 t|< t
d8t|d9ƒd:d;|d › d<�d=|d › �gd>|i|d?�S )@Nr   r³  ÚDONEz max entries for this stock today)rÍ  Úreasonr·  ÚWAITz	too earlyr.   r¸  z of z barsÚEVALrí   r—  éd   Úup_from_open_pctrÅ  rŠ  rŒ  zfailed r£  zmissing rr  rñ   )rÍ  rÏ  Úmetricsr¹  r[  zvwap z+.2fz% > zvwap unknownrº  zopen zopen unknownÚEXTENDEDz	PASS but z; r»  r»   r-   r  Úmarket_from_open_pctÚmin_from_open_pctr1   ÚMARKETú z% from its openÚunknownc                 S  rQ  )rÂ  rN   r  rN   rN   rO   r‘   :  r’   zevaluate.<locals>.<listcomp>r´  rµ  r~   r¶  Té   ÚBUYzentry_rules PASS (VAS ú)zVAS Ú
indicators)ÚstoprÍ  rÏ  ÚreasonsÚplanrÔ  )ÚADAPTER_DEFAULTSr\   rà   ri   rj   r   r–   r»   r½  rW   r±  Úminutes_since_openrÆ   rˆ   r†   rÀ  rã   rË  rÐ   rª  rÅ   Ú
benchmarksrm   rt  r›   )rÌ  rÍ  r  ÚkeyÚfinishedrå   ÚindÚsession_openrÅ  ÚwhyÚextrÙ   ÚbbÚmktÚlowsrß  rN   rN   rO   Úevaluate  sd   
0*ÿ,ÿ *ÿ
þ",þrî  )rP   rQ   rR   rQ   rS   rQ   )rC   r   rS   r   )rC   r   rl   rm   rS   r   )rt   ru   rv   rD   rw   rm   rS   rD   )rx   rD   rz   rD   rS   ru   )rx   ru   rz   ru   rS   ru   )rv   ru   r€   r�   rS   r‚   )r†   r‡   rZ   rm   rS   ru   )rŠ   r   rS   r   )rS   r±   )rz   rQ   rS   rB   r°  )rå   rQ   rS   rQ   )rÌ  r²  rÍ  rQ   rS   r±  );Ú__doc__Ú
__future__r   rf   r¬  Úloggingr˜   r§  rµ   rø   Úcollectionsr   Údataclassesr   r   r   r   Útypingr   r	   r
   r   r   r   Úzoneinfor   Ú	getLoggerr^   rj   rA   rM   rB   rX   rh   rk   rs   ry   r|   r   r…   r‰   rŒ   rœ   r±   Ú	rules_apir±  r²  ÚNAMErâ  r¼  r½  rÀ  rÃ  rÚ   rÏ   rÁ   rÂ   rË  rî  rN   rN   rN   rO   Ú<module>   sÄ    P 
ÿþýüúùø	÷
öõóòðïîìë&& òå.








	
   Oþ




