o
    _�¿j3<  ã                   @  sâ   d Z ddlmZ ddlZddlZddlmZ ddlmZ ddl	m
Z
mZ ddlmZmZ ddlmZmZmZmZ dd	lmZmZmZmZmZ dd
lmZ ddlmZ ddlmZ ddl m!Z" e #d¡Z$dZ%G dd„ dƒZ&dgZ'dS )uþ  NSEClient â€” high-level async client for NSE option chain data.

Usage::

    import asyncio
    from indiaopt import NSEClient

    async def main():
        async with NSEClient() as client:
            result = await client.fetch_option_chain("NIFTY", is_index=True)
            print(f"Spot: {result.spot_price}, ATM: {result.atm_strike}")
            for row in result.atm_window(n=5):
                print(f"{row.strike:>8}  CE OI: {row.call_oi:>10}  PE OI: {row.put_oi:>10}")

    asyncio.run(main())
é    )ÚannotationsN)ÚThreadPoolExecutor)ÚAny)ÚCircuitBreakerÚget_default_breaker)ÚSettingsÚget_settings)ÚNSE_BASE_URLÚNSE_CONTRACT_INFO_PATHÚNSE_OPTION_CHAIN_PATHÚRATE_LIMIT_STATUS_CODES)Ú
FetchErrorÚFetchTimeoutErrorÚNetworkErrorÚ
ParseErrorÚRateLimitError)ÚRetryPolicy)Úcreate_nse_session)ÚOptionChainResult)Únsezindiaopt.exchanges.nseÚNSEc                   @  s¨   e Zd ZdZ		d3d4d	d
„Zd5dd„Zd6dd„Zd7dd„Zdddœd8dd„Zdddœd9dd „Z	d:d"d#„Z
d;d$d%„Zd<d'd(„Zd=d*d+„Zd8d,d-„Zd>d/d0„Zd:d1d2„ZdS )?Ú	NSEClientaZ  Async client for NSE option chain and market data.

    Manages its own session lifetime, thread pool, circuit breaker, and
    retry policy. Supports both context-manager and direct usage.

    Args:
        settings: Optional :class:`~indiaopt.config.settings.Settings` override.
                  Uses global settings (from env/``.env``) when not provided.
        breaker:  Optional :class:`~indiaopt.circuit.CircuitBreaker` override.
                  Shares the global default when not provided.

    Context manager (recommended)::

        async with NSEClient() as client:
            result = await client.fetch_option_chain("NIFTY")

    Direct usage (must call :meth:`close` manually)::

        client = NSEClient()
        try:
            result = await client.fetch_option_chain("NIFTY")
        finally:
            await client.close()
    NÚsettingsúSettings | NoneÚbreakerúCircuitBreaker | NoneÚreturnÚNonec                 C  sP   |pt ƒ | _|pt| jj| jjd�| _t | j¡| _t	| jj
dd�| _d | _d S )N)Úfailure_thresholdÚbackoff_secondszindiaopt-nse)Úmax_workersÚthread_name_prefix)r   Ú	_settingsr   Úcircuit_failure_thresholdÚcircuit_backoff_secondsÚ_breakerr   Úfrom_settingsÚ_retry_policyr   Úthread_pool_workersÚ_poolÚ_session)Úselfr   r   © r,   úQ/home/dinkstrade/pdmp/venv/lib/python3.10/site-packages/indiaopt/exchanges/nse.pyÚ__init__L   s   þþ
zNSEClient.__init__c                 Ã  s   �| S ©Nr,   ©r+   r,   r,   r-   Ú
__aenter___   s   €zNSEClient.__aenter__Ú_Úobjectc                 Ç  s   �|   ¡ I d H  d S r/   )Úclose)r+   r2   r,   r,   r-   Ú	__aexit__b   s   €zNSEClient.__aexit__c                 Ã  sF   �| j durz| j  ¡  W n	 ty   Y nw d| _ | jjdd� dS )z-Release all resources (session, thread pool).NF)Úwait)r*   r4   Ú	Exceptionr)   Úshutdownr0   r,   r,   r-   r4   e   s   €
ÿzNSEClient.closeT)Úis_indexÚexpiryÚsymbolÚstrr9   Úboolr:   ú
str | Noneúdict[str, Any]c                Ã  s‚   �|  ¡  ¡ }| j t|¡ t ¡ }ztj| | j	| j
|||¡| jjd�I dH W S  tjy@   | j t|¡ t|| jjtd�‚w )z=Fetch the unparsed raw JSON dictionary for *symbol* from NSE.©ÚtimeoutN©r;   Ú	timeout_sÚexchange)ÚupperÚstripr%   Úraise_if_openÚEXCHANGEÚasyncioÚget_running_loopÚwait_forÚrun_in_executorr)   Ú_fetch_syncr"   Úasync_timeoutÚTimeoutErrorÚrecord_failurer   )r+   r;   r9   r:   Úloopr,   r,   r-   Úfetch_raw_option_chainq   s.   €ûø
ýþz NSEClient.fetch_raw_option_chainr   c                Ã  s�   �|  ¡  ¡ }| j t|¡ t ¡ }ztj| | j	| j
|||¡| jjd�I dH }W n tjyA   | j t|¡ t|| jjtd�‚w t ||¡S )uú  Fetch the full option chain for *symbol*.

        Args:
            symbol:   NSE trading symbol (e.g. ``"NIFTY"``, ``"RELIANCE"``).
            is_index: ``True`` for index instruments (NIFTY, BANKNIFTY, â€¦),
                      ``False`` for equity derivatives.
            expiry:   Specific expiry date string (``"27-Jun-2024"`` format).
                      Uses the nearest expiry when not provided.

        Returns:
            :class:`~indiaopt.models.option_chain.OptionChainResult`

        Raises:
            :class:`~indiaopt.exceptions.CircuitOpenError`: Circuit is open.
            :class:`~indiaopt.exceptions.FetchTimeoutError`: Exceeded async timeout.
            :class:`~indiaopt.exceptions.FetchError`: All retries exhausted.
        r@   NrB   )rE   rF   r%   rG   rH   rI   rJ   rK   rL   r)   rM   r"   rN   rO   rP   r   Ú
