o
    f<aY                     @   s   d Z ddlZddlZddlZddlmZ ddlZddlZdZ	d
dZd
dZdd	d
ddddddddddddZdZdd Zdd ZG dd deZdd ZG d d! d!ZdS )"zC pydatastream main module

    (c) Vladimir Filimonov, 2013 - 2021
    N)wrapsz@https://product.datastream.com/dswsclient/V1/DSService.svc/rest/zoDSCD,EXMNEM,GEOGC,GEOGN,IBTKR,INDC,INDG,INDM,INDX,INDXEG,INDXFS,INDXL,INDXS,ISIN,ISINID,LOC,MNEM,NAME,SECD,TYPE,zXMNEM,NAME,FLOT,FEX,GEOGC,GEOGN,EXCODE,LTDT,FUTBDATE,PCUR,ISOCUR,TICKS,TICKV,TCYCLE,TPLATzBonds & Convertiblesz#Bond Indices & Credit Default SwapsCommodities	EconomicsEquitieszEquity IndiceszExchange RatesFutureszInterest RateszInvestment TrustsOptionszUnit TrustsWarrantszNot available)BDBDINDCMDECEQEQINDEXFTINTINVTOPUTEWTNAa  PyDatastream documentation (GitHub):
https://github.com/vfilimonov/pydatastream

Datastream Navigator:
http://product.datastream.com/navigator/

Official support
https://customers.reuters.com/sc/Contactus/simple?product=Datastream&env=PU&TP=Y

Webpage for testing REST API requests
http://product.datastream.com/dswsclient/Docs/TestRestV1.aspx

Documentation for DSWS API
http://product.datastream.com/dswsclient/Docs/Default.aspx

Datastream Web Service Developer community
https://developers.refinitiv.com/eikon-apis/datastream-web-service
c                 C   s6   | du rdS t | tr|  dkrdS t| dS )z Convert date to YYYY-MM-DD N BDATEz%Y-%m-%d)
isinstancestrupperpd	Timestampstrftime)date r!   ^/var/www/html/stock_analysis/be/venv/lib/python3.10/site-packages/pydatastream/pydatastream.py_convert_date;   s
   r#   c                 C   sH   | du rdS t | trtt| gd S dd | D }tj|ddjS )z Parse dates
        Example:
            /Date(1565817068486)       -> 2019-08-14T21:11:08.486000000
            /Date(1565568000000+0000)  -> 2019-08-12T00:00:00.000000000
    Nr   c                 S   s(   g | ]}t |d d|v rdnd qS )   +i)int.0_r!   r!   r"   
<listcomp>N   s   ( z _parse_dates.<locals>.<listcomp>ms)unit)r   r   r   r   _parse_datesto_datetimevalues)datesresr!   r!   r"   r.   D   s   
r.   c                   @   s   e Zd ZdZdS )DatastreamExceptionz  Exception class for Datastream N)__name__
__module____qualname____doc__r!   r!   r!   r"   r3   R   s    r3   c                    s(   dj   tt fdd}|S )z& Lazy-evaluated property of an object __lazy__c                    s$   t |  st|  |  t|  S )N)hasattrsetattrgetattrself	attr_namefnr!   r"   _lazy_property[   s   

z%lazy_property.<locals>._lazy_property)r4   propertyr   )r@   rA   r!   r>   r"   lazy_propertyW   s
   
rC   c                   @   s*  e Zd ZdZd=ddZedd Zdd	 Zd>d
dZe	dd Z
e	dd Zdd Zdd Ze			d?ddZedd Zdd Zd@ddZdAddZ			dBd d!Zd>d"d#Zd>d$d%Zd>d&d'Zd@d(d)Zd*d+ Zd,d- Zd.d/ ZdCd0d1ZdDd3d4Zd@d5d6ZdEd7d8Zed9d: Z d>d;d<Z!dS )F
Datastreamzb Python interface to the Refinitiv Datastream API via Datastream Web
        Services (DSWS).
    TNc                 K   sr   || _ d| _d| _d| _t|tr||d| _n|du r d| _ntd|dt	| _
