o
    � j6)  ã                   @   s  d Z ddlmZmZ ddlmZ 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gdddddœZeG dd „ d ƒƒZd!ed"efd#d$„Z	d%e
d"efd&d'„Zd(ed)ed"efd*d+„Zd,ed)ed"efd-d.„Z	/	/	/d5d0ed1ed2e
d)ed"ef
d3d4„Zd/S )6aC  
winner_score.py

[Phase 4 -- new] Composite 0-100 ranking score, ADDITIVE ONLY -- see the
2026-09-08 design discussion: entry_score.py's five separate scores
(trend/structure/prediction/entry/risk) remain the ONLY thing that gates
should_enter, unchanged. WINNER_SCORE never overrides that gate; it exists
purely for cross-symbol ranking, the market-state output, and score-velocity
tracking (RISING_FAST etc.) -- exactly the two independent fields the
project's own requested output schema already has (`winner_score` and
`entry_status` side by side, not one derived from the other).

Composition follows the project's own suggested starting weights (all
configurable, none hard-coded): trend_structure 20, relative_strength 15,
volume_expansion 15, vwap_structure 15, premarket_setup 10, breakout_quality
10, volatility_expansion 5, sector_strength 5, market_regime 5.

SCORE MOMENTUM (velocity/acceleration/trend) needs a caller-held history --
same ownership pattern as every other stateful piece in this pipeline
(entry_score.py's confirmation_state, stream_features.py's `prior`): the
caller holds a bounded list of (timestamp, score, velocity) tuples, oldest
first, passes it in as `score_history`, and is responsible for appending
this call's own (now, score, velocity) to that list afterward -- this
function never mutates or stores it.
é    )Ú	dataclassÚfield)Ú
get_configé   é   é
   é   ©	Útrend_structureÚrelative_strengthÚvolume_expansionÚvwap_structureÚpremarket_setupÚbreakout_qualityÚvolatility_expansionÚsector_strengthÚmarket_regimeg333333ã?gš™™™™™Ù?)ÚtrendÚ	structure)ÚaccelerationÚrelative_volumeç       @ç      I@éd   éK   é<   é2   é(   é   )ÚSTRONG_VWAP_SUPPORTÚVWAP_RECLAIMÚ	VWAP_HOLDÚVWAP_EXTENSIONÚ	VWAP_FLATÚVWAP_DECLININGÚVWAP_FAILUREç      ð?g333333ë?gÍÌÌÌÌÌä?ç      à?)ÚSTRONG_RISK_ONÚRISK_ONÚNEUTRALÚRISK_OFFÚSTRONG_RISK_OFFÚBREAKOUT_RETESTÚCOMPRESSION_EXPANSIONç      @)ÚweightsÚtrend_structure_blendÚvolume_expansion_blendÚrelative_volume_saturationÚ#volatility_expansion_saturation_pctÚno_sector_default_scoreÚvwap_classification_scoresÚmarket_regime_creditÚno_market_regime_default_creditÚbreakout_setup_namesÚ breakout_quality_no_setup_creditÚvelocity_lookback_readingsÚscore_trend_threshold_per_minÚ"score_trend_fast_threshold_per_minc                   @   sv   e Zd ZU eed< dZeed< dZeed< dZeed< dZ	eed< e
ed�Zeed	< d
Zeed< e
ed�Zeed< dS )ÚWinnerScoreReadingÚsymbolç        Úwinner_scoreÚvelocity_per_minÚacceleration_per_minÚSTABLEÚscore_trend)Údefault_factoryÚ	breakdownFÚinsufficient_dataÚmissingN)Ú__name__Ú
__module__Ú__qualname__ÚstrÚ__annotations__rA   ÚfloatrB   rC   rE   r   ÚdictrG   rH   ÚboolÚlistrI   © rS   rS   ú+/var/www/screener/premarket/winner_score.pyr>   :   s   
 r>   ÚxÚreturnc                 C   s   t dtd| ƒƒS )Nr@   r&   )ÚmaxÚmin)rU   rS   rS   rT   Ú_clamp01F   s   rY   Úpointsc                    s~   t | ƒ}|dk r
