
    i.                        U d Z ddlmZ ddlZddlZddlZddlZddlmZ ddl	m
Z
mZ ddlZ ej                  e      Z ed      j#                         Zddd	d
ddddddd
Zded<   dZddZ G d d      Zy)u  
APEX V18 — TradingView market-data provider.

Concrete MarketDataProvider impl backed by tvdatafeed-enhanced (StreamAlpha
fork), used as primary feed for indicators when env USE_TV_FEED=1.

Why TV instead of BrokerMarketDataProvider:
  - TopstepX bars on 4hour timeframe are sparse/gappy; TV has clean H4.
  - TV history depth covers calibration windows; TopstepX is intraday-only.
  - Decouples indicator pipeline from broker connectivity (broker can be
    down for orders without losing technical reads).

Wiring (main.py): opt-in via env USE_TV_FEED=1 + TV_USERNAME / TV_PASSWORD.
Auth optional — anonymous fallback works for shorter histories.

Failure modes (network down, auth expired, symbol unmapped, TV returns
None / empty) -> empty DataFrame matching the V16 contract, warning
logged. Caller skips the iteration (same contract as Fake/Broker).

DataFrame contract (consumer-side, see analysis.market_data docstring):
  columns [open, high, low, close, volume] + 'time' column (UTC),
  sorted ascending, last row = most recent bar.
    )annotationsN)Path)AnyCallablez~/.apex_v18/tv_token.json)MESCME_MINI)MNQr   )MYM	CBOT_MINI)MGCCOMEX)MCLNYMEX)6ECME)6Br   )6Ar   )6Jr   )6Cr   )
r   r	   r
   r   r   r   r   r   r   r   zdict[str, tuple[str, str]]TV_SYMBOL_MAP5min1hour4hourc                 V    ddl m}  | j                  | j                  | j                  dS )Nr   Intervalr   )
tvDatafeedr   in_5_minute	in_1_hour	in_4_hourr   s    0/home/work/apex_v16/analysis/tv_data_provider.py_build_interval_mapr#   K   s*    #%%####     c                      e Zd ZdZ	 	 ddddddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZddZddZddZ	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 dd	Z	e
dd
       Zy)TVDataFeedProvideruH  
    MarketDataProvider impl that fetches bars from TradingView via
    tvdatafeed-enhanced.

    Auth modes (preference order):
      1. token: TV auth_token extracted from browser. Pre-written to
         token_cache_file; lib's _load_token() picks it up and skips
         the username/password login flow. Survives CAPTCHA.
      2. username + password: lib attempts POST /signin. Subject to
         TradingView CAPTCHA challenges on datacenter IPs (the lib
         falls back to interactive browser-token paste — unsuitable
         for unattended runtime). Use only on residential IPs.
      3. None: anonymous mode. ~5000 bars/req cap, stricter rate
         limits, but sufficient for the V16 200-bar fetches.

    Args:
        username: TV account username, or None for anonymous access.
        password: TV account password, or None for anonymous access.
        token: TV auth_token (JWT). Pre-loaded into cache so the lib
            uses it instead of attempting interactive login.
        token_cache_file: where to pre-write the token. Defaults to
            ~/.apex_v18/tv_token.json (NOT the lib's ~/.tv_token.json,
            to avoid clobbering caches from other TV tools).
        symbol_map: override TV_SYMBOL_MAP (used by tests to inject
            unmapped/edge symbols).
        client_factory: callable (**kwargs) -> TV client. Receives
            username, password, token, token_cache_file. Tests inject
            a fake.
        interval_map_builder: callable () -> {timeframe: Interval}.
            Default: lazy import from tvdatafeed. Tests bypass the
            tvdatafeed import by passing a fake builder.

    The TV client is constructed lazily on the first get_bars call and
    cached for the provider lifetime (TvDatafeed maintains a websocket
    session internally; one instance per provider is correct).
    N)tokentoken_cache_file
symbol_mapclient_factoryinterval_map_builderc                   || _         || _        || _        |rt        |      j	                         nt
        | _        ||nt        | _        d | _	        || _
        |xs t        | _        d | _        y )N)usernamepasswordr'   r   
expanduserDEFAULT_TOKEN_CACHEr(   r   r)   _client_client_factoryr#   _interval_map_builder_interval_map)selfr-   r.   r'   r(   r)   r*   r+   s           r"   __init__zTVDataFeedProvider.__init__~   sr     ! 
   !"--/%8 	 )3(>*M -%9%P=P"48r$   c                   | j                   | j                   S | j                  r| j                          | j                  W| j                  | j                  | j
                  | j                  t        | j                              | _         | j                   S ddlm	} | j                  re |t        | j                              | _         | j                   j                  | j                  k7  rt        j                  d       | j                   S | j                  rO| j
                  rCt        j                  d        || j                  | j
                        | _         | j                   S  |       | _         | j                   S )N)r-   r.   r'   r(   r   )
