o
    ABi~                     @   s   d Z ddlZddlZddl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mZ ddlmZ ddlmZmZmZ dd	lmZmZ eeZG d
d deZG dd deeZG dd deZdS )z
    ticker.py

    Websocket implementation for kite ticker

    :copyright: (c) 2021 by Zerodha Technology Pvt. Ltd.
    :license: see LICENSE for details.
    N)datetime)reactorssl)log)ReconnectingClientFactory)WebSocketClientProtocolWebSocketClientFactory	connectWS   )__version__	__title__c                       sr   e Zd ZdZdZdZdZdZdZdZ	 fddZ
dd Zd	d
 Zdd Zdd Zdd Z	 dd Zdd Z  ZS )KiteTickerClientProtocolz(Kite ticker autobahn WebSocket protocol.g      @   Nc                    s   t t| j|i | dS )z9Initialize protocol with all options passed from factory.N)superr   __init__selfargskwargs	__class__ R/var/www/html/Trade-python/venv/lib/python3.10/site-packages/kiteconnect/ticker.pyr   (   s   z!KiteTickerClientProtocol.__init__c                 C   s,   | | j _| j jr| j | | | j   dS )z7Called when WebSocket server connection was establishedN)factoryws
on_connect
resetDelayr   responser   r   r   	onConnect-   s   z"KiteTickerClientProtocol.onConnectc                 C   s,   |    |   | jjr| j|  dS dS )zBCalled when the initial WebSocket opening handshake was completed.N)
_loop_ping_loop_pong_checkr   on_openr   r   r   r   onOpen8   s
   zKiteTickerClientProtocol.onOpenc                 C   s    | j jr| j | || dS dS )z/Called when text or binary message is received.N)r   
on_message)r   payload	is_binaryr   r   r   	onMessageC   s   z"KiteTickerClientProtocol.onMessagec                 C   sh   |s| j jr| j | || | j jr| j | || d| _d| _| jr(| j  | jr2| j  dS dS )z!Called when connection is closed.N)r   on_erroron_close_last_ping_time_last_pong_time
_next_pingcancel_next_pong_check)r   	was_cleancodereasonr   r   r   onCloseI   s   
z KiteTickerClientProtocol.onClosec                 C   sR   | j r| jjrtdt | j   t | _ | jjr'td| dS dS )z%Called when pong message is received.zlast pong was {} seconds back.z
pong => {}N)r,   r   debugr   formattimer   r   r   r   onPong\   s   
zKiteTickerClientProtocol.onPongc                 C   sL   | j jr| jrtdt | j  t | _| j j| j| j	| _
dS )z>Start a ping loop where it sends ping message every X seconds.zlast ping was {} seconds back.N)r   r4   r+   r   r5   r6   r   	callLaterPING_INTERVALr    r-   r#   r   r   r   r    j   s
   
z#KiteTickerClientProtocol._loop_pingc                 C   s`   | j r#t | j  }|d| j kr#| jjrtd| | jdd | jj	| j| j
| _dS )z
        Timer sortof to check if connection is still there.

        Checks last pong message time and disconnects the existing connection to make sure it doesn't become a ghost connection.
           zBLast pong was {} seconds ago. So dropping connection to reconnect.T)abortN)r,   r6   r9   r   r4   r   r5   dropConnectionr   r8   r!   r/   )r   last_pong_diffr   r   r   r!   v   s   z)KiteTickerClientProtocol._loop_pong_check)__name__
__module____qualname____doc__r9   KEEPALIVE_INTERVALr-   r/   r,   r+   r   r   r$   r(   r3   r7   r    r!   __classcell__r   r   r   r   r      s"    
r   c                       sP   e Zd ZdZeZdZdZdZ fddZ	dd Z
d	d
 Zdd Zdd Z  ZS )KiteTickerClientFactoryzQAutobahn WebSocket client factory to implement reconnection and custom callbacks.r   
   Nc                    sP   d| _ d| _d| _d| _d| _d| _d| _d| _d| _t	t
