
    iM                        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
 ddlmZ ddlmZmZ e
rdd	lmZ ddlZdd
lmZ ddlmZ  ej2                  e      ZdZdZ G d de      Zy)zATradingView live data feed with real-time streaming capabilities.    )annotationsN)datetime)TYPE_CHECKING)relativedelta   )
TvDatafeedInterval)CallableConsumerSeis2      c                  (    e Zd ZdZ G d de      Z	 	 	 d	 	 	 	 	 	 	 d fdZddZ	 d	 	 	 	 	 	 	 	 	 d fdZ	 d	 	 	 	 	 dd	Z		 d	 	 	 	 	 	 	 dd
Z
	 d	 	 	 	 	 ddZd fdZdej                  ddddf	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d fdZddZddZ xZS )TvDatafeedLivea  Live data feed for TradingView with real-time streaming.

    Extends TvDatafeed to provide real-time data streaming via threaded
    callback architecture. Users can monitor multiple symbol-exchange-interval
    sets (Seis) and register callback functions to process new data bars.

    Args:
        username: TradingView username (optional)
        password: TradingView password (optional)

    Example:
        >>> tvl = TvDatafeedLive(username='user', password='pass')
        >>> def callback(seis, data):
        ...     print(f"New bar: {data}")
        >>> seis = tvl.new_seis('ETHUSDT', 'BINANCE', Interval.in_1_hour)
        >>> consumer = seis.new_consumer(callback)
    c                       e Zd ZdZd fdZddZ	 	 	 	 	 	 	 	 ddZddZddZddZ	ddZ
dd fd	Zd fd
ZddZd fdZ fdZd fdZ xZS ) TvDatafeedLive._SeisesAndTriggerzInternal class to manage Seis objects and interval timing.

        Tracks symbol-exchange-interval sets and their expiry times
        for efficient polling of new data bars.
        c                   t         |           d| _        d| _        t	        j
                         | _        i dt        d      dt        d      dt        d	      d
t        d      dt        d      dt        d      dt        d      dt        d      dt        d      dt        d      dt        d      dt        d      dt        d      dt        d      dt        d      d t        d!      | _        y)"zInitialize interval tracker.FN1r   )minutes3   5r   15   30   45-   1H)hours2H   3H4H   1D)days1W)weeks1M)months3M6M   12M   )	super__init___trigger_quit_trigger_dt	threadingEvent_trigger_interruptrd_timeframes)self	__class__s    L/home/work/apex_v16/venv/lib/python3.12/site-packages/tvDatafeed/datafeed.pyr4   z)TvDatafeedLive._SeisesAndTrigger.__init__6   s3   G!&D*.D&/oo&7D# R] R]  R]  bn	 
 bn  bn  bqk  bqk  bqk  bqk  baj  bqk  bl  bl  bl   r}! D    c                    | j                         sy| j                         D cg c]  }|d   	 }}|j                          |d   S c c}w )z~Get the next closest expiry datetime.

            Returns:
                Next expiry datetime or None if empty
            Nr   r   )valuessort)r<   rA   interval_dt_lists      r>   _next_trigger_dtz1TvDatafeedLive._SeisesAndTrigger._next_trigger_dtR   sK     ;;=8<Ffq	FF!!##A&&  Gs   Ac                x    | D ]5  }|j                   |k(  s|j                  |k(  s#|j                  |k(  s3|c S  y)a  Get Seis by symbol, exchange, and interval.

            Args:
                symbol: Symbol name
                exchange: Exchange name
                interval: Time interval

            Returns:
                Matching Seis or None if not found
            N)symbolexchangeinterval)r<   rF   rG   rH   seiss        r>   get_seisz)TvDatafeedLive._SeisesAndTrigger.get_seis_   sA        KK6)MMX-MMX-K	 
 r?   c                   | j                   s| j                  j                          | j                         | _        | j                  y	 | j                  t        j                         z
  }| j                  j                  t        |j                         d            }|r| j                   ry|s| j                  j                          	 y)zWait until next interval expires.

            Returns:
                True after waiting, False if interrupted for shutdown
            Tr   F)
