o
    Þ„j"  ã                
   @   s  d Z ddlZddlmZmZmZ ddlmZ ddlmZ ddl	m
Z
 ddlmZ ddlZddlZedƒZd	efd
d„Zd	efdd„Zded	efdd„Zd#dededed	efdd„Zedkr„eƒ ZeD ]#Zeed d›ded d›ded d d ›d!ed d" d ›�ƒ qbdS dS )$u=  
premarket_scanner.py

Runs at ~09:00 ET. Scans the tradable universe ($5-$15, liquid enough)
and produces a ranked list of candidates using scorer.py. Called by
monitor.py, but also runnable standalone for testing:

    python premarket_scanner.py

It does NOT buy anything â€” this module only discovers and ranks.
é    N)ÚdatetimeÚ	timedeltaÚtimezone)Ú
get_config)Ú
get_logger)Ú
get_client)Úscore_premarket_candidateÚpremarket_scannerÚreturnc              
   C   sJ   g }| D ]}|  |jt|jƒt|jƒt|jƒt|jƒt|jƒdœ¡ q|S )N)ÚtÚoÚhÚlÚcÚv)ÚappendÚ	timestampÚfloatÚopenÚhighÚlowÚcloseÚvolume)ÚbarsÚoutÚb© r   úM/var/www/screener/trade/premarket_backup_2026-09-08_2010/premarket_scanner.pyÚ_bars_to_dicts   s   
þr   c                    s€   t ƒ d }| d¡r|d S |  ¡ }t| dg ¡ƒ‰ ‡ fdd„|D ƒ}t|ƒ}tj|dd�}t d|t|ƒ › d	|› d
�¡ |S )až  
    Pulls active, tradable US equities and applies:
      1. the include/exclude_symbols lists from config
      2. ETF/fund/trust exclusion (symbol_filters.is_etf_or_fund)
      3. leveraged/inverse ("multiplier") product exclusion
      4. company-name syllable-count cap

    Alpaca's asset_class=US_EQUITY includes ETFs/ETNs, so filters 2-4
    are done on the asset's `name` field via symbol_filters.py.
    ÚuniverseÚinclude_only_symbolsÚexclude_symbolsc                    s&   g | ]}t |d dƒr|jˆ vr|‘qS )ÚtradableF)ÚgetattrÚsymbol)Ú.0Úa©Úexcludedr   r   Ú
<listcomp>6   s   & z(get_universe_symbols.<locals>.<listcomp>T)Úlog_rejectionszC[PREMARKET] Symbol filters (ETF/leveraged/name-complexity) removed z of ú candidates)	r   ÚgetÚget_tradable_assetsÚsetÚlenÚsymbol_filtersÚfilter_symbol_listÚlogÚinfo)ÚclientÚcfgÚassetsÚ
candidatesÚbeforeÚ	survivorsr   r'   r   Úget_universe_symbols$   s   


ÿ
ÿr:   Úsymbolsc              
   C   sÒ   t ƒ d }t ƒ d }g }|  |¡}|s|S | ¡ D ]M\}}z>|j}|j}	|du r*W qt|jƒ}
|d |
  kr=|d ks@n W q|	rGt|	jƒnd}|rU||d d k rUW q| |¡ W q t	t
fyf   Y qw |S )	zÀ
    Cheap first pass using snapshot data to cut the universe down to a
    manageable set before pulling minute bars for full scoring. Filters
    on price band and a basic volume floor.
    r   Úpremarket_scoringNÚ	price_minÚ	price_maxr   Úmin_avg_daily_volumegš™™™™™©?)r   Úget_snapshotsÚitemsÚlatest_tradeÚ	daily_barr   Úpricer   r   ÚAttributeErrorÚ	TypeError)r4   r;   r5   Úscoring_cfgr9   Ú	snapshotsr$   ÚsnaprB   rC   rD   Úprev_daily_volr   r   r   Úprefilter_by_snapshot>   s.   



