o
    Ÿ~¾jp4  ã                   @   s  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 ddlmZ ddl	m
Z
 ddlZedƒZej deeƒ¡ ddlZe
dƒZed d	 d
 Zed d d ZdZdddddddddddddœZdd„ Zdd„ Zdd „ ZG d!d"„ d"ƒZd#d$„ Zd%d&„ Zd'd(„ Zed)krŠeƒ  dS dS )*aÅ  
reference_trader.py -- [2026-10-01] The user's playbook-reference trading idea, as a replay:
every minute, match the stock's WHOLE day so far (from 9:30) against library stock-days at
the same minute, follow the closest group, buy where they say up, hold toward their target,
sell when the stock leaves their band (version A) or the re-match turns down (version B).

Library: every stock-day in reports/open_window/bars (~2 years, 9:28/9:30 lists) with a
14-day reference file. Only days BEFORE the test day are used as references (no look-ahead).
Per stock-day, per minute (forward-filled), scaled by the stock's daily ATR% (shape, not size):
    P = price vs open / ATR%,  W = price vs VWAP / ATR%,  X = 0.5*log(1 + volume vs normal)
Distance at minute m = mean over minutes 0..m of (dP^2 + dW^2 + dX^2)  (whole day so far)

Rules (defaults; --set key=value):
  k              150 closest references
  start_m        1   (9:31) first possible buy;  last_buy_m 345 (15:15)
  min_p_up       0.40  buy if share of references that went +2% before -1% from here >= this
  min_edge       0.10  ... and that share exceeds the -1%-first share by at least this much
  band_q         (0.25, 0.75) the references' price band after the buy (relative to minute m)
  band_minutes   3    version A: sell when below the band's lower edge this many minutes in a row
  target         median of the references' best gain from the buy minute to 15:55
  stop_pct       1.0  hard stop
  version        "A" (leave band -> sell) or "B" (leave band -> re-match; sell only if the new
                 group's -1%-first share > +2%-first share)
  max_trades     2 per stock-day
Outcomes use the test day's own 1-min bars: buy at the next bar's open, sell at the next
bar's open (stop: at the stop price), 0.1% round-trip cost.

    python3 reference_trader.py --days 2026-10-01 --symbols SVCO,ORN,LWLG,...   (test set)
    python3 reference_trader.py --days 2026-08-27..2026-09-30                   (whole lists)
é    N)Údatetime)ÚPath)ÚZoneInfoz/var/www/screener/tradezAmerica/New_YorkÚreportsÚopen_windowÚbarsÚdataÚplaybookzlibrary_paths.npzi†  é–   é   iY  gš™™™™™Ù?gš™™™™™¹?g      Ð?g      è?é   g      ð?ÚAé   )ÚkÚstart_mÚ
last_buy_mÚmin_p_upÚmin_edgeÚband_loÚband_hiÚband_minutesÚstop_pctÚversionÚ
max_tradesÚcostc              
   C   sR  i }| D ]4}t |d tƒst |d t¡n|d  t¡}|jd d |j d }d|  kr2tk r8n q|||< qt|ƒdk rAdS |t	|ƒ d }| 
d¡r]| 
d	¡r]|d |d	  d nd
}t t¡t t¡t t¡t t¡f\}}	}
}t ttj¡t ttj¡t ttj¡}}}|dd}}}ttƒD ]€}| 
|¡}d}|rÖ|d |d |d |d |d f\||< |	|< |
|< }}||d |d  |d  d | 7 }||7 }n| ||<  |	|< |
|< |||< |rì|| n|}|| d d | ||< || d d | ||< dt |t|d | dƒ ¡ ||< q˜|||	|
|t |||gd¡dœS )zOrows [[ts,o,h,l,c,v]] -> dict of per-minute arrays (forward-filled) + features.r   é	   é<   é   éd   Nr   Úatr14Ú
prev_closeg      @ç        r   r   é   é   g      à?Úvol_per_min)ÚoÚCÚHÚLÚOÚF)Ú
isinstancer   ÚfromtimestampÚETÚ
astimezoneÚhourÚminuteÚNÚlenÚminÚgetÚnpÚzerosÚfloat32ÚrangeÚmathÚlog1pÚmaxÚstack)ÚrowsÚrefÚbyÚbÚtÚmr%   Úatrpr&   r'   r(   r)   ÚPÚWÚXÚlastÚpvÚcvÚvÚvw© rL   ú>/var/www/screener/trade/reports/playbook/./reference_trader.pyÚ
day_arrays7   s8   ,€,,.
8$
& rN   c           	      C   sŽ   | | }d }}t |d dƒD ]}|| |d krd} n|| |d kr)d} nq|d dk r@||d d…  ¡ | d d nd}|||fS )	z\For a reference day at minute m: +2% first? -1% first? best gain to 15:55, path rel to C[m].Fr   é‚  ç®Gáz®ï?TçR¸…ëQð?r   r!   )r8   r;   )	r&   r'   r(   rB   ÚeÚupÚdnr   ÚbestrL   rL   rM   Úoutcomes_fromW   s   þ0
rV   c               	   C   s˜  t  ¡ rtjt dd�} | d | d | d | d t| d ƒfS g g g g g f\}}}}}tt d¡ƒD ]i}t t	 
|d	¡¡}|d
 D ]E}t |d |¡}	|	sMq@t|d | d |	ƒ}
