o
    �c¯jZ  ã                   @   s”   d Z ddlmZmZ ddlmZmZmZm	Z	m
Z
mZ dZdZdddd	d
ddddœZeG dd„ dƒƒZdd„ Zddedededededefdd„ZdS )a­  
exit_cents_grace_experimental.py

[2026-09-20] NOT wired into monitor.py's live loop -- backtest-only.
Brings back a mechanism that actually ran live in the sibling
"premarket" bot (see premarket_backup_2026-09-08_2010/config.json's
`stop` section and position_manager.py's `_check_fade_confirmation`,
added there 2026-08-18): a flat $0.10 trailing stop off the position's
own peak, with ONE $0.05 "grace" extension if the stock's own health
signals don't confirm real deterioration at the moment the stop is
touched. That project's own note on raising its ATR multiplier
(2026-09-02) records 14/14 trades touching the stop and needing the
grace extension just to survive the first touch on one real day --
i.e. even there this was a live, load-bearing mechanism, not a rarely
used edge case.

Ported here rather than copied verbatim: the original's "health"
gate was a separate intraday_health.py module (price/VWAP slope +
volume participation + structure classification) that doesn't exist
in this project and pulls in unrelated dependencies. This version
reuses THIS bot's own already-existing "healthy" definition instead --
exit.py's own Layer 3 vote (price vs VWAP, VWAP slope, price trend,
lower-highs/lower-lows) literally logs "healthy (N/4 deterioration
signals)" when fewer than deterioration_min_signals agree. Using that
same vote here means "still healthy" means the same thing everywhere
in this codebase, not a second, differently-tuned health concept.

Genuinely different SHAPE of rule than everything else tried this
session (PRF, giveback-vote, resistance-timer, velocity): a flat
DOLLAR distance (not a percentage, not ATR-scaled, not a fixed time
window), with a bounded one-time forgiveness gated on the bot's own
existing structural vote rather than order-flow/imbalance (which the
2026-09-18 cross-symbol analysis found to be non-predictive) or
decline velocity (which underperformed baseline in every configuration
that actually fired).
é    )Ú	dataclassÚfield)ÚvwapÚ
vwap_slopeÚatrÚnormalized_slope_pctÚclassify_slopeÚis_lower_highs_lower_lowsÚHOLDÚEXITgš™™™™™¹?gš™™™™™©?é   é   é   g      Ð?é   )Úbase_giveback_centsÚgrace_centsÚmax_grace_extensionsÚdeterioration_min_signalsÚstructure_lookback_barsÚmin_flat_threshold_pctÚflat_threshold_atr_fractionÚ
atr_periodc                   @   s^   e Zd ZU eed< eZeed< dZeed< dZ	eed< e
ed�Zeed< e
ed�Zeed	< d
S )ÚCentsGraceDecisionÚsymbolÚstateFÚshould_exitÚ Úreason)Údefault_factoryÚmetricsÚ	state_outN)Ú__name__Ú
__module__Ú__qualname__ÚstrÚ__annotations__Ú
STATE_HOLDr   r   Úboolr   r   Údictr   r    © r)   r)   ú8/var/www/screener/trade/exit_cents_grace_experimental.pyr   9   s   
 r   c                 C   s   i t ¥| pi ¥S ©N)ÚDEFAULT_CONFIG)Úcfgr)   r)   r*   Ú
_merge_cfgC   s   r.   Nr   ÚbarsÚentry_priceÚstate_inr-   Úreturnc                 C   sÌ  t |ƒ}t|pi ƒ}t|ƒdk rt| d|d�S |d d }| d|¡}t||ƒ}| dd¡}| d	d
¡}	||d  }
|
| }|t|dƒt|
dƒt|dƒt|dƒ|	dœ}||
krpt| td|
d›�|d
krednd ||dd
dœd�S ||kr‰t| td|
d›d|d›�||||	dœd�S |	|d krªt| tdd|d›d|	› d|d › d�||dd
dœd�S t	|ƒ}t
|ƒ}t|t|d tdt|ƒd ƒƒd �}|rÌ|| d! nd}d"d#„ |D ƒ}t|ƒ}t|d$ ||d%  ƒ}t||ƒ}t|t|d& t|ƒƒd'�}||k |d
k |d(k|g}t|ƒ}| t|dƒt|dƒ|||d)œ¡ ||d* k�r4t| tdd|d›d+|› d,�||dd
dœd�S ||d-  }|	d }t| td|d›d.|› d/|d- d0›d1|› d|d › d2|
| d›�||||dœd�S )3zÒ
    bars: same short rolling 1-min-bar window exit.py takes.
    state_in: caller-held state fed back every poll -- {"peak_price",
        "fade_grace_active", "grace_extensions_used"}. Fresh {} at entry.
    é   zinsufficient bar data)r   r   r    éÿÿÿÿÚcÚ
peak_priceÚfade_grace_activeg        Úgrace_extensions_usedr   r   r   )Úpricer6   Ú	real_stopÚeffective_floorr7   r8   zabove trailing stop $z.4fz -- grace resetr   )r6   r7   r8   )r   r   r   r   r    zbelow stop $z! but within granted grace floor $r   Tzfloor $z& touched, grace extensions exhausted (ú/ú))r   r   r   r   r   r    r   r   r   )Úperiodg      Y@c                 S   s   g | ]}|d  ‘qS )r5   r)   )Ú.0Úbr)   r)   r*   Ú
<listcomp>†   s    zevaluate.<locals>.<listcomp>r   r   r   )ÚlookbackÚnegative)r   r   ÚtrendÚlower_highs_lower_lowsÚagreeingr   z" touched, health confirms fading (z/4 deterioration signals)r   z touched but still healthy (z/4 signals) -- granting $z.2fz grace (z), new floor $)r.   r(   Úlenr   ÚgetÚmaxÚroundr&   Ú
STATE_EXITr   r   Úcalc_atrÚminr   r   r	   ÚsumÚupdate)r   r/   r0   r1   r-   r9   Ú
prior_peakr6   r7   r8   r:   r;   r   Ú
vwap_valueÚvslopeÚaÚatr_pctÚclosesÚ	slope_pctÚflat_threshrD   ÚlhlÚsignalsrF   Ú	new_graceÚnew_usedr)   r)   r*   ÚevaluateG   s¨   
þ
üÿü
ÿÿÿü
$
ÿ
ÿÿüÿÿÿþÿûr\   r+   )Ú__doc__Údataclassesr   r   Ú
indicatorsr   r   r   rL   r   r   r	   r&   rK   r,   r   r.   r$   ÚlistÚfloatr(   r\   r)   r)   r)   r*   Ú<module>   s$    % ø	(