nse_parserÚparse_option_chain)r+   r;   r9   r:   rQ   Úrawr,   r,   r-   Úfetch_option_chain�   s0   €ûø
ýþzNSEClient.fetch_option_chainú	list[str]c                 Ã  s\   �|  ¡  ¡ }t ¡ }ztj| | j| j|¡| jj	d�I dH }W |S  tj
y-   g  Y S w )zÂReturn available expiry dates for *symbol*.

        Args:
            symbol: NSE trading symbol.

        Returns:
            List of expiry date strings in ``"DD-Mon-YYYY"`` format.
        r@   N)rE   rF   rI   rJ   rK   rL   r)   Ú_fetch_expiry_dates_syncr"   rN   rO   )r+   r;   rQ   Úresultr,   r,   r-   Úget_expiry_datesÁ   s    €	ýú
þÿzNSEClient.get_expiry_datesc                 C  s   | j  t|¡ dS )zˆManually reset the circuit breaker for *symbol*.

        Use after manually resolving an issue (e.g. IP unblock, network fix).
        N)r%   ÚresetrH   )r+   r;   r,   r,   r-   Úreset_circuitÙ   s   zNSEClient.reset_circuitúdict[str, dict[str, object]]c                 C  s
   | j  ¡ S )z?Return a snapshot of all circuit breaker states for monitoring.)r%   Ústatsr0   r,   r,   r-   Úcircuit_statsà   s   
zNSEClient.circuit_statsr   c                 C  s&   | j du rt| jj| jjd�| _ | j S )z.Return existing session or create a fresh one.N)Ú
proxy_urlsÚwarmup_timeout)r*   r   r"   r`   ra   r0   r,   r,   r-   Ú_get_or_create_sessionæ   s   
þz NSEClient._get_or_create_sessionc                 C  s<  |   ¡ }|rdnd}|du r|  ||¡}t› t› d|› d|› d|› �}d}t| jjƒD �]V}zÓ|j|| jj	d�}	|	j
dkr–z|	 ¡ }
W n; ty~ } z/t d	||d
 |¡ d| _|   ¡ }td|› �|t|d
 t|	dƒru|	jdd… d�|‚dd�|‚d}~ww | j t|¡ t dt|||d
 ¡ |
W   S |	j
tv rïd}|	j d¡p¨|	j d¡}|r»zt|ƒ}W n	 tyº   Y nw td|	j
› d|› �|t|	j
|d
 |d�}t d||d
 | jj|	j
¡ |}|| jjd
 k rí| j |¡ W q+td|	j