ÿrK   é   Úcandidate_countÚprefilteredÚlookback_hoursc              	   C   s&  t ƒ }| p
|d d } tƒ }t d¡ |du r5t|ƒ}t dt|ƒ› �¡ t||ƒ}t dt|ƒ› d�¡ t t	j
¡}|t|d� }g }|D ]—}	t| |	||¡ƒ}
t|
ƒd	k rft d
|	› dt|
ƒ› d�¡ qFtdd„ |
D ƒƒ}tdd„ |
D ƒƒ}| |	¡}|r‡|jr‡t|jƒnd}|r“|jr“t|jƒnd}t ƒ d d }t|	|
|||||ƒ}||d< ||d< |d  dd¡sÀt d
|	› d�¡ qF|d  dd¡sÒt d
|	› d�¡ qF| |¡ t |	|¡ qF|jdd„ dd� |d| … }|D ]}t d
|d › d|d  › �¡ qït d!t|ƒ› d"�¡ t |¡ |S )#a§  
    Full premarket scan. Returns a ranked list of scored candidate dicts,
    truncated to `candidate_count` (defaults to config's
    premarket_candidate_count, i.e. 20).

    [FEATURE 2026-08-17] `prefiltered` lets a caller (monitor.py) supply
    an already-computed universe+snapshot-filtered symbol list instead
    of this function pulling and filtering the full tradable-asset list
    again. monitor.py caches this list once at 09:00 and reuses it for
    every subsequent 30-minute intraday full rescan, so the expensive
    get_tradable_assets() + bulk snapshot call only ever happens once
    per day rather than once per rescan. `lookback_hours` defaults to 6
    (covers the overnight/premarket session) but monitor.py's intraday
    rescans pass a much shorter window (intraday_health.
    full_rescan_lookback_hours, default 1) since a 6-hour window is
    unnecessary once the regular session is already underway.
    r7   Úpremarket_candidate_countz[PREMARKET] Starting scanNz1[PREMARKET] Universe size after asset filtering: z[PREMARKET] Prefiltered to z symbols in price/volume band)Úhoursé   z[PREMARKET] z" rejected: insufficient bar data (z bars)c                 s   ó   � | ]}|d  V  qdS )r   Nr   ©r%   r   r   r   r   Ú	<genexpr>‡   ó   € zscan.<locals>.<genexpr>c                 s   rS   )r   Nr   rT   r   r   r   rU   ˆ   rV   r   r?   Úpm_highÚpm_lowÚflagsÚmeets_min_volumeFz% rejected: below min premarket volumeÚ	spread_okTz rejected: spread too widec                 S   s   | d S )NÚtotal_scorer   )Úrr   r   r   Ú<lambda>Ÿ   s    zscan.<locals>.<lambda>)ÚkeyÚreverser$   ú score=r\   z[PREMARKET] Selected r+   )r   r   r2   r3   r:   r/   rK   r   Únowr   Úutcr   r   Úget_minute_barsÚdebugÚmaxÚminÚget_latest_quoteÚ	bid_pricer   Ú	ask_pricer   r,   r   Ú
data_storeÚappend_premarket_snapshotÚsortÚwrite_premarket_candidates)rM   rN   rO   r5   r4   r   rb   ÚstartÚscoredr$   r   rW   rX   ÚquoteÚbidÚaskÚavg_vol_baselineÚresultÚtopr]   r   r   r   Úscan`   sR   



 
rw   Ú__main__r$   Ú6sra   r\   z6.2fz price=ÚmetricsrD   z.2fz rvol=Úrvol)NNrL   )Ú__doc__Úsysr   r   r   Úconfig_loaderr   Úlogger_setupr   Úalpaca_clientr   Úscorerr   r0   rk   r2   Úlistr   r:   rK   Úintr   rw   Ú__name__Úresultsr]   Úprintr   r   r   r   Ú<module>   s0    
"Jÿ
ÿý