|| _|| _| || dS )a  Establish a connection to the Python interface to the Refinitiv Datastream
           (former Thomson Reuters Datastream) API via Datastream Web Services (DSWS).

           username / password - credentials for the DSWS account.
           raise_on_error - If True then error request will raise a "DatastreamException",
                            otherwise either empty dataframe or partially
                            retrieved data will be returned
           proxy - URL for the proxy server. Valid values:
                   (a) None: no proxy is used
                   (b) string of format "host:port" or "username:password@host:port"

           Note: credentials will be saved in memory. In case if this is not
                 desirable for security reasons, call the constructor having None
                 instead of values and manually call renew_token(username, password)
                 when needed.

           A custom REST API url (if necessary for some reasons) could be provided
           via "url" parameter.
        N)httphttpsz/Proxy parameter should be either None or stringurl)raise_on_errorlast_requestlast_metadata_last_response_rawr   r   _proxy
ValueErrorpop_URL_url	_username	_passwordrenew_token)r=   usernamepasswordrH   proxykwargsr!   r!   r"   __init__k   s   
zDatastream.__init__c                   C   s   t t dS )z Some useful links N)print_INFOr!   r!   r!   r"   info   s   zDatastream.infoc           	   
   C   s  | j | }||dd| _d| _ztj||| jd}|j| jd< W n ty4 } zt|| jd<  d}~ww zt	
| jd  }| jd< W n t	jyV } ztd|d}~ww d|v r|d }|d durm|d	|d  7 }| d
|d  }|| jd< t|| jd S )z% Call to the POST method of DSWS API N)rG   requesterror)jsonproxiesresponser]   z#Server response could not be parsedCodeSubCode/z: Message)rP   rI   rJ   requestspostrL   text	Exceptionr   r^   loadsJSONDecodeErrorr3   )	r=   methodr\   rG   r2   er`   codeerrormsgr!   r!   r"   	_api_post   s2   



zDatastream._api_postc                 C   s   |du s|du rt d dS ||d}t| d|| _t| jd d| jd< t| jd t	d tj
 t	d | jd	< dS )
z# Request new token from the server Nz<Username or password is not provided - could not renew token)UserNamePasswordGetTokenTokenExpiryUTC15m6HRenewTokenAt)warningswarndictro   _tokenr.   tz_localizeminr   	Timedeltar   utcnow)r=   rT   rU   datar!   r!   r"   rS      s   

zDatastream.renew_tokenc                 C   s*   | j d u rdS tj | j d krdS dS )NTrw   F)r{   r   r   r   r<   r!   r!   r"   _token_is_expired   s
   
zDatastream._token_is_expiredc                 C   s    | j r| | j| j | jd S )z0 Return actual token and renew it if necessary. 
TokenValue)r   rS   rQ   rR   r{   r<   r!   r!   r"   token   s   
zDatastream.tokenc                 C      || j d}| d|S )z Generic wrapper to request data in raw format. Request should be
            properly formatted dictionary (see construct_request() method).
        )DataRequestr   GetDatar   ro   )r=   r\   r   r!   r!   r"   r\      s   zDatastream.requestc                 C   r   )z Generic wrapper to request multiple requests in raw format.
            list_of_requests should be a list of properly formatted dictionaries
            (see construct_request() method).
        )DataRequestsr   GetDataBundler   )r=   list_of_requestsr   r!   r!   r"   request_many   s   zDatastream.request_manyFc                 C   s`  i i g d}t | trd}	nt| drd| } d}	ntdg }
|	s+|	du r3d| v r3|
ddd |sE|du rMd	| v sEd
| v sEd| v rM|
ddd |rW|
ddd | |
d|d< |rfdddgng }
|durt |tr||d ||
d nt |tr|r|D ]}|d ||
d qntdt|t||dur|nd|rdndd|d< |S )aK  Construct a request string for querying TR DSWS.

           tickers - ticker or symbol, or list of symbols
           fields  - field or list of fields.
           date_from, date_to - date range (used only if "date" is not specified)
           freq    - frequency of data: daily('D'), weekly('W') or monthly('M')
           static  - True for static (snapshot) requests
           IsExpression - if True, it will explicitly assume that list of tickers
                          contain expressions. Otherwise it will try to infer it.

           Some of available fields:
           P  - adjusted closing price
           PO - opening price
           PH - high price
           PL - low price
           VO - volume, which is expressed in 1000's of shares.
           UP - unadjusted price
           OI - open interest

           MV - market value
           EPS - earnings per share
           DI - dividend index
           MTVB - market to book value
           PTVB - price to book value
           ...

           The full list of data fields is available at http://dtg.tfn.com/.
        )