| j|i | dS )z/Initialize with default callback method values.FN)r4   r   r"   r)   r*   r%   r   on_reconnecton_noreconnectr   rD   r   r   r   r   r   r      s   z KiteTickerClientFactory.__init__c                 C   s$   | j s| jrtd t | _ dS )z$On connecting start or reconnection.zStart WebSocket connection.N)_last_connection_timer4   r   r6   )r   	connectorr   r   r   startedConnecting   s   
z)KiteTickerClientFactory.startedConnectingc                 C   sP   | j dkrtd| j tt| j | jr| | j  | | | 	  dS )z2On connection failure (When connect request fails)r   zNRetrying connection. Retry attempt count: {}. Next retry in around: {} secondsN)
retriesr   errorr5   introunddelayrF   retrysend_noreconnectr   rI   r2   r   r   r   clientConnectionFailed   s   

z.KiteTickerClientFactory.clientConnectionFailedc                 C   s2   | j dkr| jr| | j  | | |   dS )z>On connection lost (When ongoing connection got disconnected).r   N)rK   rF   rP   rQ   rR   r   r   r   clientConnectionLost   s
   

z,KiteTickerClientFactory.clientConnectionLostc                 C   sT   | j dur$| j| j kr&| jrtd| j  |   | jr(|   dS dS dS dS )z5Callback `no_reconnect` if max retries are exhausted.NzMaximum retries ({}) exhausted.)
maxRetriesrK   r4   r   r5   stoprG   r#   r   r   r   rQ      s   z(KiteTickerClientFactory.send_noreconnect)r>   r?   r@   rA   r   protocolmaxDelayrU   rH   r   rJ   rS   rT   rQ   rC   r   r   r   r   rD      s    rD   c                   @   s8  e Zd ZdZdddddddd	d
dd
ZdZdZdZdZdZ	dZ
dZdZdZdZdZdZdZdZdddeeefddZdd Zdd  ZdLd!d"Zd#d$ ZdMd%d&ZdMd'd(Zd)d* Zd+d, Zd-d. Zd/d0 Zd1d2 Zd3d4 Zd5d6 Z d7d8 Z!d9d: Z"d;d< Z#d=d> Z$d?d@ Z%dAdB Z&dCdD Z'dEdF Z(dNdHdIZ)dJdK Z*dS )O
KiteTickera  
    The WebSocket client for connecting to Kite Connect's streaming quotes service.

    Getting started:
    ---------------
        #!python
        import logging
        from kiteconnect import KiteTicker

        logging.basicConfig(level=logging.DEBUG)

        # Initialise
        kws = KiteTicker("your_api_key", "your_access_token")

        def on_ticks(ws, ticks):
            # Callback to receive ticks.
            logging.debug("Ticks: {}".format(ticks))

        def on_connect(ws, response):
            # Callback on successful connect.
            # Subscribe to a list of instrument_tokens (RELIANCE and ACC here).
            ws.subscribe([738561, 5633])

            # Set RELIANCE to tick in `full` mode.
            ws.set_mode(ws.MODE_FULL, [738561])

        def on_close(ws, code, reason):
            # On connection close stop the event loop.
            # Reconnection will not happen after executing `ws.stop()`
            ws.stop()

        # Assign the callbacks.
        kws.on_ticks = on_ticks
        kws.on_connect = on_connect
        kws.on_close = on_close

        # Infinite loop on the main thread. Nothing after this will run.
        # You have to use the pre-defined callbacks to manage subscriptions.
        kws.connect()

    Callbacks
    ---------
    In below examples `ws` is the currently initialised WebSocket object.

    - `on_ticks(ws, ticks)` -  Triggered when ticks are recevied.
        - `ticks` - List of `tick` object. Check below for sample structure.
    - `on_close(ws, code, reason)` -  Triggered when connection is closed.
        - `code` - WebSocket standard close event code (https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent)
        - `reason` - DOMString indicating the reason the server closed the connection
    - `on_error(ws, code, reason)` -  Triggered when connection is closed with an error.
        - `code` - WebSocket standard close event code (https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent)
        - `reason` - DOMString indicating the reason the server closed the connection
    - `on_connect` -  Triggered when connection is established successfully.
        - `response` - Response received from server on successful connection.
    - `on_message(ws, payload, is_binary)` -  Triggered when message is received from the server.
        - `payload` - Raw response from the server (either text or binary).
        - `is_binary` - Bool to check if response is binary type.
    - `on_reconnect(ws, attempts_count)` -  Triggered when auto reconnection is attempted.
        - `attempts_count` - Current reconnect attempt number.
    - `on_noreconnect(ws)` -  Triggered when number of auto reconnection attempts exceeds `reconnect_tries`.
    - `on_order_update(ws, data)` -  Triggered when there is an order update for the connected user.


    Tick structure (passed to the `on_ticks` callback)
    ---------------------------
        [{
            'instrument_token': 53490439,
            'mode': 'full',
            'volume_traded': 12510,
            'last_price': 4084.0,
            'average_traded_price': 4086.55,
            'last_traded_quantity': 1,
            'total_buy_quantity': 2356
            'total_sell_quantity': 2440,
            'change': 0.46740467404674046,
            'last_trade_time': datetime.datetime(2018, 1, 15, 13, 16, 54),
            'exchange_timestamp': datetime.datetime(2018, 1, 15, 13, 16, 56),
            'oi': 21845,
            'oi_day_low': 0,
            'oi_day_high': 0,
            'ohlc': {
                'high': 4093.0,
                'close': 4065.0,
                'open': 4088.0,
                'low': 4080.0
            },
            'tradable': True,
            'depth': {
                'sell': [{
                    'price': 4085.0,
                    'orders': 1048576,
                    'quantity': 43
                }, {
                    'price': 4086.0,
                    'orders': 2752512,
                    'quantity': 134
                }, {
                    'price': 4087.0,
                    'orders': 1703936,
                    'quantity': 133
                }, {
                    'price': 4088.0,
                    'orders': 1376256,
                    'quantity': 70
                }, {
                    'price': 4089.0,
                    'orders': 1048576,
                    'quantity': 46
                }],
                'buy': [{
                    'price': 4084.0,
                    'orders': 589824,
                    'quantity': 53
                }, {
                    'price': 4083.0,
                    'orders': 1245184,
                    'quantity': 145
                }, {
                    'price': 4082.0,
                    'orders': 1114112,
                    'quantity': 63
                }, {
                    'price': 4081.0,
                    'orders': 1835008,
                    'quantity': 69
                }, {
                    'price': 4080.0,
                    'orders': 2752512,
                    'quantity': 89
                }]
            }
        },
        ...,
        ...]

    Auto reconnection
    -----------------

    Auto reconnection is enabled by default and it can be disabled by passing `reconnect` param while initialising `KiteTicker`.
    On a side note, reconnection mechanism cannot happen if event loop is terminated using `stop` method inside `on_close` callback.

    Auto reonnection mechanism is based on [Exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff) algorithm in which
    next retry interval will be increased exponentially. `reconnect_max_delay` and `reconnect_max_tries` params can be used to tewak
    the alogrithm where `reconnect_max_delay` is the maximum delay after which subsequent reconnection interval will become constant and
    `reconnect_max_tries` is maximum number of retries before its quiting reconnection.

    For example if `reconnect_max_delay` is 60 seconds and `reconnect_max_tries` is 50 then the first reconnection interval starts from
    minimum interval which is 2 seconds and keep increasing up to 60 seconds after which it becomes constant and when reconnection attempt
    is reached upto 50 then it stops reconnecting.

    method `stop_retry` can be used to stop ongoing reconnect attempts and `on_reconnect` callback will be called with current reconnect
    attempt and `on_noreconnect` is called when reconnection attempts reaches max retries.
    r
   r:         r            	   )
