kensho-kfinance 2.6.5__py3-none-any.whl → 2.8.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.

Files changed (58) hide show
  1. {kensho_kfinance-2.6.5.dist-info → kensho_kfinance-2.8.0.dist-info}/METADATA +1 -1
  2. kensho_kfinance-2.8.0.dist-info/RECORD +70 -0
  3. kfinance/CHANGELOG.md +6 -0
  4. kfinance/decimal_with_unit.py +78 -0
  5. kfinance/fetch.py +36 -16
  6. kfinance/kfinance.py +45 -44
  7. kfinance/meta_classes.py +28 -27
  8. kfinance/models/business_relationship_models.py +26 -0
  9. kfinance/models/capitalization_models.py +90 -0
  10. kfinance/models/competitor_models.py +13 -0
  11. kfinance/models/currency_models.py +345 -0
  12. kfinance/models/date_and_period_models.py +48 -0
  13. kfinance/models/id_models.py +7 -0
  14. kfinance/models/industry_models.py +12 -0
  15. kfinance/{constants.py → models/line_item_models.py} +1 -165
  16. kfinance/models/permission_models.py +15 -0
  17. kfinance/models/price_models.py +70 -0
  18. kfinance/models/segment_models.py +8 -0
  19. kfinance/models/statement_models.py +9 -0
  20. kfinance/tests/test_batch_requests.py +61 -31
  21. kfinance/tests/test_client.py +1 -1
  22. kfinance/tests/test_decimal_with_unit.py +60 -0
  23. kfinance/tests/test_example_notebook.py +1 -0
  24. kfinance/tests/test_fetch.py +23 -12
  25. kfinance/tests/test_models/__init__.py +0 -0
  26. kfinance/tests/test_models/test_capitalization_models.py +83 -0
  27. kfinance/tests/test_models/test_price_models.py +58 -0
  28. kfinance/tests/test_objects.py +29 -23
  29. kfinance/tests/test_tools.py +63 -11
  30. kfinance/tool_calling/get_advisors_for_company_in_transaction_from_identifier.py +1 -1
  31. kfinance/tool_calling/get_business_relationship_from_identifier.py +2 -1
  32. kfinance/tool_calling/get_capitalization_from_identifier.py +4 -5
  33. kfinance/tool_calling/get_competitors_from_identifier.py +2 -1
  34. kfinance/tool_calling/get_cusip_from_ticker.py +1 -1
  35. kfinance/tool_calling/get_earnings.py +1 -1
  36. kfinance/tool_calling/get_financial_line_item_from_identifier.py +3 -1
  37. kfinance/tool_calling/get_financial_statement_from_identifier.py +3 -1
  38. kfinance/tool_calling/get_history_metadata_from_identifier.py +2 -1
  39. kfinance/tool_calling/get_info_from_identifier.py +1 -1
  40. kfinance/tool_calling/get_isin_from_ticker.py +1 -1
  41. kfinance/tool_calling/get_latest.py +2 -1
  42. kfinance/tool_calling/get_latest_earnings.py +1 -1
  43. kfinance/tool_calling/get_merger_info_from_transaction_id.py +1 -1
  44. kfinance/tool_calling/get_mergers_from_identifier.py +1 -1
  45. kfinance/tool_calling/get_n_quarters_ago.py +2 -1
  46. kfinance/tool_calling/get_next_earnings.py +1 -1
  47. kfinance/tool_calling/get_prices_from_identifier.py +4 -3
  48. kfinance/tool_calling/get_segments_from_identifier.py +3 -1
  49. kfinance/tool_calling/get_transcript.py +1 -1
  50. kfinance/tool_calling/resolve_identifier.py +1 -1
  51. kfinance/tool_calling/shared_models.py +20 -1
  52. kfinance/version.py +2 -2
  53. kensho_kfinance-2.6.5.dist-info/RECORD +0 -54
  54. {kensho_kfinance-2.6.5.dist-info → kensho_kfinance-2.8.0.dist-info}/WHEEL +0 -0
  55. {kensho_kfinance-2.6.5.dist-info → kensho_kfinance-2.8.0.dist-info}/licenses/AUTHORS.md +0 -0
  56. {kensho_kfinance-2.6.5.dist-info → kensho_kfinance-2.8.0.dist-info}/licenses/LICENSE +0 -0
  57. {kensho_kfinance-2.6.5.dist-info → kensho_kfinance-2.8.0.dist-info}/top_level.txt +0 -0
  58. /kfinance/{tests/scratch.py → models/__init__.py} +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kensho-kfinance
