diff --git a/README.md b/README.md new file mode 100644 index 0000000..12b4b51 --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +# PyIRess # + +PyIress is a Python interface onto the Iress web services API. See the [Website] (https://www.iress.com/za/company/products/iress-pro-market-data-desktop/). + +### Main Features ### + +* The code allows for easy extraction of the following types of data + Time series information + Dividend information + Index information +* Version 0.1 +* This is the first version and the intention is to add to the list of available functions. +* The following are currenly available: + - time_series : an items times series + - dividends : dividends for an item + - get_many : many of the above + - MarketCapitalizationHistorical : Index constituent information + +### Installation ### + +* Dependencies + zeep + For the [Zeep documentation] (https://python-zeep.readthedocs.io/en/master/). + pandas + For the dependencies of `pandas` please refer to the [pandas documentation](http://pandas.pydata.org/pandas-docs/stable/install.html). + +* To install the latest version + pip install pyiress + + +### Some Notes on Use ### + + Pandas is great for data analysis + + + from pyiress import Iress + import pandas as pd + companyname = "<>" + username = "<>" + password = "<>" + ApplicationID='app' + + iress = Iress(companyname=companyname,username=username,password=password,show_request=False) + tickers=['AGL','BIL'] + exchange = 'JSE' + start_date, end_date = pd.datetime(2012,9,30),pd.datetime(2018,8,30) + data=iress.get_many('time_series',tickers,exchange,start_date,end_date) + data=data[['ClosePrice']] + data.unstack(1).plot() + +### Resources ### + +* Use the help in Iress Pro. Go to Help/API Documentation and select the pertinant resource. + + +### Acknowledgements ### + +* Thanks to Vladimir Filimonov @vfilimonov whe wrote PyDatastream on which this project is based \ No newline at end of file diff --git a/pyiress/__init__.py b/pyiress/__init__.py new file mode 100644 index 0000000..0e6ebd6 --- /dev/null +++ b/pyiress/__init__.py @@ -0,0 +1,2 @@ +### Dummy __init__ file +from .pyiress import * diff --git a/pyiress/pyiress.py b/pyiress/pyiress.py new file mode 100644 index 0000000..b8f0746 --- /dev/null +++ b/pyiress/pyiress.py @@ -0,0 +1,265 @@ +from __future__ import print_function +import zeep +import numpy as np +import pandas as pd +import warnings + +_INFO = """PyIress documentation (GitHub): +https://github.com/ceaza/pyiress""" + +WSDL_URL_GENERIC='http://127.0.0.1:51234/wsdl.aspx?un={username}&cp={companyname}&svc={service}&svr=&pw={password}' + +class PyIressException(Exception): + pass + +class Iress(object): + def __init__(self, companyname,username, password, service='IRESS',raise_on_error=True, show_request=False, + proxy=None, **kwargs): + """Establish a connection to the IRESS Web Services with Version 4 desktop. + + companyname / username / password - credentials for the Iress account. + service - only service for desktop version is IRESS. + raise_on_error - If True then error request will raise a "IressException", + otherwise either empty dataframe or partially + retrieved data will be returned + show_request - If True, then every time a request string will be printed + + A custom WSDL url (if necessary for some reasons) could be provided + via "url" parameter. + """ + import logging.config + self.services=[service] + self.show_request = show_request + if self.show_request: + logging.config.dictConfig({ + 'version': 1, + 'formatters': { + 'verbose': { + 'format': '%(name)s: %(message)s' + } + }, + 'handlers': { + 'console': { + 'level': 'DEBUG', + 'class': 'logging.StreamHandler', + 'formatter': 'verbose', + }, + }, + 'loggers': { + 'zeep.transports': { + 'level': 'DEBUG', + 'propagate': True, + 'handlers': ['console'], + }, + } + }) + else: + logging.config.dictConfig({ + 'version': 1, + 'disable_existing_loggers': True, + }) + self.raise_on_error = raise_on_error + self.last_status = None # Will contain status of last request + WSDL_URL = WSDL_URL_GENERIC.format(companyname=companyname,username=username,password=password,service=service) + self._url = kwargs.pop('url', WSDL_URL) + # Trying to connect + try: + self.client = zeep.Client(wsdl=WSDL_URL, strict=False) + except: + raise PyIressException('Cannot Connect') + +# Create session + login_details={'UserName':username, + 'CompanyName':companyname, + 'Password':password, + 'ApplicationID':'app'} + IRESSSessionStartInputHeader = {"Parameters":login_details} + self.session=self.client.service.IRESSSessionStart(Input=IRESSSessionStartInputHeader) + self.IRESSSessionKey=self.session.Result.DataRows.DataRow[0].IRESSSessionKey + self.UserToken=self.session.Result.DataRows.DataRow[0].UserToken + self.last_response = None + self.header={'Header':{'SessionKey':self.IRESSSessionKey}} + + # Check available data sources + if 'IRESS' not in self.services: + warnings.warn("'IRESS' source is not available for given subscription!") + + @staticmethod + def info(): + print(_INFO) + + def sources(self): + """Return available sources of data. + Curretly only IRESS""" + return self.services + + def version(self): + """Return version of Iress Client.""" + res = self.client.namespaces + return res + + + def _time_series(self,ticker,exchange,start_date,end_date,freq='daily'): + ''' + SecurityCode string Yes No The security code to filter by. + Exchange string Yes No The exchange to filter by. + DataSource string Yes No The data source to filter by. + Frequency string No No The frequency type, one of 'daily', 'weekly', 'monthly', 'quarterly' or 'yearly'. + TimeSeriesFromDate date No No The date to retrieve time series from. + TimeSeriesToDate date No No The date to retrieve time series to. + + ''' + parameters={'Parameters': {'SecurityCode': ticker, + 'Exchange': exchange, + 'Frequency':freq, + 'TimeSeriesFromDate':start_date.strftime('%Y/%m/%d'), + 'TimeSeriesToDate':end_date.strftime('%Y/%m/%d') + } } + + inputs={**self.header, **parameters} + res=self.client.service.TimeSeriesGet2(Input=inputs) + try: + data=zeep.helpers.serialize_object(res.Result.DataRows.DataRow) + df=pd.DataFrame(data) +# print(df.tail()) + df['TimeSeriesDate']=pd.to_datetime(df.TimeSeriesDate) + df=df.set_index('TimeSeriesDate') +# print(df.columns) + except: + df=pd.DataFrame() + + return df + + + def time_series(self,ticker,exchange,start_date,end_date,freq='daily',fields=[]): + ''' + SecurityCode string Yes No The security code to filter by. + Exchange string Yes No The exchange to filter by. + DataSource string Yes No The data source to filter by. + Frequency string No No The frequency type, one of 'daily', 'weekly', 'monthly', 'quarterly' or 'yearly'. + TimeSeriesFromDate date No No The date to retrieve time series from. + TimeSeriesToDate date No No The date to retrieve time series to. + + Available fields - ['OpenPrice', 'HighPrice', 'LowPrice', 'ClosePrice', 'TotalVolume', + 'TotalValue', 'TradeCount', 'AdjustmentFactor', 'MarketVWAP', + 'ShortSold', 'ShortSoldPercent', 'ShortSellPosition', + 'ShortSellPositionPercent', '_value_1', 'exchange'] + ''' + part_date=start_date + data=pd.DataFrame() + while part_date < pd.Timestamp(end_date): + new_data=self._time_series(ticker,exchange,part_date,end_date,freq) + data=pd.concat([data,new_data]) + part_date = data.index.max() + pd.DateOffset(1,'D') + return data + + def dividends(self,ticker,exchange,start_date,end_date,freq=None,index_on='ExDividendDate'): + ''' + SecurityCode string Yes No The security code to filter by. + Exchange string Yes No The exchange to filter by. + DataSource string Yes No The data source to filter by. + Frequency string No No The frequency type, one of 'daily', 'weekly', 'monthly', 'quarterly' or 'yearly'. + TimeSeriesFromDate date No No The date to retrieve time series from. + TimeSeriesToDate date No No The date to retrieve time series to. + + available fields = ['DividendAmount', 'AdjustedDividendAmount', 'FrankedPercent', + 'PayableDate', 'BooksClosingDate', 'DividendType', 'ShareRate', + 'DividendYield', 'DRPPrice', 'DividendDescription', 'DeclarationDate', + 'STCCreditsPerShare', '_value_1', 'exchange'] + ''' + parameters={'Parameters': {'SecurityCode': ticker, + 'Exchange': exchange, + 'PayDateFrom':start_date.strftime('%Y/%m/%d'), + 'PayDateTo':end_date.strftime('%Y/%m/%d') + } } + + inputs={**self.header, **parameters} + res=self.client.service.SecurityDividendGetBySecurity(Input=inputs) + data=zeep.helpers.serialize_object(res.Result.DataRows.DataRow) + df=pd.DataFrame(data) + df[index_on]=pd.to_datetime(df[index_on]) + df=df.set_index(index_on) + + return df + + def MarketCapitalizationHistorical(self,indexcode,ticker,exchange,start_date,end_date): + ''' + Input Parameters + For the items that are nullable input None + + Pos Name Type Nullable? DefaultValue Array? ArraySize Description Alias + 1 IndexCode string Yes No The index to filter by. + 2 SecurityCode string Yes No Security code. + 3 Exchange string Yes No Exchange where the security is listed. + 4 MarketCapitalizationDateFrom date No No The date to retrieve market capitalization history from. + 5 MarketCapitalizationDateTo date No No The date to retrieve market capitalization history to. + + Output Columns + Pos Name Type Nullable? Default Value Description Alias + 1 SecurityCode string No Security code. + 2 Exchange string No Exchange where the security is listed. + 3 GICSCode int32 Yes Global Industry Classification Standard. + 4 MarketCapitalizationDate date No Date at which market capitalization was calculated. + 5 IndexCode string No Index code. + 6 IndexFactor double Yes The index factor. + 7 IndexPoints double Yes Index points. + 8 SharesOnIssue double Yes Number of shares issued for the security at the time market capitalization was calculated. + 9 MarketCapitalizationStartOfDay double Yes Market capitalization at the start of the day. + 10 MarketCapitalizationEndOfDay double Yes Market capitalization at the end of the day. + 11 MarketWeightStartOfDay double Yes Market weight at the start of the day. + 12 MarketWeightEndOfDay double Yes Market weight at the end of the day. + 13 IndexPriceStartOfDay double Yes Index price at the start of the day. + 14 IndexPriceEndOfDay double Yes Index price at the end of the day. + + + ''' + + parameters={'Parameters': {'IndexCode':indexcode, + 'SecurityCode': ticker, + 'Exchange': exchange, + 'MarketCapitalizationDateFrom':start_date.strftime('%Y/%m/%d'), + 'MarketCapitalizationDateTo':end_date.strftime('%Y/%m/%d') + } } + remove_key=[] + for k,v in parameters['Parameters'].items(): + if v==None: + remove_key.append(k) + for k in remove_key: + del parameters['Parameters'][k] + + inputs={**self.header, **parameters} + res = self.client.service.MarketCapitalizationHistoricalGet(Input=inputs) + data=zeep.helpers.serialize_object(res.Result.DataRows.DataRow) + df=pd.DataFrame(data) + return df + + + def get_many(self,data_type,tickers,exchange,start_date,end_date,freq='daily'): + ''' + + ''' + data_list=[] + date_field={'dividends':'ExDividendDate', + 'time_series':'TimeSeriesDate'} + if data_type not in ['dividends','time_series']: + warnings.warn("Not available for this data type") + return + method_to_call = getattr(self, data_type) + for ticker in tickers: + data=method_to_call(ticker,exchange,start_date,end_date,freq) + data=data.reset_index() + data['ticker']=ticker + data['exchange']=exchange + data=data.set_index([date_field[data_type],'ticker']) + data_list.append(data) + df_data=pd.concat(data_list) + return df_data + +if __name__ == "__main__": + pass + + + + + + diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..b88034e --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[metadata] +description-file = README.md diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..b1ae696 --- /dev/null +++ b/setup.py @@ -0,0 +1,35 @@ +from distutils.core import setup +import sys + +if sys.version_info.major < 3: + break +else: + install_requires_list = ['zeep', 'pandas'] + +# Long description to be published in PyPi +LONG_DESCRIPTION = """ +**PyIress** is a Python interface to the **Iress Pro Desktop Web Services +(IPD)** SOAP client (non free), with some convenience functions for retrieving +Iress data specifically. This package requires valid credentials for this +API. + +For the documentation please refer to README.md inside the package or on the +GitHub (https://github.com/ceaza/pyiress/blob/master/README.md). +""" + +_URL = 'https://github.com/ceaza/pyiress' +_VERSION = '0.0.1' + +setup(name='PyIress', + version=_VERSION, + description='Python interface to the **Iress Pro Desktop Web Services API', + long_description=LONG_DESCRIPTION, + url=_URL, + download_url=_URL + '/archive/v' + _VERSION + '.zip', + author='Charles Allderman', + author_email='charles@allderman.com', + license='MIT License', + packages=['pyiress'], + install_requires=install_requires_list, + classifiers=['Programming Language :: Python :: 3', ] + )