TvDatafeed)r(   uY   TVDataFeed: TV_TOKEN rejected by validator (likely expired) — falling back to anonymouszTVDataFeed: using username/password login. TradingView CAPTCHA may force interactive prompts; prefer TV_TOKEN env var on server deployments.)r1   r'   _write_token_cacher2   r-   r.   strr(   r   r8   loggerwarning)r5   r8   s     r"   _get_clientzTVDataFeedProvider._get_client   s8   <<#<< ::##%+//jj!$T%:%:!;	 0 DLB ||5 .zz  *%()>)>%?  <<%%3NNI" || 4== E
  *$--G ||  *|||r$   c                6   	 | j                   j                  j                  dd       | j                   j                  t	        j
                  d| j                  i             y# t        $ r+}t        j                  d| j                   |       Y d}~yd}~ww xY w)zrWrite self.token to self.token_cache_file in the format the
        lib's _load_token() expects: {"token": "..."}.T)parentsexist_okr'   z1TVDataFeed: failed to write token cache to %s: %sN)
r(   parentmkdir
write_textjsondumpsr'   OSErrorr;   r<   )r5   excs     r"   r9   z%TVDataFeedProvider._write_token_cache   s    		!!((..td.K!!,,

GTZZ01  	NNC%%s 	s   A!A$ $	B-!BBc                    | j                   	 | j                         | _         | j                   j                  |      S # t        $ r'}t        j	                  d|       i | _         Y d }~Fd }~ww xY w)Nz\TVDataFeed: tvdatafeed-enhanced not installed (%s); set USE_TV_FEED=0 or install the package)r4   r3   ImportErrorr;   r<   get)r5   	timeframerG   s      r"   _get_intervalz TVDataFeedProvider._get_interval   sp    %(%)%?%?%A" !!%%i00  (?
 &(""(s   > 	A.A))A.c                8  K   t        j                  g d      }|| j                  vrt        j	                  d|       |S | j                  |      }|t        j	                  d|t               |S | j                  |   \  }}	 t        j                  | j                  ||||       d {   }||j                  rt        j	                  d||       |S | j                  |      S 7 <# t        $ r%}	t        j	                  d||||	       |cY d }	~	S d }	~	ww xY ww)Ntimeopenhighlowclosevolumecolumnsz;TVDataFeed: symbol %r not in TV_SYMBOL_MAP; returning emptyz9TVDataFeed: timeframe %r unsupported (expected one of %s)z*TVDataFeed.get_bars(%s, %s, %d) failed: %sz1TVDataFeed: no bars for %s %s (TV returned empty))pd	DataFramer)   r;   r<   rL   _TV_INTERVAL_KEYSasyncio	to_thread_fetch_sync	Exceptionempty
_normalize)
r5   symbolrK   nr^   tv_interval	tv_symboltv_exchangedfrG   s
             r"   get_barszTVDataFeedProvider.get_bars   s     F
 (NNM L((3NNK, L!%!8	;		((  )[+q B :NNC	 Lr""#  	NN<	1c L	sH   BD&C) *C'+C) /8D'C) )	D2DDDDDc                N    | j                         }|j                  ||||d      S )N   )r`   exchangeintervaln_barsfut_contract)r=   get_hist)r5   rc   rd   rb   ra   clients         r"   r\   zTVDataFeedProvider._fetch_sync  s7     !!#    
 	
r$   c                   | j                         }d|j                  v r|j                  dg      }|j                         }d|j                  v r|j	                  ddi      }n"d|j                  v r|j	                  ddi      }t        j                  |d   d      |d<   |j                  d      j                  d	      }|g d
   S )aL  
        Convert tvdatafeed output to V16 contract.

        TV format: DatetimeIndex (named 'datetime' or unnamed) +
            columns ['symbol', 'open', 'high', 'low', 'close', 'volume'].
        V16 format: columns ['time', 'open', 'high', 'low', 'close',
            'volume'], sorted ascending, time as UTC datetime.
        r`   rU   datetimerO   indexF)utcT)droprN   )copyrV   rs   reset_indexrenamerW   to_datetimesort_values)re   outs     r"   r_   zTVDataFeedProvider._normalize  s     ggis{{"((H:(.Coo$**j&%9*:C#**gv%6*7CnnS[e<Foof%11t1<EFFr$   )NN)r-   
str | Noner.   rz   r'   rz   r(   zstr | Path | Noner)   z!dict[str, tuple[str, str]] | Noner*   zCallable[..., Any] | Noner+   z#Callable[[], dict[str, Any]] | NonereturnNone)r{   r   )r{   r|   )rK   r:   r{   z
Any | None)r`   r:   rK   r:   ra   intr{   pd.DataFrame)
rc   r:   rd   r:   rb   r   ra   r}   r{   zpd.DataFrame | None)re   r~   r{   r~   )__name__
__module____qualname____doc__r6   r=   r9   rL   rf   r\   staticmethodr_    r$   r"   r&   r&   X   s   #N  $#9
 !.28<48DH99 9
 9 ,9 69 29 B9 
90*X1-#-# -# 	-#
 
-#^

 
 	

 
 

  G Gr$   r&   )r{   zdict[str, Any])r   
__future__r   rZ   rD   loggingospathlibr   typingr   r   pandasrW   	getLoggerr   r;   r/   r0   r   __annotations__rY   r#   r&   r   r$   r"   <module>r      s   0 #    	    			8	$ 67BBD  ! !-)   / YG YGr$   