o
    @¨ jW  ã                   @   sº  d Z ddlmZmZ ddlmZ dZdZdZdZ	dZ
d	Zd
ZdZdZdZdZdZdZdZdZdZdZdZdZi 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/“d0d1“d2d“d3i eg d4¢“ed5dg“e
d6g“ed5g“e	dg“ed6g“ed7g“eg “eg “eg “eg “ed8g“eg “ed7g“ed7d6g“ed6g“ed6d5g“ed6d5ged5d6gi¥“ZeG d9d:„ d:ƒƒZd;ed<ed=efd>d?„Z	@	@dFd;edAedBedCed=ef
dDdE„Z d@S )Ga+  
regime_engine.py

[SIMULATION-ONLY 2026-09-05] LIVE, continuously-re-evaluated regime
classifier -- combines trend_engine.TrendReading + structure_engine.
StructureReading + stream_features.StreamFeatures into one label
describing what's happening RIGHT NOW, and which entry setups (per the
future setup_engine.py) are allowed in that regime. Not consumed
anywhere in the live bot yet -- see config.json's regime_engine._note.

NOT THE SAME MODULE AS regime_detector.py (built earlier this session)
-- deliberately kept separate, answering a different question:

    regime_detector.py   "What KIND OF SESSION did/is this symbol
                          have(ing), overall?" -- computed once (or on a
                          fixed window) for offline strategy simulation
                          against a full day's data
                          (simulate_regime_strategies.py). Its regimes:
                          NORMAL_MOMENTUM, GAP_MOMENTUM,
                          CATALYST_GAP_HIGH_VOLATILITY, RANGE_CHOP,
                          FAILED_BREAKOUT, RECOVERY_RECLAIM,
                          INTRADAY_SPIKE_NO_GAP_WHIPSAW.

    regime_engine.py (this file)   "What's happening in this symbol's
                          price action RIGHT NOW, this instant?" --
                          re-evaluated every cycle from live trend/
                          structure/feature readings, feeding the live
                          entry decision. Its regimes are the original
                          design brief's list: STRONG_UPTREND,
                          WEAK_UPTREND, BREAKOUT, PULLBACK, VWAP_RECLAIM,
                          OPENING_VOLATILITY, RANGE, CHOP, DISTRIBUTION,
                          DOWNTREND, FAILED_BREAKOUT, RECOVERY.

Same names appearing in both (FAILED_BREAKOUT) describe genuinely
related but differently-scoped ideas -- one retrospective/whole-session,
one instantaneous/live -- exactly like structure_engine.py's swing-pivot
detection vs indicators.is_higher_highs_higher_lows()'s windowed-bar
check: neither is a duplicate of the other.
é    )Ú	dataclassÚfield)Ú
get_configÚOPENING_VOLATILITYÚFAILED_BREAKOUTÚRECOVERYÚVWAP_RECLAIMÚBREAKOUTÚPULLBACKÚDISTRIBUTIONÚSTRONG_UPTRENDÚWEAK_UPTRENDÚ	DOWNTRENDÚRANGEÚCHOPÚSTRUCTURE_BREAKDOWNÚACCUMULATIONÚPRESSURE_BUILDINGÚBREAKOUT_ATTEMPTÚBREAKOUT_CONFIRMEDÚ	EXPANSIONÚCONTINUATIONÚ!opening_volatility_window_minutesé   Úopening_volatility_min_atr_pctç      ð?Úpullback_trend_slope_thresholdg      à¿Ú$distribution_min_volume_accelerationgÍÌÌÌÌÌô?Údistribution_max_trend_slopegš™™™™™É¿Úvwap_reclaim_min_time_below_pctg      >@Úchop_min_atr_pctgš™™™™™é?Úrange_max_atr_pctÚstructure_breakdown_hard_blockTÚ"accumulation_min_compression_scoreé7   Ú-pressure_building_min_breakout_pressure_scoreéF   Úbreakout_confirm_barsé   Úexpansion_min_barsé   Úcontinuation_min_barsé   Úbreakout_volume_confirm_ratioÚallowed_setups)ÚPULLBACK_CONTINUATIONÚBREAKOUT_RETESTr   r/   r0   ÚCOMPRESSION_EXPANSIONÚHIGHER_LOW_REVERSALc                   @   sp   e Zd ZU eed< eZeed< dZeed< e	e
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 )ÚRegimeReadingÚsymbolÚregimeg        Úscore)Údefault_factoryr.   ÚreasonsFÚinsufficient_dataÚmissingN)Ú__name__Ú
__module__Ú__qualname__ÚstrÚ__annotations__r   r5   r6   Úfloatr   Úlistr.   r8   r9   Úboolr:   © rC   rC   ú,/var/www/screener/premarket/regime_engine.pyr3   Œ   s   
 r3   r4   r:   Úreturnc                 C   s   t | d|d�S )NT)r4   r9   r:   )r3   )r4   r:   rC   rC   rD   Ú_insufficient—   s   rF   NÚsession_elapsed_minutesÚstateÚcfgc	              	   C   s"  i t ¥|ptƒ  di ¡¥}|d }	g }
