
    U>qjrl                        d dl Z d dl mZ d dlZd dlZddlmZmZmZm	Z	 d dl
mZmZ d dlmZ d dlZddlmZmZ ddlmZ dd	lmZ d dlZdd
lmZmZmZ d ZdZd Zd Z G d de      Z  G d de       Z!dZ"ddddZ# G d d      Z$y)    N)	lru_cache   )requestsnew_sessionis_supported_session
cookie_jar)urlspliturljoin)BeautifulSoup)utilscache)
frozendict)YfConfig)YFExceptionYFDataExceptionYFRateLimitErrorc                     t        | t        t        j                  t        f      ryt        |       j                  }h d}||v S )zDCheck if error is transient (network/timeout) and should be retried.T>   TimeoutReadTimeoutTimeoutErrorConnectTimeoutConnectionErrorRemoteDisconnectedChunkedEncodingError)
isinstancer   socketerrorOSErrortype__name__)	exceptionerror_type_nametransient_error_typess      L/opt/rentech/trading_bot/.venv/lib/python3.12/site-packages/yfinance/data.py_is_transient_errorr%      s?    )lFLL'BC9o..O 333    @   c                 0    t        | t              r| | dS | S )N)httphttps)r   str)proxys    r$   _normalize_proxyr-   !   s    %..Lr&   c                      t        j                          fd       } j                  |_         j                  |_        |S )z
    Decorator transforms mutable dictionary and list arguments into immutable types
    Needed so lru_cache can cache method calls what has dict or list arguments.
    c            	         t        | D cg c]  }t        |t              rt        |      n|! c}      } |j	                         D ci c]#  \  }}|t        |t              rt        |      n|% }}}t        | D cg c]  }t        |t
              rt        |      n|! c}      } |j	                         D ci c]#  \  }}|t        |t
              rt        |      n|% }}} | i |S c c}w c c}}w c c}w c c}}w N)tupler   dictr   itemslist)argskwargsargkvfuncs        r$   wrappedz%lru_cache_freezeargs.<locals>.wrapped-   s    SWXCC)>joCGXYMS\\^\TQ!jD&9Z]q@\\dSsJsD$9eCjsBSTHNW1!At!4U1X!;WWT$V$$	 Y\SWs   $C.	(C3=$C9;(C>)	functoolswraps
cache_infocache_clear)r:   r;   s   ` r$   lru_cache_freezeargsr@   '   sA     __T% % G**GNr&   c                   H     e Zd ZdZi Z ej                         Z fdZ xZ	S )SingletonMetaz6
    Metaclass that creates a Singleton instance.
    c                 X   | j                   5  | | j                  vrt        |   |i |}|| j                  | <   nLd|v s|rFt	        |      dkD  r8d|v r|j                  d      n|d   }| j                  |    j                  |       | j                  |    cd d d        S # 1 sw Y   y xY w)Nsessionr   )_lock
_instancessuper__call__lenget_set_session)clsr5   r6   instancerD   	__class__s        r$   rH   zSingletonMeta.__call__C   s    YY 		'#..( 7+T<V<&.s# &4CIM7@F7Jfjj3PTUVPWGNN3'44W=>>#&		' 		' 		's   BB  B))
r    
__module____qualname____doc__rF   	threadingLockrE   rH   __classcell__)rN   s   @r$   rB   rB   <   s'     JINNE
' 
'r&   rB   c                   J   e Zd ZdZddZd Zd Zd ZddZe	j                  d        Ze	j                  d	        Ze	j                  dd
       Ze	j                  dd       Ze	j                  d        Ze	j                  d        Ze	j                  dd       Ze	j                  dd       Ze	j                  dd       Ze	j                  d d       Ze	j                  d d       Ze ee      dd              ZddZdedefdZdej>                  de dej>                  fdZ!y)!YfDataz
    Have one place to retrieve data from Yahoo API in order to ease caching and speed up operations.
    Singleton means one session one cookie shared by all threads.
    Nc                     d | _         d | _        d| _        d| _        t	        j
                         | _        d| _        d | _        | j                  |xs
 t                      y )NFbasic)_crumb_cookie
_logged_in_cookie_strategyrR   rS   _cookie_lockfundamentals_use_chunked_sessionrK   r   selfrD   s     r$   __init__zYfData.__init__V   sZ    
   !( &NN, /4%'2[]3r&   c                     | j                   5  | j                  j                  j                  ||d       d| _        d| _        d | _        d d d        y # 1 sw Y   y xY w)N)TYT)r]   r_   cookiesupdaterZ   r[   rY   )ra   cookie_tcookie_ys      r$   set_login_cookieszYfData.set_login_cookieso   s^     	MM!!((*   DL #DO
 DK	 	 	s   >AAc                 T    | j                   5  || _        ddd       y# 1 sw Y   yxY w)a!  Thread-safe update of the login flag (also read under this lock in
        _set_cookie_strategy). Auth calls this after verifying the real login
        state, possibly from another thread, so the write must be serialized to
        avoid a stale value clobbering a concurrent fresh login.N)r]   r[   )ra   values     r$   _set_logged_inzYfData._set_logged_in   s)    
  	$#DO	$ 	$ 	$s   'c                 h   |y t        |dd       t        d      t        |      st        dt        |       d      | j                  5  || _        t        j                  j                  2t        t        j                  j                        | j
                  _
        d d d        y # 1 sw Y   y xY w)Nr   znCaching sessions (e.g. requests_cache) are not supported. Solution: stop setting session, let yfinance handle.zUnsupported session type z^; expected curl_cffi or requests Session. Solution: stop setting session, let yfinance handle.)getattrr   r   r   r]   r_   r   networkr,   r-   proxiesr`   s     r$   rK   zYfData._set_session   s    ? 7GT*6!  #S  T  T#G,!$=d7m_  Mk  #l  m  m 	Q#DM%%1(89I9I9O9O(P%	Q 	Q 	Qs   AB((B1c                 d   || j                   k(  ry |s| j                  j                          	 | j                   dk(  rit        j                         j                  d| j                    d       | j                  s$| j                  j                  j                          d| _         n8t        j                         j                  d| j                    d       d| _         d | _
        d | _        |s| j                  j                          y y # t        $ r | j                  j                           w xY w)Ncsrfztoggling cookie strategy z	 -> basicrX   z -> csrf)r\   r]   acquirer   get_yf_loggerdebugr[   r_   rf   clearrZ   rY   	Exceptionrelease)ra   strategy	have_locks      r$   _set_cookie_strategyzYfData._set_cookie_strategy   s   t,,,%%'	$$.##%++.GH]H]G^^g,hi
 MM))//1(/%##%++.GH]H]G^^f,gh(.%DLDK
 %%' 	  	%%'	s   B>D
 
