kensho-kfinance 1.1.0__tar.gz → 1.2.0__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-1.2.0/.readthedocs.yaml +23 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/PKG-INFO +2 -1
- kensho_kfinance-1.2.0/docs/conf.py +66 -0
- kensho_kfinance-1.2.0/docs/index.rst +18 -0
- kensho_kfinance-1.2.0/docs/kfinance.rst +7 -0
- kensho_kfinance-1.2.0/docs/llm_tools.rst +7 -0
- kensho_kfinance-1.2.0/docs/requirements.txt +4 -0
- kensho_kfinance-1.2.0/docs/templates/apidoc/package.rst_t +50 -0
- kensho_kfinance-1.2.0/docs/templates/apidoc/toc.rst_t +10 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/justfile +6 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/kensho_kfinance.egg-info/PKG-INFO +2 -1
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/kensho_kfinance.egg-info/SOURCES.txt +8 -2
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/kensho_kfinance.egg-info/requires.txt +1 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/kfinance/CHANGELOG.md +3 -0
- kensho_kfinance-1.2.0/kfinance/batch_request_handling.py +137 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/kfinance/fetch.py +70 -7
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/kfinance/kfinance.py +21 -2
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/kfinance/llm_tools.py +24 -20
- kensho_kfinance-1.2.0/kfinance/tests/test_batch_requests.py +256 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/kfinance/tool_schemas.py +3 -1
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/kfinance/version.py +2 -2
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/pyproject.toml +1 -0
- kensho_kfinance-1.1.0/docs/index.rst +0 -9
- kensho_kfinance-1.1.0/docs/kfinance.rst +0 -5033
- kensho_kfinance-1.1.0/docs/llm_tools.rst +0 -191
- kensho_kfinance-1.1.0/scripts/docs/make_rst.py +0 -98
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/.coveragerc +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/.github/workflows/ci-lint.yml +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/.github/workflows/ci-test.yml +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/.github/workflows/python-publish.yml +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/.gitignore +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/AUTHORS.md +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/CODE_OF_CONDUCT.md +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/CONTRIBUTING.md +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/LICENSE +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/README.md +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/kensho_kfinance.egg-info/dependency_links.txt +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/kensho_kfinance.egg-info/top_level.txt +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/kfinance/__init__.py +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/kfinance/constants.py +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/kfinance/meta_classes.py +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/kfinance/prompt.py +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/kfinance/py.typed +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/kfinance/server_thread.py +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/kfinance/tests/__init__.py +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/kfinance/tests/test_fetch.py +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/kfinance/tests/test_objects.py +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/scripts/copyright_line_check.sh +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/scripts/lint.sh +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/scripts/test.sh +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/setup.cfg +0 -0
- {kensho_kfinance-1.1.0 → kensho_kfinance-1.2.0}/setup.py +0 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
build:
|
|
3
|
+
os: ubuntu-22.04
|
|
4
|
+
tools:
|
|
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
|
+
jobs:
|
|
11
|
+
pre_install:
|
|
12
|
+
- git update-index --assume-unchanged docs/conf.py
|
|
13
|
+
|
|
14
|
+
# Build documentation in the docs/ directory with Sphinx
|
|
15
|
+
sphinx:
|
|
16
|
+
configuration: docs/conf.py
|
|
17
|
+
|
|
18
|
+
# Dependencies required to build your docs
|
|
19
|
+
python:
|
|
20
|
+
install:
|
|
21
|
+
- method: pip
|
|
22
|
+
path: .
|
|
23
|
+
- requirements: docs/requirements.txt
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kensho-kfinance
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
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
|
|
@@ -28,6 +28,7 @@ Requires-Dist: mypy<2,>=1.15.0; extra == "dev"
|
|
|
28
28
|
Requires-Dist: pytest<7,>=6.1.2; extra == "dev"
|
|
29
29
|
Requires-Dist: pytest-cov<7,>=6.0.0; extra == "dev"
|
|
30
30
|
Requires-Dist: ruff<1,>=0.9.4; extra == "dev"
|
|
31
|
+
Requires-Dist: requests_mock<1.2,>=1.1; extra == "dev"
|
|
31
32
|
Dynamic: license-file
|
|
32
33
|
|
|
33
34
|
# kFinance
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Configuration file for the Sphinx documentation builder.
|
|
2
|
+
#
|
|
3
|
+
# For the full list of built-in configuration values, see the documentation:
|
|
4
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
|
5
|
+
|
|
6
|
+
# -- Project information -----------------------------------------------------
|
|
7
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
|
8
|
+
from importlib.metadata import version as get_version
|
|
9
|
+
|
|
10
|
+
project = 'kensho-kfinance'
|
|
11
|
+
copyright = '2025, Kensho Technologies'
|
|
12
|
+
author = 'Kensho Technologies'
|
|
13
|
+
|
|
14
|
+
# borrowed from here:
|
|
15
|
+
# https://setuptools-scm.readthedocs.io/en/latest/usage/#usage-from-sphinx
|
|
16
|
+
release: str = get_version(project)
|
|
17
|
+
# for example take major/minor
|
|
18
|
+
version: str = ".".join(release.split('.')[:2])
|
|
19
|
+
|
|
20
|
+
# -- General configuration ---------------------------------------------------
|
|
21
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
|
22
|
+
|
|
23
|
+
# borrowed from internal Kensho Sphinx configuration
|
|
24
|
+
extensions = [
|
|
25
|
+
"sphinx.ext.autodoc",
|
|
26
|
+
"sphinx.ext.intersphinx",
|
|
27
|
+
"sphinx.ext.coverage",
|
|
28
|
+
"sphinx.ext.mathjax",
|
|
29
|
+
"sphinx.ext.githubpages",
|
|
30
|
+
"sphinx.ext.autosummary",
|
|
31
|
+
"sphinx.ext.viewcode",
|
|
32
|
+
"sphinx.ext.napoleon",
|
|
33
|
+
# 3rd party extensions
|
|
34
|
+
# m2r2 is to add support to .md files specifically to include README.md files.
|
|
35
|
+
# See this discussion: https://github.com/sphinx-doc/sphinx/issues/7000
|
|
36
|
+
"m2r2",
|
|
37
|
+
# A ReadTheDocs theme for Sphinx
|
|
38
|
+
"sphinx_rtd_theme",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
napoleon_google_docstring = True
|
|
42
|
+
napoleon_use_ivar = True
|
|
43
|
+
|
|
44
|
+
autosummary_generate = True
|
|
45
|
+
|
|
46
|
+
# Don't prepend module path prefixes to function definitions. Makes automodule docs less cluttered
|
|
47
|
+
add_module_names = False
|
|
48
|
+
|
|
49
|
+
# Add any paths that contain templates here, relative to this directory.
|
|
50
|
+
templates_path = ["templates"]
|
|
51
|
+
|
|
52
|
+
# The suffix of source filenames.
|
|
53
|
+
source_suffix = [".rst", ".md"]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# -- Options for HTML output -------------------------------------------------
|
|
58
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
|
59
|
+
|
|
60
|
+
html_theme = 'sphinx_rtd_theme'
|
|
61
|
+
|
|
62
|
+
# https://sphinx-rtd-theme.readthedocs.io/en/stable/configuring.html
|
|
63
|
+
html_theme_options = {"body_min_width": 0, "body_max_width": "none"}
|
|
64
|
+
|
|
65
|
+
# https://www.sphinx-doc.org/en/1.4.9/config.html#confval-html_sidebars
|
|
66
|
+
html_sidebars = {"**": ["globaltoc.html", "relations.html", "searchbox.html"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Index
|
|
2
|
+
#####################
|
|
3
|
+
|
|
4
|
+
Documentation page for kensho-kfinance library.
|
|
5
|
+
|
|
6
|
+
.. toctree::
|
|
7
|
+
:hidden:
|
|
8
|
+
:glob:
|
|
9
|
+
:maxdepth: 1
|
|
10
|
+
|
|
11
|
+
kfinance <kfinance>
|
|
12
|
+
llm_tools <llm_tools>
|
|
13
|
+
|
|
14
|
+
Indices and tables
|
|
15
|
+
==================
|
|
16
|
+
|
|
17
|
+
* :ref:`genindex`
|
|
18
|
+
* :ref:`modindex`
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{% raw %}
|
|
2
|
+
{%- macro automodule(modname, options) -%}
|
|
3
|
+
.. automodule:: {{ modname }}
|
|
4
|
+
{%- for option in options %}
|
|
5
|
+
:{{ option }}:
|
|
6
|
+
{%- endfor %}
|
|
7
|
+
{%- endmacro %}
|
|
8
|
+
|
|
9
|
+
{%- macro toctree(docnames) -%}
|
|
10
|
+
.. toctree::
|
|
11
|
+
:maxdepth: {{ maxdepth }}
|
|
12
|
+
{% for docname in docnames %}
|
|
13
|
+
{{ docname }}
|
|
14
|
+
{%- endfor %}
|
|
15
|
+
{%- endmacro %}
|
|
16
|
+
|
|
17
|
+
{%- if is_namespace %}
|
|
18
|
+
{{- [pkgname, "namespace"] | join(" ") | e | heading }}
|
|
19
|
+
{% else %}
|
|
20
|
+
{{- [pkgname] | join(" ") | e | heading }}
|
|
21
|
+
{% endif %}
|
|
22
|
+
|
|
23
|
+
{%- if modulefirst and not is_namespace %}
|
|
24
|
+
{{ automodule(pkgname, automodule_options) }}
|
|
25
|
+
{% endif %}
|
|
26
|
+
|
|
27
|
+
{%- if subpackages %}
|
|
28
|
+
{{ toctree(subpackages) }}
|
|
29
|
+
{% endif %}
|
|
30
|
+
|
|
31
|
+
{%- if submodules %}
|
|
32
|
+
{% if separatemodules %}
|
|
33
|
+
{{ toctree(submodules) }}
|
|
34
|
+
{% else %}
|
|
35
|
+
{%- for submodule in submodules %}
|
|
36
|
+
{% if show_headings %}
|
|
37
|
+
{{- [submodule] | join(" ") | e | heading(2) }}
|
|
38
|
+
{% endif %}
|
|
39
|
+
{{ automodule(submodule, automodule_options) }}
|
|
40
|
+
{% endfor %}
|
|
41
|
+
{%- endif %}
|
|
42
|
+
{%- endif %}
|
|
43
|
+
|
|
44
|
+
{%- if not modulefirst and not is_namespace %}
|
|
45
|
+
Module contents
|
|
46
|
+
---------------
|
|
47
|
+
|
|
48
|
+
{{ automodule(pkgname, automodule_options) }}
|
|
49
|
+
{% endif %}
|
|
50
|
+
{% endraw %}
|
|
@@ -29,3 +29,9 @@ alias t := unit-test
|
|
|
29
29
|
# Run unit tests. Use args for optional settings
|
|
30
30
|
unit-test *args:
|
|
31
31
|
python -m pytest {{args}}
|
|
32
|
+
|
|
33
|
+
# Build the sphinx documents locally
|
|
34
|
+
# First, copy the dependencies in docs/requirements.txt into pyproject.toml and install in a venv.
|
|
35
|
+
# Don't merge changes to pyproject and docs/output into the remote repo!
|
|
36
|
+
sphinx *args:
|
|
37
|
+
sphinx-build docs docs/output
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kensho-kfinance
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
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
|
|
@@ -28,6 +28,7 @@ Requires-Dist: mypy<2,>=1.15.0; extra == "dev"
|
|
|
28
28
|
Requires-Dist: pytest<7,>=6.1.2; extra == "dev"
|
|
29
29
|
Requires-Dist: pytest-cov<7,>=6.0.0; extra == "dev"
|
|
30
30
|
Requires-Dist: ruff<1,>=0.9.4; extra == "dev"
|
|
31
|
+
Requires-Dist: requests_mock<1.2,>=1.1; extra == "dev"
|
|
31
32
|
Dynamic: license-file
|
|
32
33
|
|
|
33
34
|
# kFinance
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
.coveragerc
|
|
2
2
|
.gitignore
|
|
3
|
+
.readthedocs.yaml
|
|
3
4
|
AUTHORS.md
|
|
4
5
|
CODE_OF_CONDUCT.md
|
|
5
6
|
CONTRIBUTING.md
|
|
@@ -11,9 +12,13 @@ setup.py
|
|
|
11
12
|
.github/workflows/ci-lint.yml
|
|
12
13
|
.github/workflows/ci-test.yml
|
|
13
14
|
.github/workflows/python-publish.yml
|
|
15
|
+
docs/conf.py
|
|
14
16
|
docs/index.rst
|
|
15
17
|
docs/kfinance.rst
|
|
16
18
|
docs/llm_tools.rst
|
|
19
|
+
docs/requirements.txt
|
|
20
|
+
docs/templates/apidoc/package.rst_t
|
|
21
|
+
docs/templates/apidoc/toc.rst_t
|
|
17
22
|
kensho_kfinance.egg-info/PKG-INFO
|
|
18
23
|
kensho_kfinance.egg-info/SOURCES.txt
|
|
19
24
|
kensho_kfinance.egg-info/dependency_links.txt
|
|
@@ -21,6 +26,7 @@ kensho_kfinance.egg-info/requires.txt
|
|
|
21
26
|
kensho_kfinance.egg-info/top_level.txt
|
|
22
27
|
kfinance/CHANGELOG.md
|
|
23
28
|
kfinance/__init__.py
|
|
29
|
+
kfinance/batch_request_handling.py
|
|
24
30
|
kfinance/constants.py
|
|
25
31
|
kfinance/fetch.py
|
|
26
32
|
kfinance/kfinance.py
|
|
@@ -32,9 +38,9 @@ kfinance/server_thread.py
|
|
|
32
38
|
kfinance/tool_schemas.py
|
|
33
39
|
kfinance/version.py
|
|
34
40
|
kfinance/tests/__init__.py
|
|
41
|
+
kfinance/tests/test_batch_requests.py
|
|
35
42
|
kfinance/tests/test_fetch.py
|
|
36
43
|
kfinance/tests/test_objects.py
|
|
37
44
|
scripts/copyright_line_check.sh
|
|
38
45
|
scripts/lint.sh
|
|
39
|
-
scripts/test.sh
|
|
40
|
-
scripts/docs/make_rst.py
|
|
46
|
+
scripts/test.sh
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
2
|
+
import functools
|
|
3
|
+
from functools import cached_property
|
|
4
|
+
import threading
|
|
5
|
+
from typing import Any, Callable, Iterable, Protocol, Sized, Type, TypeVar
|
|
6
|
+
|
|
7
|
+
from requests.exceptions import HTTPError
|
|
8
|
+
|
|
9
|
+
from .fetch import KFinanceApiClient
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
T = TypeVar("T")
|
|
13
|
+
|
|
14
|
+
MAX_WORKERS_CAP: int = 10
|
|
15
|
+
|
|
16
|
+
throttle = threading.Semaphore(MAX_WORKERS_CAP)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def add_methods_of_singular_class_to_iterable_class(singular_cls: Type[T]) -> Callable:
|
|
20
|
+
"""Returns a decorator that sets each method, property, and cached_property of"""
|
|
21
|
+
"[singular_cls] as an attribute of the decorated class."
|
|
22
|
+
|
|
23
|
+
class IterableKfinanceClass(Protocol, Sized, Iterable[T]):
|
|
24
|
+
"""A protocol to represent a iterable Kfinance classes like Tickers and Companies.
|
|
25
|
+
|
|
26
|
+
Each of these classes has a kfinance_api_client attribute.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
kfinance_api_client: KFinanceApiClient
|
|
30
|
+
|
|
31
|
+
def decorator(iterable_cls: Type[IterableKfinanceClass]) -> Type[IterableKfinanceClass]:
|
|
32
|
+
"""Adds functions from a singular class to an iterable class.
|
|
33
|
+
|
|
34
|
+
This decorator modifies the [iterable_cls] so that when an attribute
|
|
35
|
+
(method, property, or cached property) added by the decorator is accessed,
|
|
36
|
+
it returns a dictionary. This dictionary maps each object in [iterable_cls]
|
|
37
|
+
to the result of invoking the attribute on that specific object.
|
|
38
|
+
|
|
39
|
+
For example, consider a `Company` class with a `city` property and a
|
|
40
|
+
`Companies` class that is an iterable of `Company` instances. When the
|
|
41
|
+
`Companies` class is decorated, it gains a `city` property. Accessing this
|
|
42
|
+
property will yield a dictionary where each key is a `Company` instance
|
|
43
|
+
and the corresponding value is the city of that instance. The resulting
|
|
44
|
+
dictionary might look like:
|
|
45
|
+
|
|
46
|
+
{<kfinance.kfinance.Company object>: 'Some City'}
|
|
47
|
+
|
|
48
|
+
Error Handling:
|
|
49
|
+
- If the result is a 404 HTTP error, the corresponding value
|
|
50
|
+
for that object in the dictionary will be set to None.
|
|
51
|
+
- For any other HTTP error, the error is raised and bubbles up.
|
|
52
|
+
|
|
53
|
+
Note:
|
|
54
|
+
This decorator requires [iterable_cls] to be an iterable of
|
|
55
|
+
instances of [singular_cls].
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def process_in_thread_pool(
|
|
59
|
+
executor: ThreadPoolExecutor, method: Callable, obj: T, *args: Any, **kwargs: Any
|
|
60
|
+
) -> Any:
|
|
61
|
+
with throttle:
|
|
62
|
+
future = executor.submit(method, obj, *args, **kwargs)
|
|
63
|
+
try:
|
|
64
|
+
return future.result()
|
|
65
|
+
except HTTPError as http_err:
|
|
66
|
+
error_code = http_err.response.status_code
|
|
67
|
+
if error_code == 404:
|
|
68
|
+
return None
|
|
69
|
+
else:
|
|
70
|
+
raise http_err
|
|
71
|
+
|
|
72
|
+
def process_in_batch(
|
|
73
|
+
method: Callable, self: IterableKfinanceClass, *args: Any, **kwargs: Any
|
|
74
|
+
) -> dict:
|
|
75
|
+
results = {}
|
|
76
|
+
kfinance_api_client = self.kfinance_api_client
|
|
77
|
+
with kfinance_api_client.batch_request_header(batch_size=len(self)):
|
|
78
|
+
with kfinance_api_client.thread_pool as executor:
|
|
79
|
+
results = dict(
|
|
80
|
+
zip(
|
|
81
|
+
self,
|
|
82
|
+
[
|
|
83
|
+
process_in_thread_pool(executor, method, obj, *args, **kwargs)
|
|
84
|
+
for obj in self
|
|
85
|
+
],
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
return results
|
|
90
|
+
|
|
91
|
+
for method_name in dir(singular_cls):
|
|
92
|
+
method = getattr(singular_cls, method_name)
|
|
93
|
+
if method_name.startswith("__") or method_name.startswith("set_"):
|
|
94
|
+
continue
|
|
95
|
+
if callable(method):
|
|
96
|
+
|
|
97
|
+
def create_method_wrapper(method: Callable) -> Callable:
|
|
98
|
+
@functools.wraps(method)
|
|
99
|
+
def method_wrapper(
|
|
100
|
+
self: IterableKfinanceClass, *args: Any, **kwargs: Any
|
|
101
|
+
) -> dict:
|
|
102
|
+
return process_in_batch(method, self, *args, **kwargs)
|
|
103
|
+
|
|
104
|
+
return method_wrapper
|
|
105
|
+
|
|
106
|
+
setattr(iterable_cls, method_name, create_method_wrapper(method))
|
|
107
|
+
|
|
108
|
+
elif isinstance(method, property):
|
|
109
|
+
|
|
110
|
+
def create_prop_wrapper(method: property) -> Callable:
|
|
111
|
+
assert method.fget is not None
|
|
112
|
+
|
|
113
|
+
@functools.wraps(method.fget)
|
|
114
|
+
def prop_wrapper(self: IterableKfinanceClass) -> Any:
|
|
115
|
+
assert method.fget is not None
|
|
116
|
+
return process_in_batch(method.fget, self)
|
|
117
|
+
|
|
118
|
+
return prop_wrapper
|
|
119
|
+
|
|
120
|
+
setattr(iterable_cls, method_name, property(create_prop_wrapper(method)))
|
|
121
|
+
|
|
122
|
+
elif isinstance(method, cached_property):
|
|
123
|
+
|
|
124
|
+
def create_cached_prop_wrapper(method: cached_property) -> cached_property:
|
|
125
|
+
@functools.wraps(method.func)
|
|
126
|
+
def cached_prop_wrapper(self: IterableKfinanceClass) -> Any:
|
|
127
|
+
return process_in_batch(method.func, self)
|
|
128
|
+
|
|
129
|
+
wrapped_cached_property = cached_property(cached_prop_wrapper)
|
|
130
|
+
wrapped_cached_property.__set_name__(iterable_cls, method_name)
|
|
131
|
+
return wrapped_cached_property
|
|
132
|
+
|
|
133
|
+
setattr(iterable_cls, method_name, create_cached_prop_wrapper(method))
|
|
134
|
+
|
|
135
|
+
return iterable_cls
|
|
136
|
+
|
|
137
|
+
return decorator
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
2
|
+
from contextlib import contextmanager
|
|
1
3
|
from time import time
|
|
2
|
-
from typing import Callable, Optional
|
|
4
|
+
from typing import Callable, Generator, Optional
|
|
5
|
+
from uuid import uuid4
|
|
3
6
|
|
|
4
7
|
import jwt
|
|
5
8
|
import requests
|
|
@@ -19,6 +22,7 @@ DEFAULT_API_HOST: str = "https://kfinance.kensho.com"
|
|
|
19
22
|
DEFAULT_API_VERSION: int = 1
|
|
20
23
|
DEFAULT_OKTA_HOST: str = "https://kensho.okta.com"
|
|
21
24
|
DEFAULT_OKTA_AUTH_SERVER: str = "default"
|
|
25
|
+
DEFAULT_MAX_WORKERS: int = 10
|
|
22
26
|
|
|
23
27
|
|
|
24
28
|
class KFinanceApiClient:
|
|
@@ -27,12 +31,33 @@ class KFinanceApiClient:
|
|
|
27
31
|
refresh_token: Optional[str] = None,
|
|
28
32
|
client_id: Optional[str] = None,
|
|
29
33
|
private_key: Optional[str] = None,
|
|
34
|
+
thread_pool: Optional[ThreadPoolExecutor] = None,
|
|
30
35
|
api_host: str = DEFAULT_API_HOST,
|
|
31
36
|
api_version: int = DEFAULT_API_VERSION,
|
|
32
37
|
okta_host: str = DEFAULT_OKTA_HOST,
|
|
33
38
|
okta_auth_server: str = DEFAULT_OKTA_AUTH_SERVER,
|
|
34
39
|
):
|
|
35
|
-
"""Configuration of KFinance Client.
|
|
40
|
+
"""Configuration of KFinance Client.
|
|
41
|
+
|
|
42
|
+
:param refresh_token: users refresh token
|
|
43
|
+
:type refresh_token: str, Optional
|
|
44
|
+
:param client_id: users client id will be provided by support@kensho.com
|
|
45
|
+
:type client_id: str, Optional
|
|
46
|
+
:param private_key: users private key that corresponds to the registered public sent to support@kensho.com
|
|
47
|
+
:type private_key: str, Optional
|
|
48
|
+
:param thread_pool: the thread pool used to execute batch requests. The number of concurrent requests is
|
|
49
|
+
capped at 10. If no thread pool is provided, a thread pool with 10 max workers will be created when batch
|
|
50
|
+
requests are made.
|
|
51
|
+
:type thread_pool: ThreadPoolExecutor, Optional
|
|
52
|
+
:param api_host: the api host URL
|
|
53
|
+
:type api_host: str
|
|
54
|
+
:param api_version: the api version number
|
|
55
|
+
:type api_version: int
|
|
56
|
+
:param okta_host: the okta host URL
|
|
57
|
+
:type okta_host: str
|
|
58
|
+
:param okta_auth_server: the okta route for authentication
|
|
59
|
+
:type okta_auth_server: str
|
|
60
|
+
"""
|
|
36
61
|
if refresh_token is not None:
|
|
37
62
|
self.refresh_token = refresh_token
|
|
38
63
|
self._access_token_refresh_func: Callable[..., str] = (
|
|
@@ -48,10 +73,40 @@ class KFinanceApiClient:
|
|
|
48
73
|
self.api_version = api_version
|
|
49
74
|
self.okta_host = okta_host
|
|
50
75
|
self.okta_auth_server = okta_auth_server
|
|
76
|
+
self._thread_pool = thread_pool
|
|
51
77
|
self.url_base = f"{self.api_host}/api/v{self.api_version}/"
|
|
52
78
|
self._access_token_expiry = 0
|
|
53
79
|
self._access_token: str | None = None
|
|
54
80
|
self.user_agent_source = "object_oriented"
|
|
81
|
+
self._batch_id: str | None = None
|
|
82
|
+
self._batch_size: str | None = None
|
|
83
|
+
|
|
84
|
+
@contextmanager
|
|
85
|
+
def batch_request_header(self, batch_size: int) -> Generator:
|
|
86
|
+
"""Set batch id and batch size for batch request request headers"""
|
|
87
|
+
batch_id = str(uuid4())
|
|
88
|
+
|
|
89
|
+
self._batch_id = batch_id
|
|
90
|
+
self._batch_size = str(batch_size)
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
yield
|
|
94
|
+
finally:
|
|
95
|
+
self._batch_id = None
|
|
96
|
+
self._batch_size = None
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def thread_pool(self) -> ThreadPoolExecutor:
|
|
100
|
+
"""Returns the thread pool used to execute batch requests.
|
|
101
|
+
|
|
102
|
+
If the thread pool is not set, a thread pool with 10 max workers will be created
|
|
103
|
+
and returned.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
if self._thread_pool is None:
|
|
107
|
+
self._thread_pool = ThreadPoolExecutor(max_workers=DEFAULT_MAX_WORKERS)
|
|
108
|
+
|
|
109
|
+
return self._thread_pool
|
|
55
110
|
|
|
56
111
|
@property
|
|
57
112
|
def access_token(self) -> str:
|
|
@@ -110,13 +165,21 @@ class KFinanceApiClient:
|
|
|
110
165
|
|
|
111
166
|
def fetch(self, url: str) -> dict:
|
|
112
167
|
"""Does the request and auth"""
|
|
168
|
+
|
|
169
|
+
headers = {
|
|
170
|
+
"Content-Type": "application/json",
|
|
171
|
+
"Authorization": f"Bearer {self.access_token}",
|
|
172
|
+
"User-Agent": f"kfinance/{kfinance_version} {self.user_agent_source}",
|
|
173
|
+
}
|
|
174
|
+
if self._batch_id is not None:
|
|
175
|
+
assert self._batch_size is not None
|
|
176
|
+
headers.update(
|
|
177
|
+
{"Kfinance-Batch-Id": self._batch_id, "Kfinance-Batch-Size": self._batch_size}
|
|
178
|
+
)
|
|
179
|
+
|
|
113
180
|
response = requests.get(
|
|
114
181
|
url,
|
|
115
|
-
headers=
|
|
116
|
-
"Content-Type": "application/json",
|
|
117
|
-
"Authorization": f"Bearer {self.access_token}",
|
|
118
|
-
"User-Agent": f"kfinance/{kfinance_version} {self.user_agent_source}",
|
|
119
|
-
},
|
|
182
|
+
headers=headers,
|
|
120
183
|
timeout=60,
|
|
121
184
|
)
|
|
122
185
|
response.raise_for_status()
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
3
4
|
from datetime import date, datetime, timezone
|
|
4
5
|
from functools import cached_property
|
|
5
6
|
from io import BytesIO
|
|
@@ -14,6 +15,7 @@ import numpy as np
|
|
|
14
15
|
import pandas as pd
|
|
15
16
|
from PIL.Image import Image, open as image_open
|
|
16
17
|
|
|
18
|
+
from .batch_request_handling import add_methods_of_singular_class_to_iterable_class
|
|
17
19
|
from .constants import HistoryMetadata, IdentificationTriple, LatestPeriods, YearAndQuarter
|
|
18
20
|
from .fetch import (
|
|
19
21
|
DEFAULT_API_HOST,
|
|
@@ -31,7 +33,10 @@ from .llm_tools import (
|
|
|
31
33
|
langchain_tools,
|
|
32
34
|
openai_tool_descriptions,
|
|
33
35
|
)
|
|
34
|
-
from .meta_classes import
|
|
36
|
+
from .meta_classes import (
|
|
37
|
+
CompanyFunctionsMetaClass,
|
|
38
|
+
DelegatedCompanyFunctionsMetaClass,
|
|
39
|
+
)
|
|
35
40
|
from .prompt import PROMPT
|
|
36
41
|
from .server_thread import ServerThread
|
|
37
42
|
|
|
@@ -917,6 +922,7 @@ class BusinessRelationships(NamedTuple):
|
|
|
917
922
|
return f"{type(self).__module__}.{type(self).__qualname__} of {str(dictionary)}"
|
|
918
923
|
|
|
919
924
|
|
|
925
|
+
@add_methods_of_singular_class_to_iterable_class(Company)
|
|
920
926
|
class Companies(set):
|
|
921
927
|
"""Base class for representing a set of Companies"""
|
|
922
928
|
|
|
@@ -928,9 +934,11 @@ class Companies(set):
|
|
|
928
934
|
:param company_ids: An iterable of S&P CIQ Company ids
|
|
929
935
|
:type company_ids: Iterable[int]
|
|
930
936
|
"""
|
|
937
|
+
self.kfinance_api_client = kfinance_api_client
|
|
931
938
|
super().__init__(Company(kfinance_api_client, company_id) for company_id in company_ids)
|
|
932
939
|
|
|
933
940
|
|
|
941
|
+
@add_methods_of_singular_class_to_iterable_class(Security)
|
|
934
942
|
class Securities(set):
|
|
935
943
|
"""Base class for representing a set of Securities"""
|
|
936
944
|
|
|
@@ -945,6 +953,7 @@ class Securities(set):
|
|
|
945
953
|
super().__init__(Security(kfinance_api_client, security_id) for security_id in security_ids)
|
|
946
954
|
|
|
947
955
|
|
|
956
|
+
@add_methods_of_singular_class_to_iterable_class(TradingItem)
|
|
948
957
|
class TradingItems(set):
|
|
949
958
|
"""Base class for representing a set of Trading Items"""
|
|
950
959
|
|
|
@@ -958,14 +967,16 @@ class TradingItems(set):
|
|
|
958
967
|
:param company_ids: An iterable of S&P CIQ Company ids
|
|
959
968
|
:type company_ids: Iterable[int]
|
|
960
969
|
"""
|
|
970
|
+
self.kfinance_api_client = kfinance_api_client
|
|
961
971
|
super().__init__(
|
|
962
972
|
TradingItem(kfinance_api_client, trading_item_id)
|
|
963
973
|
for trading_item_id in trading_item_ids
|
|
964
974
|
)
|
|
965
975
|
|
|
966
976
|
|
|
977
|
+
@add_methods_of_singular_class_to_iterable_class(Ticker)
|
|
967
978
|
class Tickers(set):
|
|
968
|
-
"""Base
|
|
979
|
+
"""Base class for representing a set of Tickers"""
|
|
969
980
|
|
|
970
981
|
def __init__(
|
|
971
982
|
self,
|
|
@@ -1041,6 +1052,7 @@ class Client:
|
|
|
1041
1052
|
refresh_token: Optional[str] = None,
|
|
1042
1053
|
client_id: Optional[str] = None,
|
|
1043
1054
|
private_key: Optional[str] = None,
|
|
1055
|
+
thread_pool: Optional[ThreadPoolExecutor] = None,
|
|
1044
1056
|
api_host: str = DEFAULT_API_HOST,
|
|
1045
1057
|
api_version: int = DEFAULT_API_VERSION,
|
|
1046
1058
|
okta_host: str = DEFAULT_OKTA_HOST,
|
|
@@ -1054,6 +1066,10 @@ class Client:
|
|
|
1054
1066
|
:type client_id: str, Optional
|
|
1055
1067
|
:param private_key: users private key that corresponds to the registered public sent to support@kensho.com
|
|
1056
1068
|
:type private_key: str, Optional
|
|
1069
|
+
:param thread_pool: the thread pool used to execute batch requests. The number of concurrent requests is
|
|
1070
|
+
capped at 10. If no thread pool is provided, a thread pool with 10 max workers will be created when batch
|
|
1071
|
+
requests are made.
|
|
1072
|
+
:type thread_pool: ThreadPoolExecutor, Optional
|
|
1057
1073
|
:param api_host: the api host URL
|
|
1058
1074
|
:type api_host: str
|
|
1059
1075
|
:param api_version: the api version number
|
|
@@ -1071,6 +1087,7 @@ class Client:
|
|
|
1071
1087
|
api_host=api_host,
|
|
1072
1088
|
api_version=api_version,
|
|
1073
1089
|
okta_host=okta_host,
|
|
1090
|
+
thread_pool=thread_pool,
|
|
1074
1091
|
)
|
|
1075
1092
|
# method 2 keypair
|
|
1076
1093
|
elif client_id is not None and private_key is not None:
|
|
@@ -1081,6 +1098,7 @@ class Client:
|
|
|
1081
1098
|
api_version=api_version,
|
|
1082
1099
|
okta_host=okta_host,
|
|
1083
1100
|
okta_auth_server=okta_auth_server,
|
|
1101
|
+
thread_pool=thread_pool,
|
|
1084
1102
|
)
|
|
1085
1103
|
# method 3 automatic login getting a refresh token
|
|
1086
1104
|
else:
|
|
@@ -1099,6 +1117,7 @@ class Client:
|
|
|
1099
1117
|
api_host=api_host,
|
|
1100
1118
|
api_version=api_version,
|
|
1101
1119
|
okta_host=okta_host,
|
|
1120
|
+
thread_pool=thread_pool,
|
|
1102
1121
|
)
|
|
1103
1122
|
stdout.write("Login credentials received.\n")
|
|
1104
1123
|
|