3
- Version: 2.6.5
3
+ Version: 2.8.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
@@ -0,0 +1,70 @@
1
+ kensho_kfinance-2.8.0.dist-info/licenses/AUTHORS.md,sha256=0h9ClbI0pu1oKj1M28ROUsaxrbZg-6ukQGl6X4y9noI,68
2
+ kensho_kfinance-2.8.0.dist-info/licenses/LICENSE,sha256=bsY4blvSgq6o0FMQ3RXa2NCgco--nHCCchLXzxr6kms,83
3
+ kfinance/CHANGELOG.md,sha256=JaR2dyTzhmue8dt6LVbt8ckSFTuqnJuqP5mYaePs85A,2245
4
+ kfinance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ kfinance/batch_request_handling.py,sha256=G9rhpgQaCdb5C1dsfuJip8383iszibcOJ5k8gxld-tQ,5001
6
+ kfinance/decimal_with_unit.py,sha256=U1eJSJ-4zPHkdtHcrfaW9CmmaLPE13MiWJy6odRyeXo,2904
7
+ kfinance/fetch.py,sha256=MSdDddZ_1YdmzCuXqCXwY_vfFLdbdQeCJ6YRQrwCUCI,28157
8
+ kfinance/kfinance.py,sha256=qa8oOFBayT2zIdvDh4Gizg1aF_PNWJrsvoAd1q5Oflg,73225
9
+ kfinance/mcp.py,sha256=MbktclVfBOEwfe-eR7kPaTXopMJmn_8RMlf4Jx5CXKU,3689
10
+ kfinance/meta_classes.py,sha256=0wI70PXfQzNbRUW2nYVfxJjqCqqI5EukUIKjaMcrzVA,21804
11
+ kfinance/prompt.py,sha256=PtVB8c_FcSlVdyGgByAnIFGzuUuBaEjciCqnBJl1hSQ,25133
12
+ kfinance/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ kfinance/pydantic_models.py,sha256=avpbPqwrAyLqsCbrmFpK_B8_fj1nPlBHrnPxRcBaSkE,774
14
+ kfinance/server_thread.py,sha256=jUnt1YGoYDkqqz1MbCwd44zJs1T_Z2BCgvj75bdtLgA,2574
15
+ kfinance/version.py,sha256=xvvCLYZzL60ATcl_e94czeUS6YcsWE3LumbDKAyCfWM,511
16
+ kfinance/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ kfinance/models/business_relationship_models.py,sha256=XlkYI_XUegORH4U4oLXjdFIX9Pr9FvQUnFAc67D2M4s,770
18
+ kfinance/models/capitalization_models.py,sha256=jB0MIZ94UOPfVckuS2asED9ARjisrStC_O4KgfHmoJw,3193
19
+ kfinance/models/competitor_models.py,sha256=0yDu49Pe28ErpCMLX-BBeUnjjxqerb49ToVKSTOivNk,565
20
+ kfinance/models/currency_models.py,sha256=JfDZf-nATCyjThBw-Ky2BNvM-n2oHepBLIHMCIyCRUE,19201
21
+ kfinance/models/date_and_period_models.py,sha256=B508v1SLy_mbSSuSbPz41xeFNRAeBFv41gJSlaXyHnU,1062
22
+ kfinance/models/id_models.py,sha256=xhFKmDddBfqkQZsbOP8za8VnxIFHiwvGThKjeGKC2Hg,138
23
+ kfinance/models/industry_models.py,sha256=ydB_cTIsNsfCUeaO7K2qqLUuHdG6WfUCoAzBigKVV8M,243
24
+ kfinance/models/line_item_models.py,sha256=JIdoSYxoJDQVkmqUK6_ABpb6lULXRvrCDJGiKL6kRhE,45399
25
+ kfinance/models/permission_models.py,sha256=z6MKY7Zf7StPKwsucsoVqltUesicU6MC6rxRGmlRwZI,557
26
+ kfinance/models/price_models.py,sha256=saiJk1ZIs02cHtY54-5YvS7NgH3EABR2RGT6wpzRauY,2149
27
+ kfinance/models/segment_models.py,sha256=GSJhK1HmbMmwRDFmrDTZzx8aYrAaHN13IJ0J9GTBn9Y,145
28
+ kfinance/models/statement_models.py,sha256=RmclTeyahCODUP0-KgZZZequFgQ2pUQ8cdfHI96c9vA,207
29
+ kfinance/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ kfinance/tests/conftest.py,sha256=dhL_RSc-af3j-2_UrAGRE9mxgcbjuIRtj08DTx79pQc,1123
31
+ kfinance/tests/test_batch_requests.py,sha256=JjP5OOKyxzYqmVmTR2hzM4Q6wyYDCEDHINH4vNFd6ag,11597
32
+ kfinance/tests/test_client.py,sha256=YZT-AXpFsVHpdoZj01d2TkmUkNP-8f3cIOgeXtP_05U,3870
33
+ kfinance/tests/test_decimal_with_unit.py,sha256=98zZmSLojWq3FS0e5AuNxv9eluqGVEoqM3r_agkmt_Y,2188
34
+ kfinance/tests/test_example_notebook.py,sha256=VFS2W_T0yEtd74YiRxj3KA7eEL_fJuCpWwUAkiFqzkc,6501
35
+ kfinance/tests/test_fetch.py,sha256=s8fSkXDtNjDcK_jWQYUO1stx9ElX1I54qG_48WxfBAc,18558
36
+ kfinance/tests/test_group_objects.py,sha256=SoMEZmkG4RYdgWOAwxLHHtzIQho92KM01YbQXPUg578,1689
37
+ kfinance/tests/test_objects.py,sha256=Fd5iYy-Ofzil3xJr5hLflsvvNVwoWfEDDiRZb_-LVKc,41082
38
+ kfinance/tests/test_tools.py,sha256=jLrugYTSdK6Cw4SmJxsxdYdJ1S3X7wDcBC7kQAWe75A,31024
39
+ kfinance/tests/test_models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
+ kfinance/tests/test_models/test_capitalization_models.py,sha256=E-5eZX0ufdFsmcWATt5agyH4Ks-q8TNO-W6cnYIMOcE,3116
41
+ kfinance/tests/test_models/test_price_models.py,sha256=DHBMAFJ8axat1s_C0tElqcCjw6YhmzF9WWKpKmySwv0,2378
42
+ kfinance/tool_calling/README.md,sha256=omJq7Us6r4U45QB7hRpLjRJ5BMalCkZkh4uXBjTbJXc,2022
43
+ kfinance/tool_calling/__init__.py,sha256=UmtbtG6PvQHB1fInEL-K5q0kPHL__zTY9wzaPRSp1wg,2174
44
+ kfinance/tool_calling/get_advisors_for_company_in_transaction_from_identifier.py,sha256=kL8cnTIsnPBAZcTriKJKL2uFfFFNUuV0PMKVitD2MXQ,1630
45
+ kfinance/tool_calling/get_business_relationship_from_identifier.py,sha256=oPlVpDy4AY8nQFZnHRp9DhpL8SecIlyEY6aiklHtVDg,1551
46
+ kfinance/tool_calling/get_capitalization_from_identifier.py,sha256=qciQI9gSJh4gGatnjOkid-GJSlzXkvt7-2VcK2yBfrg,1558
47
+ kfinance/tool_calling/get_competitors_from_identifier.py,sha256=J9TVWNU_6d6ltJLIJTdCtkhbc0MqoCbqdWUxrwBkOUs,1156
48
+ kfinance/tool_calling/get_cusip_from_ticker.py,sha256=ebzFmjZFkcuydntQXmuxfh8mhPiXmmzUTb2ncHy00ao,667
49
+ kfinance/tool_calling/get_earnings.py,sha256=d7UvVsSAcrVlveWyUpua35vdloA_QgJnQ4Nmg7wT7m0,1245
50
+ kfinance/tool_calling/get_financial_line_item_from_identifier.py,sha256=hsKmObT2TJ4HMusVtQXU6FMDmyY7bKg2V-x-IXuU71c,2270
51
+ kfinance/tool_calling/get_financial_statement_from_identifier.py,sha256=ba8Z378YrQ_J0-A6KVLOtxe-hYMP68780g0xXZfAyT8,2023
52
+ kfinance/tool_calling/get_history_metadata_from_identifier.py,sha256=hPC-RwwcnJ9kkhxTUJLE-xvCqfIq5kJcKM4deiL8zlI,789
53
+ kfinance/tool_calling/get_info_from_identifier.py,sha256=2-NmGvFpYXASGV_lJaYhhdXMxQpyvi30bXY9mGWwbBw,787
54
+ kfinance/tool_calling/get_isin_from_ticker.py,sha256=tp79lowBUdkv7fm-8QX4vJqeDZiq5M_jkkp8XKC1KY4,661
55
+ kfinance/tool_calling/get_latest.py,sha256=m6nBA8Bo51Q85kGbGnP59IUz4-lm_CRo1kSuNM26iXg,857
56
+ kfinance/tool_calling/get_latest_earnings.py,sha256=R98ImaONP3AjVshygkRLwcAHKscvVRkVoPw117O9nuw,1197
57
+ kfinance/tool_calling/get_merger_info_from_transaction_id.py,sha256=zzAg79hbUmwcdg-hJ2wGjcTQ_YHWXw85eOt48lmlZLs,3165
58
+ kfinance/tool_calling/get_mergers_from_identifier.py,sha256=2w8bA-FQMdbmleuoWTbMPHK1K4IRxwHciZQPeQGGIVw,1725
59
+ kfinance/tool_calling/get_n_quarters_ago.py,sha256=VYUBbQIowHXmNuln1oKoDKeWDbnXLg4p6X0d1HoY_NY,784
60
+ kfinance/tool_calling/get_next_earnings.py,sha256=vL3sCoocPhHDRawVRu9IyQPLXVAgODrZgKSqkP3Scs8,1177
61
+ kfinance/tool_calling/get_prices_from_identifier.py,sha256=uXD9fiLFI0vUM_YY8DI_9ifC0oeeC3KCyaTQQ0IjuSA,1966
62
+ kfinance/tool_calling/get_segments_from_identifier.py,sha256=1Ux9ZWEU3xmOSuAPGedo2r8wdRa_glv8roghp08MJ-c,1980
63
+ kfinance/tool_calling/get_transcript.py,sha256=urZgpQXigmahy-vj6OjkGI9O6HJcPIpd-5CRU7NI3YQ,782
64
+ kfinance/tool_calling/prompts.py,sha256=Yw1DJIMh90cjL-8q6_RMRiSjCtFDXvJAy7QiV5_uAU8,911
65
+ kfinance/tool_calling/resolve_identifier.py,sha256=Rkhwi6m3hffE19qaxRkrsu6slRKyiAXjfLr8L41N2zc,647
66
+ kfinance/tool_calling/shared_models.py,sha256=Sx_zlwk5YLu533406Pmh499NH5oTBWrZf8Ua8zoUSRk,3746
67
+ kensho_kfinance-2.8.0.dist-info/METADATA,sha256=XiOVL58W7LuXqyo76ASS9XESdvFUCHVYHXigOl11YTY,6197
68
+ kensho_kfinance-2.8.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
69
+ kensho_kfinance-2.8.0.dist-info/top_level.txt,sha256=kT_kNwVhfQoOAecY8W7uYah5xaHMoHoAdBIvXh6DaKM,9
70
+ kensho_kfinance-2.8.0.dist-info/RECORD,,
kfinance/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## v2.8.0
4
+ - Add currency to get_capitalization and get_prices tools.
5
+
6
+ ## v2.7.0
7
+ - Expose a new list of LLM tools `grounding_tools` that return a list of all endpoint urls called
8
+
3
9
  ## v2.6.5