dS tdd„ | D ƒƒ| ‰ tdd„ | D ƒƒ| ‰t‡ ‡fdd„| D ƒƒ}t‡ fdd„| D ƒƒ}|r=|| S dS )aB  Least-squares slope of `points` ([(elapsed_seconds, value), ...])
    against real elapsed time -- the general form of indicators.
    linreg_slope() (which assumes evenly-spaced integer x=0..n-1), needed
    here because winner_score readings aren't guaranteed evenly spaced in
    wall-clock time the way 1-min bars are.é   r@   c                 s   ó   � | ]}|d  V  qdS )r   NrS   ©Ú.0ÚprS   rS   rT   Ú	<genexpr>S   ó   € z'_time_weighted_slope.<locals>.<genexpr>c                 s   r\   )é   NrS   r]   rS   rS   rT   r`   T   ra   c                 3   s$   � | ]\}}|ˆ  |ˆ  V  qd S ©NrS   )r^   rU   Úy©Úmean_xÚmean_yrS   rT   r`   U   s   €" c                 3   s    � | ]\}}|ˆ  d  V  qdS )r[   NrS   )r^   rU   Ú_)rf   rS   rT   r`   V   s   € )ÚlenÚsum)rZ   ÚnÚnumÚdenrS   re   rT   Ú_time_weighted_slopeJ   s   rn   rB   Úcfgc                 C   sJ   |d |d }}| |krdS | |krdS | | krdS | | kr#dS dS )Nr=   r<   ÚRISING_FASTÚRISINGÚDECLINING_FASTÚ	DECLININGrD   rS   )rB   ro   ÚfastÚnormalrS   rS   rT   Ú_score_trendZ   s   

rv   Úsetupsc                 C   s–   d }| rdd„ |   ¡ D ƒ}t|dd„ d d�}|d ur#t|ddƒs#|jnd}|d ur?|j|d	 v r?t|jd
 |d  d ƒd S t||d  d ƒd S )Nc                 S   s   g | ]}|j r|‘qS rS   )Úvalid)r^   ÚrrS   rS   rT   Ú
<listcomp>j   s    z%_breakout_quality.<locals>.<listcomp>c                 S   s   | j S rc   )Ú
confidence)ry   rS   rS   rT   Ú<lambda>k   s    z#_breakout_quality.<locals>.<lambda>)ÚkeyÚdefaultrH   Fr@   r9   gffffffæ?g333333Ó?ç      Y@r:   )ÚvaluesrW   ÚgetattrÚbreakout_pressure_scoreÚsetuprY   r{   )rw   Úcompressionro   Úbestrx   ÚpressurerS   rS   rT   Ú_breakout_qualityg   s   ÿÿr‡   Nr?   Úpremarket_resultÚscore_historyc           )   
      s@  ddl m }m} i t¥|ptƒ  di ¡¥}|
p| |j¡}
t|ddƒs5t|ddƒs5t|ddƒs5t|ddƒr=t| ddgd�S |d	 ‰|d
 }|j	|d  |j	|d   }|dur`t|ddƒs`|j