%D/c                    | j                   yt        | j                         j                  }t        |      dk(  ry|j	                         D cg c]	  }d|v s| }}t        |      dkD  r|D cg c]	  }d|vs| }}t        |      dkD  r't        j                         j                  d|        yt        |      dk(  ry|d   }|||   i}t        j                         j                  d|       yc c}w c c}w )	NFr   yahoor   consentz1Multiple Yahoo cookies, not sure which to cache: curlCffiT)r_   r   _cookiesrI   keysr   ru   rv   r   get_cookie_cachestore)ra   rf   r8   
yh_domains	yh_domain	yh_cookies         r$   _save_cookie_curlCffizYfData._save_cookie_curlCffi   s    == T]]+44w<1!(@A7a<a@
@z?Q%/F9A3E!FJFz?Q!''*[\f[g(hiz?aqM		 23	 &&z9= A Gs   	C5C50	C::C:c                    | j                   yt        j                         j                  d      }|t	        |      dk(  ry|d   }t        |j                               d   }||   d   d   }|j                  }|dkD  r|dz  }t        j                  j                  |t        j                  j                  	      }|t        j                  j                  t        j                  j                        k  }|r$t        j                         j                  d
       yt!        | j                         j"                  j%                  |       || _        y)NFr   r   cookie/A3g    eAg     @@)tzzcached cookie expiredT)r_   r   r   lookuprI   r4   r   expiresdatetimefromtimestamptimezoneutcnowr   ru   rv   r   r   rg   rZ   )ra   cookie_dictrf   domainr   	expiry_ts	expiry_dtexpireds           r$   _load_cookie_curlCffizYfData._load_cookie_curlCffi   s   == ,,.55jA#k"2a"7h'glln%a(%d+NN	s?#I%%33I(BSBSBWBW3X	h//33H4E4E4I4IJJ!''(?@4==!**11':r&   c                    | j                   $t        j                         j                  d       y| j	                         r$t        j                         j                  d       y	 | j
                  j                  d|d       | j                          y# t        j                  j                  $ r9}t        j                         j                  dt        |      z          Y d }~yd }~ww xY w)Nreusing cookieTreusing persistent cookiezhttps://fc.yahoo.comurltimeoutallow_redirectsz$Handling DNS error on cookie fetch: F)rZ   r   ru   rv   r   r_   rJ   r   