4
10
  - Add competitor company_name
5
11
 
@@ -0,0 +1,78 @@
1
+ from copy import deepcopy
2
+ from decimal import Decimal
3
+ from typing import Any
4
+
5
+ from pydantic import BaseModel, Field, model_validator
6
+ from typing_extensions import Self
7
+
8
+ from kfinance.models.currency_models import ISO_CODE_TO_CURRENCY
9
+
10
+
11
+ class DecimalWithUnit(BaseModel):
12
+ """DecimalWithUnit (DWU) represents a decimal with a corresponding unit like $100 or 20 shares.
13
+
14
+ In addition to a value and unit, each DWU has a `conventional_decimals` attribute,
15
+ which indicates the number of decimals that should be represented.
16
+ For example, for USD, conventional_decimals is 2, which will display as "1.00".
17
+ For shares, conventional_decimals is 0, which will display as "1"
18
+
19
+ Usually, rather than initializing a DWU directly, you'll likely want to use an
20
+ existing subclass like `Money` or `Shares` or create a new one.
21
+ """
22
+
23
+ value: Decimal
24
+ unit: str
25
+ # exclude conventional_decimals from serialization
26
+ conventional_decimals: int = Field(exclude=True)
27
+
28
+ @model_validator(mode="after")
29
+ def quantize_value(self) -> Self:
30
+ """Quantize the value at the end of the deserialization.
31
+
32
+ The value gets adjusted so that it always has the expected number of decimals defined in
33
+ conventional_decimals.
34
+ For USD with conventional_decimals=2, it will show values like "1.00"
35
+ For Shares with conventional_decimals=0, it will show values like "1"
36
+ """
37
+ exponent = Decimal("10") ** Decimal(-self.conventional_decimals)
38
+ self.value = self.value.quantize(exp=exponent)
39
+ return self
40
+
41
+
42
+ class Money(DecimalWithUnit):
43
+ @model_validator(mode="before")
44
+ @classmethod
45
+ def inject_conventional_decimals_into_data(cls, data: Any) -> Any:
46
+ """Inject conventional_decimals into data dict.
47
+
48
+ Each currency has an associated conventional_decimals defined in the
49
+ CURRENCIES list. This validator fetches that number and injects it into the
50
+ data dict.
51
+ """
52
+
53
+ if isinstance(data, dict) and "conventional_decimals" not in data:
54
+ data = deepcopy(data)
55
+ currency = ISO_CODE_TO_CURRENCY[data["unit"]]
56
+ data["conventional_decimals"] = currency.conventional_decimals
57
+
58
+ return data
59
+
60
+
61
+ class Shares(DecimalWithUnit):
62
+ unit: str = "Shares"
63
+ conventional_decimals: int = Field(exclude=True, default=0)
64
+
65
+ @model_validator(mode="before")
66
+ @classmethod
67
+ def convert_numbers_to_dicts(cls, data: Any) -> Any:
68
+ """Convert numbers into dicts.
69
+
70
+ The shares class can be built from a single number because unit and
71
+ conventional_decimals are always the same. However, the parser expects a
72
+ dict instead of a number, so we have to convert any number ("10") into a
73
+ dict {"value": "10"}.
74
+ """
75
+
76
+ if isinstance(data, (str, int, float, Decimal)):
77
+ data = {"value": data}
78
+ return data
kfinance/fetch.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from concurrent.futures import ThreadPoolExecutor
2
2
  from contextlib import contextmanager
