o
    ¨Ž—jÿ   ã                   @   s  d Z ddlZddlZddlmZ ddlmZ eeƒ ¡ j	Z
e
d Ze
d Zdefdd	„Zeeƒ ed
d�defdd„ƒZdefdd„ZG dd„ dƒZed
d�defdd„ƒZddœdd„ZG dd„ deƒZg d¢ZddgZdedefdd„Zd!deddfdd „ZdS )"a  
config_loader.py

Single source of truth for reading config.json and .env credentials.
Every other module imports get_config() / get_env() from here instead
of touching os.environ or config.json directly. This keeps parameters
out of source code, per project requirement.
é    N)ÚPath)Ú	lru_cachezconfig.jsonz.envÚpathc                 C   s    |   ¡ sdS t| dƒ�;}|D ]/}| ¡ }|r| d¡sd|vr q| d¡\}}}| ¡ }| ¡  d¡ d¡}tj ||¡ qW d  ƒ dS 1 sIw   Y  dS )z6Minimal .env loader (no external dependency required).NÚrú#ú=ú"ú')ÚexistsÚopenÚstripÚ
startswithÚ	partitionÚosÚenvironÚ
setdefault)r   ÚfÚlineÚkeyÚ_Úvalue© r   úI/var/www/screener/trade/premarket_backup_2026-09-08_2010/config_loader.pyÚ_load_dotenv   s   ø"ÿr   é   )ÚmaxsizeÚreturnc                  C   s8   t tdƒ�} t | ¡W  d   ƒ S 1 sw   Y  d S )Nr   )r   ÚCONFIG_PATHÚjsonÚload)r   r   r   r   Ú
get_config'   s   $ÿr    c                   C   s   t  ¡  t ƒ S )zEForce re-read of config.json (useful if tuning live without restart).)r    Úcache_clearr   r   r   r   Úreload_config-   s   r"   c                   @   s    e Zd ZdZdd„ Zdd„ ZdS )Ú	EnvConfigz@Wraps required Alpaca credentials with clear failure if missing.c                 C   s@   t j d¡| _t j d¡| _t j dd¡| _t j dd¡| _d S )NÚALPACA_API_KEYÚALPACA_SECRET_KEYÚALPACA_BASE_URLz https://paper-api.alpaca.marketsÚALPACA_DATA_URLzhttps://data.alpaca.markets)r   r   ÚgetÚapi_keyÚ
secret_keyÚbase_urlÚdata_url)Úselfr   r   r   Ú__init__6   s   zEnvConfig.__init__c                 C   sB   g }| j s
| d¡ | js| d¡ |rtdd |¡› d�ƒ‚d S )Nr$   r%   z(Missing required environment variables: z, z@. Copy .env.example to .env and fill in your Alpaca credentials.)r)   Úappendr*   ÚEnvironmentErrorÚjoin)r-   Úmissingr   r   r   Úvalidate<   s   

ÿÿzEnvConfig.validateN)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r.   r3   r   r   r   r   r#   3   s    r#   c                  C   s
   t ƒ } | S ©N)r#   )Úenvr   r   r   Úget_envI   s   r:   )Údefaultc                 G   s6   t ƒ }|D ]}t|tƒr||vr|   S || }q|S )zr
    Convenience getter for nested config values.
    Example: cfg("stop", "trailing_distance", default=0.10)
    )r    Ú
isinstanceÚdict)r;   ÚkeysÚnodeÚkr   r   r   ÚcfgO   s   
rA   c                   @   s   e Zd ZdZdS )ÚConfigValidationErrora:  Raised by validate_config() when config.json contains a value
    that can never be legitimate (e.g. a lookback window of 0 bars, a
    negative percentage). Collects every problem found, not just the
    first, so a single startup failure surfaces the whole list instead
    of a fix-one-rerun-find-the-next loop.N)r4   r5   r6   r7   r   r   r   r   rB   \   s    rB   )3)ÚuniverseÚ	price_min)rC   Ú	price_max)rC   Úmin_avg_daily_volume)Ú
