Ë
    /¾ðiÞ  ã                  ót   — d Z ddlmZ ddlmZmZ ddlZe G d„ de«      «       Z G d„ d«      Z	 G d	„ d
«      Z
y)u9  
APEX V16 â€” Market data provider.

The indicators pipeline (analysis/tech_snapshot.py) needs OHLCV bars
to compute RSI, ATR, swing levels, candle patterns, etc.

Rather than coupling indicators to TopstepX (or any specific broker),
we depend on the MarketDataProvider Protocol â€” an async source of
pandas DataFrames. Concrete implementations:

  * BrokerMarketDataProvider: forwards to broker.fetch_bars(...).
    NB: BrokerBase does NOT currently expose fetch_bars; the wrapper
    is duck-typed and will work as soon as the broker layer adds the
    method (TODO tracked in BACKLOG.md). Until then, use the Fake.

  * FakeMarketDataProvider: returns canned DataFrames keyed by
    (symbol, timeframe). Used by all unit tests so we never hit a
    network in CI.

DataFrame contract (consumer-side):
  - columns: open, high, low, close, volume
  - 'time' column or DatetimeIndex (used for candle_time stamping)
  - sorted ascending by time, last row = most recent bar
  - V15 used 'tick_volume'; the broker adapter aliases it to 'volume',
    so V16 standardizes on 'volume'.
é    )Úannotations)ÚProtocolÚruntime_checkableNc                  ó(   — e Zd ZdZ	 	 	 	 	 	 	 	 dd„Zy)ÚMarketDataProviderz>Async provider of OHLCV bars. Stateless from the caller's POV.c              ƒ  ó   K  — y­w)ae  
        Return the last `n` bars for `symbol` at `timeframe`.

        Args:
            symbol: instrument root (e.g. "MES", "6E").
            timeframe: V16 standard strings: "5min" | "1hour" | "4hour".
            n: number of most-recent bars requested.

        Returns:
            DataFrame with columns [open, high, low, close, volume]
            and either a 'time' column (UTC) or a DatetimeIndex.
            len(df) <= n; may be smaller near session boundaries.
            Empty DataFrame returned when no bars are available
            (callers should treat empty == "skip computation").
        N© ©ÚselfÚsymbolÚ	timeframeÚns       ú+/home/work/apex_v16/analysis/market_data.pyÚget_barszMarketDataProvider.get_bars'   s   è ø€ ð* 	ùs   ‚N©r   Ústrr   r   r   ÚintÚreturnzpd.DataFrame)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r	   ó    r   r   r   #   s0   „ áHðàðð ðð ð	ð
 
ôr   r   c                  ó0   — e Zd ZdZdd„Z	 	 	 	 	 	 	 	 dd„Zy)ÚBrokerMarketDataProvideraÇ  
    Wrap a broker that exposes `async fetch_bars(symbol, timeframe, n)`.

    BrokerBase does not yet declare fetch_bars; this class therefore
    relies on duck-typing. When BACKLOG item "broker_base.fetch_bars"
    is implemented, this becomes a typed forwarder.

    Until then, instantiating it against a BrokerBase that lacks
    fetch_bars will raise AttributeError on the first get_bars call
    (intentional fail-loud, per V16 architecture).
    c                ó   — || _         y ©N)Úbroker)r   r   s     r   Ú__init__z!BrokerMarketDataProvider.__init__P   s	   € Øˆr   c              ƒ  óX   K  — | j                   j                  |||«      ƒ d {  –—† S 7 Œ­wr   )r   Ú
fetch_barsr
   s       r   r   z!BrokerMarketDataProvider.get_barsS   s'   è ø€ ð —[‘[×+Ñ+¨F°I¸qÓA×AÐAÐAús   ‚!*£(¤*N)r   ÚNoner   ©r   r   r   r   r   r   r	   r   r   r   r   C   s<   „ ñ
óðBàðBð ðBð ð	Bð
 
ôBr   r   c                  ó0   — e Zd ZdZdd„Z	 	 	 	 	 	 	 	 dd„Zy)ÚFakeMarketDataProvideruÎ  
    Canned-bars provider for unit tests.

    Usage:
        bars = {("MES", "5min"): df_m5,
                ("MES", "1hour"): df_h1,
                ("MES", "4hour"): df_h4}
        provider = FakeMarketDataProvider(bars)
        await provider.get_bars("MES", "5min", 200)

    If a (symbol, timeframe) key is missing, get_bars returns an
    empty DataFrame â€” same contract as a broker mid-session gap.
    Tracks calls in `self.calls` for assertions.
    c                ó    — || _         g | _        y r   )ÚbarsÚcalls)r   r'   s     r   r   zFakeMarketDataProvider.__init__q   s   € ØˆŒ	Ø13ˆ
r   c              ƒ  ó  K  — | j                   j                  |||f«       | j                  j                  ||f«      }||j                  rt        j                  g d¢¬«      S |j                  |«      j                  d¬«      S ­w)N)ÚopenÚhighÚlowÚcloseÚvolume)ÚcolumnsT)Údrop)	r(   Úappendr'   ÚgetÚemptyÚpdÚ	DataFrameÚtailÚreset_index)r   r   r   r   Údfs        r   r   zFakeMarketDataProvider.get_barsu   so   è ø€ ð 	
‰
×Ñ˜6 9¨aÐ0Ô1ØY‰Y]‰]˜F IÐ.Ó/ˆØˆ:˜ŸšÜ—<‘<Ò(RÔSÐSàw‰wq‹z×%Ñ%¨4Ð%Ó0Ð0ùs   ‚BBN)r'   z#dict[tuple[str, str], pd.DataFrame]r   r"   r   r#   r	   r   r   r%   r%   a   s7   „ ñó4ð1àð1ð ð1ð ð	1ð
 
ô1r   r%   )r   Ú
__future__r   Útypingr   r   Úpandasr4   r   r   r%   r	   r   r   ú<module>r<      sF   ðñõ6 #ç .ã ð ô˜ó ó ð÷>Bñ B÷<1ò 1r   