3
3
  import logging
4
+ from queue import Queue
4
5
  from time import time
5
6
  from typing import Callable, Generator, Optional
6
7
  from uuid import uuid4
@@ -9,23 +10,22 @@ import jwt
9
10
  from pydantic import ValidationError
10
11
  import requests
11
12
 
12
- from .constants import (
13
- BusinessRelationshipType,
14
- CompetitorSource,
15
- IdentificationTriple,
16
- IndustryClassification,
17
- Periodicity,
18
- PeriodType,
19
- Permission,
20
- SegmentType,
21
- )
22
- from .pydantic_models import RelationshipResponse, RelationshipResponseNoName
13
+ from kfinance.models.business_relationship_models import BusinessRelationshipType
14
+ from kfinance.models.capitalization_models import Capitalizations
15
+ from kfinance.models.competitor_models import CompetitorSource
16
+ from kfinance.models.date_and_period_models import Periodicity, PeriodType
17
+ from kfinance.models.id_models import IdentificationTriple
18
+ from kfinance.models.industry_models import IndustryClassification
19
+ from kfinance.models.permission_models import Permission
20
+ from kfinance.models.price_models import PriceHistory
21
+ from kfinance.models.segment_models import SegmentType
22
+ from kfinance.pydantic_models import RelationshipResponse, RelationshipResponseNoName
23
23
 
