
    zCj                        d Z ddlZddlZddlmZmZmZ ddlmZmZm	Z	 ddl
mZmZ dedefdZd	edefd
Zd	edefdZ	 dd	edee         deeee	f                  fdZdedededeeee	f                  fdZd	edefdZdS )uG  
Params storage layer.

Layout on disk:
    {PARAMS_DATA_DIR}/{canonical_symbol}/{trade_week_start}.json

Each file is a JSON object:
{
  "symbol": "USDJPY",
  "trade_week_start": "2026-06-26",
  "trade_week_end":   "2026-07-03",
  "published_at":     "2026-06-29T21:59:34Z",
  "params": { ... arbitrary key/value pairs, passthrough to the EA ... }
}

trade_week_start/trade_week_end are plain ISO dates (YYYY-MM-DD), matching
the "Trade week: 2026-06-26 -> 2026-07-03" convention already used in your
WalkForward_NextWeek_*.txt output — deliberately reusing that exact
convention rather than inventing a different week-boundary definition
(e.g. a generic Sunday-00:00-UTC rule), since your Python walk-forward
pipeline already computes this correctly per-instrument and that's the
one source of truth that should decide what "this week" means.

Selection rule when asked "what are the current/coming params for X":
  1. If a published file's [trade_week_start, trade_week_end) window
     contains today -> that's the answer.
  2. Else, if there's a published file whose trade_week_start is in the
     future -> the soonest such file is the answer (covers "asked on
     Friday for the week that starts that same Friday" and the 24/7
     instrument case of "got new params sometime Sunday, can start using
     them immediately").
  3. Else (nothing current, nothing upcoming) -> NOT_AVAILABLE. The EA's
     job at that point is to keep using its own last cached params and
     retry the server periodically — this layer just reports "no",
     it doesn't decide what the EA does with that.

This module deliberately does NOT decide "is this too old to trust" by
itself beyond the MAX_PARAMS_AGE_DAYS sanity backstop in config — actual
freshness/business logic about old-vs-new boundaries belongs in the route
handler, not buried in the storage layer.
    N)datedatetime	timedelta)OptionalDictAny)PARAMS_DATA_DIRMAX_PARAMS_AGE_DAYSsreturnc                 P    t          j        | d                                          S )Nz%Y-%m-%d)r   strptimer   )r   s    $/wd/td-params-server/params_store.py_parse_iso_dater   3   s!    Q
++00222    canonical_symbolc                     d                     d | D                       }t          j                             t          |          S )N c              3   J   K   | ]}|                                 s|d v |V  dS )z._-N)isalnum).0cs     r   	<genexpr>z_symbol_dir.<locals>.<genexpr>;   s3      LL!))++Le1LLr   )joinospathr	   )r   safes     r   _symbol_dirr   7   s;     77LL.LLLLLD7<<...r   c                 |   t          |           }t          j                            |          sg S g }t          j        |          D ]}|                    d          st          j                            ||          }	 t          |dd          5 }t          j	        |          }ddd           n# 1 swxY w Y   t          |d                   }t          |d                   }n## t          $ r t          d| d	           Y w xY w|                    |||f           |                    d
            |S )zReturns list of (trade_week_start: date, trade_week_end: date, filepath: str)
    for every published file for this symbol, sorted by trade_week_start.z.jsonrutf-8encodingNtrade_week_starttrade_week_endz([params_store] WARNING: could not parse z
, skippingc                     | d         S )Nr    )ts    r   <lambda>z&list_published_weeks.<locals>.<lambda>X   s
    qt r   )key)r   r   r   isdirlistdirendswithr   openjsonloadr   	Exceptionprintappendsort)	r   dresultsfnamefpathfhdocstartends	            r   list_published_weeksr=   ?   s    	$%%A7== 	GA , ,~~g&& 	Q&&
	eS7333 $rimm$ $ $ $ $ $ $ $ $ $ $ $ $ $ $#C(:$;<<E!#&6"788CC 	 	 	 NUNNNOOOH	 	sE*++++LL^^L$$$Ns6    C)B3'C)3B7	7C):B7	;-C))D	D	todayc                 
   t          j                    t          |           }|sdS |D ])\  }}}|cxk    r|k     rn t          ||          c S *fd|D             }|r|d         \  }}}t          ||          S dS )z{Returns the full JSON doc (dict) for the active or soonest-upcoming
    trade week, or None if nothing usable is published.Nc                 ,    g | ]}|d          k    |S )r   r'   )r   wr>   s     r   
<listcomp>z2get_current_or_upcoming_params.<locals>.<listcomp>n   s"    111aAaD5LLLLLr   r   )r   r>   r=   _load_and_sanity_check)r   r>   weeksr;   r<   r8   upcomings    `     r   get_current_or_upcoming_paramsrF   \   s    
 }
 !122E t # = =sEEC)%e<<<<<   21115111H 9$QKsE%eS%8884r   r8   r%   c                     ||z
  t          t                    k    rt          d|  d           d S t          | dd          5 }t	          j        |          cd d d            S # 1 swxY w Y   d S )N)daysz[params_store] WARNING: zB is stale beyond MAX_PARAMS_AGE_DAYS, refusing to serve as currentr    r!   r"   )r   r
   r2   r.   r/   r0   )r8   r%   r>   r9   s       r   rC   rC   v   s     	)1D"E"E"EEE C C C C 	D 	D 	Dt	eS7	+	+	+ ry}}                 s   A%%A),A)c                 Z    t           j                            t          |                     S )u   Distinguishes 'this symbol is simply unknown to us' from 'symbol is
    known but nothing is published yet' — lets the API return a more
    useful error code (UNKNOWN_SYMBOL vs NOT_AVAILABLE).)r   r   r+   r   )r   s    r   symbol_has_any_datarJ      s!     7==%566777r   )N)__doc__r/   r   r   r   r   typingr   r   r   configr	   r
   strr   r   listr=   rF   rC   boolrJ   r'   r   r   <module>rQ      s  ( (T  				 . . . . . . . . . . & & & & & & & & & & 7 7 7 7 7 7 7 73s 3t 3 3 3 3/# /# / / / /3 4    < 48 "*4.d38n   4 $-1d38n    8# 8$ 8 8 8 8 8 8r   