o
    ""»j²  ã                   @   sl  d 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ƒ¡ edƒZed d Zd	Zd
ZdZdZdedefdd„Zdedefdd„Zd$dededededef
dd„Zi Z defdd„Z!e"dkr´ej#d d… Z$d!e$vr�ee %e¡ ¡ ƒne &e$e$ 'd!¡d   ¡Z(d"e$v r©e$e$ 'd"¡d    )d#¡ndZ*e+ee(e*ƒƒ dS dS )%aA  
reference.py -- [2026-09-28] Fixed 14-day reference data per stock (entry rules v2).

Built once per trading day after the close (cron ~16:35) for every stock that
could make the watchlist (universe filters + last close $4-16 + 14-day average
volume >= 500k), and saved as one small file per stock:

    data/reference/<for_date>/<SYMBOL>.json      for_date = the NEXT trading day

The live bot and the simulator load the same files, so tests and live trading
use identical references. Nothing here changes a trade; it is data.

Per stock (from the previous 14 completed sessions):
  minute profile (390 regular-session minutes, 9:30 = index 0):
    vol_per_min[m]     average volume in minute m            -> per-minute RVOL
    cum_vol[m]         average total volume by the end of m  -> RVOL so far
    abs_move_pct[m]    average |close(m) / open - 1| in %    -> move vs normal (noise area)
    open5_vol          average 9:30-9:35 volume              -> opening RVOL
  daily: atr14, adv14 (avg daily volume), avg_range_pct14, prev close/high/low,
         high_20d, ref_5d_* (5-day frame), sr14 (14-day support/resistance levels)

    python3 reference.py                 build for the next trading day (after the close)
    python3 reference.py --for 2026-09-29 [--symbols AGEN,TGB]
Load:  reference.load(for_date, symbol) -> dict or None
é    N)ÚdatetimeÚ	timedeltaÚdate)ÚPath)ÚZoneInfozAmerica/New_YorkÚdataÚ	referenceé   i†  )g      @g      0@i ¡ ÚdÚreturnc                 C   s8   | t dd�7 } |  ¡ dkr| t dd�7 } |  ¡ dks| S )Né   ©Údaysé   )r   Úweekday)r
   © r   ú$/var/www/screener/trade/reference.pyÚnext_weekday-   s
   ÿr   r   c                    s@  t | ƒ‰ dgt }dgt }dgt }d}|  ¡ D ]d\}}dd„ |D ƒ}|r,|d d nd}d|}	}
ttƒD ]E}| |¡}|rD|d nd}|rL|d }
|	|7 }	||  |7  < ||  |	7  < |rt|
rt||  t|
| d ƒd	 7  < |dk r|||7 }q7q‡ fd
d„|D ƒ‡ fd
d„|D ƒ‡ fdd„|D ƒt|ˆ  dƒˆ dœS )z]days: {date: [(minute_index, open, high, low, close, volume), ...]} for regular-session bars.g        c                 S   s   i | ]}|d  |“qS )r   r   ©Ú.0Úbr   r   r   Ú
<dictcomp><   s    z#_minute_profile.<locals>.<dictcomp>r   r   Nr   é   éd   c                    ó   g | ]	}t |ˆ  d ƒ‘qS )r   ©Úround©r   Úx©Únr   r   Ú
<listcomp>K   ó    z#_minute_profile.<locals>.<listcomp>c                    r   )r   r   r   r   r   r   r!   L   r"   )Úvol_per_minÚcum_volÚabs_move_pctÚ	open5_volr   )ÚlenÚMINUTESÚitemsÚrangeÚgetÚabsr   )r   ÚvolÚcumÚmvÚopen5r
   ÚbarsÚby_mÚday_openÚrunÚ
last_closeÚmr   Úvr   r   r   Ú_minute_profile4   s4   




 €õ ÿr8   é   TÚfor_dateÚsymbolsÚchunkÚverbosec           %         s|  ddl m} ddlm}m} ddlm} ddl}ddl}	t	 	¡ }
|ƒ }t
| j| j| jtd�‰ |du}|du r;| |¡}| |ˆ tdd� ˆ td	d
� ¡}i }| ¡ D ]Q\}}‡ fdd„|D ƒ}t|ƒdk rfqRtdd„ |t d… D ƒƒt|t d… ƒ }|sŸtd |d d   kr‘td	 kr£n qR|tksŸ|d d tkr£|||< qRdD ])}||vrÏ‡ fdd„| |gˆ tdd� ˆ td	d
� ¡ |g ¡D ƒ}|rÏ|||< q¦tdd„ | ¡ D ƒƒt d… }|rûtt|ƒ› dt|ƒ› d|d › d|d › �dd� t|  ¡  }|jddd� t
|d j|d j|d jddtd�}t
|d j|d j|d jddtd�}t|ƒ}d}tdt|ƒ|ƒD �]Y}|||| … }z|||d	|j ƒ||d|j!d �}|j" #|¡j$}W n  t%�y } ztd!|› d"|› �dd� W Y d}~�q<d}~ww |D ]ì}i }| |g ¡D ]K}|j& 't¡} | j(d d# | j) d }!d|!  k�r¬t*k �rÖn �qŒ|  +¡ |v �rÖ| ,|  +¡ g ¡ -|!t.|j/ƒt.|j0ƒt.|j1ƒt.|j2ƒt.|j3ƒf¡ �qŒ|�sÝ�q‚|D ]	}"||"  4¡  �qß|| }|t d… }#||  ¡ t
 5t¡jd$d%�d&d„ t|ƒD ƒd'œt6|ƒ¥|	 7|¡t8td(d„ |#D ƒƒt|#ƒ ƒt8td)d„ |#D ƒƒt|#ƒ dƒ|d d |d d* |d d+ t9d,d„ |d-d… D ƒƒd.œ¥|	 :|¡¥d/|	 ;|¡i¥}$||› d0�  <t=j>|$d1d2�¡ |d	7 }�q‚~t? @¡  |�r•td3tA|| t|ƒƒ› d4t|ƒ› d5t	 	¡ |
 d6›d7�dd� �q<|d8  <t= >|  ¡ |d9d„ |D ƒt8t	 	¡ |
 ƒd:œ¡¡ |t8t	 	¡ |
 ƒd;œS )<zDBuild reference files for `for_date` from the 14 sessions before it.r   )ÚStockBarsRequest)Ú	TimeFrameÚTimeFrameUnit)Ú