r5   r9   clearrD   r6   dtnowwaitmaxtotal_seconds)r<   	wait_timeinterrupteds      r>   rO   z%TvDatafeedLive._SeisesAndTrigger.waitv   s     %%''--/#446D' ,,rvvx7	"55::	//115 4#5#5 $++113 r?   c                    g }| j                         D ]I  \  }}t        j                         |d   k\  s!|j                  |       |d   | j                  |   z   |d<   K |S )zGet list of expired intervals and update their expiry times.

            Returns:
                List of interval strings that have expired
            r   )itemsrM   rN   appendr;   )r<   expired_intervalsrH   rA   s       r>   get_expiredz,TvDatafeedLive._SeisesAndTrigger.get_expired   sk     !#$(JJL G &668vay(%,,X6 &q	D,<,<X,F FF1IG
 %$r?   c                F    d| _         | j                  j                          y)z&Signal shutdown and interrupt waiting.TN)r5   r9   setr<   s    r>   quitz%TvDatafeedLive._SeisesAndTrigger.quit   s    !%D##'')r?   c                    t        d      )z%Clear all interval groups and Seises.zClear operation not supported)NotImplementedErrorr[   s    r>   rL   z&TvDatafeedLive._SeisesAndTrigger.clear   s    %&EFFr?   c                   | s!d| _         | j                  j                          |j                  j                  }|| j                         v r"t        |   |      d   j                  |       y|t        d      || j                  |   z   }| j                  ||g|g       | j                         x}| j                  k7  r"|| _        | j                  j                          yy)zAppend new Seis instance to tracker.

            Args:
                seis: Seis instance to add
                update_dt: Last update datetime (required for new intervals)
            Fr   Nz.Missing update datetime for new interval group)r5   r9   rL   rH   valuekeysr3   __getitem__rV   