24
24
 
25
25
  # version.py gets autogenerated by setuptools-scm and is not available
26
26
  # during local development.
27
27
  try:
28
- from .version import __version__ as kfinance_version
28
+ from kfinance.version import __version__ as kfinance_version
29
29
  except ImportError:
30
30
  kfinance_version = "dev"
31
31
 
@@ -95,6 +95,7 @@ class KFinanceApiClient:
95
95
  self._batch_id: str | None = None
96
96
  self._batch_size: str | None = None
97
97
  self._user_permissions: set[Permission] | None = None
98
+ self._endpoint_tracker_queue: Queue[str] | None = None
98
99
 
99
100
  @contextmanager
100
101
  def batch_request_header(self, batch_size: int) -> Generator:
@@ -208,9 +209,28 @@ class KFinanceApiClient:
208
209
  permission_str,
209
210
  )
210
211
 
212
+ @contextmanager
213
+ def endpoint_tracker(self) -> Generator:
214
+ """Context manager to track and return endpoint URLs in our thread-safe queue during execution.
215
+
216
+ endpoint_tracker yields a queue into which all endpoint URLs are written until the context manager gets exited.
217
+ It is up to the callers to dequeue the queue before the context manager gets exited and the queue gets wiped.
218
+ This functionality is currently used by `run_with_grounding` to collect and forward endpoint URLs.
219
+ """
220
+ self._endpoint_tracker_queue = Queue[str]()
221
+
222
+ try:
223
+ yield self._endpoint_tracker_queue
224
+ finally:
225
+ self._endpoint_tracker_queue = None
226
+
211
227
  def fetch(self, url: str) -> dict:
212
228
  """Does the request and auth"""
213
229
 
230
+ # _endpoint_tracker_queue will only be initialized if inside the endpoint_tracker context manager
231
+ if self._endpoint_tracker_queue:
232
+ self._endpoint_tracker_queue.put(url)
233
+
214
234
  headers = {
215
235
  "Content-Type": "application/json",
216
236
  "Authorization": f"Bearer {self.access_token}",
@@ -279,7 +299,7 @@ class KFinanceApiClient:
279
299
  start_date: Optional[str] = None,
280
300
  end_date: Optional[str] = None,
281
301
  periodicity: Optional[Periodicity] = None,
282
- ) -> dict:
302
+ ) -> PriceHistory:
283
303
  """Get the pricing history."""
284
304
  url = (
285
305
  f"{self.url_base}pricing/{trading_item_id}/"
@@ -288,7 +308,7 @@ class KFinanceApiClient:
288
308
  f"{periodicity if periodicity else 'none'}/"
289
309
  f"{'adjusted' if is_adjusted else 'unadjusted'}"
290
310
  )
291
- return self.fetch(url)
311
+ return PriceHistory.model_validate(self.fetch(url))
292
312
 
293
313
  def fetch_history_metadata(self, trading_item_id: int) -> dict[str, str]:
294
314
  """Get the pricing history metadata."""
@@ -300,14 +320,14 @@ class KFinanceApiClient:
300
320
  company_id: int,
301
321
  start_date: Optional[str] = None,
302
322
  end_date: Optional[str] = None,
303
- ) -> dict:
323
+ ) -> Capitalizations:
304
324
  """Get the market cap, TEV, and shares outstanding for a company."""
305
325
  url = (
306
326
  f"{self.url_base}market_cap/{company_id}/"
307
327
  f"{start_date if start_date is not None else 'none'}/"
308
328
  f"{end_date if end_date is not None else 'none'}"
309
329
  )
310
- return self.fetch(url)
330
+ return Capitalizations.model_validate(self.fetch(url))
311
331
 