n|d }|d }|j d¡}|j d¡}t|pxdd d ƒd }t|p„d|d  ƒd }||d  ||d   }|j d¡}|d  |d¡}|	r®|	 dd¡n|d }t|p¶i ||ƒ}|j d¡}|durÐt|pÈd|d  ƒd n|d }|d }|durÿt|ddƒsÿ|jdurÿt|jpìdd  d! ƒd }|jd d" |d"  }|du�rt|ddƒ�s|d#  |j|d$ ¡n|d$ d } ||||||||| d%œ	‰ ‡ ‡fd&d'„ˆ D ƒ}!tˆ ¡ ƒ}"ttt|! ¡ ƒ|" ƒd d(ƒ}#|�pKg ‰ˆ�rX‡fd)d*„ˆD ƒng }$|$ ˆ�ri|
ˆd d   ¡ nd|#f¡ t|$ƒ}%t|%d+ d,ƒ}&ˆ�r‚ˆd- d( nd}'t|&|' d,ƒ}(t| |#|&|(t|&|ƒd.d'„ ˆ  ¡ D ƒdd/�S )0a®  
    features/trend/structure/prediction: required, same readings the rest
        of this pipeline already computes.
    relative_strength: optional relative_strength_engine.RelativeStrengthReading.
        Omit to fall back to a neutral component (never a hard block, per
        the project's "sector confirmation is additional, not required"
        principle extended to RS in general when the market data isn't
        available yet).
    compression: optional compression_engine.CompressionReading.
    setups: optional {name: setup_engine.SetupResult} dict.
    market_regime: optional relative_strength_engine.MarketRegimeReading.
    premarket_result: optional scorer.score_premarket_candidate() dict for
        THIS symbol -- reuses its total_score directly as the
        premarket_setup component (the "premarket produces the thesis"
        fix). Omit if unavailable; falls back to a neutral 50.
    score_history: optional caller-held list of (timestamp, score,
        velocity_per_min) tuples, oldest first, NOT including this call's
        own reading -- see module docstring. Omit (or pass []) for a cold
        start (velocity/acceleration read as 0.0, trend as STABLE).
    r   )ÚdatetimeÚtimezonerA   rH   FTz7one or more required upstream readings was insufficient)r?   rH   rI   r0   r1   r   r   Nr5   r2   Úvolume_accelerationr   r&   gš™™™™™é?g333333ó?r   r@   r3   r   Úclassificationr6   r   Útotal_scorer   Úrange_expansion_pctr4   r   r/   r'   r7   r8   r	   c                    s(   i | ]}|t ˆ | ˆ|  d  dƒ“qS )r   r[   ©Úround)r^   Úk)Ú
componentsÚwrS   rT   Ú
<dictcomp>¿   s   ( z(compute_winner_score.<locals>.<dictcomp>r[   c                    s*   g | ]\}}}|ˆ d  d     ¡ |f‘qS )r   )Útotal_seconds)r^   ÚtÚsÚ_v)ÚhistoryrS   rT   rz   Å   s   * z(compute_winner_score.<locals>.<listcomp>g      N@é   éÿÿÿÿc                 S   s   i | ]
\}}|t |d ƒ“qS )r[   r�   )r^   r’   ÚvrS   rS   rT   r•   Ð   s    )r?   rA   rB   rC   rE   rG   rH   )rŠ   r‹   ÚDEFAULT_CONFIGr   ÚgetÚnowÚutcr�   r>   ÚscoreÚfinal_scoreÚvolumerY   Úvwapr‡   Ú
volatilityÚsector_breadthÚsector_momentumÚregimerj   r€   r‘   Úappendr–   rn   rv   Úitems))r?   Úfeaturesr   r   Ú
predictionr   r„   rw   r   rˆ   r    r‰   ro   rŠ   r‹   ÚtbÚtrend_structure_scoreÚrs_scoreÚvbÚ	vol_accelÚrvolÚaccel_creditÚrvol_creditÚvolume_expansion_scoreÚ
vwap_classÚvwap_structure_scoreÚpremarket_setup_scoreÚbreakout_quality_scoreÚrange_expansionÚvolatility_expansion_scoreÚsector_scoreÚmomentum_creditÚmarket_regime_scorerG   Úweight_totalrA   rZ   Úslope_per_secrB   Úprior_velocityrC   rS   )r“   rš   r”   rT   Úcompute_winner_scores   sŠ   
ÿ
ÿÿ
ÿÿÿÿÿÿ
ÿþû
(ûrÃ   )NNNNNNNN)Ú__doc__Údataclassesr   r   Úconfig_loaderr   rž   r>   rO   rY   rR   rn   rP   rM   rv   r‡   rÃ   rS   rS   rS   rT   Ú<module>   sT    ýþ
ÿéýþýýý