o
    ]¨j&  ã                   @   s  d Z ddlmZmZ ddlmZ ddlmZ dd„ Zdefdd	„Z	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fdd„Zdefdd„Zdefdd„Zdefdd„Zdefdd„Zdefdd„Zdefdd „Zdefd!d"„Zdefd#d$„Zdefd%d&„Zd'S )(a#  
market_time.py

All schedule/time comparisons go through here so the rest of the code
never does naive string or local-time comparisons (a common source of
"incorrect market-time handling" bugs, especially around DST). Uses
zoneinfo (stdlib, Python 3.9+) with America/New_York from config.
é    )ÚdatetimeÚtime)ÚZoneInfo)Ú
get_configc                   C   s   t tƒ d d ƒS )NÚscheduleÚtimezone)r   r   © r   r   ú*/var/www/screener/premarket/market_time.pyÚ_tz   s   r
   Úreturnc                   C   s   t  tƒ ¡S )N)r   Únowr
   r   r   r   r	   Únow_et   s   r   Údtc                 C   s   |   tƒ ¡ ¡ S )z¼Trading-day date for an arbitrary (tz-aware) timestamp, e.g. a
    UTC exit_time/updated_at pulled from persisted state, converted to
    the exchange's own calendar day rather than UTC's.)Ú
astimezoner
   Údate)r   r   r   r	   Úet_date   s   r   Úhhmmssc                 C   s&   dd„ |   d¡D ƒ\}}}t|||ƒS )Nc                 S   s   g | ]}t |ƒ‘qS r   )Úint)Ú.0Úxr   r   r	   Ú
<listcomp>    s    z_parse_time.<locals>.<listcomp>ú:)ÚsplitÚdtime)r   ÚhÚmÚsr   r   r	   Ú_parse_time   s   r   Úcurrentc                 C   s   |   ¡ jd d�t|ƒkS )N)Útzinfo)ÚtimetzÚreplacer   )r   r   r   r   r	   Ú	_is_after$   s   r"   c                  C   ó   t ƒ d } ttƒ | d ƒS )Nr   Úpremarket_scan_time©r   r"   r   ©Úcfgr   r   r	   Úis_premarket_scan_time(   ó   
r(   c                  C   s.   t ƒ d } tƒ }t|| d ƒot|| d ƒ S )Nr   Úmarket_open_timeÚmarket_close_time)r   r   r"   )r'   r   r   r   r	   Úis_market_open-   s   
r,   c                  C   r#   )Nr   r*   r%   r&   r   r   r	   Úis_past_market_open3   r)   r-   c                  C   r#   )Nr   Úno_new_entries_afterr%   r&   r   r   r	   Úis_new_entries_cutoff8   r)   r/   c                  C   r#   )Nr   Úforce_liquidate_timer%   r&   r   r   r	   Úis_force_liquidate_time=   r)   r1   c                  C   r#   )Nr   r+   r%   r&   r   r   r	   Úis_market_closed_for_dayB   r)   r2   c                   C   s   t ƒ  ¡ dk S )Né   )r   Úweekdayr   r   r   r	   Ú
is_weekdayG   s   r5   c                  C   s4   t ƒ d } t| d ƒ}tƒ }|j|j|j|jdd�S )a—  [BUGFIX 2026-09-09] Today's market_open_time as a tz-aware ET
    datetime -- for stream.py's historical backfill, which needs a real
    start timestamp for "give me every 1-min bar since the session
    opened," not just a minutes-elapsed float. Same open_time-parsing
    logic minutes_since_open() already uses, factored out here since
    that one only needs the elapsed count, not the datetime itself.r   r*   r   )ÚhourÚminuteÚsecondÚmicrosecond)r   r   r   r!   r6   r7   r8   )r'   Ú	open_timer   r   r   r	   Úsession_open_dtK   s   
ÿr;   c                  C   s   t ƒ } tƒ |   ¡ d S )a�  [FEATURE 2026-09-05] Minutes since market_open_time today, for
    regime_engine.classify_regime()'s session_elapsed_minutes param (its
    OPENING_VOLATILITY window check). Negative before the open -- callers
    that only care about "are we in the opening window" should treat a
    negative value the same as regime_engine already treats None (can't
    be in the opening window if the session hasn't started).g      N@)r;   r   Útotal_seconds)Úopen_dtr   r   r	   Úminutes_since_openY   s   r>   c                  C   sB   t ƒ d } t| d ƒ}t| d ƒ}|jd |j |jd |j  S )ap  [FEATURE 2026-09-14] market_close_time - market_open_time in
    minutes (390 for a normal 09:30-16:00 session), for fast_entry_gate.py's
    volume-pace floor -- relative_volume is cumulative-volume-so-far over
    the PRIOR full day's total, so it needs to be divided by how much of
    TODAY's session has elapsed to be comparable across different times
    of day.r   r*   r+   é<   )r   r   r6   r7   )r'   Úopen_tÚclose_tr   r   r	   Úsession_length_minutesd   s   
 rB   N)Ú__doc__r   r   r   Úzoneinfor   Úconfig_loaderr   r
   r   r   Ústrr   Úboolr"   r(   r,   r-   r/   r1   r2   r5   r;   Úfloatr>   rB   r   r   r   r	   Ú<module>   s&    	