InstrumentDate	DataTypesN__len__r   Tz7ticker should be either string or list/array of stringsIsList)KeyValue#()IsExpression
ReturnName)r   
Propertiesr   r   z7fields should be either string or list/array of stringsr   r      )StartEnd	FrequencyKindr   )r   r   r9   joinrM   appendlistr#   )tickerfields	date_fromdate_tofreqstaticr   return_namesreqis_listpropsfr!   r!   r"   construct_request   sH    





zDatastream.construct_requestc                    s   i } D ]N}|dv r/ | sd||< qt  | dd }|dd|j_d|_|||< q|dkrLt  fd	d
 d D }|jdddj||< q | ||< q|S )z1 Parse SymbolNames, DataTypeNames and Currencies )DataTypeNamesSymbolNamesNr   r   Namesr   Name
Currenciesc                    s    i | ]}|t  d  | qS )r   )r   	DataFrame)r)   keymetar!   r"   
<dictcomp>;  s    z*Datastream._parse_meta.<locals>.<dictcomp>Currencyr   level)	r   r   	set_indexreplaceindexnameconcatxsT)r   r2   r   namescurr!   r   r"   _parse_meta-  s    

zDatastream._parse_metac           	   	      sL   d }t  d } fdd D }i }i  |D ]u}|d }i  |< i ||< |d D ]Pd }d d	krS| jrItd
d  d| d| tj | d < nd dkrdt | | d < n| | d < fddD || d < q+|du rd	g}tj | |d |< qt jddj	
  ||d<  | |fS )zE Parse one response (either 'DataResponse' or one of 'DataResponses')DataTypeValuesDatesc                    s   i | ]}|d vr| | qS ))r   r   r!   r(   )r2   r!   r"   r   F      z)Datastream._parse_one.<locals>.<dictcomp>DataTypeSymbolValuesr   Typer   "Symbolz"("z"):    c                    s   i | ]}|d kr| | qS )r   r!   r(   )vr!   r"   r   Z  r   N)r   r   r   r   )r.   rH   r3   mathnanr   r   r   unstackr   
sort_indexr   )	r=   r2   r   r1   res_metar   d	data_typevaluer!   )r2   r   r"   
_parse_oneB  s2    zDatastream._parse_onec                    s|   d|v r  |d \}}| _|r||fS |S d|v r: fdd|d D }dd |D  _|r3|S dd |D S td)a   Parse raw JSON response

            If return_metadata is True, then result is tuple (dataframe, metadata),
            where metadata is a dictionary. Otherwise only dataframe is returned.

            In case of response being constructed from several requests (method
            request_many()), then the result is a list of parsed responses. Here
            again, if return_metadata is True then each element is a tuple
            (dataframe, metadata), otherwise each element is a dataframe.
        DataResponseDataResponsesc                    s   g | ]}  |qS r!   )r   )r)   rr<   r!   r"   r+   u  s    z-Datastream.parse_response.<locals>.<listcomp>c                 S      g | ]}|d  qS r   r!   r(   r!   r!   r"   r+   v      c                 S   r   )r   r!   r(   r!   r!   r"   r+   w  r   z0Neither DataResponse nor DataResponses are found)r   rJ   r3   )r=   r`   return_metadatar2   r   resultsr!   r<   r"   parse_responsee  s   zDatastream.parse_responser   c                    sz    du rt d t j    fddt|D ddd }|}t |}|d j	
d|_d|j_|S )a6   Request usage statistics

            date - if None, stats for the current month will be fetched,
                   otherwise for the month which contains the specified date.
            months - number of consecutive months prior to "date" for which
                     stats should be retrieved.
        Nnowc              	      s*   g | ]}j d d tj| ddqS )STATSzDS.USERSTATSTr   )r   r   offsets
MonthBegin)r)   nr    r=   r!   r"   r+     s
    
z/Datastream.usage_statistics.<locals>.<listcomp>z
Start Datez%B %Y)r   r   	normalizer   r   ranger   r   r   dtr   r   r   )r=   r    monthsr   r2   r!   r   r"   usage_statistics{  s   
zDatastream.usage_statisticsc
              
   C   s   | j ||||||||d}