ValueErrorr;   __setitem__rD   r6   rZ   )r<   rI   	update_dtinterval_keynext_update_dt
trigger_dtr=   s         r>   rV   z'TvDatafeedLive._SeisesAndTrigger.append   s     %*"''--/==..Ltyy{*#L1!4;;DA $$%UVV "+T-=-=l-K!K  /GH #'"7"7"99Jd>N>NN'1D$++//1 Or?   c                v   || vrt        d      |j                  j                  }t        |   |      d   j                  |       t        |   |      d   s`| j                  |       | j                         x}| j                  k7  r/| j                  s"|| _        | j                  j                          yyyy)zRemove Seis instance from tracker.

            Args:
                seis: Seis instance to remove

            Raises:
                KeyError: If Seis not in tracker
            zSeis not found in trackerr   N)KeyErrorrH   r`   r3   rb   removepoprD   r6   r5   r9   rZ   )r<   rI   rf   rh   r=   s       r>   discardz(TvDatafeedLive._SeisesAndTrigger.discard   s     4:;;==..LG-a077= 7&|4Q7& #'"7"7"99Jd>N>NNW[WiWi'1D$++//1 XjN	 8r?   c                "    | j                         S )zeGet list of interval groups.

            Returns:
                View of interval keys
            )ra   r[   s    r>   	intervalsz*TvDatafeedLive._SeisesAndTrigger.intervals   s     99;r?   c                (    t         |   |      d   S )zGet list of Seis for an interval.

            Args:
                interval_key: Interval string

            Returns:
                List of Seis instances
            r   )r3   rb   )r<   rf   r=   s     r>   rb   z,TvDatafeedLive._SeisesAndTrigger.__getitem__   s     7&|4Q77r?   c                ^    g }t         |          D ]
  }||d   z  } |j                         S )z Iterate over all Seis instances.r   )r3   rA   __iter__)r<   seises_list	seis_listr=   s      r>   rr   z)TvDatafeedLive._SeisesAndTrigger.__iter__   s;    K"W^- ,	y|+,''))r?   c                >    t         |          D ]  }||d   v s y y)zCheck if Seis is in tracker.

            Args:
                seis: Seis instance to check

            Returns:
                True if present, False otherwise
            r   TF)r3   rA   )r<   rI   rt   r=   s      r>   __contains__z-TvDatafeedLive._SeisesAndTrigger.__contains__   s/     #W^-  	9Q<'  r?   returnNone)rx   	dt | None)rF   strrG   r{   rH   r	   rx   zSeis | None)rx   bool)rx   z	list[str])N)rI   r   re   rz   rx   ry   )rI   r   rx   ry   )rx   zdict.KeysView)rf   r{   rx   z
list[Seis])rI   r   rx   r|   )__name__
__module____qualname____doc__r4   rD   rJ   rO   rX   r\   rL   rV   rm   ro   rb   rr   rv   __classcell__r=   s   @r>   _SeisesAndTriggerr   /   su    		8	'		 	 		
 	.	8	%	*
	G	2<	20			8	*	 	r?   r   Nc                    ddl m} t        |   |||       t	        j
                         | _        d| _        | j                         | _	        y)zInitialize live data feed.

        Args:
            username: TradingView username
            password: TradingView password
            token_cache_file: Path to cache authentication token
        r   )PathN)
pathlibr   r3   r4   r7   Lock_lock_main_threadr   _sat)r<   usernamepasswordtoken_cache_filer   r=   s        r>   r4   zTvDatafeedLive.__init__  s@     	!8-=>^^%
59**,	r?   c                b    | j                  ||      }|sy|D ]  }|d   |k(  s|d   |k(  s y y)zValidate symbol and exchange combination.

        Args:
            symbol: Symbol name
            exchange: Exchange name

        Returns:
            True if invalid, False if valid
        TrF   rG   F)search_symbol)r<   rF   rG   result_listitems        r>   _args_invalidzTvDatafeedLive._args_invalid  sK     ((: 	DH~'D,<,H	 r?   c                R   | j                  ||      rt        d| d| d      | j                  j                  |||      x}r|S ddlm}  ||||      }| j                  j                  |dkD  r|nd      sy		 | |_        || j                  v r7| j                  j                  |||      | j                  j                          S |j                  j                  }|| j                  j                         vrt        | 9  |j                  |j                   |j                  d
      }	|	t#        |	      dk(  rt        d| d|       |	j$                  j'                         d   }
| j                  j)                  ||
       n| j                  j)                  |       | j*                  At-        j.                  d| j0                  d      | _        | j*                  j3                          || j                  j                          S # | j                  j                          w xY w)a  Create and add new Seis to live feed.

        Args:
            symbol: Ticker symbol
            exchange: Exchange name
            interval: Time interval
            timeout: Maximum wait time in seconds (-1 for blocking)

        Returns:
            Seis instance (existing or new), or False if timeout

        Raises:
            ValueError: If symbol/exchange combination invalid
        zSymbol 'z' on exchange 'z' not found in TradingViewr   r   r   NtimeoutFr$   )n_barszFailed to get initial data for z on tvdatafeed_main_loopT)nametargetdaemon)r   rc   r   rJ   rI   r   r   acquire
tvdatafeedreleaserH   r`   ro   r3   get_histrF   rG   lenindexto_pydatetimerV   r   r7   Thread
_main_loopstart)r<   rF   rG   rH   r   rI   	SeisClassnew_seisrf   ticker_datare   r=   s              r>   r   zTvDatafeedLive.new_seis0  s   * fh/6(/(;UV 
 99%%fhAA4AK 	,VXx8zz!!Wq['d!K'	!"&H 499$yy))&(HED JJ ? $,,22L499#6#6#88#g.OO%%%%	 /  &#k*:a*?$9&hZP  (--;;=a@			  95		  *   ($-$4$4/??%!
 !!'') JJ DJJ s   <1H
 D'H
 
H&c                   || j                   vrt        d      | j                  j                  |dkD  r|nd      sy	 |j	                         D ]  }|j                  d        | j                   j                  |       |`| j                   s| j                   j                          	 | j                  j                          y# | j                  j                          w xY w)a  Remove Seis from live feed.

        Args:
            seis: Seis to remove
            timeout: Maximum wait time in seconds (-1 for blocking)

        Returns:
            True if successful, False if timeout

        Raises:
            ValueError: If Seis not in live feed
        Seis not in live feedr   Nr   FT)
r   rc   r   r   get_consumersputrm   r   r\   r   )r<   rI   r   consumers       r>   del_seiszTvDatafeedLive.del_seis~  s    " tyy 455zz!!Wq['d!K	! ..0 #T"# IId# 99		  JJ DJJ s   A)C C c                T   || j                   vrt        d      ddlm}  |||      }| j                  j                  |dkD  r|nd      sy	 |j                  |       |j                          || j                  j                          S # | j                  j                          w xY w)a  Create new Consumer for Seis with callback function.

        Args:
            seis: Seis to consume data from
            callback: Function(seis, data) to call with new data
            timeout: Maximum wait time in seconds (-1 for blocking)

        Returns:
            Consumer instance, or False if timeout

        Raises:
            ValueError: If Seis not in live feed
        r   r   r   r   Nr   F)	r   rc   r   r   r   r   add_consumerr   r   )r<   rI   callbackr   ConsumerClassr   s         r>   new_consumerzTvDatafeedLive.new_consumer  s    & tyy 455 	8 x0zz!!Wq['d!K	!h'NN JJ DJJ s   "B B'c                0   | j                   j                  |dkD  r|nd      sy	 |j                  |j                  j                  |       |j	                          	 | j                   j                          y# | j                   j                          w xY w)zRemove Consumer from its Seis.

        Args:
            consumer: Consumer to remove
            timeout: Maximum wait time in seconds (-1 for blocking)

        Returns:
            True if successful, False if timeout
        r   Nr   FT)r   r   rI   pop_consumerstopr   )r<   r   r   s      r>   del_consumerzTvDatafeedLive.del_consumer  st     zz!!Wq['d!K	!}}(**84MMO JJ DJJ s   7A9 9Bc                   | j                   j                         r}| j                  5  | j                   j                         D ]-  }| j                   |   D ]  }d}t	        t
              D ]  }	 t        |   |j                  |j                  |j                  d      }|@|j                  |      r/t        |      dkD  r|j                  |j                  d         } ncddt%        |t&              z  z  }t)        j*                  |        t         j-                  d|t
               | j                   j/                          ||j1                         D ]  }|j3                  |         0 	 ddd       | j                   j                         r}| j                  5  t5        | j                         D ]S  }|j1                         D ]#  }|j7                  |       |j9                          % | j                   j;                  |       U d| _        ddd       y# t        $ r+}t         j#                  d|dz   t
        ||       Y d}~nd}~ww xY w# 1 sw Y   xY w# 1 sw Y   yxY w)	a  Main loop for retrieving and distributing live data.

        Continuously monitors tracked Seises and retrieves new data bars
        when intervals expire. Distributes data to registered consumers.
        Implements exponential backoff retry logic.
        Nr$   )rH   r   r   )labelszAttempt %d/%d failed for %s: %sg?z@Failed to retrieve data for %s after %d attempts - shutting down)r   rO   r   rX   rangeRETRY_LIMITr3   r   rF   rG   rH   is_new_datar   dropr   	ExceptionloggerwarningminMAX_BACKOFFtimesleepcriticalr\   r   r   listr   r   rm   r   )	r<   rH   rI   dataattemptebackoffr   r=   s	           r>   r   zTvDatafeedLive._main_loop  s4    iinn /3 $		 5 5 7 .3H $		( 3 -3# (-['9 $%G"',w'7$(KK$(MM-1]]+,	 (8 (" $(#38H8H8N'*4y1}/3yy

1y/N$) '*Q#g{2K-K&LG JJw/5$%< #OO b $ +
 !IINN,$  +,0,>,>,@ 3 (T 23Y-3.3/3 iinnf ZZ 	%TYY ( $ 2 2 4 $H%%h/MMO$ 		!!$'( !%D	% 	%; $- " &$E$+aK$/$($%!" !""+/3 /3d	% 	%sK   AI0A1H"!A'I	,I&A3I%"	I+ IIIII"%I.NSE
   Fc                    | j                   j                  |dkD  r|nd      sy	 t        	|   ||||||      }|| j                   j	                          S # | j                   j	                          w xY w)a  Get historical data (thread-safe version).

        Args:
            symbol: Symbol name
            exchange: Exchange name
            interval: Time interval
            n_bars: Number of bars to fetch
            fut_contract: Futures contract number
            extended_session: Include extended hours
            timeout: Maximum wait time in seconds (-1 for blocking)

        Returns:
            DataFrame with OHLCV data, or False if timeout
        r   Nr   F)r   r   r3   r   r   )
r<   rF   rG   rH   r   fut_contractextended_sessionr   r   r=   s
            r>   r   zTvDatafeedLive.get_hist+  ss    0 zz!!Wq['d!K	!7# D  JJ DJJ s   A A3c                   	 | j                   j                  d      r6	 | j                  j                          | j                   j	                          n| j                  j                          | j
                  | j
                  j                  d       yy# | j                   j	                          w xY w# t        $ r }t        j                  d|       Y d}~yd}~ww xY w)z!Cleanup when object is destroyed.F)blockingNr   r   zError during cleanup: %s)
r   r   r   r\   r   r   joinr   r   debug)r<   r   s     r>   __del__zTvDatafeedLive.__del__T  s    	8zz!!5!1)IINN$JJ&&( 		   ,!!&&q&1 - JJ&&(  	8LL3Q77	8s.   B7 B AB7 B44B7 7	C  CC c                >    | j                   | j                          yy)z%Explicitly stop and delete live feed.N)r   r   r[   s    r>   del_tvdatafeedzTvDatafeedLive.del_tvdatafeedf  s    (LLN )r?   )NNz~/.tv_token.json)r   
str | Noner   r   r   z
str | Pathrx   ry   )rF   r{   rG   r{   rx   r|   )r   )
rF   r{   rG   r{   rH   r	   r   intrx   zSeis | bool)rI   r   r   r   rx   r|   )rI   r   r   r
   r   r   rx   zConsumer | bool)r   r   r   r   rx   r|   rw   )rF   r{   rG   r{   rH   r	   r   r   r   z
int | Noner   r|   r   r   rx   zpd.DataFrame | bool)r}   r~   r   r   dictr   r4   r   r   r   r   r   r   r	   in_dailyr   r   r   r   r   s   @r>   r   r      s   $VD Vt  $#'9	-- - %	-
 
-(4 L!L! L! 	L!
 L! 
L!b '!'! '! 
	'!Z 	#!#! #! 	#!
 
#!P !! ! 
	!6B%N %..#'!&'!'! '! 	'!
 '! !'! '! '! 
'!R8$r?   r   )r   
__future__r   loggingqueuer7   r   r   rM   typingr   dateutil.relativedeltar   r:   mainr   r	   collections.abcr
   pandaspdr   r   rI   r   	getLoggerr}   r   r   r   r    r?   r>   <module>r      s\    G "     #   6 &("			8	$M	Z M	r?   