t|ddƒr|
 d¡ t|ddƒr)|
 d¡ t|ddƒr4|
 d¡ |
r;t| |
ƒS g }|jduoP|j d	d
¡|jkoP|jdk}|dur{|du p_|| d¡k}|ru|rl| dd
¡d nd
|d< ||d< n|s{d
|d< |p~i  d|r„dnd
¡}|d r¤|j	 d¡r¤| d¡ t
| td|j |	t |d�S |durÕ||d krÕ|j dd
¡|d krÕ| d|d›d|j d¡› d�¡ t
| td|	t |d�S |j	 d¡rì| d¡ t
| t|j|	t |d�S dd„ |jD ƒ}t|ƒd k�r!|d! jd"k�r!|d# jd$k�r!|jd
k�r!| d%¡ t
| td&|	t |d�S |j d'd
¡}|j d(d
¡}|d
k�rV||d) k�rV|jd
k�rV| d*|d+›d,�¡ t
| td-|	t |d�S |�rÒ|j d.¡�pad/|d0 k}|d1 }||d2  }||d3  }||k �r‘t}| d4|j d	¡› d5|j› d6|› �¡ n5||k �rª|�r›tnt}| d7|› d8|› d�¡ n||k �r»t}| d9|› d:�¡ nt}| d;|› d<�¡ t
| ||j|	| |d�S |jd=v �rü|j|d> k�rü|jd?k�rü| d@|j› d�¡ t
| t|j|	t |d�S |j d.¡}|du�r4||dA k�r4|j|dB k�r4|jdCk�r4| dD|› dE|j› dF�¡ t
| td&|	t |d�S |jd=v �r`|jdCk�r`| dG|j› dH�¡ |jdIk�rRt nt!}t
| ||j|	| |d�S |jdJv �r{| dG|j› �¡ t
| t"|j|	t" |d�S |j dd
¡}|jdKk�rò||dL k�rò|du�rÝt|ddƒ�sÝ|j#|dM k�r¼| dN|› dO|j#› dP�¡ t
| t$|j#|	t$ |d�S |j%|dQ k�rÝ| dN|› dR|j%› dS�¡ t
| t&|j%|	t& |d�S | dN|› d�¡ t
| t'|j|	t' |d�S | dT|j› dU|j› dV|› d�¡ t
| t(d|j |	t( |d�S )Wah  
    features: stream_features.StreamFeatures
    trend: trend_engine.TrendReading
    structure: structure_engine.StructureReading
    session_elapsed_minutes: minutes since regular-session open, or None
        if unknown (in which case OPENING_VOLATILITY can never fire --
        fails safe to "don't know if we're in the opening window" rather
        than guessing).
    compression: [Phase 4, optional] compression_engine.CompressionReading.
        Omit entirely to reproduce this function's exact pre-Phase-4
        behavior for the RANGE branch (ACCUMULATION/PRESSURE_BUILDING can
        only ever fire when this is supplied).
    state: [Phase 4, optional] caller-held per-symbol dict, same ownership
        pattern as entry_score.py's confirmation_state -- this function
        mutates state["bars_since_breakout"] in place. A fresh {} (or None)
        starts the counter at 0, exactly like a cold start anywhere else in
        this pipeline. Drives the BREAKOUT_ATTEMPT -> BREAKOUT_CONFIRMED ->
        EXPANSION -> CONTINUATION progression below.
    bar_t: [Phase 5 -- BUGFIX 2026-09-08, optional] the CURRENT bar's own
        timestamp (bars[-1]["t"]) -- caller should always pass this live.
        Without it, bars_since_breakout increments once per CALL, which in
        production means once per poll_interval_seconds_intraday (5s by
        default), not once per actual 1-minute bar close. Confirmed: this
        made breakout_confirm_bars/expansion_min_bars/continuation_min_bars
        (2/3/5, intended as ~minutes of persistence) actually complete in
        10-50 SECONDS live -- a ~12x faster progression than the config
        values were ever tuned for, found while validating the entry gate
        against real 2026-09-08 SMR/MARA data. When bar_t is supplied, the
        counter only advances the first time a given bar_t is seen (i.e.
        real bar closes), and repeated polls of the same still-forming bar
        leave it unchanged -- matching what "bars_since_breakout" actually
        means. Omitting bar_t reproduces the pre-fix (poll-counting)
        behavior for any caller not yet updated.
    Úregime_enginer.   r9   FÚstream_featuresÚtrendÚ	structureNÚlastr   ÚUPÚ
last_bar_tÚbars_since_breakouté   r"   Úsupport_failurez�structure_engine confirmed a support failure (broke below the last swing low) -- highest-priority risk state, overrides opening-window handlingg      Y@)r4   r5   r6   r.   r8   r   Úatr_pctr   zwithin opening window (z.1fz%m) with elevated volatility (atr_pct=ú)g     €V@Úfailed_breakoutzMstructure_engine confirmed a failed breakout (broke resistance, gave it back)c                 S   s$   g | ]}|j d kr|jdur|‘qS )ÚLOWN)ÚkindÚlabel)Ú.0ÚsrC   rC   rD   Ú
<listcomp>  s   $ z#classify_regime.<locals>.<listcomp>r(   éþÿÿÿÚLLéÿÿÿÿÚHLzKbottomed (LL) then printed a higher low (HL) with momentum turning positiveg      N@Úprice_vs_vwap_pctÚtime_below_pctr   z#price reclaimed VWAP (was below it z.0fz6% of the recent window) with momentum turning positiveg     @P@Úvolume_accelerationr   r-   r'   r)   r+   zprice (z) above last swing high (z), uptrend intact, attempt #zbreakout held for z$ consecutive reads (volume_confirms=zbreakout held z* reads past the initial break -- expansionzbreakout sustained z reads -- continuation)r   r   r   ÚBULLISH_STRUCTUREzRuptrend intact (structure=BULLISH) but short-term momentum weakening (trend_slope=r   r   ÚBEARISH_STRUCTUREzelevated volume (accel=z%x) with fading momentum (trend_slope=z ) ahead of a confirmed breakdownztrend_engine=z", structure does not contradict itr   )ÚWEAK_DOWNTRENDÚSTRONG_DOWNTRENDÚRANGE_STRUCTUREr!   r%   z*structure=RANGE, calm volatility (atr_pct=z), breakout_pressure_score=z* -- compression + bullish context buildingr#   z), compression_score=z -- qualifies as accumulationz%no clear directional edge (structure=z, trend=z
, atr_pct=))ÚDEFAULT_CONFIGr   ÚgetÚgetattrÚappendrF   Úlast_swing_highÚpriceÚ	directionÚflagsr3   r   r6   Ú
volatilityr   r   ÚswingsÚlenrY   Útrend_sloper   Úvwapr   Úvolumer   r   r   r   rH   rM   r
   r   r   r   r   Úbreakout_pressure_scorer   Úcompression_scorer   r   r   )r4   ÚfeaturesrL   rM   rG   ÚcompressionrH   Úbar_trI   Úallowed_setups_mapr:   r8   Úis_breakout_conditionÚ
is_new_barrQ   Ú
low_swingsÚprice_vs_vwapÚ
time_belowÚvolume_confirmsÚ
confirm_atÚexpansion_atÚcontinuation_atr5   Ú	vol_accelrT   rC   rC   rD   Úclassify_regime›   s"  %




	ÿýÿÿ
	
ÿ

ÿÿ

ÿ.
ÿ$ÿ

ÿ
ÿ
ÿ

ÿ
ÿ
ÿ
ÿÿ
ÿ
ÿ	
ÿ
ÿ
ÿ
ÿ
ÿÿ
ÿÿr‡   )NNNNN)!Ú__doc__Údataclassesr   r   Úconfig_loaderr   r   r   r   r   r	   r
   r   r   r   r   r   r   r   r   r   r   r   r   r   ri   r3   r>   rA   rF   r@   Údictr‡   rC   rC   rC   rD   Ú<module>   sÔ    (	ÿþýüûúùøôðïèçæå ÿþýüûúùø	÷
öõôòñðïçåà@
þÿþþþ