candidatesÚpremarket_candidate_count)rG   Úfinal_candidate_count)ÚscheduleÚpoll_interval_seconds_intraday)rJ   Ú eod_liquidation_max_wait_seconds)Úpremarket_scoringÚmin_premarket_volume)rM   Úmin_rvol)rM   Úvwap_lookback_bars)rM   Úmomentum_lookback_snapshots)rM   Úacceleration_min_ratio)Úintraday_healthÚeval_interval_seconds)rS   Úslope_lookback_bars)rS   Ústructure_lookback_bars)rS   Ú
atr_period)rS   Úhealthy_confirm_reads)rS   Úunhealthy_confirm_reads)rS   Ú"remove_after_consecutive_unhealthy)rS   Úfull_rescan_interval_minutes)rS   Úfull_rescan_pool_size)rS   Úfull_rescan_lookback_hours)rS   Úentry_shortlist_size)rS   Ú volume_participation_rvol_target)rS   Ú%max_consecutive_confirmation_failures)rS   Ú%confirmation_failure_cooldown_seconds)ÚentryÚvwap_rising_lookback_bars)rb   Úopening_volume_expansion_ratio)rb   Úbreakout_hold_bars)rb   Úhigher_lows_lookback_bars)rb   Úmax_spread_pct_entry)rb   Úmax_extension_from_vwap_pct)rb   Úmin_confirmation_score)rb   Ú
rsi_period)rb   Úcancel_unfilled_after_seconds)ÚriskÚaccount_risk_pct_per_trade)rl   Ú#max_position_notional_pct_of_equity)rl   Ú
min_shares)rl   Úmax_daily_loss_pct)rl   Úmax_consecutive_losses_pause)ÚstopÚinitial_distance)rr   Útrailing_distance)rr   Úpercentage_initial_pct)rr   Úpercentage_trailing_pct)rr   rW   )rr   Úatr_multiplier_initial)rr   Úatr_multiplier_trailing)rr   Úmin_stop_distance_cents)rr   Úmax_stop_distance_pct)ÚtradingÚmax_positions)r{   Úsimulated_equity_default)rM   Úweights)rS   r~   c                 C   s6   | }|D ]}t |tƒr||vr dS || }q|dfS )N)NFT)r<   r=   )rA   r   r?   r   r   r   r   Ú	_get_path±   s   
r   c                 C   sP  | dur| nt ƒ } g }tD ]9}t| |ƒ\}}|sqt|ttfƒr%t|tƒr4| d |¡› d|›d�¡ q|dkrF| d |¡› d|›d�¡ qt	D ]K}t| |ƒ\}}|rYt|t
ƒsZqI| ¡ D ]5\}}d |¡d|› � }t|ttfƒrxt|tƒr„| |› d|›d�¡ q^|dk r“| |› d|›d�¡ q^qI|r¦tdt|ƒ› d	�d
 |¡ ƒ‚dS )aƒ  
    Fail-fast sanity check, run once at startup (see monitor.py's
    SessionOrchestrator.__init__ and dry_run_check.py). Raises
    ConfigValidationError listing every problem found if config.json
    contains a value that can never be legitimate. Deliberately narrow
    in scope -- see _STRICTLY_POSITIVE_PATHS's comment above for what
    this does and does not check, and why.
    NÚ.z = z is not a numberr   z must be > 0z must be >= 0zconfig.json has z invalid value(s):
  z
  )r    Ú_STRICTLY_POSITIVE_PATHSr   r<   ÚintÚfloatÚboolr/   r1   Ú_NONNEGATIVE_WEIGHT_DICTSr=   ÚitemsrB   Úlen)rA   Úproblemsr   r   Úpresentr?   r   Ú	full_pathr   r   r   Úvalidate_configº   s<   	€€ûÿÿÿr‹   r8   )r7   r   r   Úpathlibr   Ú	functoolsr   Ú__file__ÚresolveÚparentÚBASE_DIRr   ÚENV_PATHr   r=   r    r"   r#   r:   rA   Ú
ValueErrorrB   r�   r…   Útupler   r‹   r   r   r   r   Ú<module>   s0    	:þ	