nsenfocdsbsebfobcdmcxmcxsxindicesbsecds   <   2   zwss://ws.kite.tradefullquoteltpT   	subscribeunsubscribemodei,  FNc	           	      C   s   |p| j | _|| jkrtdj| jd | j| _n|| _|| jk r1tdj| jd | j| _n|| _|| _	dj| j||d| _
|| _d| _d| _d| _d| _d| _d| _d| _d| _d| _d| _i | _dS )a  
        Initialise websocket client instance.

        - `api_key` is the API key issued to you
        - `access_token` is the token obtained after the login flow in
            exchange for the `request_token`. Pre-login, this will default to None,
            but once you have obtained it, you should
            persist it in a database or session to pass
            to the Kite Connect class initialisation for subsequent requests.
        - `root` is the websocket API end point root. Unless you explicitly
            want to send API requests to a non-default endpoint, this
            can be ignored.
        - `reconnect` is a boolean to enable WebSocket autreconnect in case of network failure/disconnection.
        - `reconnect_max_delay` in seconds is the maximum delay after which subsequent reconnection interval will become constant. Defaults to 60s and minimum acceptable value is 5s.
        - `reconnect_max_tries` is maximum number reconnection attempts. Defaults to 50 attempts and maximum up to 300 attempts.
        - `connect_timeout` in seconds is the maximum interval after which connection is considered as timeout. Defaults to 30s.
        z\`reconnect_max_tries` can not be more than {val}. Setting to highest possible value - {val}.)valz[`reconnect_max_delay` can not be less than {val}. Setting to lowest possible value - {val}.z4{root}?api_key={api_key}&access_token={access_token})rootapi_keyaccess_tokenN)ROOT_URIru   _maximum_reconnect_max_triesr   warningr5   reconnect_max_tries_minimum_reconnect_max_delayreconnect_max_delayconnect_timeout