|
du r]q@| |
d ¡ | |
d ¡ | |
d ¡ | |
d ¡ | |d › d|› �¡ q@tj ¡  td|jdd… t|ƒdd� q1t |tj¡}t |tj¡}t |tj¡}t |tj¡}tjt ||||t |¡d� |||||fS )zFAll stock-days -> F (n, 390, 3) float32, C/H/L (n, 390) float32, keys.T)Úallow_pickler*   r&   r'   r(   Úkeyszbars_20*.json.gzÚrtÚtop30_currentÚdateÚsymbolsr   Nú|Úlibraryr#   é   ©Úflush)r*   r&   r'   r(   rX   )Ú	LIB_CACHEÚexistsr5   ÚloadÚlistÚsortedÚBARSÚglobÚjsonÚgzipÚopenÚREFrN   ÚappendÚ_CACHEÚclearÚprintÚnamer2   Úarrayr7   Úsavez)Úzr*   r&   r'   r(   rX   ÚpÚdÚsr>   ÚarL   rL   rM   Úbuild_libraryf   s(   &8
 8ry   c                   @   s,   e Zd Zdd„ Zdd„ Zdd„ Zdd„ Zd	S )
ÚLibc                 C   s<   t ƒ \| _| _| _| _| _t dd„ | jD ƒ¡| _i | _	d S )Nc                 S   s   g | ]	}|  d ¡d ‘qS ©r]   r   ©Úsplit©Ú.0r   rL   rL   rM   Ú
<listcomp>�   s    z Lib.__init__.<locals>.<listcomp>)
ry   r*   r&   r'   r(   rX   r5   rr   ÚdatesÚ_out)ÚselfrL   rL   rM   Ú__init__   s   
zLib.__init__c                 C   sþ   | j ||f dd…df }| j||d d…f | j||d d…f }}|jd dkr2dgt|ƒ S ||d k}||d k}d}t | d¡| d¡|¡}	t | d¡| d¡|¡}
| 	d¡|dd…df  d d	 }t
t|	|
k |	|k @ |
|	k|
|k @ |ƒƒS )
zNVectorized: per reference (up first?, down first?, best gain %) from minute m.Nr   rO   r   )FFr!   rQ   rP   i@B r   )r&   r'   r(   Úshaper2   r5   ÚwhereÚanyÚargmaxr;   re   Úzip)rƒ   ÚidxrB   rR   ÚHsÚLsrS   rT   ÚBIGÚfuÚfdrU   rL   rL   rM   Úoutcomes…   s   ."(zLib.outcomesc                 C   sD   t  | j|k ¡d | _| j| j | _t  t| jƒt j¡| _	d| _
dS )zKBegin matching a test day: running whole-day distance to every earlier day.r   éÿÿÿÿN)r5   r†   r�   Úokr*   Úsubr6   r2   Úfloat64ÚDÚm_done)rƒ   Úbefore_daterL   rL   rM   Ústart“   s   
z	Lib.startc                 C   s´   | j |k r3|  j d7  _ | j }|  j| jdd…|dd…f |d|dd…f  d  d¡7  _| j |k st | j|¡d|… }|t | j| ¡ }| j| t| j|  	¡ |d  ƒfS )z‰f: (390,3) test-day features; distance = mean over minutes 0..m (whole day so far).
        Running sum: each new minute adds one column.r   Nr   )
r–   r•   r“   Úsumr5   ÚargpartitionÚargsortr’   Úfloatr;   )rƒ   ÚfrB   r—   r   ÚjÚorderrL   rL   rM   Úmatchš   s   
>
ý$z	Lib.matchN)Ú__name__Ú
__module__Ú__qualname__r„   r�   r˜   r    rL   rL   rL   rM   rz   ~   s
    rz   c                    s2  g }d}d}ˆ   |¡ t|d dƒD �]‰|d }|du rÄˆ|d ks+t|ƒ|d kr,qˆ  |ˆ||d ¡\}	}
