kensho-kfinance 1.2.0__py3-none-any.whl → 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kensho-kfinance might be problematic. Click here for more details.
- {kensho_kfinance-1.2.0.dist-info → kensho_kfinance-2.0.0.dist-info}/METADATA +13 -9
- kensho_kfinance-2.0.0.dist-info/RECORD +40 -0
- {kensho_kfinance-1.2.0.dist-info → kensho_kfinance-2.0.0.dist-info}/WHEEL +1 -1
- kfinance/CHANGELOG.md +10 -0
- kfinance/constants.py +51 -8
- kfinance/fetch.py +68 -29
- kfinance/kfinance.py +128 -38
- kfinance/meta_classes.py +3 -9
- kfinance/tests/test_fetch.py +7 -26
- kfinance/tests/test_tools.py +430 -0
- kfinance/tool_calling/README.md +38 -0
- kfinance/tool_calling/__init__.py +47 -0
- kfinance/tool_calling/get_business_relationship_from_identifier.py +28 -0
- kfinance/tool_calling/get_capitalization_from_identifier.py +35 -0
- kfinance/tool_calling/get_company_id_from_identifier.py +14 -0
- kfinance/tool_calling/get_cusip_from_ticker.py +18 -0
- kfinance/tool_calling/get_earnings_call_datetimes_from_identifier.py +17 -0
- kfinance/tool_calling/get_financial_line_item_from_identifier.py +45 -0
- kfinance/tool_calling/get_financial_statement_from_identifier.py +41 -0
- kfinance/tool_calling/get_history_metadata_from_identifier.py +15 -0
- kfinance/tool_calling/get_info_from_identifier.py +14 -0
- kfinance/tool_calling/get_isin_from_ticker.py +18 -0
- kfinance/tool_calling/get_latest.py +21 -0
- kfinance/tool_calling/get_n_quarters_ago.py +21 -0
- kfinance/tool_calling/get_prices_from_identifier.py +44 -0
- kfinance/tool_calling/get_security_id_from_identifier.py +14 -0
- kfinance/tool_calling/get_trading_item_id_from_identifier.py +14 -0
- kfinance/tool_calling/shared_models.py +53 -0
- kfinance/version.py +2 -2
- kensho_kfinance-1.2.0.dist-info/RECORD +0 -23
- kfinance/llm_tools.py +0 -747
- kfinance/tool_schemas.py +0 -148
- {kensho_kfinance-1.2.0.dist-info → kensho_kfinance-2.0.0.dist-info}/licenses/AUTHORS.md +0 -0
- {kensho_kfinance-1.2.0.dist-info → kensho_kfinance-2.0.0.dist-info}/licenses/LICENSE +0 -0
- {kensho_kfinance-1.2.0.dist-info → kensho_kfinance-2.0.0.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kensho-kfinance
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.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
|
|
@@ -12,30 +12,34 @@ 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:
|
|
16
|
-
Requires-Dist:
|
|
17
|
-
Requires-Dist: urllib3>=1.21.1
|
|
18
|
-
Requires-Dist: pyjwt>=2.8.0
|
|
15
|
+
Requires-Dist: langchain-core>=0.3.15
|
|
16
|
+
Requires-Dist: langchain-google-genai<3,>=2.1.0
|
|
19
17
|
Requires-Dist: numpy>=1.22.4
|
|
20
18
|
Requires-Dist: pandas>=2.0.0
|
|
21
|
-
Requires-Dist: types-requests<3,>=2.22.0
|
|
22
19
|
Requires-Dist: pillow>=10
|
|
23
|
-
Requires-Dist:
|
|
20
|
+
Requires-Dist: pydantic<3,>=2.10.0
|
|
21
|
+
Requires-Dist: pyjwt>=2.8.0
|
|
22
|
+
Requires-Dist: python-dateutil<2.9,>=2.8.2
|
|
24
23
|
Requires-Dist: strenum>=0.4.15
|
|
24
|
+
Requires-Dist: tabulate>=0.9.0
|
|
25
|
+
Requires-Dist: types-requests<3,>=2.22.0
|
|
26
|
+
Requires-Dist: requests<3,>=2.22.0
|
|
27
|
+
Requires-Dist: urllib3>=1.21.1
|
|
25
28
|
Provides-Extra: dev
|
|
26
29
|
Requires-Dist: coverage<8,>=7.6.10; extra == "dev"
|
|
27
30
|
Requires-Dist: mypy<2,>=1.15.0; extra == "dev"
|
|
28
31
|
Requires-Dist: pytest<7,>=6.1.2; extra == "dev"
|
|
29
32
|
Requires-Dist: pytest-cov<7,>=6.0.0; extra == "dev"
|
|
33
|
+
Requires-Dist: requests_mock<2,>=1.12; extra == "dev"
|
|
30
34
|
Requires-Dist: ruff<1,>=0.9.4; extra == "dev"
|
|
31
|
-
Requires-Dist:
|
|
35
|
+
Requires-Dist: time_machine<3,>=2.1; extra == "dev"
|
|
32
36
|
Dynamic: license-file
|
|
33
37
|
|
|
34
38
|
# kFinance
|
|
35
39
|
|
|
36
40
|
The kFinance Python library provides a simple interface for the LLM-ready API, streamlining API requests and response handling. It can be used on its own, with LLMs, or integrated into applications.
|
|
37
41
|
|
|
38
|
-
For a complete overview of the functions, usage, and features of the kFinance Python library, please refer to documentation [here](https://kfinance.
|
|
42
|
+
For a complete overview of the functions, usage, and features of the kFinance Python library, please refer to documentation [here](https://kensho-kfinance.readthedocs.io/en/stable/).
|
|
39
43
|
|
|
40
44
|
Any questions or suggestions can be sent to the [kFinance Maintainers](kfinance-maintainers@kensho.com).
|
|
41
45
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
kensho_kfinance-2.0.0.dist-info/licenses/AUTHORS.md,sha256=0h9ClbI0pu1oKj1M28ROUsaxrbZg-6ukQGl6X4y9noI,68
|
|
2
|
+
kensho_kfinance-2.0.0.dist-info/licenses/LICENSE,sha256=bsY4blvSgq6o0FMQ3RXa2NCgco--nHCCchLXzxr6kms,83
|
|
3
|
+
kfinance/CHANGELOG.md,sha256=Gjau3iwqbJoOw-ReXpn9H3mkRhsWNTttBljc7D2-i4Y,641
|
|
4
|
+
kfinance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
kfinance/batch_request_handling.py,sha256=s0uXRY4CC-GnShI0i8PLPgLddRdqLyQ8jnqDKdmaZzs,5531
|
|
6
|
+
kfinance/constants.py,sha256=HrTh2QuhbA70xxcJ6N8WfTOJs6YiM30P59GbSDu_3AY,48312
|
|
7
|
+
kfinance/fetch.py,sha256=ygvp8q1o8mqPYY4ctQwch37dmprO0UMDteDumk8A-Gw,20172
|
|
8
|
+
kfinance/kfinance.py,sha256=nw2SPkixFht3PkcH4tZ1FnLhVxmyZm1_gbOqnmC6PBQ,49219
|
|
9
|
+
kfinance/meta_classes.py,sha256=sNezG7uHAQOWj2Xdjao6qc3z_gHppgJat7O9J2k_yqI,15730
|
|
10
|
+
kfinance/prompt.py,sha256=PtVB8c_FcSlVdyGgByAnIFGzuUuBaEjciCqnBJl1hSQ,25133
|
|
11
|
+
kfinance/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
kfinance/server_thread.py,sha256=jUnt1YGoYDkqqz1MbCwd44zJs1T_Z2BCgvj75bdtLgA,2574
|
|
13
|
+
kfinance/version.py,sha256=2thmcF9DS_Zp1zHI3N0kjBeMAuCP9mdDGnL0clqQpS8,511
|
|
14
|
+
kfinance/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
kfinance/tests/test_batch_requests.py,sha256=NsldoZqjjQpv0QKM2th80F1nru2tIF4LJeXU-RamQvA,9836
|
|
16
|
+
kfinance/tests/test_fetch.py,sha256=iJLOPBOb8xdAGF6Jel5jPhWb4iW6yPpt9f5tW4uZEyo,12189
|
|
17
|
+
kfinance/tests/test_objects.py,sha256=egXkhfoK2MepZdXtrBxzWxWni7f-zVCefUbnyDnWDOE,22554
|
|
18
|
+
kfinance/tests/test_tools.py,sha256=YG5UVWnR4EyHGsL4bBwUp2auSd55lcvUEEncXcMXw1Q,16941
|
|
19
|
+
kfinance/tool_calling/README.md,sha256=omJq7Us6r4U45QB7hRpLjRJ5BMalCkZkh4uXBjTbJXc,2022
|
|
20
|
+
kfinance/tool_calling/__init__.py,sha256=GDEeK2zM2tOfYam6ejwYACXecoRdlkRve_ZC5VTKugo,2038
|
|
21
|
+
kfinance/tool_calling/get_business_relationship_from_identifier.py,sha256=S_-nCsppi9tvwC81GMKvJd-eZSwRejT96PZhlOf5Wv4,1381
|
|
22
|
+
kfinance/tool_calling/get_capitalization_from_identifier.py,sha256=LmH5yEWSRJfAP4kRYHREdbMtrmxcZfgfbNB4dGQwyb4,1435
|
|
23
|
+
kfinance/tool_calling/get_company_id_from_identifier.py,sha256=18XAmWxTXRw1qaqw4WuA-yQ4d7kI2HnbMi32d-xNGzE,485
|
|
24
|
+
kfinance/tool_calling/get_cusip_from_ticker.py,sha256=uzVMbtcV4eKtWO4bYn-cicCo8tfAEGk-v95ZOON_cbA,533
|
|
25
|
+
kfinance/tool_calling/get_earnings_call_datetimes_from_identifier.py,sha256=3v73ZZz9n-Rdp5vp1RPmFD41nJ0Z3EgTjFSbnm30xdA,665
|
|
26
|
+
kfinance/tool_calling/get_financial_line_item_from_identifier.py,sha256=zJ-kcr_B9N3ZIXK1W-2TEUlF6tj4rhzmLSsW8EUhJn4,2064
|
|
27
|
+
kfinance/tool_calling/get_financial_statement_from_identifier.py,sha256=LyGmh81-_yljOhMVpsXa1M4u6hNJNP69Ms1M1NsxwPM,1817
|
|
28
|
+
kfinance/tool_calling/get_history_metadata_from_identifier.py,sha256=0h7xdhrIJg3pJJrrCgcsiZd3ft1GpConhNZe2pcOREA,666
|
|
29
|
+
kfinance/tool_calling/get_info_from_identifier.py,sha256=bv4lRhXWG9N7U8XEli9v44QEXNgf7-ZTuTDmE6XIeEg,659
|
|
30
|
+
kfinance/tool_calling/get_isin_from_ticker.py,sha256=yz7SOCFgwL03Zd8zG3pIfa3McB_tdRU-_oXYfSfqWoo,527
|
|
31
|
+
kfinance/tool_calling/get_latest.py,sha256=U_5SuGlD0G-u7YreiNkwEtx2zkUhZzUvwUwdL_tmqFk,724
|
|
32
|
+
kfinance/tool_calling/get_n_quarters_ago.py,sha256=VVKKyUNbmw7fS7e-vi4YXSdd_OO2bo8k50Grt5F_Mvo,651
|
|
33
|
+
kfinance/tool_calling/get_prices_from_identifier.py,sha256=b-cK9dWXbVBjppTORlC4HK9zd_qe8V0n98s823v0dCc,1796
|
|
34
|
+
kfinance/tool_calling/get_security_id_from_identifier.py,sha256=vPFYuFydUGxQ0b23CWkU3YxWVeoW-nzsrPbB9ZH8U_w,489
|
|
35
|
+
kfinance/tool_calling/get_trading_item_id_from_identifier.py,sha256=fB8QBFzhn-kgUL8dSF6w_rtO3zG5yLbm5s29Xz6U1AY,504
|
|
36
|
+
kfinance/tool_calling/shared_models.py,sha256=6y74yJCORHDVBxhhBJhTUOWCF9D11Lgg9eYQeDIT3aY,2047
|
|
37
|
+
kensho_kfinance-2.0.0.dist-info/METADATA,sha256=g5E_LG0HZWOwX6V8Bd7OE4Eeu7HoD69scUh5QqH3hSs,3051
|
|
38
|
+
kensho_kfinance-2.0.0.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
|
|
39
|
+
kensho_kfinance-2.0.0.dist-info/top_level.txt,sha256=kT_kNwVhfQoOAecY8W7uYah5xaHMoHoAdBIvXh6DaKM,9
|
|
40
|
+
kensho_kfinance-2.0.0.dist-info/RECORD,,
|
kfinance/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
|
+
|
|
3
|
+
## v2.0.0
|
|
4
|
+
- Refactor llm tools to use langchain `BaseTool`.
|
|
5
|
+
|
|
6
|
+
## v1.2.2
|
|
7
|
+
- Add tabulate and pydantic as dependencies
|
|
8
|
+
|
|
9
|
+
## v1.2.1
|
|
10
|
+
- Add fetch for industry_code, industry_classification and gics_code.
|
|
11
|
+
|
|
2
12
|
## v1.2.0
|
|
3
13
|
- Add batch requests for iterable classes
|
|
4
14
|
|
kfinance/constants.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from datetime import date
|
|
2
|
+
from enum import Enum
|
|
2
3
|
from itertools import chain
|
|
3
4
|
from typing import TypedDict
|
|
4
5
|
|
|
@@ -26,7 +27,45 @@ class IdentificationTriple(TypedDict):
|
|
|
26
27
|
company_id: int
|
|
27
28
|
|
|
28
29
|
|
|
30
|
+
class Capitalization(Enum):
|
|
31
|
+
"""The capitalization type"""
|
|
32
|
+
|
|
33
|
+
market_cap = "market_cap"
|
|
34
|
+
tev = "tev"
|
|
35
|
+
shares_outstanding = "shares_outstanding"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class PeriodType(Enum):
|
|
39
|
+
"""The period type"""
|
|
40
|
+
|
|
41
|
+
annual = "annual"
|
|
42
|
+
quarterly = "quarterly"
|
|
43
|
+
ltm = "ltm"
|
|
44
|
+
ytd = "ytd"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class Periodicity(Enum):
|
|
48
|
+
"""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
|
+
|
|
50
|
+
day = "day"
|
|
51
|
+
week = "week"
|
|
52
|
+
month = "month"
|
|
53
|
+
year = "year"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class StatementType(Enum):
|
|
57
|
+
"""The type of financial statement"""
|
|
58
|
+
|
|
59
|
+
balance_sheet = "balance_sheet"
|
|
60
|
+
bs = "balance_sheet"
|
|
61
|
+
income_statement = "income_statement"
|
|
62
|
+
cashflow = "cashflow"
|
|
63
|
+
cf = "cashflow"
|
|
64
|
+
|
|
65
|
+
|
|
29
66
|
class BusinessRelationshipType(StrEnum):
|
|
67
|
+
"""The type of business relationship"""
|
|
68
|
+
|
|
30
69
|
supplier = "supplier"
|
|
31
70
|
customer = "customer"
|
|
32
71
|
distributor = "distributor"
|
|
@@ -76,6 +115,17 @@ class LatestPeriods(TypedDict):
|
|
|
76
115
|
now: CurrentPeriod
|
|
77
116
|
|
|
78
117
|
|
|
118
|
+
class IndustryClassification(StrEnum):
|
|
119
|
+
sic = "sic"
|
|
120
|
+
naics = "naics"
|
|
121
|
+
nace = "nace"
|
|
122
|
+
anzsic = "anzsic"
|
|
123
|
+
spcapiqetf = "spcapiqetf"
|
|
124
|
+
spratings = "spratings"
|
|
125
|
+
gics = "gics"
|
|
126
|
+
simple = "simple"
|
|
127
|
+
|
|
128
|
+
|
|
79
129
|
# all of these values must be lower case keys
|
|
80
130
|
LINE_ITEMS: list[LineItemType] = [
|
|
81
131
|
{
|
|
@@ -1661,18 +1711,11 @@ LINE_ITEMS: list[LineItemType] = [
|
|
|
1661
1711
|
},
|
|
1662
1712
|
]
|
|
1663
1713
|
|
|
1664
|
-
LINE_ITEMS_TO_DATA_ITEM_ID = {
|
|
1665
|
-
line_item["name"]: line_item["dataitemid"] for line_item in LINE_ITEMS
|
|
1666
|
-
}
|
|
1667
1714
|
|
|
1668
|
-
LINE_ITEM_NAMES_AND_ALIASES = list(
|
|
1715
|
+
LINE_ITEM_NAMES_AND_ALIASES: list[str] = list(
|
|
1669
1716
|
chain(*[[line_item["name"]] + list(line_item["aliases"]) for line_item in LINE_ITEMS])
|
|
1670
1717
|
)
|
|
1671
1718
|
|
|
1672
|
-
LINE_ITEM_SYNONYMS = {
|
|
1673
|
-
alias: line_item["dataitemid"] for line_item in LINE_ITEMS for alias in line_item["aliases"]
|
|
1674
|
-
}
|
|
1675
|
-
|
|
1676
1719
|
|
|
1677
1720
|
FINANCIAL_STATEMENTS: dict[str, int] = {
|
|
1678
1721
|
"income_statement": 1,
|
kfinance/fetch.py
CHANGED
|
@@ -7,7 +7,13 @@ from uuid import uuid4
|
|
|
7
7
|
import jwt
|
|
8
8
|
import requests
|
|
9
9
|
|
|
10
|
-
from .constants import
|
|
10
|
+
from .constants import (
|
|
11
|
+
BusinessRelationshipType,
|
|
12
|
+
IdentificationTriple,
|
|
13
|
+
IndustryClassification,
|
|
14
|
+
Periodicity,
|
|
15
|
+
PeriodType,
|
|
16
|
+
)
|
|
11
17
|
|
|
12
18
|
|
|
13
19
|
# version.py gets autogenerated by setuptools-scm and is not available
|
|
@@ -228,14 +234,14 @@ class KFinanceApiClient:
|
|
|
228
234
|
is_adjusted: bool = True,
|
|
229
235
|
start_date: Optional[str] = None,
|
|
230
236
|
end_date: Optional[str] = None,
|
|
231
|
-
periodicity: Optional[
|
|
237
|
+
periodicity: Optional[Periodicity] = None,
|
|
232
238
|
) -> dict:
|
|
233
239
|
"""Get the pricing history."""
|
|
234
240
|
url = (
|
|
235
241
|
f"{self.url_base}pricing/{trading_item_id}/"
|
|
236
242
|
f"{start_date if start_date is not None else 'none'}/"
|
|
237
243
|
f"{end_date if end_date is not None else 'none'}/"
|
|
238
|
-
f"{periodicity if periodicity
|
|
244
|
+
f"{periodicity.value if periodicity else 'none'}/"
|
|
239
245
|
f"{'adjusted' if is_adjusted else 'unadjusted'}"
|
|
240
246
|
)
|
|
241
247
|
return self.fetch(url)
|
|
@@ -265,14 +271,14 @@ class KFinanceApiClient:
|
|
|
265
271
|
is_adjusted: bool = True,
|
|
266
272
|
start_date: Optional[str] = None,
|
|
267
273
|
end_date: Optional[str] = None,
|
|
268
|
-
periodicity: Optional[
|
|
274
|
+
periodicity: Optional[Periodicity] = None,
|
|
269
275
|
) -> bytes:
|
|
270
276
|
"""Get the price chart."""
|
|
271
277
|
url = (
|
|
272
278
|
f"{self.url_base}price_chart/{trading_item_id}/"
|
|
273
279
|
f"{start_date if start_date is not None else 'none'}/"
|
|
274
280
|
f"{end_date if end_date is not None else 'none'}/"
|
|
275
|
-
f"{periodicity if periodicity
|
|
281
|
+
f"{periodicity.value if periodicity else 'none'}/"
|
|
276
282
|
f"{'adjusted' if is_adjusted else 'unadjusted'}"
|
|
277
283
|
)
|
|
278
284
|
|
|
@@ -291,7 +297,7 @@ class KFinanceApiClient:
|
|
|
291
297
|
self,
|
|
292
298
|
company_id: int,
|
|
293
299
|
statement_type: str,
|
|
294
|
-
period_type: Optional[
|
|
300
|
+
period_type: Optional[PeriodType] = None,
|
|
295
301
|
start_year: Optional[int] = None,
|
|
296
302
|
end_year: Optional[int] = None,
|
|
297
303
|
start_quarter: Optional[int] = None,
|
|
@@ -300,7 +306,7 @@ class KFinanceApiClient:
|
|
|
300
306
|
"""Get a specified financial statement for a specified duration."""
|
|
301
307
|
url = (
|
|
302
308
|
f"{self.url_base}statements/{company_id}/{statement_type}/"
|
|
303
|
-
f"{period_type if period_type
|
|
309
|
+
f"{period_type.value if period_type else 'none'}/"
|
|
304
310
|
f"{start_year if start_year is not None else 'none'}/"
|
|
305
311
|
f"{end_year if end_year is not None else 'none'}/"
|
|
306
312
|
f"{start_quarter if start_quarter is not None else 'none'}/"
|
|
@@ -312,7 +318,7 @@ class KFinanceApiClient:
|
|
|
312
318
|
self,
|
|
313
319
|
company_id: int,
|
|
314
320
|
line_item: str,
|
|
315
|
-
period_type: Optional[
|
|
321
|
+
period_type: Optional[PeriodType] = None,
|
|
316
322
|
start_year: Optional[int] = None,
|
|
317
323
|
end_year: Optional[int] = None,
|
|
318
324
|
start_quarter: Optional[int] = None,
|
|
@@ -321,7 +327,7 @@ class KFinanceApiClient:
|
|
|
321
327
|
"""Get a specified financial line item for a specified duration."""
|
|
322
328
|
url = (
|
|
323
329
|
f"{self.url_base}line_item/{company_id}/{line_item}/"
|
|
324
|
-
f"{period_type if period_type
|
|
330
|
+
f"{period_type.value if period_type else 'none'}/"
|
|
325
331
|
f"{start_year if start_year is not None else 'none'}/"
|
|
326
332
|
f"{end_year if end_year is not None else 'none'}/"
|
|
327
333
|
f"{start_quarter if start_quarter is not None else 'none'}/"
|
|
@@ -368,26 +374,6 @@ class KFinanceApiClient:
|
|
|
368
374
|
country_iso_code=country_iso_code, state_iso_code=state_iso_code, fetch_ticker=False
|
|
369
375
|
)
|
|
370
376
|
|
|
371
|
-
def fetch_simple_industry_groups(
|
|
372
|
-
self, simple_industry: str, fetch_ticker: bool = True
|
|
373
|
-
) -> dict[str, list]:
|
|
374
|
-
"""Fetch simple industry groups"""
|
|
375
|
-
url = f"{self.url_base}{'ticker_groups' if fetch_ticker else 'company_groups'}/industry/simple/{simple_industry}"
|
|
376
|
-
return self.fetch(url)
|
|
377
|
-
|
|
378
|
-
def fetch_ticker_simple_industry_groups(
|
|
379
|
-
self, simple_industry: str
|
|
380
|
-
) -> dict[str, list[IdentificationTriple]]:
|
|
381
|
-
"""Fetch ticker simple industry groups"""
|
|
382
|
-
return self.fetch_simple_industry_groups(simple_industry=simple_industry, fetch_ticker=True)
|
|
383
|
-
|
|
384
|
-
def fetch_company_simple_industry_groups(self, simple_industry: str) -> dict[str, list[int]]:
|
|
385
|
-
"""Fetch company simple industry groups"""
|
|
386
|
-
return self.fetch_simple_industry_groups(
|
|
387
|
-
simple_industry=simple_industry,
|
|
388
|
-
fetch_ticker=False,
|
|
389
|
-
)
|
|
390
|
-
|
|
391
377
|
def fetch_exchange_groups(
|
|
392
378
|
self, exchange_code: str, fetch_ticker: bool = True
|
|
393
379
|
) -> dict[str, list]:
|
|
@@ -456,3 +442,56 @@ class KFinanceApiClient:
|
|
|
456
442
|
"""
|
|
457
443
|
url = f"{self.url_base}relationship/{company_id}/{relationship_type}"
|
|
458
444
|
return self.fetch(url)
|
|
445
|
+
|
|
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
|
+
def fetch_ticker_from_industry_code(
|
|
458
|
+
self,
|
|
459
|
+
industry_code: str,
|
|
460
|
+
industry_classification: IndustryClassification,
|
|
461
|
+
) -> dict[str, list[IdentificationTriple]]:
|
|
462
|
+
"""Fetches a list of identification triples that are classified in the given industry_code and industry_classification.
|
|
463
|
+
|
|
464
|
+
Returns a dictionary of shape {"tickers": List[{“company_id”: <company_id>, “security_id”: <security_id>, “trading_item_id”: <trading_item_id>}]}.
|
|
465
|
+
:param industry_code: The industry_code to filter on. The industry_code is a string corresponding to the Industry classifications ontology.
|
|
466
|
+
:type industry_code: str
|
|
467
|
+
:param industry_classification: The type of industry_classification to filter on.
|
|
468
|
+
:type industry_classification: IndustryClassification
|
|
469
|
+
:return: A dictionary containing the list of identification triple [company_id, security_id, trading_item_id] that are classified in the given industry_code and industry_classification.
|
|
470
|
+
:rtype: dict[str, list[IdentificationTriple]]
|
|
471
|
+
"""
|
|
472
|
+
return self.fetch_from_industry_code(
|
|
473
|
+
industry_code=industry_code,
|
|
474
|
+
industry_classification=industry_classification,
|
|
475
|
+
fetch_ticker=True,
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
def fetch_company_from_industry_code(
|
|
479
|
+
self,
|
|
480
|
+
industry_code: str,
|
|
481
|
+
industry_classification: IndustryClassification,
|
|
482
|
+
) -> dict[str, list[int]]:
|
|
483
|
+
"""Fetches a list of companies that are classified in the given industry_code and industry_classification.
|
|
484
|
+
|
|
485
|
+
Returns a dictionary of shape {"companies": List[<company_id>]}.
|
|
486
|
+
:param industry_code: The industry_code to filter on. The industry_code is a string corresponding to the Industry classifications ontology.
|
|
487
|
+
:type industry_code: str
|
|
488
|
+
:param industry_classification: The type of industry_classification to filter on.
|
|
489
|
+
:type industry_classification: IndustryClassification
|
|
490
|
+
:return: A dictionary containing the list of companies that are classified in the given industry_code and industry_classification.
|
|
491
|
+
:rtype: dict[str, list[int]]
|
|
492
|
+
"""
|
|
493
|
+
return self.fetch_from_industry_code(
|
|
494
|
+
industry_code=industry_code,
|
|
495
|
+
industry_classification=industry_classification,
|
|
496
|
+
fetch_ticker=False,
|
|
497
|
+
)
|
kfinance/kfinance.py
CHANGED
|
@@ -7,16 +7,25 @@ from io import BytesIO
|
|
|
7
7
|
import logging
|
|
8
8
|
import re
|
|
9
9
|
from sys import stdout
|
|
10
|
-
from typing import Iterable, NamedTuple, Optional
|
|
10
|
+
from typing import TYPE_CHECKING, Any, Callable, Iterable, NamedTuple, Optional
|
|
11
11
|
from urllib.parse import urljoin
|
|
12
12
|
import webbrowser
|
|
13
13
|
|
|
14
|
+
import google.ai.generativelanguage_v1beta.types as gapic
|
|
15
|
+
from langchain_core.utils.function_calling import convert_to_openai_tool
|
|
16
|
+
from langchain_google_genai._function_utils import convert_to_genai_function_declarations
|
|
14
17
|
import numpy as np
|
|
15
18
|
import pandas as pd
|
|
16
19
|
from PIL.Image import Image, open as image_open
|
|
17
20
|
|
|
18
21
|
from .batch_request_handling import add_methods_of_singular_class_to_iterable_class
|
|
19
|
-
from .constants import
|
|
22
|
+
from .constants import (
|
|
23
|
+
HistoryMetadata,
|
|
24
|
+
IdentificationTriple,
|
|
25
|
+
LatestPeriods,
|
|
26
|
+
Periodicity,
|
|
27
|
+
YearAndQuarter,
|
|
28
|
+
)
|
|
20
29
|
from .fetch import (
|
|
21
30
|
DEFAULT_API_HOST,
|
|
22
31
|
DEFAULT_API_VERSION,
|
|
@@ -24,15 +33,6 @@ from .fetch import (
|
|
|
24
33
|
DEFAULT_OKTA_HOST,
|
|
25
34
|
KFinanceApiClient,
|
|
26
35
|
)
|
|
27
|
-
from .llm_tools import (
|
|
28
|
-
_llm_tools,
|
|
29
|
-
anthropic_tool_descriptions,
|
|
30
|
-
gemini_tool_descriptions,
|
|
31
|
-
get_latest,
|
|
32
|
-
get_n_quarters_ago,
|
|
33
|
-
langchain_tools,
|
|
34
|
-
openai_tool_descriptions,
|
|
35
|
-
)
|
|
36
36
|
from .meta_classes import (
|
|
37
37
|
CompanyFunctionsMetaClass,
|
|
38
38
|
DelegatedCompanyFunctionsMetaClass,
|
|
@@ -41,6 +41,9 @@ from .prompt import PROMPT
|
|
|
41
41
|
from .server_thread import ServerThread
|
|
42
42
|
|
|
43
43
|
|
|
44
|
+
if TYPE_CHECKING:
|
|
45
|
+
from kfinance.tool_calling.shared_models import KfinanceTool
|
|
46
|
+
|
|
44
47
|
logger = logging.getLogger(__name__)
|
|
45
48
|
|
|
46
49
|
|
|
@@ -124,23 +127,20 @@ class TradingItem:
|
|
|
124
127
|
|
|
125
128
|
def history(
|
|
126
129
|
self,
|
|
127
|
-
periodicity:
|
|
130
|
+
periodicity: Periodicity = Periodicity.day,
|
|
128
131
|
adjusted: bool = True,
|
|
129
132
|
start_date: Optional[str] = None,
|
|
130
133
|
end_date: Optional[str] = None,
|
|
131
134
|
) -> pd.DataFrame:
|
|
132
135
|
"""Retrieves the historical price data for a given asset over a specified date range.
|
|
133
136
|
|
|
134
|
-
:param
|
|
137
|
+
:param periodicity: Determines the frequency of the historical data returned. Defaults to Periodicity.day.
|
|
135
138
|
:param Optional[bool] adjusted: Whether to retrieve adjusted prices that account for corporate actions such as dividends and splits, it defaults True
|
|
136
139
|
:param Optional[str] start_date: The start date for historical price retrieval in format "YYYY-MM-DD", default to None
|
|
137
140
|
:param Optional[str] end_date: The end date for historical price retrieval in format "YYYY-MM-DD", default to None
|
|
138
|
-
:return: A pd.DataFrame containing historical price data with columns corresponding to the specified periodicity, with Date as the index, and columns "open", "high", "low", "close", "volume" in type decimal. The Date index is a string that depends on the periodicity. If
|
|
141
|
+
:return: A pd.DataFrame containing historical price data with columns corresponding to the specified periodicity, with Date as the index, and columns "open", "high", "low", "close", "volume" in type decimal. The Date index is a string that depends on the periodicity. If Periodicity.day, the Date index is the day in format "YYYY-MM-DD", eg "2024-05-13" If Periodicity.week, the Date index is the week number of the year in format "YYYY Week ##", eg "2024 Week 2" If Periodicity.month, the Date index is the month name of the year in format "<Month> YYYY", eg "January 2024". If Periodicity.year, the Date index is the year in format "YYYY", eg "2024".
|
|
139
142
|
:rtype: pd.DataFrame
|
|
140
143
|
"""
|
|
141
|
-
if periodicity not in {"day", "week", "month", "year"}:
|
|
142
|
-
raise RuntimeError(f"Periodicity type {periodicity} is not valid.")
|
|
143
|
-
|
|
144
144
|
if start_date and end_date:
|
|
145
145
|
if (
|
|
146
146
|
datetime.strptime(start_date, "%Y-%m-%d").date()
|
|
@@ -165,14 +165,14 @@ class TradingItem:
|
|
|
165
165
|
|
|
166
166
|
def price_chart(
|
|
167
167
|
self,
|
|
168
|
-
periodicity:
|
|
168
|
+
periodicity: Periodicity = Periodicity.day,
|
|
169
169
|
adjusted: bool = True,
|
|
170
170
|
start_date: Optional[str] = None,
|
|
171
171
|
end_date: Optional[str] = None,
|
|
172
172
|
) -> Image:
|
|
173
173
|
"""Get the price chart.
|
|
174
174
|
|
|
175
|
-
:param str periodicity: Determines the frequency of the historical data returned.
|
|
175
|
+
:param str periodicity: Determines the frequency of the historical data returned. Defaults to Periodicity.day.
|
|
176
176
|
:param Optional[bool] adjusted: Whether to retrieve adjusted prices that account for corporate actions such as dividends and splits, it defaults True
|
|
177
177
|
:param Optional[str] start_date: The start date for historical price retrieval in format "YYYY-MM-DD", default to None
|
|
178
178
|
:param Optional[str] end_date: The end date for historical price retrieval in format "YYYY-MM-DD", default to None
|
|
@@ -180,9 +180,6 @@ class TradingItem:
|
|
|
180
180
|
:rtype: Image
|
|
181
181
|
"""
|
|
182
182
|
|
|
183
|
-
if periodicity not in {"day", "week", "month", "year"}:
|
|
184
|
-
raise RuntimeError(f"Periodicity type {periodicity} is not valid.")
|
|
185
|
-
|
|
186
183
|
if start_date and end_date:
|
|
187
184
|
if (
|
|
188
185
|
datetime.strptime(start_date, "%Y-%m-%d").date()
|
|
@@ -855,22 +852,22 @@ class Ticker(DelegatedCompanyFunctionsMetaClass):
|
|
|
855
852
|
|
|
856
853
|
def history(
|
|
857
854
|
self,
|
|
858
|
-
periodicity:
|
|
855
|
+
periodicity: Periodicity = Periodicity.day,
|
|
859
856
|
adjusted: bool = True,
|
|
860
857
|
start_date: Optional[str] = None,
|
|
861
858
|
end_date: Optional[str] = None,
|
|
862
859
|
) -> pd.DataFrame:
|
|
863
860
|
"""Retrieves the historical price data for a given asset over a specified date range.
|
|
864
861
|
|
|
865
|
-
:param periodicity: Determines the frequency of the historical data returned.
|
|
866
|
-
:type periodicity:
|
|
862
|
+
:param periodicity: Determines the frequency of the historical data returned. Defaults to Periodicity.day.
|
|
863
|
+
:type periodicity: Periodicity
|
|
867
864
|
:param adjusted: Whether to retrieve adjusted prices that account for corporate actions such as dividends and splits, it defaults True
|
|
868
865
|
:type adjusted: bool, optional
|
|
869
866
|
:param start_date: The start date for historical price retrieval in format "YYYY-MM-DD", default to None
|
|
870
867
|
:type start_date: str, optional
|
|
871
868
|
:param end_date: The end date for historical price retrieval in format "YYYY-MM-DD", default to None
|
|
872
869
|
:type end_date: str, optional
|
|
873
|
-
:return: A pd.DataFrame containing historical price data with columns corresponding to the specified periodicity, with Date as the index, and columns "open", "high", "low", "close", "volume" in type decimal. The Date index is a string that depends on the periodicity. If
|
|
870
|
+
:return: A pd.DataFrame containing historical price data with columns corresponding to the specified periodicity, with Date as the index, and columns "open", "high", "low", "close", "volume" in type decimal. The Date index is a string that depends on the periodicity. If Periodicity.day, the Date index is the day in format "YYYY-MM-DD", eg "2024-05-13" If Periodicity.week, the Date index is the week number of the year in format "YYYY Week ##", eg "2024 Week 2" If Periodicity.month, the Date index is the month name of the year in format "<Month> YYYY", eg "January 2024". If Periodicity.year, the Date index is the year in format "YYYY", eg "2024".
|
|
874
871
|
:rtype: pd.DataFrame
|
|
875
872
|
"""
|
|
876
873
|
return self.primary_trading_item.history(
|
|
@@ -882,15 +879,15 @@ class Ticker(DelegatedCompanyFunctionsMetaClass):
|
|
|
882
879
|
|
|
883
880
|
def price_chart(
|
|
884
881
|
self,
|
|
885
|
-
periodicity:
|
|
882
|
+
periodicity: Periodicity = Periodicity.day,
|
|
886
883
|
adjusted: bool = True,
|
|
887
884
|
start_date: Optional[str] = None,
|
|
888
885
|
end_date: Optional[str] = None,
|
|
889
886
|
) -> Image:
|
|
890
887
|
"""Get the price chart.
|
|
891
888
|
|
|
892
|
-
:param periodicity: Determines the frequency of the historical data returned.
|
|
893
|
-
:type periodicity:
|
|
889
|
+
:param str periodicity: Determines the frequency of the historical data returned. Defaults to Periodicity.day.
|
|
890
|
+
:type periodicity: Periodicity
|
|
894
891
|
:param adjusted: Whether to retrieve adjusted prices that account for corporate actions such as dividends and splits, it defaults True
|
|
895
892
|
:type adjusted: bool, optional
|
|
896
893
|
:param start_date: The start date for historical price retrieval in format "YYYY-MM-DD", default to None
|
|
@@ -1121,11 +1118,63 @@ class Client:
|
|
|
1121
1118
|
)
|
|
1122
1119
|
stdout.write("Login credentials received.\n")
|
|
1123
1120
|
|
|
1124
|
-
self.
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1121
|
+
self._tools: list[KfinanceTool] | None = None
|
|
1122
|
+
|
|
1123
|
+
@property
|
|
1124
|
+
def langchain_tools(self) -> list["KfinanceTool"]:
|
|
1125
|
+
"""Return a list of all Kfinance tools for tool calling."""
|
|
1126
|
+
if self._tools is None:
|
|
1127
|
+
from kfinance.tool_calling import ALL_TOOLS
|
|
1128
|
+
|
|
1129
|
+
self._tools = [t(kfinance_client=self) for t in ALL_TOOLS] # type: ignore[call-arg]
|
|
1130
|
+
return self._tools
|
|
1131
|
+
|
|
1132
|
+
@property
|
|
1133
|
+
def tools(self) -> dict[str, Callable]:
|
|
1134
|
+
"""Return a mapping of tool calling function names to the corresponding functions.
|
|
1135
|
+
|
|
1136
|
+
`tools` is intended for running without langchain. When running with langchain,
|
|
1137
|
+
use `langchain_tools`.
|
|
1138
|
+
"""
|
|
1139
|
+
return {t.name: t.run_without_langchain for t in self.langchain_tools}
|
|
1140
|
+
|
|
1141
|
+
@property
|
|
1142
|
+
def anthropic_tool_descriptions(self) -> list[dict[str, Any]]:
|
|
1143
|
+
"""Return tool descriptions for anthropic"""
|
|
1144
|
+
|
|
1145
|
+
anthropic_tool_descriptions = []
|
|
1146
|
+
|
|
1147
|
+
for tool in self.langchain_tools:
|
|
1148
|
+
# Copied from https://python.langchain.com/api_reference/_modules/langchain_anthropic/chat_models.html#convert_to_anthropic_tool
|
|
1149
|
+
# to avoid adding a langchain-anthropic dependency.
|
|
1150
|
+
oai_formatted = convert_to_openai_tool(tool)["function"]
|
|
1151
|
+
anthropic_tool_descriptions.append(
|
|
1152
|
+
dict(
|
|
1153
|
+
name=oai_formatted["name"],
|
|
1154
|
+
description=oai_formatted["description"],
|
|
1155
|
+
input_schema=oai_formatted["parameters"],
|
|
1156
|
+
)
|
|
1157
|
+
)
|
|
1158
|
+
|
|
1159
|
+
return anthropic_tool_descriptions
|
|
1160
|
+
|
|
1161
|
+
@property
|
|
1162
|
+
def gemini_tool_descriptions(self) -> gapic.Tool:
|
|
1163
|
+
"""Return tool descriptions for gemini.
|
|
1164
|
+
|
|
1165
|
+
The conversion from BaseTool -> openai tool description -> google tool mirrors the
|
|
1166
|
+
langchain implementation.
|
|
1167
|
+
"""
|
|
1168
|
+
openai_tool_descriptions = [
|
|
1169
|
+
convert_to_openai_tool(t)["function"] for t in self.langchain_tools
|
|
1170
|
+
]
|
|
1171
|
+
return convert_to_genai_function_declarations(openai_tool_descriptions)
|
|
1172
|
+
|
|
1173
|
+
@property
|
|
1174
|
+
def openai_tool_descriptions(self) -> list[dict[str, Any]]:
|
|
1175
|
+
"""Return tool descriptions for gemini"""
|
|
1176
|
+
openai_tool_descriptions = [convert_to_openai_tool(t) for t in self.langchain_tools]
|
|
1177
|
+
return openai_tool_descriptions
|
|
1129
1178
|
|
|
1130
1179
|
@property
|
|
1131
1180
|
def access_token(self) -> str:
|
|
@@ -1154,7 +1203,7 @@ class Client:
|
|
|
1154
1203
|
:rtype: Ticker
|
|
1155
1204
|
"""
|
|
1156
1205
|
if function_called:
|
|
1157
|
-
self.kfinance_api_client.user_agent_source = "
|
|
1206
|
+
self.kfinance_api_client.user_agent_source = "tool_calling"
|
|
1158
1207
|
return Ticker(self.kfinance_api_client, str(identifier), exchange_code)
|
|
1159
1208
|
|
|
1160
1209
|
def tickers(
|
|
@@ -1222,14 +1271,42 @@ class Client:
|
|
|
1222
1271
|
)
|
|
1223
1272
|
|
|
1224
1273
|
@staticmethod
|
|
1225
|
-
def get_latest() -> LatestPeriods:
|
|
1274
|
+
def get_latest(use_local_timezone: bool = True) -> LatestPeriods:
|
|
1226
1275
|
"""Get the latest annual reporting year, latest quarterly reporting quarter and year, and current date.
|
|
1227
1276
|
|
|
1277
|
+
:param use_local_timezone: whether to use the local timezone of the user
|
|
1278
|
+
:type use_local_timezone: bool
|
|
1228
1279
|
:return: A dict in the form of {"annual": {"latest_year": int}, "quarterly": {"latest_quarter": int, "latest_year": int}, "now": {"current_year": int, "current_quarter": int, "current_month": int, "current_date": str of Y-m-d}}
|
|
1229
1280
|
:rtype: Latest
|
|
1230
1281
|
"""
|
|
1231
1282
|
|
|
1232
|
-
|
|
1283
|
+
datetime_now = datetime.now() if use_local_timezone else datetime.now(timezone.utc)
|
|
1284
|
+
current_year = datetime_now.year
|
|
1285
|
+
current_qtr = (datetime_now.month - 1) // 3 + 1
|
|
1286
|
+
|
|
1287
|
+
# Quarterly data. Get most recent year and quarter
|
|
1288
|
+
if current_qtr == 1:
|
|
1289
|
+
most_recent_year_qtrly = current_year - 1
|
|
1290
|
+
most_recent_qtr = 4
|
|
1291
|
+
else:
|
|
1292
|
+
most_recent_year_qtrly = current_year
|
|
1293
|
+
most_recent_qtr = current_qtr - 1
|
|
1294
|
+
|
|
1295
|
+
# Annual data. Get most recent year
|
|
1296
|
+
most_recent_year_annual = current_year - 1
|
|
1297
|
+
|
|
1298
|
+
current_month = datetime_now.month
|
|
1299
|
+
latest: LatestPeriods = {
|
|
1300
|
+
"annual": {"latest_year": most_recent_year_annual},
|
|
1301
|
+
"quarterly": {"latest_quarter": most_recent_qtr, "latest_year": most_recent_year_qtrly},
|
|
1302
|
+
"now": {
|
|
1303
|
+
"current_year": current_year,
|
|
1304
|
+
"current_quarter": current_qtr,
|
|
1305
|
+
"current_month": current_month,
|
|
1306
|
+
"current_date": datetime_now.date().isoformat(),
|
|
1307
|
+
},
|
|
1308
|
+
}
|
|
1309
|
+
return latest
|
|
1233
1310
|
|
|
1234
1311
|
@staticmethod
|
|
1235
1312
|
def get_n_quarters_ago(n: int) -> YearAndQuarter:
|
|
@@ -1240,4 +1317,17 @@ class Client:
|
|
|
1240
1317
|
:rtype: YearAndQuarter
|
|
1241
1318
|
"""
|
|
1242
1319
|
|
|
1243
|
-
|
|
1320
|
+
datetime_now = datetime.now()
|
|
1321
|
+
current_qtr = (datetime_now.month - 1) // 3 + 1
|
|
1322
|
+
total_quarters_completed = datetime_now.year * 4 + current_qtr - 1
|
|
1323
|
+
total_quarters_completed_n_quarters_ago = total_quarters_completed - n
|
|
1324
|
+
|
|
1325
|
+
year_n_quarters_ago = total_quarters_completed_n_quarters_ago // 4
|
|
1326
|
+
quarter_n_quarters_ago = total_quarters_completed_n_quarters_ago % 4 + 1
|
|
1327
|
+
|
|
1328
|
+
year_quarter_n_quarters_ago: YearAndQuarter = {
|
|
1329
|
+
"year": year_n_quarters_ago,
|
|
1330
|
+
"quarter": quarter_n_quarters_ago,
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
return year_quarter_n_quarters_ago
|