socket_urlr4   r   on_ticksr"   r*   r)   r   r%   rF   rG   on_order_updatesubscribed_tokens)	r   rv   rw   r4   ru   	reconnectr{   r}   r~   r   r   r   r     s@   




zKiteTicker.__init__c                 K   s   t |fi || _| jj| _| j| j_| j| j_| j| j_| j| j_	| j
| j_| j| j_| j| j_| j| j_| j| j_| j| j_dS )z%Create a WebSocket client connection.N)rD   r   r   r4   _on_openr"   	_on_errorr)   	_on_closer*   _on_messager%   _on_connectr   _on_reconnectrF   _on_noreconnectrG   r}   rX   r{   rU   )r   urlr   r   r   r   _create_connection  s   









zKiteTicker._create_connectionc                 C   s   t d  t S )Nz-python/)r   
capitalizer   r#   r   r   r   _user_agent  s   zKiteTicker._user_agentc                 C   s   ddi}| j | j|  ||d d}| jjr|st }t| j|| jd | j	r.t
tj i }tjsW|rMd|d< tjtj|d| _d	| j_| j  dS tjd
i | dS dS )aA  
        Establish a websocket connection.

        - `threaded` is a boolean indicating if the websocket client has to be run in threaded mode or not
        - `disable_ssl_verification` disables building ssl context
        - `proxy` is a dictionary with keys `host` and `port` which denotes the proxy settings
        zX-Kite-Version3)	useragentproxyheadersN)contextFactorytimeoutFinstallSignalHandlers)targetr   Tr   )r   r   r   r   isSecurer   ClientContextFactoryr	   r~   r4   twisted_logstartLoggingsysstdoutr   running	threadingThreadrunwebsocket_threaddaemonstart)r   threadeddisable_ssl_verificationr   r   context_factoryoptsr   r   r   connect  s*   