› d|› �|t|	j
|d
 d�‚ ttf�y   ‚  t�yR } z:td|› d|› �|t|d
 dd�}t d||d
 | jj|¡ d| _|   ¡ }|| jjd
 k �rH| j |¡ W Y d}~q+d}~w t�y‚ } z$|}t d||d
 | jj|¡ || jjd
 k �rx| j |¡ W Y d}~q+d}~ww | j  t|¡ td| jj› d |› �|t| jjd!d�|‚)"zvSynchronous fetch with retry, circuit breaker, and session reuse.

        This runs in a thread pool worker.
        ÚIndicesÚEquitiesNz?type=z&symbol=z&expiry=r@   éÈ   z?JSON decode failed for %s (attempt %d): %s. Refreshing session.é   z.Invalid JSON from NSE (likely cookie expiry): ÚtextÚ )r;   rD   ÚattemptsÚraw_previewz-Fetched %s/%s expiry=%s HTTP 200 (attempt %d)zRetry-Afterzretry-afterzHTTP z from NSE for )r;   rD   Ústatus_coderi   Úretry_afterz:Rate-limited for %s (attempt %d/%d, HTTP %d). Backing off.zUnexpected HTTP z for )r;   rD   rk   ri   zNetwork error for z: z3Check network connectivity and proxy configuration.)r;   rD   ri   Úrecoveryz(Network error for %s (attempt %d/%d): %sz+Unexpected error for %s (attempt %d/%d): %szAll z attempts failed for z<Check NSE status and your network. Circuit breaker may open.)!rb   Ú_get_nearest_expiry_syncr	   r   Úranger'   Úmax_retriesÚgetr"   Úfetch_timeoutrk   Újsonr7   ÚloggerÚwarningr*   r   rH   Úhasattrrg   r%   Úrecord_successÚdebugr   ÚheadersÚfloatÚ
ValueErrorr   Ú	sleep_forr   ÚOSErrorr   rP   )r+   r;   r9   r:   ÚsessionÚtype_strÚurlÚlast_excÚattemptÚrespÚdataÚjson_excrl   Úraw_raÚexcr,   r,   r-   rM   ï   s  

ÿÿÿÿ
ûûúûú€õû

ÿúúûûû€û€öûúzNSEClient._fetch_syncr~   c           
   
   C  s®   ddl m } | ¡  d¡}t› t› d|› �}z&|j|| jjd�}|jdkr7| 	¡ }| d¡p/g }|r:|d W S W |S W |S  t
yV }	 zt d||	¡ W Y d	}	~	|S d	}	~	ww )
z�Fetch the nearest expiry date for *symbol* from NSE contract info.

        Falls back to today's date string if the API call fails.
        r   )Údatetimez%d-%b-%Yú?symbol=r@   re   ÚexpiryDatesz8Could not fetch expiry dates for %s: %s. Using fallback.N)rˆ   ÚnowÚstrftimer	   r
   rq   r"   rr   rk   rs   r7   rt   rx   )
r+   r~   r;   rˆ   Úfallbackr€   rƒ   r„   Údatesr‡   r,   r,   r-   rn   |  s&   

üüþ€þz"NSEClient._get_nearest_expiry_syncc              
   C  sŠ   |   ¡ }t› t› d|› �}z|j|| jjd�}|jdkr(| ¡ }| d¡p&g W S W g S  tyD } zt	 
d||¡ W Y d}~g S d}~ww )z.Synchronous helper to fetch expiry dates list.r‰   r@   re   rŠ   z'Could not fetch expiry dates for %s: %sN)rb   r	   r
   rq   r"   rr   rk   rs   r7   rt   rx   )r+   r;   r~   r€   rƒ   r„   r‡   r,   r,   r-   rX   �  s   
þþ€þz"NSEClient._fetch_expiry_dates_sync)NN)r   r   r   r   r   r   )r   r   )r2   r3   r   r   )r   r   )r;   r<   r9   r=   r:   r>   r   r?   )r;   r<   r9   r=   r:   r>   r   r   )r;   r<   r   rW   )r;   r<   r   r   )r   r]   )r   r   )r~   r   r;   r<   r   r<   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r.   r1   r5   r4   rR   rV   rZ   r\   r_   rb   rM   rn   rX   r,   r,   r,   r-   r   2   s,    ý


û#û
1



	 
r   )(r’   Ú
__future__r   rI   ÚloggingÚconcurrent.futuresr   Útypingr   Úindiaopt.circuitr   r   Úindiaopt.config.settingsr   r   Úindiaopt.constantsr	   r
   r   r   Úindiaopt.exceptionsr   r   r   r   r   Úindiaopt.http.retryr   Úindiaopt.http.sessionr   Úindiaopt.models.option_chainr   Úindiaopt.parsersr   rS   Ú	getLoggerrt   rH   r   Ú__all__r,   r,   r,   r-   Ú<module>   s(    
  
n