312
332
  def fetch_segments(
313
333
  self,
kfinance/kfinance.py CHANGED
@@ -15,33 +15,28 @@ import webbrowser
15
15
  import google.ai.generativelanguage_v1beta.types as gapic
16
16
  from langchain_core.utils.function_calling import convert_to_openai_tool
17
17
  from langchain_google_genai._function_utils import convert_to_genai_function_declarations
18
- import numpy as np
19
18
  import pandas as pd
20
19
  from PIL.Image import Image, open as image_open
21
20
 
22
- from .batch_request_handling import add_methods_of_singular_class_to_iterable_class
23
- from .constants import (
24
- HistoryMetadata,
25
- IdentificationTriple,
26
- IndustryClassification,
27
- LatestPeriods,
28
- Periodicity,
29
- YearAndQuarter,
30
- )
31
- from .fetch import (
21
+ from kfinance.batch_request_handling import add_methods_of_singular_class_to_iterable_class
22
+ from kfinance.fetch import (
32
23
  DEFAULT_API_HOST,
33
24
  DEFAULT_API_VERSION,
34
25
  DEFAULT_OKTA_AUTH_SERVER,
35
26
  DEFAULT_OKTA_HOST,
36
27
  KFinanceApiClient,
37
28
  )
38
- from .meta_classes import (
29
+ from kfinance.meta_classes import (
39
30
  CompanyFunctionsMetaClass,
40
31
  DelegatedCompanyFunctionsMetaClass,
41
32
  )
42
- from .prompt import PROMPT
43
- from .pydantic_models import TranscriptComponent
44
- from .server_thread import ServerThread
33
+ from kfinance.models.date_and_period_models import LatestPeriods, Periodicity, YearAndQuarter
34
+ from kfinance.models.id_models import IdentificationTriple
35
+ from kfinance.models.industry_models import IndustryClassification
36
+ from kfinance.models.price_models import HistoryMetadata, PriceHistory
37
+ from kfinance.prompt import PROMPT
38
+ from kfinance.pydantic_models import TranscriptComponent
39
+ from kfinance.server_thread import ServerThread
45
40
 
46
41
 
47
42
  if TYPE_CHECKING:
@@ -140,36 +135,29 @@ class TradingItem:
140
135
  adjusted: bool = True,
141
136
  start_date: Optional[str] = None,
142
137
  end_date: Optional[str] = None,
143
- ) -> pd.DataFrame:
138
+ ) -> PriceHistory:
144
139
  """Retrieves the historical price data for a given asset over a specified date range.
145
140
 
146
141
  :param periodicity: Determines the frequency of the historical data returned. Defaults to Periodicity.day.
147
142
  :param Optional[bool] adjusted: Whether to retrieve adjusted prices that account for corporate actions such as dividends and splits, it defaults True
148
143
  :param Optional[str] start_date: The start date for historical price retrieval in format "YYYY-MM-DD", default to None
149
144
  :param Optional[str] end_date: The end date for historical price retrieval in format "YYYY-MM-DD", default to None
150
- :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".
151
- :rtype: pd.DataFrame
145
+ :return: A PriceHistory containing historical price data including "open", "high", "low", "close", "volume" in type Money. The date value 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".
146
+ :rtype: PriceHistory
152
147
  """
153
148
  if start_date and end_date:
154
149
  if (
155
150
  datetime.strptime(start_date, "%Y-%m-%d").date()
156
151
  > datetime.strptime(end_date, "%Y-%m-%d").date()
157
152
  ):
158
- return pd.DataFrame()
153
+ return PriceHistory(prices=[])
159
154
 
160
- return (
161
- pd.DataFrame(
162
- self.kfinance_api_client.fetch_history(
163
- trading_item_id=self.trading_item_id,
164
- is_adjusted=adjusted,
165
- start_date=start_date,
166
- end_date=end_date,
167
- periodicity=periodicity,
168
- )["prices"]
169
- )
170
- .set_index("date")
171
- .apply(pd.to_numeric)
172
- .replace(np.nan, None)
155
+ return self.kfinance_api_client.fetch_history(
156
+ trading_item_id=self.trading_item_id,
157
+ is_adjusted=adjusted,
158
+ start_date=start_date,
159
+ end_date=end_date,
160
+ periodicity=periodicity,
173
161
  )
174
162
 
175
163
  def price_chart(
@@ -815,7 +803,11 @@ class Security:
815
803
  "trading_items"
816
804
  ]
817
805
  self._trading_items = TradingItems(
818
- kfinance_api_client=self.kfinance_api_client, trading_item_ids=trading_item_ids
806
+ kfinance_api_client=self.kfinance_api_client,
807
+ trading_items=[
808
+ TradingItem(kfinance_api_client=self.kfinance_api_client, trading_item_id=tii)
809
+ for tii in trading_item_ids
810
+ ],
819
811
  )
820
812
  return self._trading_items
821
813
 
@@ -1194,7 +1186,7 @@ class Ticker(DelegatedCompanyFunctionsMetaClass):
1194
1186
  adjusted: bool = True,
1195
1187
  start_date: Optional[str] = None,
1196
1188
  end_date: Optional[str] = None,
1197
- ) -> pd.DataFrame:
1189
+ ) -> PriceHistory:
1198
1190
  """Retrieves the historical price data for a given asset over a specified date range.
1199
1191
 
1200
1192
  :param periodicity: Determines the frequency of the historical data returned. Defaults to Periodicity.day.
@@ -1205,8 +1197,8 @@ class Ticker(DelegatedCompanyFunctionsMetaClass):
1205
1197
  :type start_date: str, optional
1206
1198
  :param end_date: The end date for historical price retrieval in format "YYYY-MM-DD", default to None
1207
1199
  :type end_date: str, optional
1208
- :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".
1209
- :rtype: pd.DataFrame
1200
+ :return: A PriceHistory containing historical price data including "open", "high", "low", "close", "volume" in type Money. The date value 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".
1201
+ :rtype: PriceHistory
1210
1202
  """