exceptionsDNSErrorr+   r   )ra   r   es      r$   _get_cookie_basiczYfData._get_cookie_basic   s    <<#!''(89'')!''(CD		MM* $  & 	""$ ""++ 	 !''(NQTUVQW(WX		s   &B C+2/C&&C+c                    | j                   /t        j                         j                  d       | j                   S | j	                         sy d|dd} | j
                  j                  di |}|j                  | _         |j                  dk(  sd| j                   v r:t        j                         j                  d| j                           t               | j                   d| j                   v r$t        j                         j                  d	       y t        j                         j                  d
| j                    d       | j                   S )Nreusing crumbz1https://query1.finance.yahoo.com/v1/test/getcrumbTr     Too Many RequestsDidn't receive crumb <html>Didn't receive crumb	crumb = '' )
rY   r   ru   rv   r   r_   rJ   textstatus_coder   )ra   r   get_argscrumb_responses       r$   _get_crumb_basiczYfData._get_crumb_basic   s   ;;"!''8;;%%' G#

 +**6X6$))%%,0Ct{{0R!''*?}(MN"$$;;(dkk"9!''(>?##i}A$>?{{r&   c                 H    | j                  |      sy | j                  |      S r0   )r   r   )ra   r   s     r$   _get_cookie_and_crumb_basicz"YfData._get_cookie_and_crumb_basic  s$    %%g.$$W--r&   c                    | j                   $t        j                         j                  d       y| j	                         r+t        j                         j                  d       d| _         yd|i}i |ddi}	  | j
                  j                  di |}t        |j                  d	      }|j                  d
ddi      }|$t        j                         j                  d       y|d   }t        j                         j                  d|        |j                  d
ddi      }|d   }	t        j                         j                  d|	        d}
d}ddgd|	||
|d}i |d|	 |d}i |d|	 |d}	  | j
                  j                  di |  | j
                  j                  di | d| _         | j                          y# t        j                  j                  $ r& t        j                         j                  d       Y yw xY w# t        j                  j                  $ r& t        j                         j                  d       Y w xY w)Nr   Tr   r   r   zhttps://guce.yahoo.com/consentzR_get_cookie_csrf() encountering requests.exceptions.ChunkedEncodingError, abortingFhtml.parserinputname	csrfToken)attrsz&Failed to find "csrfToken" in responserl   zcsrfToken = 	sessionIdzsessionId='zhttps://finance.yahoo.com/r~   agreedefault)r   consentUUIDr   r   originalDoneUrl	namespacez6https://consent.yahoo.com/v2/collectConsent?sessionId=)r   dataz-https://guce.yahoo.com/copyConsent?sessionId=r   )rZ   r   ru   rv   r   r_   rJ   r   r   r   r   contentfindpostr   )ra   r   	base_argsr   responsesoupcsrfTokenInputr   sessionIdInputr   r   r   r   	post_argss                 r$   _get_cookie_csrfzYfData._get_cookie_csrf  sm   <<#!''(89'')!''(CDDL w 	 JiI(HI	(t}}((484H X--}=76;2GH!!''(PQ"7+	##l9+$>?76;2GH"7+	##k)$=>6	w'$""."