ˆ  |	ˆ¡}td	d
„ |D ƒƒt|ƒ }tdd
„ |D ƒƒt|ƒ }||d krÃ|| |d krÃ|d ˆd  }t ‡ ‡fdd„|	D ƒ¡}tj||d dd�}t	t 
dd„ |D ƒ¡ƒ}ˆd ||d|d d   ||d|d   t|dƒt|dƒ‡ fdd„|	dd… D ƒt|dƒdœ	}d}qˆ|d d  }d\}}|d ˆ |d krâ|d d}}nÝ|d ˆ |d  krô|d  d }}nË|d! ˆ |d"  }|d t|t|d ƒd ƒ }||k �r|d nd}||d# k�r¿|d$ d%k�r2|d ˆd  d&}}n�ˆ  |d ˆ||d ¡\}	}
ˆ  |	ˆ¡}td'd
„ |D ƒƒt|ƒ }td(d
„ |D ƒƒt|ƒ }||k�rz|d ˆd  d)|d*›d+|d*›d,�}}nEt ‡ ‡fd-d„|	D ƒ¡}t t |¡tj||d dd�|d! ˆ  |d"  g¡|d< |d! ˆ dt	t 
d.d„ |D ƒ¡ƒd   |d < d}|du �rÒˆd/k�rÒ|d! ˆ d0}}|du�r||d"  d d |d1  }| |||d tt	|d" ƒd2ƒˆd tt	|ƒd2ƒtt	|ƒdƒ||d3 |d4 |d5 |d6 d7œ¡ d}q|S )8z;Replay one test stock-day minute by minute; returns trades.Nr   r   i�  r*   r   r   r   c                 s   ó   � | ]}|d  V  qdS ©r   NrL   ©r   r%   rL   rL   rM   Ú	<genexpr>³   ó   € ztrade_day.<locals>.<genexpr>c                 s   r¤   ©r   NrL   r¦   rL   rL   rM   r§   ´   r¨   r   r   r)   r   c                    ó,   g | ]}ˆ j | ˆd … ˆ j | ˆ  ‘qS ©rO   ©r&   ©r   Úi©ÚlibrB   rL   rM   r€   ·   ó   , ztrade_day.<locals>.<listcomp>r   )Úaxisc                 S   ó   g | ]}|d  ‘qS ©r   rL   r¦   rL   rL   rM   r€   ¹   ó    r   r   r   c                    s   g | ]}ˆ j | ‘qS rL   )rX   r­   )r°   rL   rM   r€   ¼   s    r#   )	rB   ÚentryÚstopr   ÚtargetÚp_upÚp_dnÚrefsÚ
target_pctrB   )NNr(   r·   r'   r¸   r&   r¶   r   r   r   zleft the reference bandc                 s   r¤   r¥   rL   r¦   rL   rL   rM   r§   Ð   r¨   c                 s   r¤   r©   rL   r¦   rL   rL   rM   r§   Ñ   r¨   zre-match says down (ú.2fz vs ú)c                    rª   r«   r¬   r­   r¯   rL   rM   r€   Õ   r±   c                 S   r³   r´   rL   r¦   rL   rL   rM   r€   Ø   rµ   i€  z
end of dayr   r"   r¹   rº   r¼   r»   )r[   ÚsymbolÚbuy_mÚbuyÚsell_mÚsellÚpl_pctÚwhyr¹   rº   r¼   r»   )r˜   r8   r2   r    r�   r™   r5   rr   Úquantilerœ   ÚmedianÚroundr3   Úconcatenater6   rm   )r°   ÚdayÚsymrx   ÚcÚtradesÚposÚbelowr�   rŠ   Ú_Úoutcr¹   rº   rR   Úpathsr   r¸   r   Úexit_pxrÅ   ÚrelÚlorÄ   rL   r¯   rM   Ú	trade_day¦   s|   
 þ
*
ÿÿ0
ý€rÖ   c                 C   s$   dd|  d  › dd|  d d›�S )Nr   r   r   ú:Ú02drL   )rB   rL   rL   rM   Úhmæ   s   $rÙ   c                     s¼  t  ¡ } | jdddd� | jdddd� | jd	d
g d� | jddd� |  ¡ }i t¥dd„ |jD ƒ¥}tƒ }d|jv rQ|j d¡\‰ ‰t	‡ ‡fdd„|j
D ƒƒ}n|jg}ddlm} d }g }|D �]+}dd„ |j d¡D ƒ}	i }
td|› d� }| ¡ r†t t |d¡¡ng i dœ}|	s‘|d }	|	D ]<}||d v r¦|d | d |
|< q“|pª|ƒ }t |¡jtd�}|j||jd d!d"�|jd#d$�d%d&�}d'd„ |D ƒ|
|< q“|
 ¡ D ]¸\}}t ||¡}|�s	ztjt |¡ ¡ |gd(d)� tj ¡  t ||¡}W n t�y   d }Y nw |�st ||d*ƒ qÔt!||ƒ}|d u �rqÔt"|||||ƒ}||7 }|D ]O}t |› d+|d,›d-t#|d. ƒ› d+|d/ d0›d1|d2 › d3|d4 › d5|d6 › d7t#|d8 ƒ› d+|d9 d0›d+|d: d;›d<|d= › d>d? $|d@ d dA… ¡› �ddB� �q,|�sŒt |› d+|d,›dC�ddB� qÔqat%|ƒ}|�rÉt&dDdE„ |D ƒƒ}t dF|› dG|› dHt' (dId„ |D ƒ¡d;›dJt&dKdE„ |D ƒƒdL›dM|dN › dO|dP › �ƒ |j)�rÜtj*|t|j)dQƒdRt+dS� d S d S )TNz--daysTzYYYY-MM-DD or A..B)ÚrequiredÚhelpz	--symbolsÚ z$comma list (default: the day's list))ÚdefaultrÛ   z--setrm   )ÚactionrÝ   z--out)rÝ   c              	   S   s0   i | ]}|  d d¡d t |  d d¡d ¡“qS )ú=r   r   )r}   ri   Úloads)r   ÚkvrL   rL   rM   Ú
