diff --git a/README.md b/README.md index 83eba56..1eaa8d5 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,29 @@ date ## Demo ![](https://raw.githubusercontent.com/timkpaine/pyEX/main/docs/img/example1.gif) -## Full API +## Rules Engine +`pyEX` implements methods for interacting with the [Rules Engine](https://iexcloud.io/docs/api/#rules-engine-beta). + +```python +rule = { + 'conditions': [['changePercent','>',500], + ['latestPrice','>',100000]], + 'outputs': [{'frequency': 60, + 'method': 'email', + 'to': 'your_email@domain' + }] + } + +c.createRule(rule, 'MyTestRule', 'AAPL', 'all') # returns {"id": , "weight": 2} +c.pauseRule("") +c.resumeRule("") +c.deleteRule("") +``` + +We also provide helper classes in python for constructing rules such that they abide by the rules schema (dictated in the `schema()` helper function) + +## Data +### Full API Please see the [readthedocs](https://pyEX.readthedocs.io) for a full API spec ![](https://raw.githubusercontent.com/timkpaine/pyEX/main/docs/img/rtd.png) diff --git a/pyEX/__init__.py b/pyEX/__init__.py index d631b39..3242ab7 100644 --- a/pyEX/__init__.py +++ b/pyEX/__init__.py @@ -34,6 +34,7 @@ internationalExchanges, internationalExchangesDF, # noqa: F401 sectors, sectorsDF, # noqa: F401 tags, tagsDF) # noqa: F401 +from .rules import schema, create, lookup, pause, resume, delete # noqa: F401 from .stats import stats, statsDF, recent, recentDF, records, recordsDF, summary, summaryDF, daily, dailyDF # noqa: F401 from .stocks import (advancedStats, advancedStatsDF, # noqa: F401 analystRecommendations, analystRecommendationsDF, # noqa: F401 diff --git a/pyEX/account/__init__.py b/pyEX/account/__init__.py index c11735f..e14933b 100644 --- a/pyEX/account/__init__.py +++ b/pyEX/account/__init__.py @@ -16,7 +16,7 @@ def messageBudget(totalMessages=None, token='', version=''): _requireSecret(token) if not isinstance(totalMessages, int): raise PyEXception("`totalMessages` must be integer, got {}({})".format(type(totalMessages), totalMessages)) - return _postJson('account/messagebudget?totalMessages={}'.format(totalMessages), token, version) + return _postJson('account/messagebudget?totalMessages={}'.format(totalMessages), token=token, version=version) def metadata(token='', version=''): @@ -63,7 +63,7 @@ def payAsYouGo(allow=False, token='', version=''): _requireSecret(token) if not isinstance(allow, bool): raise PyEXception("`allow` must be bool, got {}({})".format(type(allow), allow)) - return _postJson('account/messagebudget?allow={}'.format(allow), token, version) + return _postJson('account/messagebudget?allow={}'.format(allow), token=token, version=version) def usage(type=None, token='', version=''): diff --git a/pyEX/client.py b/pyEX/client.py index 1fb8cd7..63f5b52 100644 --- a/pyEX/client.py +++ b/pyEX/client.py @@ -100,6 +100,7 @@ internationalExchanges, internationalExchangesDF, \ sectors, sectorsDF, \ tags, tagsDF +from .rules import schema, create, lookup, pause, resume, delete from .stats import stats, statsDF, \ recent, recentDF, \ records, recordsDF, \ @@ -187,6 +188,13 @@ DEFAULT_API_LIMIT = 5 _INCLUDE_FUNCTIONS = [ + # Rules + ('schema', schema), + ('createRule', create), + ('lookupRule', lookup), + ('pauseRule', pause), + ('resumeRule', resume), + ('deleteRule', delete), # Refdata ('symbols', symbols), ('iexSymbols', iexSymbols), diff --git a/pyEX/common.py b/pyEX/common.py index 1bec247..469c500 100644 --- a/pyEX/common.py +++ b/pyEX/common.py @@ -386,11 +386,18 @@ def _getJson(url, token='', version='', filter=''): return _getJsonOrig(url) -def _postJson(url, token='', version=''): +def _postJson(url, data=None, json=None, token='', version='', token_in_params=True): token = token or os.environ.get('IEX_TOKEN') if version == 'sandbox': - return _postJsonIEXCloudSandbox(url, token, version) - return _postJsonIEXCloud(url, token, version) + return _postJsonIEXCloudSandbox(url, data, json, token, version, token_in_params) + return _postJsonIEXCloud(url, data, json, token, version, token_in_params) + + +def _deleteJson(url, token='', version=''): + token = token or os.environ.get('IEX_TOKEN') + if version == 'sandbox': + return _deleteJsonIEXCloudSandbox(url, token, version) + return _deleteJsonIEXCloud(url, token, version) def _getJsonOrig(url): @@ -409,11 +416,24 @@ def _getJsonIEXCloud(url, token='', version='v1', filter=''): raise PyEXception('Response %d - ' % resp.status_code, resp.text) -def _postJsonIEXCloud(url, token='', version='v1'): +def _postJsonIEXCloud(url, data=None, json=None, token='', version='v1', token_in_params=True): + '''for iex cloud''' + url = _URL_PREFIX2.format(version=version) + url + if token_in_params: + params = {'token': token} + else: + params = {} + resp = requests.post(urlparse(url).geturl(), data=data, json=json, proxies=_PYEX_PROXIES, params=params) + if resp.status_code == 200: + return resp.json() + raise PyEXception('Response %d - ' % resp.status_code, resp.text) + + +def _deleteJsonIEXCloud(url, token='', version='v1'): '''for iex cloud''' url = _URL_PREFIX2.format(version=version) + url params = {'token': token} - resp = requests.post(urlparse(url).geturl(), proxies=_PYEX_PROXIES, params=params) + resp = requests.delete(urlparse(url).geturl(), proxies=_PYEX_PROXIES, params=params) if resp.status_code == 200: return resp.json() raise PyEXception('Response %d - ' % resp.status_code, resp.text) @@ -431,11 +451,24 @@ def _getJsonIEXCloudSandbox(url, token='', version='v1', filter=''): raise PyEXception('Response %d - ' % resp.status_code, resp.text) -def _postJsonIEXCloudSandbox(url, token='', version='v1'): +def _postJsonIEXCloudSandbox(url, data=None, json=None, token='', version='v1', token_in_params=True): + '''for iex cloud''' + url = _URL_PREFIX2_SANDBOX.format(version='v1') + url + if token_in_params: + params = {'token': token} + else: + params = {} + resp = requests.post(urlparse(url).geturl(), data=data, json=json, proxies=_PYEX_PROXIES, params=params) + if resp.status_code == 200: + return resp.json() + raise PyEXception('Response %d - ' % resp.status_code, resp.text) + + +def _deleteJsonIEXCloudSandbox(url, token='', version='v1'): '''for iex cloud''' url = _URL_PREFIX2_SANDBOX.format(version='v1') + url params = {'token': token} - resp = requests.post(urlparse(url).geturl(), proxies=_PYEX_PROXIES, params=params) + resp = requests.delete(urlparse(url).geturl(), proxies=_PYEX_PROXIES, params=params) if resp.status_code == 200: return resp.json() raise PyEXception('Response %d - ' % resp.status_code, resp.text) diff --git a/pyEX/rules/__init__.py b/pyEX/rules/__init__.py new file mode 100644 index 0000000..2c849e0 --- /dev/null +++ b/pyEX/rules/__init__.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +from functools import wraps +from ..common import _getJson, _postJson, _deleteJson, _raiseIfNotStr, PyEXception +from .engine import Rule # noqa: F401 + + +def lookup(lookup='', token='', version=''): + '''Pull the latest schema for data points, notification types, and operators used to construct rules. + + https://iexcloud.io/docs/api/#rules-schema + + Args: + lookup (string); If a schema object has “isLookup”: true, pass the value key to /stable/rules/lookup/{value}. This returns all valid values for the rightValue of a condition. + token (string); Access token + version (string); API version + + Returns: + dict: result + ''' + _raiseIfNotStr(lookup) + if lookup: + return _getJson('rules/lookup/{}'.format(lookup), token, version, None) + return _getJson('rules/schema', token, version, None) + + +@wraps(lookup) +def schema(token='', version=''): + return lookup(token=token, version=version) + + +def create(rule, ruleName, ruleSet, type='any', existingId=None, token='', version=''): + '''This endpoint is used to both create and edit rules. Note that rules run be default after being created. + + Args: + rule (Rule or dict): rule object to create + ruleName (str): name for rule + ruleSet (str): Valid US symbol or the string ANYEVENT. If the string ANYEVENT is passed, the rule will be triggered for any symbol in the system. The cool down period for alerts (frequency) is applied on a per symbol basis. + type (str): Specify either any, where if any condition is true you get an alert, or all, where all conditions must be true to trigger an alert. any is the default value + existingId (Optional[str]): The id of an existing rule only if you are editing the existing rule + + conditions array Required An array of arrays. Each condition array will consist of three values; left condition, operator, right condition. + + Ex: [ [‘latestPrice’, ‘>’, 200.25], [‘peRatio’, ‘<’, 20] ] + outputs array Required An array of one object. The object’s schema is defined for each notification type, and is returned by the notificationTypes array in the /rules/schema endpoint. + Every output object will contain method (which should match the value key of the notificationType, and frequency which is the number of seconds to wait between alerts. + + Ex: [ { method: ‘webhook’, url: ‘https://myserver.com/iexcloud-webhook’, frequency: 60 } ] + additionalKeys array Optional. An array of schema data values to be included in alert message in addition to the data values in the conditions. + + Ex: ['latestPrice', 'peRatio', 'nextEarningsDate'] + ''' + if type not in ('any', 'all'): + raise PyEXception('type must be in (any, all). got: {}'.format(type)) + + if isinstance(rule, Rule): + rule = rule.toJson() + + rule['token'] = token + rule['ruleSet'] = ruleSet + rule['type'] = type + rule['ruleName'] = ruleName + + # Conditions, outputs, and additionalKeys handled by rule object + if 'conditions' not in rule: + raise PyEXception('rule is missing `conditions` key!') + if 'outputs' not in rule: + raise PyEXception('rule is missing `outputs` key!') + + if existingId is not None: + rule['id'] = existingId + return _postJson('rules/create', json=rule, token=token, version=version, token_in_params=False) + + +def pause(ruleId, token='', version=''): + '''You can control the output of rules by pausing and resume per rule id. + + Args: + ruleId (str): The id of an existing rule to puase + ''' + return _postJson('rules/pause', json={"ruleId": ruleId, "token": token}, token=token, version=version, token_in_params=False) + + +def resume(ruleId, token='', version=''): + '''You can control the output of rules by pausing and resume per rule id. + + Args: + ruleId (str): The id of an existing rule to puase + ''' + return _postJson('rules/resume', json={"ruleId": ruleId, "token": token}, token=token, version=version, token_in_params=False) + + +def delete(ruleId, token='', version=''): + '''You can delete a rule by using an __HTTP DELETE__ request. This will stop rule executions and delete the rule from your dashboard. If you only want to temporarily stop a rule, use the pause/resume functionality instead. + + Args: + ruleId (str): The id of an existing rule to puase + ''' + return _deleteJson('rules/{}'.format(ruleId), token=token, version=version) diff --git a/pyEX/rules/engine.py b/pyEX/rules/engine.py new file mode 100644 index 0000000..31db0e8 --- /dev/null +++ b/pyEX/rules/engine.py @@ -0,0 +1,312 @@ +import six +import time +from ..common import PyEXception + + +_DATA_TYPES = [ + 'boolean', + 'date', + 'dynamictime', + 'event', + 'number', + 'number', + 'string' +] + + +def _tryEx(meth, name, userDat=None): + try: + meth() + except TypeError: + if userDat is not None: + raise PyEXception('Construction of {} errored, malformed user data: {}'.format(name, userDat)) + raise PyEXception('Construction of {} errored: malformed data!'.format(name)) + + +class Input(object): + def __init__(self, engine, label, value, type, scope, isLookup=False, weight=0, weightKey=''): + self.engine = engine + self.label = label + self.value = value + self.type = type + self.scope = scope + self.isLookup = isLookup + self.weight = weight + self.weightKey = weightKey + + def toJson(self): + return { + 'label': self.label, + 'value': self.value, + 'type': self.type, + 'scope': self.scope, + 'isLookup': self.isLookup, + 'weight': self.weight, + 'weightKey': self.weightKey + } + + +class Operator(object): + def __init__(self, type, label, value): + self.type = type + self.label = label + self.value = value + + def toJson(self): + return { + 'type': self.type, + 'label': self.label, + 'value': self.value + } + + +class NotificationType(object): + def __init__(self, engine, label, value, weight, enabled, schema=None): + self.engine = engine + self.label = label + self.value = value + self.weight = weight + self.enabled = enabled + self.schema = schema or {} + + def toJson(self): + return { + 'label': self.label, + 'value': self.value, + 'weight': self.weight, + 'enabled': self.enabled, + 'schema': self.schema, + } + + def validate(self, output): + if self.schema: + try: + from jsonschema import validate + validate(instance=output.toJson(), schema=self.schema) + except ImportError: + raise PyEXception('jsonschema package required for schema validation') + + +class Output(object): + def __init__(self, engine, notificationType, **kwargs): + self.engine = engine + self.notificationType = notificationType + self.notificationType.validate(self) + self.kwargs = kwargs + + def toJson(self): + return self.kwargs + + def validate(self): + self.notificationType.validate(self) + + +class Condition(object): + def __init__(self, engine, left_value, operator, right_value): + self.engine = engine + self.left_value = self._constructValue(left_value) + self.operator = self._constructOperator(operator) + self.right_value = self._constructDependentValue(right_value) + self._validate() + + def _constructValue(self, val): + if isinstance(val, Input): + # already fine + return val + return self.engine.input(val) + + def _constructOperator(self, val): + '''this operator is dependent on the type of the left value. + + Note that this will only do partial validation, to avoid extra API calls + ''' + if isinstance(val, Operator): + # already fine + ret = val + else: + ret = self.engine.operator(val) + + if self.left_value.type != ret.type: + raise PyEXception('Input {} does not match operator {}'.format(self.left_value.toJson(), ret.toJson())) + return ret + + def _constructDependentValue(self, val): + '''this value is dependent on the type of the operator + + Note that this will only do partial validation, to avoid extra API calls + ''' + if self.operator.type == 'number': + if not isinstance(val, (int, float)): + raise PyEXception('RValue of type (int, float) expected for operator {}, got {}'.format(self.operator.toJson(), val)) + elif self.operator.type == 'string': + if not isinstance(val, six.string_types): + raise PyEXception('RValue of type str expected for operator {}, got {}'.format(self.operator.toJson(), val)) + elif self.operator.type == 'boolean': + if not isinstance(val, bool): + raise PyEXception('RValue of type bool expected for operator {}, got {}'.format(self.operator.toJson(), val)) + elif self.operator.type == 'time': + # format in HH:MM:SS - HH:MM:SS + try: + left, right = val.split('-') + time.strptime(left.strip(), '%H:%M:%S') + time.strptime(right.strip(), '%H:%M:%S') + except (IndexError, ValueError): + raise PyEXception('Malformed time: {}'.format(val)) + return val + + def validate(self): + '''In this instance, we just need to check that the rvalue + matches the options for the lvalue when lvalue.isLookup is true''' + # TODO + + def toJson(self): + return [self._left_value.value, + self._operator.value, + self._right_value] + + +class Rule(object): + def __init__(self, engine, conditions, outputs, additionalKeys=None): + self._engine = engine + additionalKeys = additionalKeys or [] + + # do some rudimentary initial validation + if not isinstance(conditions, list): + raise PyEXception('conditions must be list of list[str] or Condition') + self._conditions = conditions + + if not isinstance(outputs, list) or len(outputs) != 1 or not isinstance(outputs[0], (Output, dict)): + raise PyEXception('outputs must be List[dict] or List[Output] of length 1') + self._outputs = outputs + + if not isinstance(additionalKeys, list): + raise PyEXception('additionalKeys must be List[str] or List[Input]') + self._additionalKeys = additionalKeys + + self._construct() + + def _construct(self): + _tryEx(self._constructConditions, 'conditions', self._conditions) + _tryEx(self._constructOutputs, 'outputs', self._outputs) + _tryEx(self._constructAdditionalKeys, 'additionalKeys', self._additionalKeys) + + def _constructHelper(self, type, lst): + # Convert x to type + for i, t in enumerate(lst): + if isinstance(t, type): + continue + lst[i] = type(self._engine, *t) + + def _constructConditions(self): + # Convert conditions to Condition + self._constructHelper(Condition, self._conditions) + + def _constructOutputs(self): + # Convert outputs to Output + self._constructHelper(Output, self._outputs) + + def _constructAdditionalKeys(self): + # Convert keys to Input + self._constructHelper(Input, self._additionalKeys) + + def toJson(self): + return { + 'conditions': [c.toJson() for c in self._conditions], + 'outputs': [o.toJson() for o in self._outputs], + 'additionalKeys': [a.value for a in self._additionalKeys] + } + + def validate(self, prompt=True): + ''' + Validate that this rule is correctly formed. Some rules can be asserted at time of + object construction, however some schema validation might require additional API calls, + which is why this method is separated. + ''' + if prompt: + if input('Validating rule, this function may incur additional API usage...type q to quit') == 'q': + return + for x in self._conditions + self._outputs + self._additionalKeys: + x.validate() + + +class RulesEngine(object): + def __init__(self, schema, version, token): + self.reinitialize(schema, version, token) + + def reinitialize(self, schema, version, token): + self._schema = schema + self._version = version + self._token = token + self._construct() + + def _construct(self): + _tryEx(self._constructOperators, 'operators') + _tryEx(self._constructNotifications, 'notificationTypes') + _tryEx(self._constructInputs, 'schema') + + def _constructOperators(self): + self._operators = {} + for k, v in self._schema['operators'].items(): + self._operators[k] = [Operator(self, k, **vv) for vv in v] + + def operator(self, key): + for k in self.operators(): + if k == key: + return self._operators[key] + raise PyEXception('Operator not found: {}'.format(key)) + + def operators(self): + for k in self._operators: + yield k + + def _constructNotifications(self): + self._notifications = {} + for v in self._schema['notificationTypes']: + self._notifications[v['label']] = NotificationType(self, **v) + + def output(self, key): + for k in self.outputs(): + if k == key: + return self._output[key] + raise PyEXception('Output not found: {}'.format(key)) + + def outputs(self): + for k in self._notifications: + yield k + + def _constructInputs(self): + self._inputs = {} + for v in self._schema['schema']: + self._inputs[v['label']] = Input(self, **v) + + def inputs(self): + for k in self._inputs: + yield k + + def input(self, key): + for k in self.inputs(): + if k == key: + return self._inputs[key] + raise PyEXception('Input not found: {}'.format(key)) + + def newRule(self, conditions, outputs, additionalKeys=None): + ''' + Args: + conditions (List[Condition or List[str]]): Each condition array will consist of three values; left condition, operator, right condition. + Ex: [ [‘latestPrice’, ‘>’, 200.25], [‘peRatio’, ‘<’, 20] ] + + outputs (List[Output or List[str]]): The object’s schema is defined for each notification type, and is returned by the notificationTypes array in the /rules/schema endpoint. + Every output object will contain method (which should match the value key of the notificationType, and frequency which is the number of seconds to wait between alerts. + + Ex: [ { method: ‘webhook’, url: ‘https://myserver.com/iexcloud-webhook’, frequency: 60 } ] + + additionalKeys (List[Input or str]): List of schema data values to be included in alert message in addition to the data values in the conditions. + + Ex: ['latestPrice', 'peRatio', 'nextEarningsDate'] + ''' + return Rule( + self, + conditions, + outputs, + additionalKeys or [] + ) diff --git a/pyEX/stocks/corporateActions.py b/pyEX/stocks/corporateActions.py index 5d1188a..003920d 100644 --- a/pyEX/stocks/corporateActions.py +++ b/pyEX/stocks/corporateActions.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from functools import wraps import pandas as pd from ..common import _getJson, _raiseIfNotStr @@ -18,33 +19,18 @@ def bonusIssue(symbol='', refid='', token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' _raiseIfNotStr(symbol) if refid and symbol: - return _getJson('time-series/advanced_bonus/' + symbol + '/' + refid, token, version, filter) + return _getJson('time-series/advanced_bonus/{}/{}'.format(symbol, refid), token, version, filter) elif symbol: - return _getJson('time-series/advanced_bonus/' + symbol, token, version, filter) + return _getJson('time-series/advanced_bonus/{}'.format(symbol), token, version, filter) return _getJson('time-series/advanced_bonus', token, version, filter) +@wraps(bonusIssue) def bonusIssueDF(symbol='', refid='', token='', version='', filter=''): - '''Bonus Issue Obtain up-to-date and detailed information on all new announcements, as well as 12+ years of historical records. - - Updated at 5am, 10am, 8pm UTC daily - - https://iexcloud.io/docs/api/#bonus-issue - - Args: - symbol (string); Symbol to look up - refid (string); Optional. Id that matches the refid field returned in the response object. This allows you to pull a specific event for a symbol. - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' return pd.DataFrame(bonusIssue(symbol, refid, token, version, filter)) @@ -63,33 +49,18 @@ def distribution(symbol='', refid='', token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' _raiseIfNotStr(symbol) if refid and symbol: - return _getJson('time-series/advanced_distribution/' + symbol + '/' + refid, token, version, filter) + return _getJson('time-series/advanced_distribution/{}/{}'.format(symbol, refid), token, version, filter) elif symbol: - return _getJson('time-series/advanced_distribution/' + symbol, token, version, filter) + return _getJson('time-series/advanced_distribution/{}'.format(symbol), token, version, filter) return _getJson('time-series/advanced_distribution', token, version, filter) +@wraps(distribution) def distributionDF(symbol='', refid='', token='', version='', filter=''): - '''Distribution Obtain up-to-date and detailed information on all new announcements, as well as 12+ years of historical records. - - Updated at 5am, 10am, 8pm UTC daily - - https://iexcloud.io/docs/api/#distribution - - Args: - symbol (string); Symbol to look up - refid (string); Optional. Id that matches the refid field returned in the response object. This allows you to pull a specific event for a symbol. - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' return pd.DataFrame(distribution(symbol, refid, token, version, filter)) @@ -108,33 +79,18 @@ def returnOfCapital(symbol='', refid='', token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' _raiseIfNotStr(symbol) if refid and symbol: - return _getJson('time-series/advanced_return_of_capital/' + symbol + '/' + refid, token, version, filter) + return _getJson('time-series/advanced_return_of_capital/{}/{}'.format(symbol, refid), token, version, filter) elif symbol: - return _getJson('time-series/advanced_return_of_capital/' + symbol, token, version, filter) + return _getJson('time-series/advanced_return_of_capital/{}'.format(symbol), token, version, filter) return _getJson('time-series/advanced_return_of_capital', token, version, filter) +@wraps(returnOfCapital) def returnOfCapitalDF(symbol='', refid='', token='', version='', filter=''): - '''Return of capital up-to-date and detailed information on all new announcements, as well as 12+ years of historical records. - - Updated at 5am, 10am, 8pm UTC daily - - https://iexcloud.io/docs/api/#return-of-capital - - Args: - symbol (string); Symbol to look up - refid (string); Optional. Id that matches the refid field returned in the response object. This allows you to pull a specific event for a symbol. - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' return pd.DataFrame(returnOfCapital(symbol, refid, token, version, filter)) @@ -153,33 +109,18 @@ def rightsIssue(symbol='', refid='', token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' _raiseIfNotStr(symbol) if refid and symbol: - return _getJson('time-series/advanced_rights/' + symbol + '/' + refid, token, version, filter) + return _getJson('time-series/advanced_rights/{}/{}'.format(symbol, refid), token, version, filter) elif symbol: - return _getJson('time-series/advanced_rights/' + symbol, token, version, filter) + return _getJson('time-series/advanced_rights/{}'.format(symbol), token, version, filter) return _getJson('time-series/advanced_rights', token, version, filter) +@wraps(rightsIssue) def rightsIssueDF(symbol='', refid='', token='', version='', filter=''): - '''Rights issue up-to-date and detailed information on all new announcements, as well as 12+ years of historical records. - - Updated at 5am, 10am, 8pm UTC daily - - https://iexcloud.io/docs/api/#rights-issue - - Args: - symbol (string); Symbol to look up - refid (string); Optional. Id that matches the refid field returned in the response object. This allows you to pull a specific event for a symbol. - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' return pd.DataFrame(rightsIssue(symbol, refid, token, version, filter)) @@ -198,33 +139,18 @@ def rightToPurchase(symbol='', refid='', token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' _raiseIfNotStr(symbol) if refid and symbol: - return _getJson('time-series/advanced_right_to_purchase/' + symbol + '/' + refid, token, version, filter) + return _getJson('time-series/advanced_right_to_purchase/{}/{}'.format(symbol, refid), token, version, filter) elif symbol: - return _getJson('time-series/advanced_right_to_purchase/' + symbol, token, version, filter) + return _getJson('time-series/advanced_right_to_purchase/{}'.format(symbol), token, version, filter) return _getJson('time-series/advanced_right_to_purchase', token, version, filter) +@wraps(rightToPurchase) def rightToPurchaseDF(symbol='', refid='', token='', version='', filter=''): - '''Right to purchase up-to-date and detailed information on all new announcements, as well as 12+ years of historical records. - - Updated at 5am, 10am, 8pm UTC daily - - https://iexcloud.io/docs/api/#right-to-purchase - - Args: - symbol (string); Symbol to look up - refid (string); Optional. Id that matches the refid field returned in the response object. This allows you to pull a specific event for a symbol. - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' return pd.DataFrame(rightToPurchase(symbol, refid, token, version, filter)) @@ -243,33 +169,18 @@ def securityReclassification(symbol='', refid='', token='', version='', filter=' filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' _raiseIfNotStr(symbol) if refid and symbol: - return _getJson('time-series/advanced_security_reclassification/' + symbol + '/' + refid, token, version, filter) + return _getJson('time-series/advanced_security_reclassification/{}/{}'.format(symbol, refid), token, version, filter) elif symbol: - return _getJson('time-series/advanced_security_reclassification/' + symbol, token, version, filter) + return _getJson('time-series/advanced_security_reclassification/{}'.format(symbol), token, version, filter) return _getJson('time-series/advanced_security_reclassification', token, version, filter) +@wraps(securityReclassification) def securityReclassificationDF(symbol='', refid='', token='', version='', filter=''): - '''Security reclassification up-to-date and detailed information on all new announcements, as well as 12+ years of historical records. - - Updated at 5am, 10am, 8pm UTC daily - - https://iexcloud.io/docs/api/#security-reclassification - - Args: - symbol (string); Symbol to look up - refid (string); Optional. Id that matches the refid field returned in the response object. This allows you to pull a specific event for a symbol. - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' return pd.DataFrame(securityReclassification(symbol, refid, token, version, filter)) @@ -288,33 +199,18 @@ def securitySwap(symbol='', refid='', token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' _raiseIfNotStr(symbol) if refid and symbol: - return _getJson('time-series/advanced_security_swap/' + symbol + '/' + refid, token, version, filter) + return _getJson('time-series/advanced_security_swap/{}/{}'.format(symbol, refid), token, version, filter) elif symbol: - return _getJson('time-series/advanced_security_swap/' + symbol, token, version, filter) + return _getJson('time-series/advanced_security_swap/{}'.format(symbol), token, version, filter) return _getJson('time-series/advanced_security_swap', token, version, filter) +@wraps(securitySwap) def securitySwapDF(symbol='', refid='', token='', version='', filter=''): - '''Security Swap up-to-date and detailed information on all new announcements, as well as 12+ years of historical records. - - Updated at 5am, 10am, 8pm UTC daily - - https://iexcloud.io/docs/api/#security-swap - - Args: - symbol (string); Symbol to look up - refid (string); Optional. Id that matches the refid field returned in the response object. This allows you to pull a specific event for a symbol. - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' return pd.DataFrame(securitySwap(symbol, refid, token, version, filter)) @@ -333,33 +229,18 @@ def spinoff(symbol='', refid='', token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' _raiseIfNotStr(symbol) if refid and symbol: - return _getJson('time-series/advanced_spinoff/' + symbol + '/' + refid, token, version, filter) + return _getJson('time-series/advanced_spinoff/{}/{}'.format(symbol, refid), token, version, filter) elif symbol: - return _getJson('time-series/advanced_spinoff/' + symbol, token, version, filter) + return _getJson('time-series/advanced_spinoff/{}'.format(symbol), token, version, filter) return _getJson('time-series/advanced_spinoff', token, version, filter) +@wraps(spinoff) def spinoffDF(symbol='', refid='', token='', version='', filter=''): - '''Security spinoff up-to-date and detailed information on all new announcements, as well as 12+ years of historical records. - - Updated at 5am, 10am, 8pm UTC daily - - https://iexcloud.io/docs/api/#spinoff - - Args: - symbol (string); Symbol to look up - refid (string); Optional. Id that matches the refid field returned in the response object. This allows you to pull a specific event for a symbol. - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' return pd.DataFrame(spinoff(symbol, refid, token, version, filter)) @@ -378,31 +259,16 @@ def splits(symbol='', refid='', token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' _raiseIfNotStr(symbol) if refid and symbol: - return _getJson('time-series/advanced_splits/' + symbol + '/' + refid, token, version, filter) + return _getJson('time-series/advanced_splits/{}/{}'.format(symbol, refid), token, version, filter) elif symbol: - return _getJson('time-series/advanced_splits/' + symbol, token, version, filter) + return _getJson('time-series/advanced_splits/{}'.format(symbol), token, version, filter) return _getJson('time-series/advanced_splits', token, version, filter) +@wraps(splits) def splitsDF(symbol='', refid='', token='', version='', filter=''): - '''Security splits up-to-date and detailed information on all new announcements, as well as 12+ years of historical records. - - Updated at 5am, 10am, 8pm UTC daily - - https://iexcloud.io/docs/api/#splits - - Args: - symbol (string); Symbol to look up - refid (string); Optional. Id that matches the refid field returned in the response object. This allows you to pull a specific event for a symbol. - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' return pd.DataFrame(splits(symbol, refid, token, version, filter)) diff --git a/pyEX/stocks/fundamentals.py b/pyEX/stocks/fundamentals.py index bcc52f4..cd2091c 100644 --- a/pyEX/stocks/fundamentals.py +++ b/pyEX/stocks/fundamentals.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from functools import wraps import pandas as pd import numpy as np from ..common import _expire, _TIMEFRAME_DIVSPLIT, _getJson, _raiseIfNotStr, PyEXception, _reindex, _toDatetime, _checkPeriodLast, _UTC @@ -21,31 +22,15 @@ def balanceSheet(symbol, period='quarter', last=1, token='', version='', filter= filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' _raiseIfNotStr(symbol) _checkPeriodLast(period, last) return _getJson('stock/{}/balance-sheet?period={}&last={}'.format(symbol, period, last), token, version, filter) +@wraps(balanceSheet) def balanceSheetDF(symbol, period='quarter', last=1, token='', version='', filter=''): - '''Pulls balance sheet data. Available quarterly (4 quarters) and annually (4 years) - - https://iexcloud.io/docs/api/#balance-sheet - Updates at 8am, 9am UTC daily - - - Args: - symbol (string); Ticker to request - period (string); Period, either 'annual' or 'quarter' - last (int); Number of records to fetch, up to 12 for 'quarter' and 4 for 'annual' - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' val = balanceSheet(symbol, period, last, token, version, filter) df = pd.io.json.json_normalize(val, 'balancesheet', 'symbol') _toDatetime(df) @@ -70,31 +55,15 @@ def cashFlow(symbol, period='quarter', last=1, token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' _raiseIfNotStr(symbol) _checkPeriodLast(period, last) return _getJson('stock/{}/cash-flow?period={}&last={}'.format(symbol, period, last), token, version, filter) +@wraps(cashFlow) def cashFlowDF(symbol, period='quarter', last=1, token='', version='', filter=''): - '''Pulls cash flow data. Available quarterly (4 quarters) or annually (4 years). - - https://iexcloud.io/docs/api/#cash-flow - Updates at 8am, 9am UTC daily - - - Args: - symbol (string); Ticker to request - period (string); Period, either 'annual' or 'quarter' - last (int); Number of records to fetch, up to 12 for 'quarter' and 4 for 'annual' - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' val = cashFlow(symbol, period, last, token, version, filter) df = pd.io.json.json_normalize(val, 'cashflow', 'symbol') _toDatetime(df) @@ -117,7 +86,7 @@ def dividends(symbol, timeframe='ytd', token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' _raiseIfNotStr(symbol) if timeframe not in _TIMEFRAME_DIVSPLIT: @@ -133,21 +102,8 @@ def _dividendsToDF(d): return df +@wraps(dividends) def dividendsDF(symbol, timeframe='ytd', token='', version='', filter=''): - '''Dividend history - - https://iexcloud.io/docs/api/#dividends - Updated at 9am UTC every day - - Args: - symbol (string); Ticker to request - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' d = dividends(symbol, timeframe, token, version, filter) df = _dividendsToDF(d) return df @@ -169,7 +125,7 @@ def earnings(symbol, period='quarter', last=1, field='', token='', version='', f filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' _raiseIfNotStr(symbol) _checkPeriodLast(period, last) @@ -189,23 +145,8 @@ def _earningsToDF(e): return df +@wraps(earnings) def earningsDF(symbol, period='quarter', last=1, field='', token='', version='', filter=''): - '''Earnings data for a given company including the actual EPS, consensus, and fiscal period. Earnings are available quarterly (last 4 quarters) and annually (last 4 years). - - https://iexcloud.io/docs/api/#earnings - Updates at 9am, 11am, 12pm UTC every day - - Args: - symbol (string); Ticker to request - period (string); Period, either 'annual' or 'quarter' - last (int); Number of records to fetch, up to 12 for 'quarter' and 4 for 'annual' - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' e = earnings(symbol, period, last, field, token, version, filter) df = _earningsToDF(e) return df @@ -226,7 +167,7 @@ def financials(symbol, period='quarter', token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' _raiseIfNotStr(symbol) _checkPeriodLast(period, 1) @@ -244,22 +185,8 @@ def _financialsToDF(f): return df +@wraps(financials) def financialsDF(symbol, period='quarter', token='', version='', filter=''): - '''Pulls income statement, balance sheet, and cash flow data from the four most recent reported quarters. - - https://iexcloud.io/docs/api/#financials - Updates at 8am, 9am UTC daily - - Args: - symbol (string); Ticker to request - period (string); Period, either 'annual' or 'quarter' - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' f = financials(symbol, period, token, version, filter) df = _financialsToDF(f) return df @@ -281,30 +208,15 @@ def incomeStatement(symbol, period='quarter', last=1, token='', version='', filt filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' _raiseIfNotStr(symbol) _checkPeriodLast(period, last) return _getJson('stock/{}/income?period={}&last={}'.format(symbol, period, last), token, version, filter) +@wraps(incomeStatement) def incomeStatementDF(symbol, period='quarter', last=1, token='', version='', filter=''): - '''Pulls income statement data. Available quarterly (4 quarters) or annually (4 years). - - https://iexcloud.io/docs/api/#income-statement - Updates at 8am, 9am UTC daily - - Args: - symbol (string); Ticker to request - period (string); Period, either 'annual' or 'quarter' - last (int); Number of records to fetch, up to 12 for 'quarter' and 4 for 'annual' - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' val = incomeStatement(symbol, period, last, token, version, filter) df = pd.io.json.json_normalize(val, 'income', 'symbol') _toDatetime(df) @@ -326,7 +238,7 @@ def stockSplits(symbol, timeframe='ytd', token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' _raiseIfNotStr(symbol) if timeframe not in _TIMEFRAME_DIVSPLIT: @@ -342,21 +254,8 @@ def _splitsToDF(s): return df +@wraps(stockSplits) def stockSplitsDF(symbol, timeframe='ytd', token='', version='', filter=''): - '''Stock split history - - https://iexcloud.io/docs/api/#splits - Updated at 9am UTC every day - - Args: - symbol (string); Ticker to request - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' s = stockSplits(symbol, timeframe, token, version, filter) df = _splitsToDF(s) return df diff --git a/pyEX/stocks/marketInfo.py b/pyEX/stocks/marketInfo.py index f434f36..0266d82 100644 --- a/pyEX/stocks/marketInfo.py +++ b/pyEX/stocks/marketInfo.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from functools import wraps import pandas as pd from ..common import _expire, _LIST_OPTIONS, _COLLECTION_TAGS, _getJson, _raiseIfNotStr, PyEXception, _strOrDate, _reindex, _toDatetime, _UTC, _EST @@ -18,29 +19,15 @@ def collections(tag, collectionName, token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' if tag not in _COLLECTION_TAGS: raise PyEXception('Tag must be in %s' % str(_COLLECTION_TAGS)) return _getJson('stock/market/collection/' + tag + '?collectionName=' + collectionName, token, version, filter) +@wraps(collections) def collectionsDF(tag, query, token='', version='', filter=''): - '''Returns an array of quote objects for a given collection type. Currently supported collection types are sector, tag, and list - - - https://iexcloud.io/docs/api/#collections - - Args: - tag (string); Sector, Tag, or List - collectionName (string); Associated name for tag - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' df = pd.DataFrame(collections(tag, query, token, version, filter)) _toDatetime(df) _reindex(df, 'symbol') @@ -62,27 +49,13 @@ def earningsToday(token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' return _getJson('stock/market/today-earnings', token, version, filter) +@wraps(earningsToday) def earningsTodayDF(token='', version='', filter=''): - '''Returns earnings that will be reported today as two arrays: before the open bto and after market close amc. - Each array contains an object with all keys from earnings, a quote object, and a headline key. - - https://iexcloud.io/docs/api/#earnings-today - Updates at 9am, 11am, 12pm UTC daily - - - Args: - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' x = earningsToday(token, version, filter) z = [] for k in x: @@ -114,26 +87,13 @@ def ipoToday(token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' return _getJson('stock/market/today-ipos', token, version, filter) +@wraps(ipoToday) def ipoTodayDF(token='', version='', filter=''): - '''This returns a list of upcoming or today IPOs scheduled for the current and next month. The response is split into two structures: - rawData and viewData. rawData represents all available data for an IPO. viewData represents data structured for display to a user. - - https://iexcloud.io/docs/api/#ipo-calendar - 10am, 10:30am UTC daily - - Args: - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' val = ipoToday(token, version, filter) if val: df = pd.io.json.json_normalize(val, 'rawData') @@ -158,26 +118,13 @@ def ipoUpcoming(token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' return _getJson('stock/market/upcoming-ipos', token, version, filter) +@wraps(ipoUpcoming) def ipoUpcomingDF(token='', version='', filter=''): - '''This returns a list of upcoming or today IPOs scheduled for the current and next month. The response is split into two structures: - rawData and viewData. rawData represents all available data for an IPO. viewData represents data structured for display to a user. - - https://iexcloud.io/docs/api/#ipo-calendar - 10am, 10:30am UTC daily - - Args: - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' val = ipoUpcoming(token, version, filter) if val: df = pd.io.json.json_normalize(val, 'rawData') @@ -202,29 +149,15 @@ def list(option='mostactive', token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' if option not in _LIST_OPTIONS: raise PyEXception('Option must be in %s' % str(_LIST_OPTIONS)) return _getJson('stock/market/list/' + option, token, version, filter) +@wraps(list) def listDF(option='mostactive', token='', version='', filter=''): - '''Returns an array of quotes for the top 10 symbols in a specified list. - - - https://iexcloud.io/docs/api/#list - Updated intraday - - Args: - option (string); Option to query - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' df = pd.DataFrame(list(option, token, version, filter)) _toDatetime(df) _reindex(df, 'symbol') @@ -243,25 +176,13 @@ def marketVolume(token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' return _getJson('market/', token, version, filter) +@wraps(marketVolume) def marketVolumeDF(token='', version='', filter=''): - '''This endpoint returns real time traded volume on U.S. markets. - - https://iexcloud.io/docs/api/#market-volume-u-s - 7:45am-5:15pm ET Mon-Fri - - Args: - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' df = pd.DataFrame(marketVolume()) _toDatetime(df, cols=[], tcols=['lastUpdated']) return df @@ -279,25 +200,13 @@ def marketOhlc(token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' return _getJson('stock/market/ohlc', token, version, filter) +@wraps(marketOhlc) def marketOhlcDF(token='', version='', filter=''): - '''Returns the official open and close for whole market. - - https://iexcloud.io/docs/api/#news - 9:30am-5pm ET Mon-Fri - - Args: - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' x = marketOhlc(token, version, filter) data = [] for key in x: @@ -323,26 +232,13 @@ def marketYesterday(token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' return _getJson('stock/market/previous', token, version, filter) +@wraps(marketYesterday) def marketYesterdayDF(token='', version='', filter=''): - '''This returns previous day adjusted price data for whole market - - https://iexcloud.io/docs/api/#previous-day-prices - Available after 4am ET Tue-Sat - - Args: - symbol (string); Ticker to request - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' x = marketYesterday(token, version, filter) data = [] for key in x: @@ -366,25 +262,13 @@ def sectorPerformance(token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' return _getJson('stock/market/sector-performance', token, version, filter) +@wraps(sectorPerformance) def sectorPerformanceDF(token='', version='', filter=''): - '''This returns an array of each sector and performance for the current trading day. Performance is based on each sector ETF. - - https://iexcloud.io/docs/api/#sector-performance - 8am-5pm ET Mon-Fri - - Args: - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' df = pd.DataFrame(sectorPerformance(token, version, filter)) _toDatetime(df, cols=[], tcols=['lastUpdated']) _reindex(df, 'name') @@ -406,7 +290,7 @@ def marketShortInterest(date=None, token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result ''' if date: date = _strOrDate(date) @@ -414,22 +298,8 @@ def marketShortInterest(date=None, token='', version='', filter=''): return _getJson('stock/market/short-interest', token, version, filter) +@wraps(marketShortInterest) def marketShortInterestDF(date=None, token='', version='', filter=''): - '''The consolidated market short interest positions in all IEX-listed securities are included in the IEX Short Interest Report. - - The report data will be published daily at 4:00pm ET. - - https://iexcloud.io/docs/api/#listed-short-interest-list-in-dev - - Args: - date (datetime); Effective Datetime - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' df = pd.DataFrame(marketShortInterest(date, token, version, filter)) _toDatetime(df) return df @@ -448,7 +318,8 @@ def upcomingEvents(symbol='', refid='', token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result + ''' _raiseIfNotStr(symbol) if symbol: @@ -456,20 +327,8 @@ def upcomingEvents(symbol='', refid='', token='', version='', filter=''): return _getJson('stock/market/upcoming-events', token, version, filter) +@wraps(upcomingEvents) def upcomingEventsDF(symbol='', token='', version='', filter=''): - '''This will return all upcoming estimates, dividends, splits for a given symbol or the market. If market is passed for the symbol, IPOs will also be included. - - https://iexcloud.io/docs/api/#upcoming-events - - Args: - symbol (string); Symbol to look up - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' return pd.io.json.json_normalize(upcomingEvents(symbol=symbol, token=token, version=version, filter=filter)) @@ -486,7 +345,8 @@ def upcomingEarnings(symbol='', refid='', token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result + ''' _raiseIfNotStr(symbol) if symbol: @@ -494,20 +354,8 @@ def upcomingEarnings(symbol='', refid='', token='', version='', filter=''): return _getJson('stock/market/upcoming-earnings', token, version, filter) +@wraps(upcomingEarnings) def upcomingEarningsDF(symbol='', token='', version='', filter=''): - '''This will return all upcoming estimates, dividends, splits for a given symbol or the market. If market is passed for the symbol, IPOs will also be included. - - https://iexcloud.io/docs/api/#upcoming-events - - Args: - symbol (string); Symbol to look up - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' return pd.io.json.json_normalize(upcomingEarnings(symbol=symbol, token=token, version=version, filter=filter)) @@ -524,7 +372,8 @@ def upcomingDividends(symbol='', refid='', token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result + ''' _raiseIfNotStr(symbol) if symbol: @@ -532,20 +381,8 @@ def upcomingDividends(symbol='', refid='', token='', version='', filter=''): return _getJson('stock/market/upcoming-dividends', token, version, filter) +@wraps(upcomingDividends) def upcomingDividendsDF(symbol='', token='', version='', filter=''): - '''This will return all upcoming estimates, dividends, splits for a given symbol or the market. If market is passed for the symbol, IPOs will also be included. - - https://iexcloud.io/docs/api/#upcoming-events - - Args: - symbol (string); Symbol to look up - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' return pd.io.json.json_normalize(upcomingDividends(symbol=symbol, token=token, version=version, filter=filter)) @@ -562,7 +399,8 @@ def upcomingSplits(symbol='', refid='', token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result + ''' _raiseIfNotStr(symbol) if symbol: @@ -570,20 +408,8 @@ def upcomingSplits(symbol='', refid='', token='', version='', filter=''): return _getJson('stock/market/upcoming-splits', token, version, filter) +@wraps(upcomingSplits) def upcomingSplitsDF(symbol='', token='', version='', filter=''): - '''This will return all upcoming estimates, dividends, splits for a given symbol or the market. If market is passed for the symbol, IPOs will also be included. - - https://iexcloud.io/docs/api/#upcoming-events - - Args: - symbol (string); Symbol to look up - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' return pd.io.json.json_normalize(upcomingSplits(symbol=symbol, token=token, version=version, filter=filter)) @@ -600,7 +426,8 @@ def upcomingIPOs(symbol='', refid='', token='', version='', filter=''): filter (string); filters: https://iexcloud.io/docs/api/#filter-results Returns: - dict: result + dict or DataFrame: result + ''' _raiseIfNotStr(symbol) if symbol: @@ -608,18 +435,6 @@ def upcomingIPOs(symbol='', refid='', token='', version='', filter=''): return _getJson('stock/market/upcoming-ipos', token, version, filter) +@wraps(upcomingIPOs) def upcomingIPOsDF(symbol='', token='', version='', filter=''): - '''This will return all upcoming estimates, dividends, splits for a given symbol or the market. If market is passed for the symbol, IPOs will also be included. - - https://iexcloud.io/docs/api/#upcoming-events - - Args: - symbol (string); Symbol to look up - token (string); Access token - version (string); API version - filter (string); filters: https://iexcloud.io/docs/api/#filter-results - - Returns: - DataFrame: result - ''' return pd.io.json.json_normalize(upcomingIPOs(symbol=symbol, token=token, version=version, filter=filter)) diff --git a/setup.py b/setup.py index a81fd00..5c0e879 100644 --- a/setup.py +++ b/setup.py @@ -28,6 +28,7 @@ def get_version(file, name='__version__'): 'pandas>=0.22', 'pytz>=2019.1', 'requests>=2.21.0', + 'six', 'socketIO-client-nexus>=0.7.6', 'sseclient>=0.0.22', 'temporal-cache>=0.1.1',