get_clientN)Útzinfoé-   r   r   )Úsecondsc                    ó   g | ]
}|d  ˆ k r|‘qS ©Útr   r   ©Úday0r   r   r!   `   s    zbuild.<locals>.<listcomp>é   c                 s   ó   � | ]}|d  V  qdS ©r7   Nr   r   r   r   r   Ú	<genexpr>c   ó   € zbuild.<locals>.<genexpr>éÿÿÿÿÚcr7   )ÚSPYÚIWMc                    rE   rF   r   r   rH   r   r   r!   i   s    ÿc                 S   s(   h | ]}|D ]}|d    t¡ ¡ ’qqS rF   )Ú
astimezoneÚETr   )r   Úblr   r   r   r   Ú	<setcomp>m   s   ( zbuild.<locals>.<setcomp>z stocks qualify (of z); sessions z .. T)Úflush)ÚparentsÚexist_oké	   é   é   )Úsymbol_or_symbolsÚ	timeframeÚstartÚendÚlimitÚfeedz  chunk z	 failed: é<   rD   )Útimespecc                 S   ó   g | ]}|  ¡ ‘qS r   ©Ú	isoformat©r   r
   r   r   r   r!   Ž   ó    )Úsymbolr:   ÚbuiltÚsessionsc                 s   rK   rL   r   r   r   r   r   rM   �   rN   c                 s   s(   � | ]}|d  |d  d d V  qdS )ÚhÚlr   r   Nr   r   r   r   r   rM   ‘   s   €& rm   rn   c                 s   rK   )rm   Nr   r   r   r   r   rM   “   rN   iìÿÿÿ)Úatr14Úadv14Úavg_range_pct14Ú
prev_closeÚ	prev_highÚprev_lowÚhigh_20dÚsr14ú.json)ú,ú:)Ú
separatorsz  ú/z	 stocks, z.0fÚsz_index.jsonc                 S   re   r   rf   rh   r   r   r   r!   �   ri   )r:   Ústocksrl   rD   )r}   rD   )BÚalpaca.data.requestsr>   Úalpaca.data.timeframer?   r@   Úalpaca_clientrA   ÚuniverseÚ
volatilityÚtimer   ÚyearÚmonthÚdayrT   Úget_universe_symbolsÚget_daily_bars_bulkr   r)   r'   ÚsumÚSESSIONSÚ
PRICE_BANDÚMIN_ADVr+   ÚsortedÚvaluesÚprintÚOUTrg   Úmkdirr*   ÚMinuteÚ_feedÚ	hist_dataÚget_stock_barsr   Ú	ExceptionÚ	timestamprS   ÚhourÚminuter(   r   Ú
setdefaultÚappendÚfloatÚopenÚhighÚlowÚcloseÚvolumeÚsortÚnowr8   Ú	daily_atrr   ÚmaxÚfive_day_referenceÚ	sr_levelsÚ
write_textÚjsonÚdumpsÚgcÚcollectÚmin)%r:   r;   r<   r=   r>   r?   r@   rA   r�   r‚   Út0ÚclientÚexplicitÚdailyÚkeepr|   rU   ÚadvÚbenchÚsessÚoutdirr_   r`   ÚsymsÚdoneÚiÚpartÚreqr   Úer   r   rG   r6   r
   Úlast14Úrefr   rH   r   ÚbuildO   s¼   
".D€ÿÿ€ 2&&
ÿ€þ,*ÿ€ÿþ$úù
ø	<€
þr¿   rj   c                 C   sP   t | ƒ|f}|tvr$tt | ƒ |› d� }| ¡ r t | ¡ ¡ndt|< t| S )zIThe reference for `symbol` on `for_date` (date or 'YYYY-MM-DD'), or None.rw   N)ÚstrÚ_CACHEr�   Úexistsr©   ÚloadsÚ	read_text)r:   rj   ÚkeyÚpr   r   r   Úload¥   s
   rÇ   Ú__main__r   z--forz	--symbolsrx   )Nr9   T),Ú__doc__r«   r©   Úsysrƒ   r   r   r   Úpathlibr   Úzoneinfor   Ú__file__ÚresolveÚparentÚBASEÚpathÚinsertrÀ   rT   r�   rŠ   r(   r‹   rŒ   r   Údictr8   ÚlistÚintÚboolr¿   rÁ   rÇ   Ú__name__ÚargvÚargsr£   ÚfromisoformatÚindexÚfdÚsplitr·   r�   r   r   r   r   Ú<module>   s6     S	2$ü