zKiteTicker.connectc                 C   s   | j r| j j| j jkrdS dS )z-Check if WebSocket connection is established.TF)r   state
STATE_OPENr#   r   r   r   is_connected  s   zKiteTicker.is_connectedc                 C   s   | j r| j || dS dS zClose the WebSocket connection.N)r   	sendCloser   r1   r2   r   r   r   _close"  s   zKiteTicker._closec                 C   s   |    | || dS r   )
stop_retryr   r   r   r   r   close'  s   zKiteTicker.closec                 C   s   t   dS )zStop the event loop. Should be used if main thread has to be closed in `on_close` method.
        Reconnection mechanism cannot happen past this method
        N)r   rV   r#   r   r   r   rV   ,  s   zKiteTicker.stopc                 C   s   | j r
| j   dS dS )z'Stop auto retry when it is in progress.N)r   
stopTryingr#   r   r   r   r   2  s   zKiteTicker.stop_retryc              
   C   sn   z| j tt| j|d |D ]}| j| j|< qW dS  t	y6 } z| j
dt|d  d}~ww )z
        Subscribe to a list of instrument_tokens.

        - `instrument_tokens` is list of instrument instrument_tokens to subscribe
        avTzError while subscribe: {}r2   N)r   sendMessagesixbjsondumps_message_subscribe
MODE_QUOTEr   	Exceptionr   r5   strr   instrument_tokenstokener   r   r   rq   7  s   zKiteTicker.subscribec              
   C   s   z'| j tt| j|d |D ]}z| j|= W q ty$   Y qw W dS  t	y? } z| j
dt|d  d}~ww )z
        Unsubscribe the given list of instrument_tokens.

        - `instrument_tokens` is list of instrument_tokens to unsubscribe.
        r   TzError while unsubscribe: {}r   N)r   r   r   r   r   r   _message_unsubscriber   KeyErrorr   r   r5   r   r   r   r   r   rr   J  s    zKiteTicker.unsubscribec              
   C   sp   z| j tt| j||gd |D ]}|| j|< qW dS  ty7 } z| j	d