1211
1203
  return self.primary_trading_item.history(
1212
1204
  periodicity,
@@ -1425,20 +1417,17 @@ class TradingItems(set):
1425
1417
  """Base class for representing a set of Trading Items"""
1426
1418
 
1427
1419
  def __init__(
1428
- self, kfinance_api_client: KFinanceApiClient, trading_item_ids: Iterable[int]
1420
+ self, kfinance_api_client: KFinanceApiClient, trading_items: Iterable[TradingItem]
1429
1421
  ) -> None:
1430
1422
  """Initialize the Trading Items
1431
1423
 
1432
1424
  :param kfinance_api_client: The KFinanceApiClient used to fetch data
1433
1425
  :type kfinance_api_client: KFinanceApiClient
1434
- :param company_ids: An iterable of S&P CIQ Company ids
1435
- :type company_ids: Iterable[int]
1426
+ :param trading_items: An iterable of TradingItem
1427
+ :type trading_items: Iterable[TradingItem]
1436
1428
  """
1437
1429
  self.kfinance_api_client = kfinance_api_client
1438
- super().__init__(
1439
- TradingItem(kfinance_api_client, trading_item_id)
1440
- for trading_item_id in trading_item_ids
1441
- )
1430
+ super().__init__(trading_items)
1442
1431
 
1443
1432
 
1444
1433
  @add_methods_of_singular_class_to_iterable_class(Ticker)
@@ -1515,7 +1504,14 @@ class Tickers(set):
1515
1504
  :rtype: TradingItems
1516
1505
  """
1517
1506
  return TradingItems(
1518
- self.kfinance_api_client, (ticker.trading_item_id for ticker in self.__iter__())
1507
+ self.kfinance_api_client,
1508
+ [
1509
+ TradingItem(
1510
+ kfinance_api_client=self.kfinance_api_client,
1511
+ trading_item_id=ticker.trading_item_id,
1512
+ )
1513
+ for ticker in self.__iter__()
1514
+ ],
1519
1515
  )
1520
1516
 
1521
1517
 
@@ -1663,6 +1659,11 @@ class Client:
1663
1659
  """
1664
1660
  return {t.name: t.run_without_langchain for t in self.langchain_tools}
1665
1661
 
1662
+ @property
1663
+ def grounding_tools(self) -> dict[str, Callable]:
1664
+ """Return a mapping of tool calling function names to the corresponding functions for the grounding agent."""
1665
+ return {t.name: t.run_with_grounding for t in self.langchain_tools}
1666
+
1666
1667
  @property
1667
1668
  def anthropic_tool_descriptions(self) -> list[dict[str, Any]]:
1668
1669
  """Return tool descriptions for anthropic"""
kfinance/meta_classes.py CHANGED
@@ -1,20 +1,19 @@
1
1
  from abc import abstractmethod
2
2
  from datetime import datetime
3
3
  import logging
4
- from typing import TYPE_CHECKING, Any, Callable, Literal, Optional
4
+ from typing import TYPE_CHECKING, Any, Callable, Optional
5
5
 
6
6
  from cachetools import LRUCache, cached
7
7
  import numpy as np
8
8
  import pandas as pd
9
9
 
10
- from .constants import (
11
- LINE_ITEMS,
12
- BusinessRelationshipType,
13
- CompetitorSource,
14
- PeriodType,
15
- SegmentType,
16
- )
17
10
  from .fetch import KFinanceApiClient
11
+ from .models.business_relationship_models import BusinessRelationshipType
12
+ from .models.capitalization_models import Capitalization
13
+ from .models.competitor_models import CompetitorSource
14
+ from .models.date_and_period_models import PeriodType
15
+ from .models.line_item_models import LINE_ITEMS
16
+ from .models.segment_models import SegmentType
18
17
  from .pydantic_models import RelationshipResponse
19
18
 
20
19
 
@@ -262,73 +261,75 @@ class CompanyFunctionsMetaClass:
262
261
  self,
263
262
  start_date: Optional[str] = None,
264
263
  end_date: Optional[str] = None,
265
- ) -> pd.DataFrame:
264
+ ) -> dict:
266
265
  """Retrieves market caps for a company between start and end date.
267
266
 
268
267
  :param start_date: The start date in format "YYYY-MM-DD", default to None
269
268
  :type start_date: str, optional
270
269
  :param end_date: The end date in format "YYYY-MM-DD", default to None
271
270
  :type end_date: str, optional
272
- :return: A DataFrame with a `market_cap` column. The dates are the index.
273
- :rtype: pd.DataFrame
271
+ :return: A dict with market_cap
272
+ :rtype: dict
274
273
  """
