kensho-kfinance 2.0.1__tar.gz → 2.1.2__tar.gz
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.
Potentially problematic release.
This version of kensho-kfinance might be problematic. Click here for more details.
- {kensho_kfinance-2.0.1/kensho_kfinance.egg-info → kensho_kfinance-2.1.2}/PKG-INFO +3 -1
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2/kensho_kfinance.egg-info}/PKG-INFO +3 -1
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kensho_kfinance.egg-info/SOURCES.txt +3 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kensho_kfinance.egg-info/requires.txt +2 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/CHANGELOG.md +12 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/batch_request_handling.py +10 -10
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/constants.py +16 -7
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/fetch.py +84 -40
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/kfinance.py +129 -28
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/meta_classes.py +9 -8
- kensho_kfinance-2.1.2/kfinance/tests/conftest.py +32 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tests/test_batch_requests.py +13 -7
- kensho_kfinance-2.1.2/kfinance/tests/test_client.py +54 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tests/test_fetch.py +8 -2
- kensho_kfinance-2.1.2/kfinance/tests/test_group_objects.py +32 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tests/test_tools.py +1 -27
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_business_relationship_from_identifier.py +2 -1
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_capitalization_from_identifier.py +2 -1
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_company_id_from_identifier.py +2 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_cusip_from_ticker.py +2 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_earnings_call_datetimes_from_identifier.py +2 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_financial_line_item_from_identifier.py +2 -1
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_financial_statement_from_identifier.py +2 -1
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_history_metadata_from_identifier.py +2 -1
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_info_from_identifier.py +2 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_isin_from_ticker.py +2 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_latest.py +2 -1
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_n_quarters_ago.py +2 -1
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_prices_from_identifier.py +2 -1
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_security_id_from_identifier.py +2 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_trading_item_id_from_identifier.py +2 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tool_calling/shared_models.py +2 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/version.py +2 -2
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/pyproject.toml +3 -1
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/.coveragerc +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/.github/workflows/ci-lint.yml +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/.github/workflows/ci-test.yml +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/.github/workflows/python-publish.yml +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/.gitignore +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/.readthedocs.yaml +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/AUTHORS.md +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/CODE_OF_CONDUCT.md +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/CONTRIBUTING.md +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/LICENSE +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/README.md +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/docs/build_tool_calling_documentation.py +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/docs/conf.py +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/docs/index.rst +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/docs/kfinance.rst +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/docs/requirements.txt +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/docs/templates/apidoc/package.rst_t +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/docs/templates/apidoc/toc.rst_t +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/docs/tool_calling.rst +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/justfile +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kensho_kfinance.egg-info/dependency_links.txt +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kensho_kfinance.egg-info/top_level.txt +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/__init__.py +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/prompt.py +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/py.typed +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/server_thread.py +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tests/__init__.py +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tests/test_objects.py +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tool_calling/README.md +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/kfinance/tool_calling/__init__.py +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/scripts/copyright_line_check.sh +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/scripts/lint.sh +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/scripts/test.sh +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/setup.cfg +0 -0
- {kensho_kfinance-2.0.1 → kensho_kfinance-2.1.2}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kensho-kfinance
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.2
|
|
4
4
|
Summary: Python CLI for kFinance
|
|
5
5
|
Author-email: Luke Brown <luke.brown@kensho.com>, Michelle Keoy <michelle.keoy@kensho.com>, Keith Page <keith.page@kensho.com>, Matthew Rosen <matthew.rosen@kensho.com>, Nick Roshdieh <nick.roshdieh@kensho.com>
|
|
6
6
|
Project-URL: source, https://github.com/kensho-technologies/kfinance
|
|
@@ -12,6 +12,7 @@ Requires-Python: >=3.10
|
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
14
|
License-File: AUTHORS.md
|
|
15
|
+
Requires-Dist: cachetools<6,>=5.5
|
|
15
16
|
Requires-Dist: langchain-core>=0.3.15
|
|
16
17
|
Requires-Dist: langchain-google-genai<3,>=2.1.0
|
|
17
18
|
Requires-Dist: numpy>=1.22.4
|
|
@@ -33,6 +34,7 @@ Requires-Dist: pytest-cov<7,>=6.0.0; extra == "dev"
|
|
|
33
34
|
Requires-Dist: requests_mock<2,>=1.12; extra == "dev"
|
|
34
35
|
Requires-Dist: ruff<1,>=0.9.4; extra == "dev"
|
|
35
36
|
Requires-Dist: time_machine<3,>=2.1; extra == "dev"
|
|
37
|
+
Requires-Dist: types-cachetools<6,>=5.5; extra == "dev"
|
|
36
38
|
Dynamic: license-file
|
|
37
39
|
|
|
38
40
|
# kFinance
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kensho-kfinance
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.2
|
|
4
4
|
Summary: Python CLI for kFinance
|
|
5
5
|
Author-email: Luke Brown <luke.brown@kensho.com>, Michelle Keoy <michelle.keoy@kensho.com>, Keith Page <keith.page@kensho.com>, Matthew Rosen <matthew.rosen@kensho.com>, Nick Roshdieh <nick.roshdieh@kensho.com>
|
|
6
6
|
Project-URL: source, https://github.com/kensho-technologies/kfinance
|
|
@@ -12,6 +12,7 @@ Requires-Python: >=3.10
|
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
14
|
License-File: AUTHORS.md
|
|
15
|
+
Requires-Dist: cachetools<6,>=5.5
|
|
15
16
|
Requires-Dist: langchain-core>=0.3.15
|
|
16
17
|
Requires-Dist: langchain-google-genai<3,>=2.1.0
|
|
17
18
|
Requires-Dist: numpy>=1.22.4
|
|
@@ -33,6 +34,7 @@ Requires-Dist: pytest-cov<7,>=6.0.0; extra == "dev"
|
|
|
33
34
|
Requires-Dist: requests_mock<2,>=1.12; extra == "dev"
|
|
34
35
|
Requires-Dist: ruff<1,>=0.9.4; extra == "dev"
|
|
35
36
|
Requires-Dist: time_machine<3,>=2.1; extra == "dev"
|
|
37
|
+
Requires-Dist: types-cachetools<6,>=5.5; extra == "dev"
|
|
36
38
|
Dynamic: license-file
|
|
37
39
|
|
|
38
40
|
# kFinance
|
|
@@ -37,8 +37,11 @@ kfinance/py.typed
|
|
|
37
37
|
kfinance/server_thread.py
|
|
38
38
|
kfinance/version.py
|
|
39
39
|
kfinance/tests/__init__.py
|
|
40
|
+
kfinance/tests/conftest.py
|
|
40
41
|
kfinance/tests/test_batch_requests.py
|
|
42
|
+
kfinance/tests/test_client.py
|
|
41
43
|
kfinance/tests/test_fetch.py
|
|
44
|
+
kfinance/tests/test_group_objects.py
|
|
42
45
|
kfinance/tests/test_objects.py
|
|
43
46
|
kfinance/tests/test_tools.py
|
|
44
47
|
kfinance/tool_calling/README.md
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v2.1.2
|
|
4
|
+
- Allow batch executor to handle multiple requests
|
|
5
|
+
|
|
6
|
+
## v2.1.1
|
|
7
|
+
- Use cachetools cache
|
|
8
|
+
|
|
9
|
+
## v2.1.0
|
|
10
|
+
- Filter llm tools by user permissions
|
|
11
|
+
|
|
12
|
+
## v2.0.2
|
|
13
|
+
- Add Client.tickers() method for ticker industry filters for ISRCS and GICS
|
|
14
|
+
|
|
3
15
|
## v2.0.1
|
|
4
16
|
- Fix readthedocs integration for llm tools.
|
|
5
17
|
|
|
@@ -72,19 +72,19 @@ def add_methods_of_singular_class_to_iterable_class(singular_cls: Type[T]) -> Ca
|
|
|
72
72
|
def process_in_batch(
|
|
73
73
|
method: Callable, self: IterableKfinanceClass, *args: Any, **kwargs: Any
|
|
74
74
|
) -> dict:
|
|
75
|
-
results = {}
|
|
76
75
|
kfinance_api_client = self.kfinance_api_client
|
|
77
76
|
with kfinance_api_client.batch_request_header(batch_size=len(self)):
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
77
|
+
results = dict(
|
|
78
|
+
zip(
|
|
79
|
+
self,
|
|
80
|
+
[
|
|
81
|
+
process_in_thread_pool(
|
|
82
|
+
kfinance_api_client.thread_pool, method, obj, *args, **kwargs
|
|
83
|
+
)
|
|
84
|
+
for obj in self
|
|
85
|
+
],
|
|
87
86
|
)
|
|
87
|
+
)
|
|
88
88
|
|
|
89
89
|
return results
|
|
90
90
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from datetime import date
|
|
2
|
-
from enum import Enum
|
|
3
2
|
from itertools import chain
|
|
4
|
-
from typing import TypedDict
|
|
3
|
+
from typing import NamedTuple, TypedDict
|
|
5
4
|
|
|
6
5
|
from strenum import StrEnum
|
|
7
6
|
|
|
@@ -21,13 +20,13 @@ class HistoryMetadata(TypedDict):
|
|
|
21
20
|
first_trade_date: date
|
|
22
21
|
|
|
23
22
|
|
|
24
|
-
class IdentificationTriple(
|
|
23
|
+
class IdentificationTriple(NamedTuple):
|
|
25
24
|
trading_item_id: int
|
|
26
25
|
security_id: int
|
|
27
26
|
company_id: int
|
|
28
27
|
|
|
29
28
|
|
|
30
|
-
class Capitalization(
|
|
29
|
+
class Capitalization(StrEnum):
|
|
31
30
|
"""The capitalization type"""
|
|
32
31
|
|
|
33
32
|
market_cap = "market_cap"
|
|
@@ -35,7 +34,7 @@ class Capitalization(Enum):
|
|
|
35
34
|
shares_outstanding = "shares_outstanding"
|
|
36
35
|
|
|
37
36
|
|
|
38
|
-
class PeriodType(
|
|
37
|
+
class PeriodType(StrEnum):
|
|
39
38
|
"""The period type"""
|
|
40
39
|
|
|
41
40
|
annual = "annual"
|
|
@@ -44,7 +43,7 @@ class PeriodType(Enum):
|
|
|
44
43
|
ytd = "ytd"
|
|
45
44
|
|
|
46
45
|
|
|
47
|
-
class Periodicity(
|
|
46
|
+
class Periodicity(StrEnum):
|
|
48
47
|
"""The frequency or interval at which the historical data points are sampled or aggregated. Periodicity is not the same as the date range. The date range specifies the time span over which the data is retrieved, while periodicity determines how the data within that date range is aggregated."""
|
|
49
48
|
|
|
50
49
|
day = "day"
|
|
@@ -53,7 +52,7 @@ class Periodicity(Enum):
|
|
|
53
52
|
year = "year"
|
|
54
53
|
|
|
55
54
|
|
|
56
|
-
class StatementType(
|
|
55
|
+
class StatementType(StrEnum):
|
|
57
56
|
"""The type of financial statement"""
|
|
58
57
|
|
|
59
58
|
balance_sheet = "balance_sheet"
|
|
@@ -88,6 +87,16 @@ class BusinessRelationshipType(StrEnum):
|
|
|
88
87
|
client_services = "client_services"
|
|
89
88
|
|
|
90
89
|
|
|
90
|
+
class Permission(StrEnum):
|
|
91
|
+
EarningsPermission = "EarningsPermission"
|
|
92
|
+
GICSPermission = "GICSPermission"
|
|
93
|
+
IDPermission = "IDPermission"
|
|
94
|
+
ISCRSPermission = "ISCRSPermission"
|
|
95
|
+
PricingPermission = "PricingPermission"
|
|
96
|
+
RelationshipPermission = "RelationshipPermission"
|
|
97
|
+
StatementsPermission = "StatementsPermission"
|
|
98
|
+
|
|
99
|
+
|
|
91
100
|
class YearAndQuarter(TypedDict):
|
|
92
101
|
year: int
|
|
93
102
|
quarter: int
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from concurrent.futures import ThreadPoolExecutor
|
|
2
2
|
from contextlib import contextmanager
|
|
3
|
+
import logging
|
|
3
4
|
from time import time
|
|
4
5
|
from typing import Callable, Generator, Optional
|
|
5
6
|
from uuid import uuid4
|
|
@@ -13,6 +14,7 @@ from .constants import (
|
|
|
13
14
|
IndustryClassification,
|
|
14
15
|
Periodicity,
|
|
15
16
|
PeriodType,
|
|
17
|
+
Permission,
|
|
16
18
|
)
|
|
17
19
|
|
|
18
20
|
|
|
@@ -23,6 +25,8 @@ try:
|
|
|
23
25
|
except ImportError:
|
|
24
26
|
kfinance_version = "dev"
|
|
25
27
|
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
26
30
|
|
|
27
31
|
DEFAULT_API_HOST: str = "https://kfinance.kensho.com"
|
|
28
32
|
DEFAULT_API_VERSION: int = 1
|
|
@@ -86,6 +90,7 @@ class KFinanceApiClient:
|
|
|
86
90
|
self.user_agent_source = "object_oriented"
|
|
87
91
|
self._batch_id: str | None = None
|
|
88
92
|
self._batch_size: str | None = None
|
|
93
|
+
self._user_permissions: set[Permission] | None = None
|
|
89
94
|
|
|
90
95
|
@contextmanager
|
|
91
96
|
def batch_request_header(self, batch_size: int) -> Generator:
|
|
@@ -127,6 +132,9 @@ class KFinanceApiClient:
|
|
|
127
132
|
# nosemgrep: python.jwt.security.unverified-jwt-decode.unverified-jwt-decode
|
|
128
133
|
options={"verify_signature": False},
|
|
129
134
|
).get("exp")
|
|
135
|
+
# When the access token gets refreshed, also refresh user permissions in case they
|
|
136
|
+
# have been updated.
|
|
137
|
+
self._refresh_user_permissions()
|
|
130
138
|
return self._access_token
|
|
131
139
|
|
|
132
140
|
def _get_access_token_via_refresh_token(self) -> str:
|
|
@@ -169,6 +177,33 @@ class KFinanceApiClient:
|
|
|
169
177
|
response.raise_for_status()
|
|
170
178
|
return response.json().get("access_token")
|
|
171
179
|
|
|
180
|
+
@property
|
|
181
|
+
def user_permissions(self) -> set[Permission]:
|
|
182
|
+
"""Return the permissions that the current user holds."""
|
|
183
|
+
|
|
184
|
+
if self._user_permissions is None:
|
|
185
|
+
self._refresh_user_permissions()
|
|
186
|
+
# _refresh_user_permissions updates self._user_permissions in place
|
|
187
|
+
assert self._user_permissions is not None
|
|
188
|
+
return self._user_permissions
|
|
189
|
+
|
|
190
|
+
def _refresh_user_permissions(self) -> None:
|
|
191
|
+
"""Fetches user permissions and stores them as KfinanceApiClient._user_permissions."""
|
|
192
|
+
|
|
193
|
+
user_permission_dict = self.fetch_permissions()
|
|
194
|
+
self._user_permissions = set()
|
|
195
|
+
for permission_str in user_permission_dict["permissions"]:
|
|
196
|
+
try:
|
|
197
|
+
self._user_permissions.add(Permission[permission_str])
|
|
198
|
+
except KeyError:
|
|
199
|
+
logger.warning(
|
|
200
|
+
"Could not resolve %s to a member of the Permission enum. "
|
|
201
|
+
"%s may be a new type of permission that still needs to be "
|
|
202
|
+
"added to the enum.",
|
|
203
|
+
permission_str,
|
|
204
|
+
permission_str,
|
|
205
|
+
)
|
|
206
|
+
|
|
172
207
|
def fetch(self, url: str) -> dict:
|
|
173
208
|
"""Does the request and auth"""
|
|
174
209
|
|
|
@@ -191,6 +226,11 @@ class KFinanceApiClient:
|
|
|
191
226
|
response.raise_for_status()
|
|
192
227
|
return response.json()
|
|
193
228
|
|
|
229
|
+
def fetch_permissions(self) -> dict[str, list[str]]:
|
|
230
|
+
"""Return the permissions of the user."""
|
|
231
|
+
url = f"{self.url_base}users/permissions"
|
|
232
|
+
return self.fetch(url)
|
|
233
|
+
|
|
194
234
|
def fetch_id_triple(self, identifier: str, exchange_code: Optional[str] = None) -> dict:
|
|
195
235
|
"""Get the ID triple from [identifier]."""
|
|
196
236
|
url = f"{self.url_base}id/{identifier}"
|
|
@@ -241,7 +281,7 @@ class KFinanceApiClient:
|
|
|
241
281
|
f"{self.url_base}pricing/{trading_item_id}/"
|
|
242
282
|
f"{start_date if start_date is not None else 'none'}/"
|
|
243
283
|
f"{end_date if end_date is not None else 'none'}/"
|
|
244
|
-
f"{periodicity
|
|
284
|
+
f"{periodicity if periodicity else 'none'}/"
|
|
245
285
|
f"{'adjusted' if is_adjusted else 'unadjusted'}"
|
|
246
286
|
)
|
|
247
287
|
return self.fetch(url)
|
|
@@ -278,7 +318,7 @@ class KFinanceApiClient:
|
|
|
278
318
|
f"{self.url_base}price_chart/{trading_item_id}/"
|
|
279
319
|
f"{start_date if start_date is not None else 'none'}/"
|
|
280
320
|
f"{end_date if end_date is not None else 'none'}/"
|
|
281
|
-
f"{periodicity
|
|
321
|
+
f"{periodicity if periodicity else 'none'}/"
|
|
282
322
|
f"{'adjusted' if is_adjusted else 'unadjusted'}"
|
|
283
323
|
)
|
|
284
324
|
|
|
@@ -306,7 +346,7 @@ class KFinanceApiClient:
|
|
|
306
346
|
"""Get a specified financial statement for a specified duration."""
|
|
307
347
|
url = (
|
|
308
348
|
f"{self.url_base}statements/{company_id}/{statement_type}/"
|
|
309
|
-
f"{period_type
|
|
349
|
+
f"{period_type if period_type else 'none'}/"
|
|
310
350
|
f"{start_year if start_year is not None else 'none'}/"
|
|
311
351
|
f"{end_year if end_year is not None else 'none'}/"
|
|
312
352
|
f"{start_quarter if start_quarter is not None else 'none'}/"
|
|
@@ -327,7 +367,7 @@ class KFinanceApiClient:
|
|
|
327
367
|
"""Get a specified financial line item for a specified duration."""
|
|
328
368
|
url = (
|
|
329
369
|
f"{self.url_base}line_item/{company_id}/{line_item}/"
|
|
330
|
-
f"{period_type
|
|
370
|
+
f"{period_type if period_type else 'none'}/"
|
|
331
371
|
f"{start_year if start_year is not None else 'none'}/"
|
|
332
372
|
f"{end_year if end_year is not None else 'none'}/"
|
|
333
373
|
f"{start_quarter if start_quarter is not None else 'none'}/"
|
|
@@ -358,10 +398,12 @@ class KFinanceApiClient:
|
|
|
358
398
|
self,
|
|
359
399
|
country_iso_code: str,
|
|
360
400
|
state_iso_code: Optional[str] = None,
|
|
361
|
-
) ->
|
|
401
|
+
) -> list[IdentificationTriple]:
|
|
362
402
|
"""Fetch ticker geography groups"""
|
|
363
|
-
return self.
|
|
364
|
-
|
|
403
|
+
return self._tickers_response_to_id_triple(
|
|
404
|
+
self.fetch_geography_groups(
|
|
405
|
+
country_iso_code=country_iso_code, state_iso_code=state_iso_code, fetch_ticker=True
|
|
406
|
+
)
|
|
365
407
|
)
|
|
366
408
|
|
|
367
409
|
def fetch_company_geography_groups(
|
|
@@ -381,13 +423,13 @@ class KFinanceApiClient:
|
|
|
381
423
|
url = f"{self.url_base}{'ticker_groups' if fetch_ticker else 'trading_item_groups'}/exchange/{exchange_code}"
|
|
382
424
|
return self.fetch(url)
|
|
383
425
|
|
|
384
|
-
def fetch_ticker_exchange_groups(
|
|
385
|
-
self, exchange_code: str
|
|
386
|
-
) -> dict[str, list[IdentificationTriple]]:
|
|
426
|
+
def fetch_ticker_exchange_groups(self, exchange_code: str) -> list[IdentificationTriple]:
|
|
387
427
|
"""Fetch ticker exchange groups"""
|
|
388
|
-
return self.
|
|
389
|
-
|
|
390
|
-
|
|
428
|
+
return self._tickers_response_to_id_triple(
|
|
429
|
+
self.fetch_exchange_groups(
|
|
430
|
+
exchange_code=exchange_code,
|
|
431
|
+
fetch_ticker=True,
|
|
432
|
+
)
|
|
391
433
|
)
|
|
392
434
|
|
|
393
435
|
def fetch_trading_item_exchange_groups(self, exchange_code: str) -> dict[str, list[int]]:
|
|
@@ -397,13 +439,32 @@ class KFinanceApiClient:
|
|
|
397
439
|
fetch_ticker=False,
|
|
398
440
|
)
|
|
399
441
|
|
|
442
|
+
@staticmethod
|
|
443
|
+
def _tickers_response_to_id_triple(
|
|
444
|
+
tickers_response: dict[str, list[dict]],
|
|
445
|
+
) -> list[IdentificationTriple]:
|
|
446
|
+
"""For fetch ticker cases with a dict[str, list[dict]] response, return a list[IdentificationTriple].
|
|
447
|
+
|
|
448
|
+
For example, with a given fetch tickers response:
|
|
449
|
+
{"tickers" : [{"trading_item_id": 1, "security_id": 1, "company_id": 1}, {"trading_item_id": 2,"security_id": 2,"company_id": 2}]},
|
|
450
|
+
return [[1, 1, 1], [2, 2, 2]].
|
|
451
|
+
"""
|
|
452
|
+
return [
|
|
453
|
+
IdentificationTriple(
|
|
454
|
+
trading_item_id=ticker["trading_item_id"],
|
|
455
|
+
security_id=ticker["security_id"],
|
|
456
|
+
company_id=ticker["company_id"],
|
|
457
|
+
)
|
|
458
|
+
for ticker in tickers_response["tickers"]
|
|
459
|
+
]
|
|
460
|
+
|
|
400
461
|
def fetch_ticker_combined(
|
|
401
462
|
self,
|
|
402
463
|
country_iso_code: Optional[str] = None,
|
|
403
464
|
state_iso_code: Optional[str] = None,
|
|
404
465
|
simple_industry: Optional[str] = None,
|
|
405
466
|
exchange_code: Optional[str] = None,
|
|
406
|
-
) ->
|
|
467
|
+
) -> list[IdentificationTriple]:
|
|
407
468
|
"""Fetch tickers using combined filters route"""
|
|
408
469
|
if (
|
|
409
470
|
country_iso_code is None
|
|
@@ -414,11 +475,11 @@ class KFinanceApiClient:
|
|
|
414
475
|
raise RuntimeError("Invalid parameters: No parameters provided or all set to none")
|
|
415
476
|
elif country_iso_code is None and state_iso_code is not None:
|
|
416
477
|
raise RuntimeError(
|
|
417
|
-
"Invalid parameters:
|
|
478
|
+
"Invalid parameters: country_iso_code must be provided with a state_iso_code value"
|
|
418
479
|
)
|
|
419
480
|
else:
|
|
420
481
|
url = f"{self.url_base}ticker_groups/filters/geo/{str(country_iso_code).lower()}/{str(state_iso_code).lower()}/simple/{str(simple_industry).lower()}/exchange/{str(exchange_code).lower()}"
|
|
421
|
-
return self.fetch(url)
|
|
482
|
+
return self._tickers_response_to_id_triple(self.fetch(url))
|
|
422
483
|
|
|
423
484
|
def fetch_companies_from_business_relationship(
|
|
424
485
|
self, company_id: int, relationship_type: BusinessRelationshipType
|
|
@@ -443,22 +504,11 @@ class KFinanceApiClient:
|
|
|
443
504
|
url = f"{self.url_base}relationship/{company_id}/{relationship_type}"
|
|
444
505
|
return self.fetch(url)
|
|
445
506
|
|
|
446
|
-
def fetch_from_industry_code(
|
|
447
|
-
self,
|
|
448
|
-
industry_code: str,
|
|
449
|
-
industry_classification: IndustryClassification,
|
|
450
|
-
fetch_ticker: bool = True,
|
|
451
|
-
) -> dict[str, list]:
|
|
452
|
-
"""Fetches a list of companies or identification triples that are classified in the given industry_code and industry_classification."""
|
|
453
|
-
|
|
454
|
-
url = f"{self.url_base}{'ticker_groups' if fetch_ticker else 'company_groups'}/industry/{industry_classification}/{industry_code}"
|
|
455
|
-
return self.fetch(url)
|
|
456
|
-
|
|
457
507
|
def fetch_ticker_from_industry_code(
|
|
458
508
|
self,
|
|
459
509
|
industry_code: str,
|
|
460
510
|
industry_classification: IndustryClassification,
|
|
461
|
-
) ->
|
|
511
|
+
) -> list[IdentificationTriple]:
|
|
462
512
|
"""Fetches a list of identification triples that are classified in the given industry_code and industry_classification.
|
|
463
513
|
|
|
464
514
|
Returns a dictionary of shape {"tickers": List[{“company_id”: <company_id>, “security_id”: <security_id>, “trading_item_id”: <trading_item_id>}]}.
|
|
@@ -466,14 +516,11 @@ class KFinanceApiClient:
|
|
|
466
516
|
:type industry_code: str
|
|
467
517
|
:param industry_classification: The type of industry_classification to filter on.
|
|
468
518
|
:type industry_classification: IndustryClassification
|
|
469
|
-
:return: A
|
|
470
|
-
:rtype:
|
|
519
|
+
:return: A list of identification triples [company_id, security_id, trading_item_id] that are classified in the given industry_code and industry_classification.
|
|
520
|
+
:rtype: list[IdentificationTriple]
|
|
471
521
|
"""
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
industry_classification=industry_classification,
|
|
475
|
-
fetch_ticker=True,
|
|
476
|
-
)
|
|
522
|
+
url = f"{self.url_base}ticker_groups/industry/{industry_classification}/{industry_code}"
|
|
523
|
+
return self._tickers_response_to_id_triple(self.fetch(url))
|
|
477
524
|
|
|
478
525
|
def fetch_company_from_industry_code(
|
|
479
526
|
self,
|
|
@@ -490,8 +537,5 @@ class KFinanceApiClient:
|
|
|
490
537
|
:return: A dictionary containing the list of companies that are classified in the given industry_code and industry_classification.
|
|
491
538
|
:rtype: dict[str, list[int]]
|
|
492
539
|
"""
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
industry_classification=industry_classification,
|
|
496
|
-
fetch_ticker=False,
|
|
497
|
-
)
|
|
540
|
+
url = f"{self.url_base}company_groups/industry/{industry_classification}/{industry_code}"
|
|
541
|
+
return self.fetch(url)
|
|
@@ -22,6 +22,7 @@ from .batch_request_handling import add_methods_of_singular_class_to_iterable_cl
|
|
|
22
22
|
from .constants import (
|
|
23
23
|
HistoryMetadata,
|
|
24
24
|
IdentificationTriple,
|
|
25
|
+
IndustryClassification,
|
|
25
26
|
LatestPeriods,
|
|
26
27
|
Periodicity,
|
|
27
28
|
YearAndQuarter,
|
|
@@ -233,10 +234,9 @@ class Company(CompanyFunctionsMetaClass):
|
|
|
233
234
|
primary_security_id = self.kfinance_api_client.fetch_primary_security(self.company_id)[
|
|
234
235
|
"primary_security"
|
|
235
236
|
]
|
|
236
|
-
|
|
237
|
+
return Security(
|
|
237
238
|
kfinance_api_client=self.kfinance_api_client, security_id=primary_security_id
|
|
238
239
|
)
|
|
239
|
-
return self.primary_security
|
|
240
240
|
|
|
241
241
|
@cached_property
|
|
242
242
|
def securities(self) -> Securities:
|
|
@@ -246,10 +246,7 @@ class Company(CompanyFunctionsMetaClass):
|
|
|
246
246
|
:rtype: Securities
|
|
247
247
|
"""
|
|
248
248
|
security_ids = self.kfinance_api_client.fetch_securities(self.company_id)["securities"]
|
|
249
|
-
self.
|
|
250
|
-
kfinance_api_client=self.kfinance_api_client, security_ids=security_ids
|
|
251
|
-
)
|
|
252
|
-
return self.securities
|
|
249
|
+
return Securities(kfinance_api_client=self.kfinance_api_client, security_ids=security_ids)
|
|
253
250
|
|
|
254
251
|
@cached_property
|
|
255
252
|
def latest_earnings_call(self) -> None:
|
|
@@ -268,8 +265,7 @@ class Company(CompanyFunctionsMetaClass):
|
|
|
268
265
|
:return: a dict with containing: name, status, type, simple industry, number of employees, founding date, webpage, address, city, zip code, state, country, & iso_country
|
|
269
266
|
:rtype: dict
|
|
270
267
|
"""
|
|
271
|
-
|
|
272
|
-
return self.info
|
|
268
|
+
return self.kfinance_api_client.fetch_info(self.company_id)
|
|
273
269
|
|
|
274
270
|
@property
|
|
275
271
|
def name(self) -> str:
|
|
@@ -539,6 +535,23 @@ class Ticker(DelegatedCompanyFunctionsMetaClass):
|
|
|
539
535
|
"Neither an identifier nor an identification triple (company id, security id, & trading item id) were passed in"
|
|
540
536
|
)
|
|
541
537
|
|
|
538
|
+
@property
|
|
539
|
+
def id_triple(self) -> IdentificationTriple:
|
|
540
|
+
"""Returns a unique identifier triple for the Ticker object."""
|
|
541
|
+
return IdentificationTriple(
|
|
542
|
+
company_id=self.company_id,
|
|
543
|
+
security_id=self.security_id,
|
|
544
|
+
trading_item_id=self.trading_item_id,
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
def __hash__(self) -> int:
|
|
548
|
+
return hash(self.id_triple)
|
|
549
|
+
|
|
550
|
+
def __eq__(self, other: Any) -> bool:
|
|
551
|
+
if not isinstance(other, Ticker):
|
|
552
|
+
return False
|
|
553
|
+
return self.id_triple == other.id_triple
|
|
554
|
+
|
|
542
555
|
def __str__(self) -> str:
|
|
543
556
|
"""String representation for the ticker object"""
|
|
544
557
|
str_attributes = []
|
|
@@ -991,13 +1004,33 @@ class Tickers(set):
|
|
|
991
1004
|
super().__init__(
|
|
992
1005
|
Ticker(
|
|
993
1006
|
kfinance_api_client=kfinance_api_client,
|
|
994
|
-
company_id=id_triple
|
|
995
|
-
security_id=id_triple
|
|
996
|
-
trading_item_id=id_triple
|
|
1007
|
+
company_id=id_triple.company_id,
|
|
1008
|
+
security_id=id_triple.security_id,
|
|
1009
|
+
trading_item_id=id_triple.trading_item_id,
|
|
997
1010
|
)
|
|
998
1011
|
for id_triple in id_triples
|
|
999
1012
|
)
|
|
1000
1013
|
|
|
1014
|
+
def intersection(self, *s: Iterable[Any]) -> Tickers:
|
|
1015
|
+
"""Returns the intersection of Tickers objects"""
|
|
1016
|
+
for obj in s:
|
|
1017
|
+
if not isinstance(obj, Tickers):
|
|
1018
|
+
raise ValueError("Can only intersect Tickers object with other Tickers object.")
|
|
1019
|
+
|
|
1020
|
+
self_triples = {t.id_triple for t in self}
|
|
1021
|
+
set_triples = []
|
|
1022
|
+
|
|
1023
|
+
for ticker_set in s:
|
|
1024
|
+
set_triples.append({t.id_triple for t in ticker_set})
|
|
1025
|
+
common_triples = self_triples.intersection(*set_triples)
|
|
1026
|
+
|
|
1027
|
+
return Tickers(kfinance_api_client=self.kfinance_api_client, id_triples=common_triples)
|
|
1028
|
+
|
|
1029
|
+
def __and__(self, other: Any) -> "Tickers":
|
|
1030
|
+
if not isinstance(other, Tickers):
|
|
1031
|
+
raise ValueError("Can only combine Tickers objects with other Tickers objects.")
|
|
1032
|
+
return self.intersection(other)
|
|
1033
|
+
|
|
1001
1034
|
def companies(self) -> Companies:
|
|
1002
1035
|
"""Build a group of company objects from the group of tickers
|
|
1003
1036
|
|
|
@@ -1126,7 +1159,16 @@ class Client:
|
|
|
1126
1159
|
if self._tools is None:
|
|
1127
1160
|
from kfinance.tool_calling import ALL_TOOLS
|
|
1128
1161
|
|
|
1129
|
-
self._tools = [
|
|
1162
|
+
self._tools = []
|
|
1163
|
+
# Add tool to _tools if the user has permissions to use it.
|
|
1164
|
+
for tool_cls in ALL_TOOLS:
|
|
1165
|
+
tool = tool_cls(kfinance_client=self) # type: ignore[call-arg]
|
|
1166
|
+
if (
|
|
1167
|
+
tool.required_permission is None
|
|
1168
|
+
or tool.required_permission in self.kfinance_api_client.user_permissions
|
|
1169
|
+
):
|
|
1170
|
+
self._tools.append(tool)
|
|
1171
|
+
|
|
1130
1172
|
return self._tools
|
|
1131
1173
|
|
|
1132
1174
|
@property
|
|
@@ -1212,31 +1254,90 @@ class Client:
|
|
|
1212
1254
|
state_iso_code: Optional[str] = None,
|
|
1213
1255
|
simple_industry: Optional[str] = None,
|
|
1214
1256
|
exchange_code: Optional[str] = None,
|
|
1257
|
+
sic: Optional[str] = None,
|
|
1258
|
+
naics: Optional[str] = None,
|
|
1259
|
+
nace: Optional[str] = None,
|
|
1260
|
+
anzsic: Optional[str] = None,
|
|
1261
|
+
spcapiqetf: Optional[str] = None,
|
|
1262
|
+
spratings: Optional[str] = None,
|
|
1263
|
+
gics: Optional[str] = None,
|
|
1215
1264
|
) -> Tickers:
|
|
1216
|
-
"""Generate
|
|
1265
|
+
"""Generate a Tickers object representing the collection of Tickers that meet all the supplied parameters.
|
|
1217
1266
|
|
|
1218
|
-
One of country_iso_code, simple_industry, or exchange_code must be supplied
|
|
1267
|
+
One of country_iso_code, simple_industry, or exchange_code must be supplied, or one of sic, naics, nace, anzsic, spcapiqetf, spratings, or gics.
|
|
1219
1268
|
|
|
1220
|
-
:param country_iso_code: The ISO 3166-1 Alpha-2 or Alpha-3 code that represent the primary country the firm is based in. It
|
|
1269
|
+
:param country_iso_code: The ISO 3166-1 Alpha-2 or Alpha-3 code that represent the primary country the firm is based in. It defaults to None
|
|
1221
1270
|
:type country_iso_code: str, optional
|
|
1222
|
-
:param state_iso_code: The ISO 3166-2 Alpha-2 code that represents the primary subdivision of the country the firm the based in. Not all ISO 3166-2 codes are supported as S&P doesn't maintain the full list but a feature request for the full list is submitted to S&P product. Requires country_iso_code also to have a value other then None. It
|
|
1271
|
+
:param state_iso_code: The ISO 3166-2 Alpha-2 code that represents the primary subdivision of the country the firm the based in. Not all ISO 3166-2 codes are supported as S&P doesn't maintain the full list but a feature request for the full list is submitted to S&P product. Requires country_iso_code also to have a value other then None. It defaults to None
|
|
1223
1272
|
:type state_iso_code: str, optional
|
|
1224
|
-
:param simple_industry: The S&P CIQ Simple Industry defined in ciqSimpleIndustry in XPF. It
|
|
1273
|
+
:param simple_industry: The S&P CIQ Simple Industry defined in ciqSimpleIndustry in XPF. It defaults to None
|
|
1225
1274
|
:type simple_industry: str, optional
|
|
1226
|
-
:param exchange_code: The exchange code for the primary equity listing exchange of the firm. It
|
|
1275
|
+
:param exchange_code: The exchange code for the primary equity listing exchange of the firm. It defaults to None
|
|
1227
1276
|
:type exchange_code: str, optional
|
|
1228
|
-
:
|
|
1277
|
+
:param sic: The SIC industry code. It defaults to None
|
|
1278
|
+
:type sic: str, optional
|
|
1279
|
+
:param naics: The NAICS industry code. It defaults to None
|
|
1280
|
+
:type naics: str, optional
|
|
1281
|
+
:param nace: The NACE industry code. It defaults to None
|
|
1282
|
+
:type nace: str, optional
|
|
1283
|
+
:param anzsic: The ANZSIC industry code. It defaults to None
|
|
1284
|
+
:type anzsic: str, optional
|
|
1285
|
+
:param spcapiqetf: The S&P CapitalIQ ETF industry code. It defaults to None
|
|
1286
|
+
:type spcapiqetf: str, optional
|
|
1287
|
+
:param spratings: The S&P Ratings industry code. It defaults to None
|
|
1288
|
+
:type spratings: str, optional
|
|
1289
|
+
:param gics: The GICS code. It defaults to None
|
|
1290
|
+
:type gics: str, optional
|
|
1291
|
+
:return: A Tickers object that is the intersection of Ticker objects meeting all the supplied parameters.
|
|
1229
1292
|
:rtype: Tickers
|
|
1230
1293
|
"""
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1294
|
+
# Create a list to accumulate the fetched ticker sets
|
|
1295
|
+
ticker_sets: list[Tickers] = []
|
|
1296
|
+
|
|
1297
|
+
# Map the parameters to the industry_dict, pass the values in as the key.
|
|
1298
|
+
industry_dict = {
|
|
1299
|
+
"sic": sic,
|
|
1300
|
+
"naics": naics,
|
|
1301
|
+
"nace": nace,
|
|
1302
|
+
"anzsic": anzsic,
|
|
1303
|
+
"spcapiqetf": spcapiqetf,
|
|
1304
|
+
"spratings": spratings,
|
|
1305
|
+
"gics": gics,
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
if any(
|
|
1309
|
+
parameter is not None
|
|
1310
|
+
for parameter in [country_iso_code, state_iso_code, simple_industry, exchange_code]
|
|
1311
|
+
):
|
|
1312
|
+
ticker_sets.append(
|
|
1313
|
+
Tickers(
|
|
1314
|
+
kfinance_api_client=self.kfinance_api_client,
|
|
1315
|
+
id_triples=self.kfinance_api_client.fetch_ticker_combined(
|
|
1316
|
+
country_iso_code=country_iso_code,
|
|
1317
|
+
state_iso_code=state_iso_code,
|
|
1318
|
+
simple_industry=simple_industry,
|
|
1319
|
+
exchange_code=exchange_code,
|
|
1320
|
+
),
|
|
1321
|
+
)
|
|
1322
|
+
)
|
|
1323
|
+
|
|
1324
|
+
for key, value in industry_dict.items():
|
|
1325
|
+
if value is not None:
|
|
1326
|
+
ticker_sets.append(
|
|
1327
|
+
Tickers(
|
|
1328
|
+
kfinance_api_client=self.kfinance_api_client,
|
|
1329
|
+
id_triples=self.kfinance_api_client.fetch_ticker_from_industry_code(
|
|
1330
|
+
industry_code=value,
|
|
1331
|
+
industry_classification=IndustryClassification(key),
|
|
1332
|
+
),
|
|
1333
|
+
)
|
|
1334
|
+
)
|
|
1335
|
+
|
|
1336
|
+
if not ticker_sets:
|
|
1337
|
+
return Tickers(kfinance_api_client=self.kfinance_api_client, id_triples=set())
|
|
1338
|
+
|
|
1339
|
+
common_ticker_elements = Tickers.intersection(*ticker_sets)
|
|
1340
|
+
return common_ticker_elements
|
|
1240
1341
|
|
|
1241
1342
|
def company(self, company_id: int) -> Company:
|
|
1242
1343
|
"""Generate the Company object from company_id
|