kensho-kfinance 2.0.0__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.0 → kensho_kfinance-2.1.2}/.readthedocs.yaml +6 -4
- {kensho_kfinance-2.0.0/kensho_kfinance.egg-info → kensho_kfinance-2.1.2}/PKG-INFO +3 -1
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/docs/build_tool_calling_documentation.py +7 -12
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/docs/conf.py +0 -6
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2/kensho_kfinance.egg-info}/PKG-INFO +3 -1
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kensho_kfinance.egg-info/SOURCES.txt +3 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kensho_kfinance.egg-info/requires.txt +2 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/CHANGELOG.md +15 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/batch_request_handling.py +10 -10
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/constants.py +16 -7
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/fetch.py +84 -40
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/kfinance.py +129 -28
- {kensho_kfinance-2.0.0 → 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.0 → 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.0 → 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.0 → kensho_kfinance-2.1.2}/kfinance/tests/test_tools.py +1 -27
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_business_relationship_from_identifier.py +2 -1
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_capitalization_from_identifier.py +2 -1
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_company_id_from_identifier.py +2 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_cusip_from_ticker.py +2 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_earnings_call_datetimes_from_identifier.py +2 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_financial_line_item_from_identifier.py +2 -1
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_financial_statement_from_identifier.py +2 -1
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_history_metadata_from_identifier.py +2 -1
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_info_from_identifier.py +2 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_isin_from_ticker.py +2 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_latest.py +2 -1
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_n_quarters_ago.py +2 -1
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_prices_from_identifier.py +2 -1
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_security_id_from_identifier.py +2 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/get_trading_item_id_from_identifier.py +2 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/shared_models.py +2 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/version.py +2 -2
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/pyproject.toml +3 -1
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/.coveragerc +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/.github/workflows/ci-lint.yml +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/.github/workflows/ci-test.yml +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/.github/workflows/python-publish.yml +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/.gitignore +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/AUTHORS.md +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/CODE_OF_CONDUCT.md +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/CONTRIBUTING.md +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/LICENSE +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/README.md +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/docs/index.rst +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/docs/kfinance.rst +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/docs/requirements.txt +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/docs/templates/apidoc/package.rst_t +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/docs/templates/apidoc/toc.rst_t +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/docs/tool_calling.rst +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/justfile +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kensho_kfinance.egg-info/dependency_links.txt +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kensho_kfinance.egg-info/top_level.txt +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/__init__.py +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/prompt.py +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/py.typed +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/server_thread.py +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tests/__init__.py +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tests/test_objects.py +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/README.md +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/kfinance/tool_calling/__init__.py +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/scripts/copyright_line_check.sh +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/scripts/lint.sh +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/scripts/test.sh +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/setup.cfg +0 -0
- {kensho_kfinance-2.0.0 → kensho_kfinance-2.1.2}/setup.py +0 -0
|
@@ -3,13 +3,15 @@ build:
|
|
|
3
3
|
os: ubuntu-22.04
|
|
4
4
|
tools:
|
|
5
5
|
python: "3.10"
|
|
6
|
-
# You can also specify other tool versions:
|
|
7
|
-
# nodejs: "16"
|
|
8
|
-
# borrowed from here:
|
|
9
|
-
# https://docs.readthedocs.com/platform/stable/build-customization.html#avoid-having-a-dirty-git-index
|
|
10
6
|
jobs:
|
|
11
7
|
pre_install:
|
|
8
|
+
# borrowed from here:
|
|
9
|
+
# https://docs.readthedocs.com/platform/stable/build-customization.html#avoid-having-a-dirty-git-index
|
|
12
10
|
- git update-index --assume-unchanged docs/conf.py
|
|
11
|
+
pre_build:
|
|
12
|
+
# build documentation for tool calling before generating docs.
|
|
13
|
+
- pip install langchain-core
|
|
14
|
+
- python docs/build_tool_calling_documentation.py
|
|
13
15
|
|
|
14
16
|
# Build documentation in the docs/ directory with Sphinx
|
|
15
17
|
sphinx:
|
|
@@ -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,5 +1,5 @@
|
|
|
1
|
-
import inspect
|
|
2
1
|
from enum import Enum
|
|
2
|
+
import inspect
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
from langchain_core.utils.function_calling import convert_to_openai_tool
|
|
@@ -48,11 +48,7 @@ def add_function_to_tools_file(tool: KfinanceTool) -> None:
|
|
|
48
48
|
"""
|
|
49
49
|
|
|
50
50
|
# The signature built with the inspect module does not include necessary imports.
|
|
51
|
-
imports = [
|
|
52
|
-
"import kfinance",
|
|
53
|
-
"import datetime",
|
|
54
|
-
"from typing import Optional"
|
|
55
|
-
]
|
|
51
|
+
imports = ["import kfinance", "import datetime", "from typing import Optional"]
|
|
56
52
|
signature_str = build_signature_str(tool)
|
|
57
53
|
|
|
58
54
|
# Use inspect to retrieve the return type and add it to the imports if it's not a builtin.
|
|
@@ -66,10 +62,6 @@ def add_function_to_tools_file(tool: KfinanceTool) -> None:
|
|
|
66
62
|
for field_name, field_metadata in tool.args_schema.model_fields.items():
|
|
67
63
|
# We use the openai definition to extract the field description. This means that, just like
|
|
68
64
|
# pydantic/langchain, we use the docstring of an enum as the description for an enum field.
|
|
69
|
-
try:
|
|
70
|
-
openai_params[field_name]['description']
|
|
71
|
-
except:
|
|
72
|
-
assert False, tool.name
|
|
73
65
|
args += f"\n :param {field_name}: {openai_params[field_name]['description']}"
|
|
74
66
|
args += f"\n :type {field_name}: {display_as_type(field_metadata.annotation)}"
|
|
75
67
|
|
|
@@ -84,7 +76,8 @@ def add_function_to_tools_file(tool: KfinanceTool) -> None:
|
|
|
84
76
|
with open(inspect.getfile(tool.__class__), mode="a") as f:
|
|
85
77
|
f.write(func_str)
|
|
86
78
|
|
|
87
|
-
|
|
79
|
+
|
|
80
|
+
def build_signature_str(tool: KfinanceTool) -> str:
|
|
88
81
|
"""Return the signature string of the tool
|
|
89
82
|
|
|
90
83
|
Return value example:
|
|
@@ -104,7 +97,6 @@ def build_signature_str(tool) -> str:
|
|
|
104
97
|
return f"def {tool.name}{signature}:"
|
|
105
98
|
|
|
106
99
|
|
|
107
|
-
|
|
108
100
|
def add_module_to_tool_calling_rst(tool: KfinanceTool) -> None:
|
|
109
101
|
"""Add a module for each tool to tool_calling.rst.
|
|
110
102
|
|
|
@@ -121,3 +113,6 @@ def add_module_to_tool_calling_rst(tool: KfinanceTool) -> None:
|
|
|
121
113
|
|
|
122
114
|
with open(Path(Path(__file__).resolve().parent, "tool_calling.rst"), mode="a") as f:
|
|
123
115
|
f.write(module_str)
|
|
116
|
+
|
|
117
|
+
if __name__ == "__main__":
|
|
118
|
+
add_tool_calling_docs_for_all_tools()
|
|
@@ -7,15 +7,10 @@
|
|
|
7
7
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
|
8
8
|
from importlib.metadata import version as get_version
|
|
9
9
|
|
|
10
|
-
from docs.build_tool_calling_documentation import add_tool_calling_docs_for_all_tools
|
|
11
|
-
|
|
12
|
-
|
|
13
10
|
project = "kensho-kfinance"
|
|
14
11
|
copyright = "2025, Kensho Technologies"
|
|
15
12
|
author = "Kensho Technologies"
|
|
16
13
|
|
|
17
|
-
# Add documentation for tool calling.
|
|
18
|
-
add_tool_calling_docs_for_all_tools()
|
|
19
14
|
|
|
20
15
|
# borrowed from here:
|
|
21
16
|
# https://setuptools-scm.readthedocs.io/en/latest/usage/#usage-from-sphinx
|
|
@@ -59,7 +54,6 @@ templates_path = ["templates"]
|
|
|
59
54
|
source_suffix = [".rst", ".md"]
|
|
60
55
|
|
|
61
56
|
|
|
62
|
-
|
|
63
57
|
# -- Options for HTML output -------------------------------------------------
|
|
64
58
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
|
65
59
|
|
|
@@ -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,20 @@
|
|
|
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
|
+
|
|
15
|
+
## v2.0.1
|
|
16
|
+
- Fix readthedocs integration for llm tools.
|
|
17
|
+
|
|
3
18
|
## v2.0.0
|
|
4
19
|
- Refactor llm tools to use langchain `BaseTool`.
|
|
5
20
|
|
|
@@ -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)
|