y KI;W	i B9+N	~DMM++DMM)) ""$Q ""77 	!''(|}	F ""77 	~!''(|}	~s&   9G	 98H 	A HHA IIc                    | j                   /t        j                         j                  d       | j                   S | j	                  |      sy d|d} | j
                  j                  di |}|j                  | _         |j                  dk(  sd| j                   v r:t        j                         j                  d| j                           t               | j                   d| j                   v s| j                   dk(  r$t        j                         j                  d	       y t        j                         j                  d
| j                    d       | j                   S )Nr   z1https://query2.finance.yahoo.com/v1/test/getcrumb)r   r   r   r   r   r    r   r   r   r   )
rY   r   ru   rv   r   r_   rJ   r   r   r   )ra   r   r   rs       r$   _get_crumb_csrfzYfData._get_crumb_csrfU  s#    ;;"!''8;;$$W- G  DMM))ff==C#6$++#E!''*?}(MN"$$;;(dkk"9T[[B=N!''(>?##i}A$>?{{r&   c                    d\  }}t        j                         j                  d| j                   d       | j                  5  | j                  dk(  r7| j                         }|[| j                  dd       | j                  |      }n6| j                  |      }|#| j                  dd       | j                         }| j                  }d d d        ||fS # 1 sw Y   ||fS xY w)N)NNzcookie_mode = 'r   rs   rX   T)r{   )r   ru   rv   r\   r]   r   r|   r   )ra   r   crumbrz   s       r$   _get_cookie_and_crumbzYfData._get_cookie_and_crumbr  s    $x##od6K6K5LA$NO 	-$$.,,.=--g-F <<WEE 88A=--f-E 002E,,H	- h	- hs   B	CC$c                     | j                  || j                  j                  ||      }| j                  |j                        s	 |S | j                  ||      }|S )N)request_methodparamsr   )_make_requestr_   rJ   _is_this_consent_urlr   _accept_consent_formra   r   r   r   r   s        r$   rJ   z
YfData.get  s^    %%cDMM<M<MV\fm%n ((6
  007CHr&   c                 X    | j                  || j                  j                  ||||      S )N)r   bodyr   r   r   )r   r_   r   )ra   r   r   r   r   r   s         r$   r   zYfData.post  s8    !!#8J8JQU^dnu  }A!  B  	Br&   c                    t        |      dkD  r+t        j                         j                  d|d d  d       n&t        j                         j                  d|        t        j                         j                  d|        t	        t
        j                  j                        | j                  _	        |i }d|v rt        d      | j                         \  }}|d|i}	ni }	|i ||	|d}
|r||
d<   |r||
d	<   d
di|
d<   t        t
        j                  j                  dz         D ]  }	  |di |
} n t        j                         j                  dj$                          |j$                  dk\  r|dk(  r| j'                  d       n| j'                  d       | j                  |      \  }}||
d   d<    |di |
}t        j                         j                  d|j$                          |j$                  dk(  r
t)               |S # t        $ rM}t        |      r6|t
        j                  j                  k  rt!        j"                  d|z         n Y d }~6d }~ww xY w)N   zurl=z...zparams=r   z@Don't manually add 'crumb' to params dict, let data.py handle it)r   r   r   jsonr   zContent-Typezapplication/jsonheadersr      zresponse code=i  rX   rs   r   r   r   )rI   r   ru   rv   r-   r   rp   r,   r_   rq   r   r   rangeretriesrx   r%   _timesleepr   r|   r   )ra   r   r   r   r   r   r   r   rz   crumbsrequest_argsattemptr   r   s                 r$   r   zYfData._make_request  sg    s8c>!''$s4Cyk(=>!''$se5##gfX$67 !11A1A1G1G H>Ff`aa446xu%FF **6*
 #'L #'L '57I&JL#X--559: 	G)9L9	 	##nX5I5I4J$KL3&7"))&1))'2"88AOE8.3L"7+%55H!''.9M9M8N(OP ##s*&((+  &q)g8H8H8P8P.PKKW- .s   0H	I'AI""I')maxsizec                 (    | j                  |||      S r0   )rJ   )ra   r   r   r   s       r$   	cache_getzYfData.cache_get  s     xxVW--r&   c                     t        j                         j                  d|        | j                  |||      }|j	                          |j                         S )Nzget_raw_json(): )r   r   )r   ru   rv   rJ   raise_for_statusr   r   s        r$   get_raw_jsonzYfData.get_raw_json  sM    ##&6se$<=88C8@!!#}}r&   response_urlreturnc                     	 t        |      j                  xr$ t        |      j                  j                  d      S # t        $ r Y yw xY w)z
        Check if given response_url is consent page

        Args:
            response_url (str) : response.url
    
        Returns:
            True : This is cookie-consent page
            False : This is not cookie-consent page
        zconsent.yahoo.comF)r	   hostnameendswithrx   )ra   r   s     r$   r   zYfData._is_this_consent_url  sL    	L)22 5x8hxx 345  		s   := 	A	A	consent_respr   c                    t        |j                  d      }|j                  d      }|s|S |j                  d      xs |j                  }t        |j                  |      }	 i }|j                  d      D ]  }|j                  d      }|s|j                  d      xs dj                         }	|j                  d      xs d	}
