earningscall 0.0.12__py3-none-any.whl → 0.0.14__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- earningscall/__init__.py +1 -0
- earningscall/api.py +38 -18
- earningscall/sectors.py +10 -41
- earningscall/symbols.py +5 -41
- {earningscall-0.0.12.dist-info → earningscall-0.0.14.dist-info}/METADATA +35 -7
- earningscall-0.0.14.dist-info/RECORD +14 -0
- earningscall-0.0.12.dist-info/RECORD +0 -14
- {earningscall-0.0.12.dist-info → earningscall-0.0.14.dist-info}/WHEEL +0 -0
- {earningscall-0.0.12.dist-info → earningscall-0.0.14.dist-info}/licenses/LICENSE +0 -0
earningscall/__init__.py
CHANGED
@@ -4,5 +4,6 @@ from earningscall.exports import get_company, get_all_companies, get_sp500_compa
|
|
4
4
|
from earningscall.symbols import Symbols, load_symbols
|
5
5
|
|
6
6
|
api_key: Optional[str] = None
|
7
|
+
enable_requests_cache: bool = True
|
7
8
|
|
8
9
|
__all__ = ["get_company", "get_all_companies", "get_sp500_companies", "Symbols", "load_symbols"]
|
earningscall/api.py
CHANGED
@@ -3,6 +3,7 @@ import os
|
|
3
3
|
from typing import Optional
|
4
4
|
|
5
5
|
import requests
|
6
|
+
from requests_cache import CachedSession
|
6
7
|
|
7
8
|
import earningscall
|
8
9
|
|
@@ -27,15 +28,45 @@ def is_demo_account():
|
|
27
28
|
return get_api_key() == "demo"
|
28
29
|
|
29
30
|
|
30
|
-
def
|
31
|
+
def cache_session() -> CachedSession:
|
32
|
+
return CachedSession(
|
33
|
+
".earningscall_cache",
|
34
|
+
backend="sqlite",
|
35
|
+
cache_control=True,
|
36
|
+
use_temp=True,
|
37
|
+
ignored_parameters=['apikey'],
|
38
|
+
)
|
39
|
+
|
40
|
+
|
41
|
+
def cached_urls():
|
42
|
+
return cache_session().cache.urls()
|
43
|
+
|
44
|
+
|
45
|
+
def purge_cache():
|
46
|
+
return cache_session().cache.clear()
|
47
|
+
|
48
|
+
|
49
|
+
def do_get(path: str, use_cache: bool = False, **kwargs):
|
50
|
+
params = {
|
51
|
+
**api_key_param(),
|
52
|
+
**kwargs.get("params", {}),
|
53
|
+
}
|
54
|
+
url = f"{API_BASE}/{path}"
|
55
|
+
log.debug(f"do_get url: {url} params: {params}")
|
56
|
+
if use_cache and earningscall.enable_requests_cache:
|
57
|
+
return cache_session().get(url, params=params)
|
58
|
+
else:
|
59
|
+
return requests.get(url, params=params)
|
31
60
|
|
61
|
+
|
62
|
+
def get_events(exchange: str, symbol: str):
|
32
63
|
log.debug(f"get_events exchange: {exchange} symbol: {symbol}")
|
33
64
|
params = {
|
34
65
|
**api_key_param(),
|
35
66
|
"exchange": exchange,
|
36
67
|
"symbol": symbol,
|
37
68
|
}
|
38
|
-
response =
|
69
|
+
response = do_get("events", params=params)
|
39
70
|
if response.status_code != 200:
|
40
71
|
return None
|
41
72
|
return response.json()
|
@@ -51,34 +82,23 @@ def get_transcript(exchange: str, symbol: str, year: int, quarter: int) -> Optio
|
|
51
82
|
"year": str(year),
|
52
83
|
"quarter": str(quarter),
|
53
84
|
}
|
54
|
-
response =
|
85
|
+
response = do_get("transcript", params=params)
|
55
86
|
if response.status_code != 200:
|
56
87
|
return None
|
57
88
|
return response.json()
|
58
89
|
|
59
90
|
|
60
|
-
def get_symbols_v1():
|
61
|
-
response = requests.get(f"{API_BASE}/symbols.txt")
|
62
|
-
if response.status_code != 200:
|
63
|
-
return None
|
64
|
-
return response.text
|
65
|
-
|
66
|
-
|
67
91
|
def get_symbols_v2():
|
68
|
-
|
92
|
+
log.debug("get_symbols_v2")
|
93
|
+
response = do_get("symbols-v2.txt", use_cache=True)
|
69
94
|
if response.status_code != 200:
|
70
95
|
return None
|
71
96
|
return response.text
|
72
97
|
|
73
98
|
|
74
99
|
def get_sp500_companies_txt_file():
|
75
|
-
|
100
|
+
log.debug("get_sp500_companies_txt_file")
|
101
|
+
response = do_get("symbols/sp500.txt", use_cache=True)
|
76
102
|
if response.status_code != 200:
|
77
103
|
return None
|
78
104
|
return response.text
|
79
|
-
|
80
|
-
|
81
|
-
# def do_something():
|
82
|
-
# session = CachedSession('demo_cache', cache_control=True)
|
83
|
-
#
|
84
|
-
# # CachedSession()
|
earningscall/sectors.py
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
import json
|
2
1
|
import logging
|
3
|
-
from typing import Optional
|
4
2
|
|
5
3
|
log = logging.getLogger(__file__)
|
6
4
|
sectors_file_name = "sectors.json"
|
@@ -179,14 +177,20 @@ def sector_to_index(_sector: str) -> int:
|
|
179
177
|
|
180
178
|
def index_to_sector(_index: int) -> str:
|
181
179
|
if _index == -1:
|
182
|
-
return "
|
183
|
-
|
180
|
+
return "Unknown"
|
181
|
+
try:
|
182
|
+
return SECTORS_IN_ORDER[_index]
|
183
|
+
except IndexError:
|
184
|
+
return "Unknown"
|
184
185
|
|
185
186
|
|
186
187
|
def index_to_industry(_index: int) -> str:
|
187
188
|
if _index == -1:
|
188
|
-
return "
|
189
|
-
|
189
|
+
return "Unknown"
|
190
|
+
try:
|
191
|
+
return INDUSTRIES_IN_ORDER[_index]
|
192
|
+
except IndexError:
|
193
|
+
return "Unknown"
|
190
194
|
|
191
195
|
|
192
196
|
def industry_to_index(_industry: str) -> int:
|
@@ -194,38 +198,3 @@ def industry_to_index(_industry: str) -> int:
|
|
194
198
|
return INDUSTRIES_IN_ORDER.index(_industry)
|
195
199
|
except ValueError:
|
196
200
|
return -1
|
197
|
-
|
198
|
-
|
199
|
-
class Sectors:
|
200
|
-
|
201
|
-
def __init__(self, sectors: Optional[set] = None, industries: Optional[set] = None):
|
202
|
-
if sectors:
|
203
|
-
self.sectors = sectors
|
204
|
-
else:
|
205
|
-
self.sectors = set()
|
206
|
-
if industries:
|
207
|
-
self.industries = industries
|
208
|
-
else:
|
209
|
-
self.industries = set()
|
210
|
-
|
211
|
-
def add_sector(self, sector: str):
|
212
|
-
if sector is not None:
|
213
|
-
self.sectors.add(sector)
|
214
|
-
|
215
|
-
def add_industry(self, industry: str):
|
216
|
-
if industry is not None:
|
217
|
-
self.industries.add(industry)
|
218
|
-
|
219
|
-
def to_dicts(self) -> dict:
|
220
|
-
return {
|
221
|
-
"sectors": list(self.sectors),
|
222
|
-
"industries": list(self.industries),
|
223
|
-
}
|
224
|
-
|
225
|
-
def to_json(self) -> str:
|
226
|
-
return json.dumps(self.to_dicts())
|
227
|
-
|
228
|
-
@staticmethod
|
229
|
-
def from_json(json_str):
|
230
|
-
data = json.loads(json_str)
|
231
|
-
return Sectors(set(data["sectors"]), set(data["industries"]))
|
earningscall/symbols.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
import json
|
2
2
|
import logging
|
3
|
-
import re
|
4
3
|
from collections import defaultdict
|
5
4
|
from typing import Optional, Iterator, List
|
6
5
|
|
@@ -26,7 +25,10 @@ def exchange_to_index(_exchange: Optional[str]) -> int:
|
|
26
25
|
def index_to_exchange(_index: int) -> str:
|
27
26
|
if _index == -1:
|
28
27
|
return "UNKNOWN"
|
29
|
-
|
28
|
+
try:
|
29
|
+
return EXCHANGES_IN_ORDER[_index]
|
30
|
+
except IndexError:
|
31
|
+
return "UNKNOWN"
|
30
32
|
|
31
33
|
|
32
34
|
security_type_pattern = {
|
@@ -47,21 +49,12 @@ class CompanyInfo:
|
|
47
49
|
|
48
50
|
def __init__(self, **kwargs):
|
49
51
|
self.exchange = None
|
52
|
+
self.symbol = None
|
50
53
|
self.name = None
|
51
|
-
self.security_name = None
|
52
54
|
self.sector = None
|
53
55
|
self.industry = None
|
54
56
|
for key, value in kwargs.items():
|
55
57
|
self.__setattr__(key, value)
|
56
|
-
# If name is not set upon initialization, we'll set it from the security name. Also, include
|
57
|
-
# sanitization (removing "Common Stock" from the security name for example)
|
58
|
-
if not self.name:
|
59
|
-
if self.exchange == "OTC":
|
60
|
-
self.security_name = self.security_name.title()
|
61
|
-
if self.exchange in security_type_pattern:
|
62
|
-
self.name = re.sub(security_type_pattern[self.exchange], "", self.security_name)
|
63
|
-
else:
|
64
|
-
self.name = self.security_name
|
65
58
|
|
66
59
|
def __str__(self):
|
67
60
|
return f"({self.exchange}: {self.symbol} - {self.name})"
|
@@ -160,20 +153,6 @@ class Symbols:
|
|
160
153
|
return json.dumps(self.without_security_names())
|
161
154
|
return json.dumps(self.to_dicts())
|
162
155
|
|
163
|
-
# TODO: Test this
|
164
|
-
# def to_json_v2(self) -> str:
|
165
|
-
# return json.dumps(
|
166
|
-
# [
|
167
|
-
# [exchange_to_index(__symbol.exchange), __symbol.company_info, __symbol.name]
|
168
|
-
# for __symbol in self.get_all()
|
169
|
-
# ]
|
170
|
-
# )
|
171
|
-
|
172
|
-
def to_txt(self) -> str:
|
173
|
-
exchange_symbol_names = [__symbol.to_txt_row() for __symbol in self.get_all()]
|
174
|
-
sorted_rows = sorted(exchange_symbol_names, key=lambda row: row[1])
|
175
|
-
return "\n".join(["\t".join(row) for row in sorted_rows])
|
176
|
-
|
177
156
|
def to_txt_v2(self) -> str:
|
178
157
|
exchange_symbol_names = [__symbol.to_txt_v2_row() for __symbol in self.get_all()]
|
179
158
|
sorted_rows = sorted(exchange_symbol_names, key=lambda row: row[1])
|
@@ -186,21 +165,6 @@ class Symbols:
|
|
186
165
|
__symbols.add(CompanyInfo(**item))
|
187
166
|
return __symbols
|
188
167
|
|
189
|
-
@staticmethod
|
190
|
-
def from_json_v2(json_str):
|
191
|
-
__symbols = Symbols()
|
192
|
-
for [_exchange_index, _symbol, _name] in json.loads(json_str):
|
193
|
-
__symbols.add(CompanyInfo(exchange=index_to_exchange(_exchange_index), symbol=_symbol, name=_name))
|
194
|
-
return __symbols
|
195
|
-
|
196
|
-
@staticmethod
|
197
|
-
def from_txt(txt_str):
|
198
|
-
__symbols = Symbols()
|
199
|
-
for line in txt_str.split("\n"):
|
200
|
-
_exchange_index, _symbol, _name = line.split("\t")
|
201
|
-
__symbols.add(CompanyInfo(exchange=index_to_exchange(int(_exchange_index)), symbol=_symbol, name=_name))
|
202
|
-
return __symbols
|
203
|
-
|
204
168
|
@staticmethod
|
205
169
|
def from_txt_v2(txt_str):
|
206
170
|
__symbols = Symbols()
|
@@ -1,7 +1,7 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: earningscall
|
3
|
-
Version: 0.0.
|
4
|
-
Summary: The EarningsCall Python library.
|
3
|
+
Version: 0.0.14
|
4
|
+
Summary: The EarningsCall Python library provides convenient access to the EarningsCall API. It includes a pre-defined set of classes for API resources that initialize themselves dynamically from API responses.
|
5
5
|
Project-URL: Homepage, https://earningscall.biz
|
6
6
|
Project-URL: Documentation, https://github.com/EarningsCall/earningscall-python
|
7
7
|
Project-URL: Repository, https://github.com/EarningsCall/earningscall-python
|
@@ -31,6 +31,18 @@ License: MIT License
|
|
31
31
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
32
32
|
SOFTWARE.
|
33
33
|
License-File: LICENSE
|
34
|
+
Keywords: earnings call,earnings call transcripts,earnings calls,transcripts
|
35
|
+
Classifier: Development Status :: 3 - Alpha
|
36
|
+
Classifier: Intended Audience :: Developers
|
37
|
+
Classifier: License :: OSI Approved :: MIT License
|
38
|
+
Classifier: Programming Language :: Python :: 3
|
39
|
+
Classifier: Programming Language :: Python :: 3.8
|
40
|
+
Classifier: Programming Language :: Python :: 3.9
|
41
|
+
Classifier: Programming Language :: Python :: 3.10
|
42
|
+
Classifier: Programming Language :: Python :: 3.11
|
43
|
+
Classifier: Programming Language :: Python :: 3.12
|
44
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
45
|
+
Classifier: Typing :: Typed
|
34
46
|
Requires-Python: >=3.8
|
35
47
|
Requires-Dist: dataclasses-json>=0.6.4
|
36
48
|
Requires-Dist: dataclasses>=0.6
|
@@ -40,15 +52,20 @@ Description-Content-Type: text/markdown
|
|
40
52
|
|
41
53
|
# EarningsCall Python Library
|
42
54
|
|
43
|
-
[](https://pypi.
|
55
|
+
[](https://pypi.org/project/earningscall/)
|
44
56
|
[](https://github.com/EarningsCall/earningscall-python/actions?query=branch%3Amaster)
|
45
57
|
[](https://coveralls.io/github/EarningsCall/earningscall-python?branch=master)
|
58
|
+
[](https://pypi.org/project/earningscall/)
|
46
59
|
|
47
60
|
The EarningsCall Python library provides convenient access to the [EarningsCall API](https://earningscall.biz/api-guide) from
|
48
61
|
applications written in the Python language. It includes a pre-defined set of
|
49
62
|
classes for API resources that initialize themselves dynamically from API
|
50
63
|
responses.
|
51
64
|
|
65
|
+
# Requirements
|
66
|
+
|
67
|
+
* Python 3.8+
|
68
|
+
|
52
69
|
# Installation
|
53
70
|
|
54
71
|
You don't need this source code unless you want to modify the package. If you just want to use the package, just run:
|
@@ -57,10 +74,6 @@ You don't need this source code unless you want to modify the package. If you ju
|
|
57
74
|
pip install --upgrade earningscall
|
58
75
|
```
|
59
76
|
|
60
|
-
# Requirements
|
61
|
-
|
62
|
-
* Python 3.8+ (PyPI supported)
|
63
|
-
|
64
77
|
## Get Transcript for a Single Quarter
|
65
78
|
|
66
79
|
```python
|
@@ -151,3 +164,18 @@ for company in get_sp500_companies():
|
|
151
164
|
print(f"{company.company_info} -- {company.company_info.sector} -- {company.company_info.industry}")
|
152
165
|
```
|
153
166
|
|
167
|
+
|
168
|
+
## Advanced
|
169
|
+
|
170
|
+
### Disable Caching
|
171
|
+
|
172
|
+
When you call `get_company("aapl")` to retrieve a company, internally the library retrieves metadata
|
173
|
+
from the EarningsCall API. By default, it caches this metadata on disk in order to speed up subsequent requests.
|
174
|
+
|
175
|
+
If you prefer to disable this local caching behavior, you can do so with this code:
|
176
|
+
|
177
|
+
```python
|
178
|
+
import earningscall
|
179
|
+
|
180
|
+
earningscall.enable_requests_cache = False
|
181
|
+
```
|
@@ -0,0 +1,14 @@
|
|
1
|
+
earningscall/__init__.py,sha256=0mANmPlE7LEWtOGzV2cmmlPfBIWBWlWRDkyqPHJ1jm8,333
|
2
|
+
earningscall/api.py,sha256=guyKJCg949sRYP9FuIUfgo93u6jfC_9wSgJBXVAEwIg,2496
|
3
|
+
earningscall/company.py,sha256=ZNF75htXg37oXtNzwrHH8DoTdyFJ3PBB9qSNBp_vo8c,1943
|
4
|
+
earningscall/errors.py,sha256=EA-d6qIYgQs9csp8JptQiAaYoM0M9HhCGJgKA9GAWPg,440
|
5
|
+
earningscall/event.py,sha256=O7KAE2wO3mBhlEWIFA1choMNGRthYd0gBIvVQOmVALs,692
|
6
|
+
earningscall/exports.py,sha256=i9UWHY6Lq1OzZTZX_1SdNzrNd_PSlPwpB337lGMK4oM,837
|
7
|
+
earningscall/sectors.py,sha256=Xd6DLkAQ_fQkC2s-N9pReC8b_M3iy77OoFftoZj9FWY,5114
|
8
|
+
earningscall/symbols.py,sha256=Fsk9F2SYzn5TUQnL84AO1_xgiMg6G1DyvBGm7-_3LH4,6347
|
9
|
+
earningscall/transcript.py,sha256=rLH2bGrrgUmS8XZGAPUP7msiIcL7f4ABmbnVJWXI28c,329
|
10
|
+
earningscall/utils.py,sha256=Qx8KhlumUdzyBSZRKMS6vpWlb8MGZpLKA4OffJaMdCE,1032
|
11
|
+
earningscall-0.0.14.dist-info/METADATA,sha256=n7yhm8mMOfEblfEMjNsRMD1WM8LXB4kQlsRt_IY4Dpo,7014
|
12
|
+
earningscall-0.0.14.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
|
13
|
+
earningscall-0.0.14.dist-info/licenses/LICENSE,sha256=ktEB_UcRMg2cQlX9wiDs544xWncWizwS9mEZuGsCLrM,1069
|
14
|
+
earningscall-0.0.14.dist-info/RECORD,,
|
@@ -1,14 +0,0 @@
|
|
1
|
-
earningscall/__init__.py,sha256=NX9f2et-q3p8BRGvJB0enRSLlSGecSAwspPxhDsL95Y,298
|
2
|
-
earningscall/api.py,sha256=eK43JazoBNI9YF0OMi5c0DrYv0tVzyOy2ycNyX63y8s,2000
|
3
|
-
earningscall/company.py,sha256=ZNF75htXg37oXtNzwrHH8DoTdyFJ3PBB9qSNBp_vo8c,1943
|
4
|
-
earningscall/errors.py,sha256=EA-d6qIYgQs9csp8JptQiAaYoM0M9HhCGJgKA9GAWPg,440
|
5
|
-
earningscall/event.py,sha256=O7KAE2wO3mBhlEWIFA1choMNGRthYd0gBIvVQOmVALs,692
|
6
|
-
earningscall/exports.py,sha256=i9UWHY6Lq1OzZTZX_1SdNzrNd_PSlPwpB337lGMK4oM,837
|
7
|
-
earningscall/sectors.py,sha256=9kAwTtJEDbWah6lz5io88qRwvCtmGgjQ-MU7rayTmVI,5962
|
8
|
-
earningscall/symbols.py,sha256=EXSPUMr50zZz-Cw9CimrRbVd7F71PB9McgsFC7yJ5S4,7980
|
9
|
-
earningscall/transcript.py,sha256=rLH2bGrrgUmS8XZGAPUP7msiIcL7f4ABmbnVJWXI28c,329
|
10
|
-
earningscall/utils.py,sha256=Qx8KhlumUdzyBSZRKMS6vpWlb8MGZpLKA4OffJaMdCE,1032
|
11
|
-
earningscall-0.0.12.dist-info/METADATA,sha256=Br6DJz0J0ZVy4FrY6qkSYcFoze_unf2ooht9v7D__UQ,5714
|
12
|
-
earningscall-0.0.12.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
|
13
|
-
earningscall-0.0.12.dist-info/licenses/LICENSE,sha256=ktEB_UcRMg2cQlX9wiDs544xWncWizwS9mEZuGsCLrM,1069
|
14
|
-
earningscall-0.0.12.dist-info/RECORD,,
|
File without changes
|
File without changes
|