t|d  d}~ww )a'  
        Set streaming mode for the given list of tokens.

        - `mode` is the mode to set. It can be one of the following class constants:
            MODE_LTP, MODE_QUOTE, or MODE_FULL.
        - `instrument_tokens` is list of instrument tokens on which the mode should be applied
        r   TzError while setting mode: {}r   N)r   r   r   r   r   r   _message_setmoder   r   r   r5   r   )r   rs   r   r   r   r   r   r   set_mode`  s   zKiteTicker.set_modec                 C   s   i }| j D ]}| j | }||sg ||< || | q|D ]}| jr/td|||  | ||  | |||  qdS )z-Resubscribe to all current subscribed tokens.z!Resubscribe and set mode: {} - {}N)r   getappendr4   r   r5   rq   r   )r   modesr   mrs   r   r   r   resubscribev  s   


zKiteTicker.resubscribec                 C   s    || _ | jr| | | d S d S N)r   r   )r   r   r   r   r   r   r     s   zKiteTicker._on_connectc                 C   2   t d|t| | jr| | || dS dS )z3Call `on_close` callback when connection is closed.zConnection closed: {} - {}N)r   rL   r5   r   r*   r   r   r1   r2   r   r   r   r        zKiteTicker._on_closec                 C   r   )z9Call `on_error` callback when connection throws an error.zConnection error: {} - {}N)r   rL   r5   r   r)   r   r   r   r   r     r   zKiteTicker._on_errorc                 C   sR   | j r
|  | || | jr|rt|dkr| | | | |s'| | dS dS )z9Call `on_message` callback when text message is received.r[   N)r%   r   len_parse_binary_parse_text_message)r   r   r&   r'   r   r   r   r     s   zKiteTicker._on_messagec                 C   s(   | j s|   d| _ | jr| | S d S )NF)_is_first_connectr   r"   )r   r   r   r   r   r     s   
zKiteTicker._on_openc                 C   s   | j r	|  | |S d S r   )rF   )r   attempts_countr   r   r   r     s   zKiteTicker._on_reconnectc                 C   s   | j r|  | S d S r   )rG   r#   r   r   r   r     s   
zKiteTicker._on_noreconnectc                 C   s   t jst|tkr|d}zt|}W n
 ty   Y dS w | jr7|	ddkr7|	dr7| | |d  |	ddkrJ| 
| d|	d dS dS )zParse text message.zutf-8NtypeorderdatarL   r   )r   PY2r   bytesdecoder   loads
ValueErrorr   r   r   )r   r&   r   r   r   r   r     s   
zKiteTicker._parse_text_messagec                 C   s.  |  |}g }|D ]
}| |dd}|d@ }|| jd kr!d}n|| jd kr+d}nd}|| jd	 kr6d
nd}t|dkrQ||| j|| |dd| d q	t|dks]t|dkrt|dkrf| jn| j}	||	|| |dd| | |dd| | |dd| | |dd| | |dd| dd}
d|
d< |
d d dkr|
d |
d d  d |
d d  |
d< t|dkrzt	| |dd}W n t
y   d}Y nw ||
d< ||
 q	t|dkst|dkrt|dkr| jn| j}	||	|| |dd| | |dd| |dd| | |dd| |dd| |dd| |dd| | |dd| | |dd | | |d d| d!d"
}
d|
d< |
d d dkrk|
d |
d d  d |
d d  |
d< t|dkrzt	| |dd#}W n t
y   d}Y nw zt	| |d$d%}W n t
y   d}Y nw ||
d&< | |d#d'|
d(< | |d'd)|
d*< | |d)d$|
d+< ||
d< g g d,}ttd%t|dD ]4\}}||d-krd.nd/ | |||d | ||d |d | | j||d |d0 d1d2d3 q||
d4< ||
 q	|S )5z1Parse binary data to a (list of) ticks structure.r   r[      rb   g    cAre   g     @g      Y@rh   FTr^   )tradablers   instrument_token
last_price                   )highlowopenr   )r   rs   r   r   ohlcchanger   r   r   d   Nexchange_timestamp,      $   (   )r   r   r   r   )
r   rs   r   r   last_traded_quantityaverage_traded_pricevolume_tradedtotal_buy_quantitytotal_sell_quantityr   0   rk   @   last_trade_time4   oi8   oi_day_high
oi_day_low)buysellr   r  r   rE   Hbyte_format)quantitypriceordersdepth)_split_packets_unpack_intEXCHANGE_MAPr   r   MODE_LTPr   	MODE_FULLr   fromtimestampr   	enumeraterange)r   binpacketsr   packetr   segmentdivisorr   rs   d	timestampr   r  ipr   r   r   r     s   


((
zKiteTicker._parse_binaryIc                 C   s   t d| ||| d S )z(Unpack binary data as unsgined interger.>r   )structunpack)r   r  r   endr  r   r   r   r
  L  s   zKiteTicker._unpack_intc                 C   s|   t |dk rg S | j|dddd}g }d}t|D ]"}| j|||d dd}|||d |d |   |d | }q|S )z.Split the data to individual packets of ticks.r:   r   r  r  )r   r
  r  r   )r   r  number_of_packetsr  jr  packet_lengthr   r   r   r	  P  s   zKiteTicker._split_packets)FFN)NN)r  )+r>   r?   r@   rA   r  CONNECT_TIMEOUTRECONNECT_MAX_DELAYRECONNECT_MAX_TRIESrx   r  r   r  r   _message_coder   r   r   r|   ry   r   r   r   r   r   r   r   rV   r   rq   rr   r   r   r   r   r   r   r   r   r   r   r   r
  r	  r   r   r   r   rY      sn     
E
+


}rY   )rA   r   r   r6   r   r  loggingr   r   twisted.internetr   r   twisted.pythonr   r   twisted.internet.protocolr   autobahn.twisted.websocketr   r   r	   r   r   	getLoggerr>   r   rD   objectrY   r   r   r   r   <module>   s$   
mB