|	d
v rCd|j                         v s$d|j                         v s|j                  d      s|
d	k7  r|
nd||<   |
||<    |j                         D ch c]  }|j                          }}t        d |D              sd|d<   d|j                  i}| j                  j                  ||||d      }|S c c}w )a  
        Click 'Accept all' to cookie-consent form and return response object.

        Args:
            consent_resp (requests.Response) : Response instance of cookie-consent page
            timeout (int) : Raise TimeoutError if post doesn't respond
    
        Returns:
            response (requests.Response) : Response instance received from the server after accepting cookie-consent post.
        r   formactionr   r   r   r   rl   r   )checkboxradior   acceptchecked1c              3   0   K   | ]  }d |v xs d|v   yw)r   r   Nr   ).0r8   s     r$   	<genexpr>z.YfData._accept_consent_form.<locals>.<genexpr>,  s      DqGqL1HM1Ds   RefererT)r   r   r   r   )r   r   r   rJ   r   r
   find_alllowerhas_attrr   anyr_   r   )ra   r   r   r   r   r   r   inpr   typvalr8   loweredr   r   s                  r$   r   zYfData._accept_consent_form  s    \..> yy  (#7|'7'7))62	 ==) 	!C776?D776?,f335C'''"(bC++ tzz|+4::</||I.(+r	sDJ T
!	!& '+iik2177922DGDDDM l../==%%wQU & 
  3s   $Fr0   )F)   )Nr
  )NNr
  N)"r    rO   rP   rQ   rb   rj   rm   rK   r|   r   log_indent_decoratorr   r   r   r   r   r   r   r   rJ   r   r   r@   r   cache_maxsizer   r   r+   boolr   r   Responseintr   r   r&   r$   rV   rV   P   s   
42"$Q"(8   (   ,   .   4 .  .
 8  8t   8   ,    B  B =  =~ }%. & .  $<$--<8;<			<r&   rV   )	metaclasszDhttps://query1.finance.yahoo.com/ws/obi-integration/v1/subscriptionsgoldsilverbronze)         c                   t    e Zd ZddZdededefdZdedz  fdZdefdZ	dedz  fd	Z
ededz  fd
       Zy)AuthNc                 2    || _         t        |      | _        y r0   )r_   rV   _datar`   s     r$   rb   zAuth.__init__A  s    G_
r&   rh   ri   r   c                     | j                   j                  ||       | j                         }|s#t        j                         j                  d       |S )aW  
        Set the login cookies and verify they are valid.

        How to Obtain the Cookies:
            1. Open your browser (e.g., Chrome, Firefox).
            2. Log in to Yahoo Finance (https://finance.yahoo.com).
            3. Open the browser's Developer Tools:
               Press `F12` or `Ctrl + Shift + I` (Windows/Linux) or `Cmd + Option + I` (Mac).
            4. Go to the "Application" tab (Chrome) or "Storage" tab (Firefox).
            5. In the "Cookies" section, select `https://finance.yahoo.com`.
            6. Look for the cookies named `T` and `Y`.
            7. Copy the values of these cookies and pass them to this function.

        Args:
            cookie_t (str): The value for the 'T' cookie.
            cookie_y (str): The value for the 'Y' cookie.

        Returns:
            bool: ``True`` if the cookies are valid (the account is logged in),
            ``False`` otherwise (also emitted as a warning). The cookies are
            stored regardless. ``False`` can also mean Yahoo was transiently
            unreachable, so it is not treated as a hard error.
        zTset_login_cookies: the provided cookies are not logged in (or Yahoo is unreachable).)r  rj   check_loginr   ru   warning)ra   rh   ri   	logged_ins       r$   rj   zAuth.set_login_cookiesE  sM    0 	

