o
    �—j7  ã                   @   s|   d Z ddlmZ ddlmZmZmZmZmZm	Z	m
Z
mZmZmZ ddd„Z	dd	ed
ededededededefdd„ZdS )u  
scorer.py

Transparent scoring engine. Every score comes with a `breakdown` dict
showing exactly how many points each factor contributed, so a stock's
rank is always explainable â€” never a black box, per project requirement.

Entry point:
    score_premarket_candidate(symbol, bars, pm_high, pm_low, avg_vol_baseline)
        -> used by premarket_scanner.py for the single full-universe scan
           at schedule.premarket_scan_time (see monitor.py's module
           docstring -- the 09:00-09:25 development-monitoring window and
           09:25 final-scoring review that used to call this repeatedly
           per symbol were eliminated 2026-09-01)

All weights and thresholds come from config.json under
"premarket_scoring" â€” nothing here is hard-coded strategy tuning.
é    )Ú
get_config)
ÚvwapÚ
vwap_slopeÚrelative_volumeÚvolume_accelerationÚprice_momentumÚis_higher_highs_higher_lowsÚconsolidation_tightnessÚdistance_from_high_pctÚextension_from_vwap_pctÚ
spread_pctç        ç      Y@c                 C   s   t |t|| ƒƒS )N)ÚmaxÚmin)ÚxÚloÚhi© r   úB/var/www/screener/trade/premarket_backup_2026-09-08_2010/scorer.pyÚ_clamp   s   r   NÚsymbolÚbarsÚpm_highÚpm_lowÚavg_vol_baselineÚbidÚaskÚreturnc           0         s  t ƒ d }|d }|s| di ddidœS dd„ |D ƒ‰ d	d„ |D ƒ}	ˆ d
 }
t|	ƒ}t|ƒ}t|t|d tdt|ƒd ƒƒd�}t||ƒ}t|	ƒ}t	tˆ ƒ|d kr[ˆ |d  d… nˆ ƒ}t
|tdt|ƒƒd�}t|ƒ}t|
|ƒ}t|
|ƒ}|r€|r€t||ƒnd}i }t ƒ d d }t ƒ d d }||
  krœ|krÀn n"|| d }|| d }dt|
| ƒ|  }|d td|ƒ |d< nd|d< |d }||krÞt||d  dƒ}|d | |d< n|d ||  d |d< |d }||k�rt||d  dƒ}|d | |d< n|dk�r|d ||  d nd|d< |d  } || k�r7t|d d dƒ}!|d! t|!ddƒ |d!< n|d! t||  ddƒ d" |d!< |dk�rX|d# t|d$ ddƒ nd|d#< |�rc|d% n|d% d& |d%< |
|k�rt|d' n|d' d( |d'< |dk�r…|d) n|d) d* |d)< td+| d, ddƒ}"|d- |" |d-< |d. }#t|#| |# ddƒ}$|d/ |$ |d/< |d0 }%t|%| |% ddƒ}&|d1 |& |d1< tˆ ƒdk�rø‡ fd2d„tdtˆ ƒƒD ƒ}'td3d4„ |'D ƒƒ}(|(t|'ƒ })|d5 |) |d5< n|d5 d |d5< |d6 }*||*k�rt||* |* dƒ}+|d7  |+ |d7< nd|d7< t| ¡ ƒ},td8d4„ | ¡ D ƒƒ}-t|,|- d9 dd:ƒ}.||k||k||%k||*k||
|k|dkd;œ}/| t|.dƒt|,dƒd<d=„ | ¡ D ƒ|
t|dƒt|dƒt|dƒt|dƒ|t|dƒt|dƒt|dƒt|dƒt|dƒd>œ|/d?œS )@ao  
    bars: list of 1-min premarket bars, oldest first, each
          {"t","o","h","l","c","v"}
    pm_high / pm_low: premarket high/low so far
    avg_vol_baseline: 20-day average volume (or similar) used for RVOL
    bid/ask: latest quote, optional (affects spread/liquidity subscore)

    Returns: {"symbol", "total_score", "breakdown": {...}, "flags": {...}}
    Úpremarket_scoringÚweightsr   Úno_dataT)r   Útotal_scoreÚ	breakdownÚflagsc                 S   ó   g | ]}|d  ‘qS )Úcr   ©Ú.0Úbr   r   r   Ú
<listcomp>1   ó    z-score_premarket_candidate.<locals>.<listcomp>c                 S   r%   )Úvr   r'   r   r   r   r*   2   r+   éÿÿÿÿÚvwap_lookback_barsé   )ÚlookbackÚmomentum_lookback_snapshotsNé   g      à?ÚuniverseÚ	price_minÚ	price_maxé   Úprice_qualityg333333Ó?Úmin_premarket_volumeé   g      ð?Úvolume_qualityÚmin_rvolé   Úrvolr   Úacceleration_min_ratior   gš™™™™™Ù?r   g       @Úprice_structuregš™™™™™É?Úvwap_structureg333333Ã?r   gš™™™™™¹?é   é   Úconsolidation_qualityÚmax_distance_from_pm_high_pctÚdistance_from_pm_highÚmax_spread_pctÚspread_liquidityc                    s    g | ]}ˆ | ˆ |d    ‘qS )r/   r   )r(   Úi©Úclosesr   r   r*   „   s     c                 s   s   � | ]	}|d krdV  qdS )r   r/   Nr   )r(   Údr   r   r   Ú	<genexpr>…   s   € z,score_premarket_candidate.<locals>.<genexpr>Úmomentum_consistencyÚextension_risk_pct_thresholdÚextension_risk_penaltyc                 s   s    � | ]\}}|d kr|V  qdS )rO   Nr   ©r(   Úkr,   r   r   r   rL   ”   s   € r   éd   )Úmeets_min_volumeÚmeets_min_rvolÚ	spread_okÚextendedÚhh_hl_structureÚprice_above_vwapÚvwap_risingc                 S   s   i | ]
\}}|t |d ƒ“qS )r6   )ÚroundrP   r   r   r   Ú
<dictcomp>¥   s    z-score_premarket_candidate.<locals>.<dictcomp>)Úpricer   r   r=   r   Úpremarket_volumeÚmomentum_pctÚconsolidation_tightness_pctÚdistance_from_pm_high_pctr   r   )r   r"   Ú	raw_scorer#   Úmetricsr$   )r   Úsumr   r   r   r   Úlenr   r   r   r   r	   r
   r   r   Úabsr   ÚrangeÚvaluesÚitemsrZ   )0r   r   r   r   r   r   r   ÚcfgÚwÚvolumesÚcurrent_priceÚtotal_volumeÚv_wapÚv_sloper=   Ú	vol_accelÚmomentumÚhh_hlÚ	tightnessÚdist_from_highÚ	extensionÚsprr#   r4   r5   ÚmidÚ	band_halfÚ	closenessÚmin_volÚ	vol_ratior;   Ú
rvol_ratioÚ	accel_minÚaccel_ratioÚconsolidation_scoreÚmax_distÚ
dist_scoreÚ
max_spreadÚspread_scoreÚdiffsÚsame_directionÚconsistencyÚext_thresholdÚoverÚtotalÚmax_possibleÚ
normalizedr$   r   rI   r   Úscore_premarket_candidate    s¼   
$
*


&
 *""
ùõîrŒ   )r   r   )NN)Ú__doc__Úconfig_loaderr   Ú
indicatorsr   r   r   r   r   r   r	   r
   r   r   r   ÚstrÚlistÚfloatÚdictrŒ   r   r   r   r   Ú<module>   s    0
ÿÿÿÿÿ