275
274
 
276
275
  return self._fetch_market_cap_tev_or_shares_outstanding(
277
- column_to_extract="market_cap", start_date=start_date, end_date=end_date
276
+ capitalization_to_extract=Capitalization.market_cap,
277
+ start_date=start_date,
278
+ end_date=end_date,
278
279
  )
279
280
 
280
281
  def tev(
281
282
  self,
282
283
  start_date: Optional[str] = None,
283
284
  end_date: Optional[str] = None,
284
- ) -> pd.DataFrame:
285
+ ) -> dict:
285
286
  """Retrieves TEV (total enterprise value) for a company between start and end date.
286
287
 
287
288
  :param start_date: The start date in format "YYYY-MM-DD", default to None
288
289
  :type start_date: str, optional
289
290
  :param end_date: The end date in format "YYYY-MM-DD", default to None
290
291
  :type end_date: str, optional
291
- :return: A DataFrame with a `tev` column. The dates are the index.
292
- :rtype: pd.DataFrame
292
+ :return: A dict with TEV
293
+ :rtype: dict
293
294
  """
294
295
 
295
296
  return self._fetch_market_cap_tev_or_shares_outstanding(
296
- column_to_extract="tev", start_date=start_date, end_date=end_date
297
+ capitalization_to_extract=Capitalization.tev, start_date=start_date, end_date=end_date
297
298
  )
298
299
 
299
300
  def shares_outstanding(
300
301
  self,
301
302
  start_date: Optional[str] = None,
302
303
  end_date: Optional[str] = None,
303
- ) -> pd.DataFrame:
304
+ ) -> dict:
304
305
  """Retrieves shares outstanding for a company between start and end date.
305
306
 
306
307
  :param start_date: The start date in format "YYYY-MM-DD", default to None
307
308
  :type start_date: str, optional
308
309
  :param end_date: The end date in format "YYYY-MM-DD", default to None
309
310
  :type end_date: str, optional
310
- :return: A DataFrame with a `shares_outstanding` column. The dates are the index.
311
- :rtype: pd.DataFrame
311
+ :return: A dict with outstanding shares
312
+ :rtype: dict
312
313
  """
313
314
 
314
315
  return self._fetch_market_cap_tev_or_shares_outstanding(
315
- column_to_extract="shares_outstanding", start_date=start_date, end_date=end_date
316
+ capitalization_to_extract=Capitalization.shares_outstanding,
317
+ start_date=start_date,
318
+ end_date=end_date,
316
319
  )
317
320
 
318
321
  def _fetch_market_cap_tev_or_shares_outstanding(
319
322
  self,
320
- column_to_extract: Literal["market_cap", "tev", "shares_outstanding"],
323
+ capitalization_to_extract: Capitalization,
321
324
  start_date: str | None,
322
325
  end_date: str | None,
323
- ) -> pd.DataFrame:
326
+ ) -> dict:
324
327
  """Helper function to fetch market cap, TEV, and shares outstanding."""
325
328
 
326
- df = pd.DataFrame(
327
- self.kfinance_api_client.fetch_market_caps_tevs_and_shares_outstanding(
328
- company_id=self.company_id, start_date=start_date, end_date=end_date
329
- )["market_caps"]
329
+ capitalizations = self.kfinance_api_client.fetch_market_caps_tevs_and_shares_outstanding(
330
+ company_id=self.company_id, start_date=start_date, end_date=end_date
330
331
  )
331
- return df.set_index("date")[[column_to_extract]].apply(pd.to_numeric).replace(np.nan, None)
332
+ return capitalizations.jsonify_single_attribute(capitalization_to_extract)
332
333
 
333
334
  def _segments(
334
335
  self,
@@ -0,0 +1,26 @@
1
+ from strenum import StrEnum
2
+
3
+
4
+ class BusinessRelationshipType(StrEnum):
5
+ """The type of business relationship"""
6
+
7
+ supplier = "supplier"
8
+ customer = "customer"
9
+ distributor = "distributor"
10
+ franchisor = "franchisor"
11
+ franchisee = "franchisee"
12
+ landlord = "landlord"
13
+ tenant = "tenant"
14
+ licensor = "licensor"
15
+ licensee = "licensee"
16
+ creditor = "creditor"
17
+ borrower = "borrower"
18
+ lessor = "lessor"
19
+ lessee = "lessee"
20
+ strategic_alliance = "strategic_alliance"
21
+ investor_relations_firm = "investor_relations_firm"
22
+ investor_relations_client = "investor_relations_client"
23
+ transfer_agent = "transfer_agent"
24
+ transfer_agent_client = "transfer_agent_client"
25
+ vendor = "vendor"
26
+ client_services = "client_services"