| |
}|| _| j|dd\}}|r(|jddd}nt|jjd dkr;|	s;|jddd}|rA||fS |S )aZ	  Fetch the data from Datastream for a set of tickers and parse results.

           tickers - ticker or symbol, or list of symbols
           fields  - field or list of fields
           date_from, date_to - date range (used only if "date" is not specified)
           freq    - frequency of data: daily('D'), weekly('W') or monthly('M')
           static  - True for static (snapshot) requests
           IsExpression - if True, it will explicitly assume that list of tickers
                          contain expressions. Otherwise it will try to infer it.
           return_metadata - if True, then tuple (data, metadata) will be returned,
                             otherwise only data
           always_multiindex - if True, then even for 1 ticker requested, resulting
                               dataframe will have multiindex (ticker, date).
                               If False (default), then request of single ticker will
                               result in a dataframe indexed by date only.

           Notes: - several fields should be passed as a list, and not as a
                    comma-separated string!
                  - if no fields are provided, then the default field will be
                    fetched. In this case the column might not have any name
                    in the resulting dataframe.

           Result format depends on the number of requested tickers and fields:
             - 1 ticker         - DataFrame with fields in column names
                                  (in order to keep multiindex even for single
                                  ticker, set `always_multiindex` to True)
             - several tickers  - DataFrame with fields in column names and
                                  MultiIndex (ticker, date)
             - static request   - DataFrame indexed by tickers and with fields
                                  in column names

           Some of available fields:
           P  - adjusted closing price
           PO - opening price
           PH - high price
           PL - low price
           VO - volume, which is expressed in 1000's of shares.
           UP - unadjusted price
           OI - open interest

           MV - market value
           EPS - earnings per share
           DI - dividend index
           MTVB - market to book value
           PTVB - price to book value
           ...
        )r   r   r   r   T)r   r   r   dropr   )r   r\   rK   r   reset_indexlenr   levels)r=   tickersr   r   r   r   r   r   r   always_multiindexr   rawr   r   r!   r!   r"   fetch  s   2
zDatastream.fetchc                 C      | j |g d||dddS )zGet Open, High, Low, Close prices and daily Volume for a given ticker.

           ticker  - ticker or symbol
           date_from, date_to - date range (used only if "date" is not specified)
        )POPHPLPVODFr   r   r   r=   r   r   r   r!   r!   r"   	get_OHLCV     zDatastream.get_OHLCVc                 C   r   )zGet Open, High, Low and Close prices for a given ticker.

           ticker  - ticker or symbol
           date_from, date_to - date range (used only if "date" is not specified)
        )r   r   r   r   r   Fr   r   r   r!   r!   r"   get_OHLC  r  zDatastream.get_OHLCc                 C   s   | j |d||dddS )zGet Close price for a given ticker.

           ticker  - ticker or symbol
           date_from, date_to - date range (used only if "date" is not specified)
        r   r   Fr   r   r   r!   r!   r"   	get_price  s   zDatastream.get_pricec                 C   s&   |rddg}nt }| jd| |ddS )a   Get a list of all constituents of a given index.

            index_ticker - Datastream ticker for index
            only_list    - request only list of symbols. By default the method
                           retrieves many extra fields with information (various
                           mnemonics and codes). This might pose some problems
                           for large indices like Russel-3000. If only_list=True,
                           then only the list of symbols and names are retrieved.

            NOTE: In contrast to retired DWE interface, DSWS does not support
                  fetching historical lists of constituents.
        MNEMNAMELTr   )
_FLDS_XREFr   )r=   index_ticker	only_listr   r!   r!   r"   get_constituents  s   
zDatastream.get_constituentsc                    s   | j |ddd}tdd |jD }i }tddD ]  fdd	|D }||  j|d
| < qt|d	 }||dkj
dd  }|S )zC Get all listings and their symbols for the given security
        QTEALLTr   c                 S   s   h | ]}|d d qS )N   r!   r(   r!   r!   r"   	<setcomp>	  s    z.Datastream.get_all_listings.<locals>.<setcomp>r      c                    s   i | ]
}|  d |qS )02dr!   )r)   cindr!   r"   r         z/Datastream.get_all_listings.<locals>.<dictcomp>columnsr   r   )axis)r   r   r  r   keysrenamer   r   	swaplevelr   all)r=   r   r2   r  dfcolsr!   r  r"   get_all_listings  s   zDatastream.get_all_listingsc                 C   s   | j |tddS )z8 Get codes and symbols for the given securities
        Tr   )r   r  )r=   r   r!   r!   r"   	get_codes  s   zDatastream.get_codesc                 C   sl   | j |dddd}ttjdd}|j|dd}t|ttjfr4z|j| }W |S  t	y3   Y |S w |S )z[ Get asset types for a given list of symbols
            Note: the method does not
        TYPETF)r   r   AssetTypeNamer   )on)