$$Xx8$$&	!))- r&   c                    	 | j                   j                  t              }|j                  dk(  ra|j	                         xs i j                  d      }t        |t              r.|j                  d      r| j                   j                  d       |S | j                   j                  d       y# t        $ rK}t        j                  j                  s t        j                         j                  d|        Y d}~yd}~ww xY w)a  Fetch the account's subscription entitlement (live, not cached).

        A single lightweight JSON call to the OBI subscriptions endpoint
        determines both login state and subscription tier, avoiding any
        consumer-web-page scraping. The result is intentionally not cached:
        the endpoint is cheap and Yahoo does not rate-limit it at any realistic
        volume, so a fresh call each time keeps the answer from going stale
        (e.g. if the login session expires part-way through a long-running
        process).

        Returns:
            dict | None: The entitlement ``result`` object when logged in, or
            ``None`` when not logged in (anonymous sessions return HTTP 401).
        r   resultguidTFNzError confirming login: )r  rJ   _SUBSCRIPTIONS_URLr   r   r   r2   rm   rx   r   rv   hide_exceptionsr   ru   r   )ra   r   r   r   s       r$   _fetch_entitlementzAuth._fetch_entitlementf  s    	zz~~&89H##s*"--//R44X>fd+

60BJJ--d3!M JJ%%e, 	 >>11!''*B1#(FG	s   BB- B- -	D6AC<<Dc                 &    | j                         duS )u  Check whether the user is logged in to Yahoo Finance.

        Note: ``False`` during a transient error (e.g. Yahoo briefly
        unreachable) means "could not confirm" rather than "logged out" — the
        stored login cookies are kept and remain protected in that case.
        Nr$  )ra   s    r$   r  zAuth.check_login  s     &&(44r&   c                     | j                         }|y|j                  d      xs g D cg c]  }|j                  d      dk(  r| }}|syt        j                  |d   j                  d      d      S c c}w )	aM  Return the Yahoo Finance subscription tier of the logged-in account.

        Read directly from the account's tier id in ``subscriptionView`` (the
        value the account is billed against) rather than inferring it from the
        granted feature set, which Yahoo reshuffles between tiers.

        Returns:
            str | None: ``'gold'``, ``'silver'`` or ``'bronze'`` for a named
            subscription (``'premium'`` for a subscribed tier with no marketed
            name), ``'free'`` when logged in without a subscription, or ``None``
            when not logged in.
        NsubscriptionViewr   ACTIVEfreer   tierpremium)r$  rJ   _TIER_NAMES)ra   entitlementsactives       r$   subscription_tierzAuth.subscription_tier  s     --/)oo.@AGR 2UU8_0  2 2vay}}V4i@@	2s   A5c                 8    | j                         }|rd|d   iS dS )z
        Get the logged-in user's details.

        Returns:
            dict | None: ``{'guid': ...}`` if logged in, or ``None`` if not.
        r!  Nr&  )ra   r.  s     r$   userz	Auth.user  s)     --/0;F+,EEr&   r0   )r    rO   rP   rb   r+   r  rj   r2   r$  r  r1  propertyr3  r   r&   r$   r  r  @  ss    %#   B!D4K !F5T 5A3: A, FdTk F Fr&   r  )%r<   r   r   timer   _httpr   r   r   r   urllib.parser	   r
   bs4r   r   r   r   r   r   configr   rR   r   r   r   r   r%   r  r-   r@   r   rB   rV   r"  r-  r  r   r&   r$   <module>r:     s        J J *       F F	4 *'D '(d} dL \  X(3qF qFr&   