<dictcomp>ñ   s   0 zmain.<locals>.<dictcomp>z..c                    s<   h | ]}ˆ |  d ¡d   krˆkrn n|  d ¡d ’qS r{   r|   r~   ©Úa_Úb_rL   rM   Ú	<setcomp>õ   s   < zmain.<locals>.<setcomp>r   )Ú
get_clientc                 S   s   g | ]}|r|‘qS rL   rL   )r   rw   rL   rL   rM   r€   ü   rµ   zmain.<locals>.<listcomp>ú,Úbars_z.json.gzrY   )rZ   r\   rZ   r\   r   )Útzinfor   r   )r/   r0   é   )r/   iè  )r˜   ÚendÚlimitc              	   S   s<   g | ]}|j t|jƒt|jƒt|jƒt|jƒt|jƒg‘qS rL   )Ú	timestamprœ   rk   ÚhighÚlowÚcloseÚvolume)r   r@   rL   rL   rM   r€   	  s    4ÿF)Úverbosezno referenceú Ú5z buy rÀ   rÁ   r½   z (p_up r¹   z, p_dn rº   z
, target +r¼   z%) -> sell rÂ   rÃ   rÄ   z+.2fz% [rÅ   z]  refs z, r»   r   r`   z	 no tradec                 s   s   � | ]	}|d  dkV  qdS )rÄ   r   NrL   ©r   rA   rL   rL   rM   r§   "  s   € zmain.<locals>.<genexpr>z
TOTAL z	 trades, z wins, avg c                 S   r³   )rÄ   rL   rö   rL   rL   rM   r€   #  rµ   z% per trade, sum c                 s   r¤   )rÄ   NrL   rö   rL   rL   rM   r§   $  r¨   z+.1fz	% (after r   z% cost) | version r   Úwr   )ÚindentrÝ   ),ÚargparseÚArgumentParserÚadd_argumentÚ
parse_argsÚDEFÚsetrz   Údaysr}   rf   rX   Úalpaca_clientrç   r\   rg   rc   ri   rd   rj   rk   r   ÚfromisoformatÚreplacer-   Úget_minute_barsÚitemsrl   Úbuildr[   rn   ro   Ú	Exceptionrp   rN   rÖ   rÙ   Újoinr2   r™   r5   ÚmeanÚoutÚdumpÚstr)ÚapÚargsrÌ   r°   rÿ   rç   ÚclientÚ
all_tradesrÊ   ÚsymsÚrowsetsru   Úddrw   Úd0Úrawr=   r>   rx   ÚtrrA   Únr÷   rL   rã   rM   Úmainê   sž   

$
$ÿ
ÿ

H
ÿÿÿÿÿ
ÿ€ì(ÿÿÿÿr  Ú__main__) Ú__doc__rù   rj   ri   r9   Úsysr   Úpathlibr   Úzoneinfor   Únumpyr5   ÚTRADEÚpathÚinsertr  Ú	referencerl   r-   rg   rb   r1   rý   rN   rV   ry   rz   rÖ   rÙ   r  r¡   rL   rL   rL   rM   Ú<module>   s>    

þ (@?
ÿ