r   r   Series_ASSET_TYPE_CODESto_framer   r   r   locKeyError)r=   symbolsr2   r   r!   r!   r"   get_asset_types  s   zDatastream.get_asset_typesc                 C   sr   |rddg}nt }| jd| d|dd}d|d< |r2| jd| d|dd}d	|d< t||g}||jd
k S )a    Get list of all contracts for a given futures market

            market_code  - Datastream mnemonic for a market (e.g. LLC for the
                           Brent Crude Oil, whose contracts have mnemonics like
                           LLC0118 for January 2018)
            only_list    - request only list of symbols. By default the method
                           retrieves many extra fields with information (currency,
                           lot size, last trading date, etc). If only_list=True,
                           then only the list of symbols and names are retrieved.
            include_dead - if True, all delisted/expired contracts will be fetched
                           as well. Otherwise only active contracts will be returned.
        r  r  LFUTr  Tr   activer   Fr   )_FLDS_XREF_FUTr   r   r   r  )r=   market_coder	  include_deadr   r2   res2r!   r!   r"   get_futures_contracts*  s   
z Datastream.get_futures_contracts
1951-01-01c           
   	   C   s   | j |d||d}| jd }| j |g d||djdd}i }|jD ]}z| j |d||d }	W n	 ty<   Y q$w |	||< q$t|j S )a   Construct the vintage matrix for a given economic series.
            Requires subscription to Thomson Reuters Economic Point-in-Time (EPiT).

            Vintage matrix represents a DataFrame where columns correspond to a
            particular period (quarter or month) for the reported statistic and
            index represents timestamps at which these values were released by
            the respective official agency. I.e. every line corresponds to all
            available reported values by the given date.

            For example:

            >> DS.get_epit_vintage_matrix('USGDP...D', date_from='2015-01-01')

                        2015-02-15  2015-05-15  2015-08-15  2015-11-15              2015-04-29    16304.80         NaN         NaN         NaN
            2015-05-29    16264.10         NaN         NaN         NaN
            2015-06-24    16287.70         NaN         NaN         NaN
            2015-07-30    16177.30   16270.400         NaN         NaN
            2015-08-27    16177.30   16324.300         NaN         NaN
            2015-09-25    16177.30   16333.600         NaN         NaN
            2015-10-29    16177.30   16333.600   16394.200         NaN
            2015-11-24    16177.30   16333.600   16417.800         NaN

            From the matrix it is seen for example, that the advance GDP estimate
            for 2015-Q1 (corresponding to 2015-02-15) was released on 2015-04-29
            and was equal to 16304.80 (B USD). The first revision (16264.10) has
            happened on 2015-05-29 and the second (16287.70) - on 2015-06-24.
            On 2015-07-30 the advance GDP figure for 2015-Q2 was released
            (16270.400) together with update on the 2015-Q1 value (16177.30)
            and so on.
        REL1r   r   r   )RELD1RELD2RELD3r  )howRELV)r   dropnar   r3   r   r   r8  r   )
r=   mnemonicr   r   rel1date_0reld123r2   r    _tmpr!   r!   r"   get_epit_vintage_matrixD  s    !

z"Datastream.get_epit_vintage_matrixc                    sp   |r| j |d|dd n	| j |d|dd  jd   fddtd|r%d	nd
D }tj| jd d }|S )a&   Return initial estimate and first revisions of a given economic time
            series and a given period.
            Requires subscription to Thomson Reuters Economic Point-in-Time (EPiT).

            "Period" parameter should represent a date which falls within a time
            period of interest, e.g. 2016 Q4 could be requested with the
            period='2016-11-15' for example.

            By default up to 20 values is returned unless argument "relh50" is
            set to True (in which case up to 50 values is returned).
        RELH50T)r   r   RELHr   c                    s8   i | ]} j d |  dkr j d |   j d|  qS )z	RELHD%02dr   z	RELHV%02d)r&  r)   ir   r!   r"   r     s     z1Datastream.get_epit_revisions.<locals>.<dictcomp>r   3   r  zRELHP  r!  )r   ilocr   r   r#  r&  r   )r=   r:  periodrelh50r2   r!   rD  r"   get_epit_revisionsw  s   

zDatastream.get_epit_revisionsc           	         s   |dkrt d|dk rt d fddt|D }|}g }|D ]8}|jddd}d	|j_d
d |jD d t	dd|d< |j
ddd}fdd|jD |_|| q(t| }dD ]}tj|| dd||< qj|S )a   Return the next date of release (NDoR) for a given economic series.
            Could return results for up to 12 releases in advance.

            Returned fields:
              DATE        - Date of release (or start of range where the exact
                            date is not available)
              DATE_LATEST - End of range when a range is given
              TIME_GMT    - Expected time of release (for "official" dates only)
              DATE_FLAG   - Indicates whether the dates are "official" ones from
                            the source, or estimated by Thomson Reuters where
                            "officials" not available
              REF_PERIOD  - Corresponding reference period for the release
              TYPE        - Indicates whether the release is for a new reference
                            period ("NewValue") or an update to a period for which
                            there has already been a release ("ValueUpdate")
           z2Only up to 12 months in advance could be requestedr   z-n_releases smaller than 1 does not make sensec                    s&   g | ]}j  d |d  ddqS )DS.NDORr   Tr   )r   rB  )	mnemonicsr=   r!   r"   r+     s    z5Datastream.get_next_release_dates.<locals>.<listcomp>Tr   Mnemonicc                 S   s   g | ]	}| d d qS )r*   r   )splitr(   r!   r!   r"   r+     s    r   rK  r   	ReleaseNo)r   c                    s   g | ]
}|  d  dqS )r*   r   )r   r(   )ndor_idxr!   r"   r+     r  )DATEDATE_LATEST
REF_PERIODcoerce)errors)rh   r   r   r   r   r   r   r  r'   r   r   r   r   r   r   r/   )	r=   rL  
n_releasesreqs
res_parsedr2   r   xcolr!   )rL  rP  r=   r"   get_next_release_dates  s*   z!Datastream.get_next_release_datesc                 C   s*   | j dg ddd}||d dk dS )z7 List of mnemonics for holidays in different countries HOLIDAYS)r  ENAMEGEOGNGEOGCTr   r  r   r_  )r   sort_values)r=   r2   r!   r!   r"   vacations_list  s   zDatastream.vacations_listc                 C   s   t |tr|g}| j}||j| }t||j}|r'tdd| | j	|j
||dd }t|dkrG|jdddf j|d dS |jdddf jddj|d	d
 d| S )a   Get list of trading dates for a given countries (speficied by ISO-2c)
            Returning dataframe will contain values 1 and NaN, where 1 identifies
            the business day.

            So by multiplying this list with the price time series it will remove
            padded values on non-trading days.

            Example:
                DS.get_trading_days(['US', 'UK', 'RS'], date_from='2010-01-01')
        zUnknowns ISO codes: z, r3  r   Nr   r!  r   r  r_  r  )r   r   ra  r_  isinset
differencer3   r   r   r  r   rF  r%  r   r  r   )r=   	countriesr   r   vacsmnemsmissing_isosr2   r!   r!   r"   get_trading_days  s   
zDatastream.get_trading_days)TN)NN)NNNNFNT)F)Nr   )NNNNFNFF)FF)r1  Nr   )"r4   r5   r6   r7   rX   staticmethodr[   ro   rS   rB   r   r   r\   r   r   r   r   r   r   r   r   r  r  r
  r  r  r)  r0  r?  rI  r[  rC   ra  ri  r!   r!   r!   r"   rD   g   sR    
(



	H

#


G
	
	




3
-
rD   )r7   rx   r^   r   	functoolsr   re   pandasr   rO   rN  r  r,  r$  rZ   r#   r.   rh   r3   rC   rD   r!   r!   r!   r"   <module>   sF    	