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

Files changed (134) hide show
  1. {kensho_kfinance-2.8.0.dist-info → kensho_kfinance-3.0.0.dist-info}/METADATA +2 -2
  2. kensho_kfinance-3.0.0.dist-info/RECORD +110 -0
  3. kfinance/CHANGELOG.md +6 -0
  4. kfinance/__init__.py +1 -0
  5. kfinance/client/README.md +9 -0
  6. kfinance/{batch_request_handling.py → client/batch_request_handling.py} +63 -27
  7. kfinance/{fetch.py → client/fetch.py} +23 -29
  8. kfinance/{kfinance.py → client/kfinance.py} +105 -111
  9. kfinance/{meta_classes.py → client/meta_classes.py} +26 -35
  10. kfinance/{decimal_with_unit.py → client/models/decimal_with_unit.py} +1 -1
  11. kfinance/{tests → client/models/tests}/test_decimal_with_unit.py +1 -1
  12. kfinance/client/tests/__init__.py +0 -0
  13. kfinance/{tests → client/tests}/test_batch_requests.py +8 -6
  14. kfinance/{tests → client/tests}/test_client.py +25 -19
  15. kfinance/{tests → client/tests}/test_fetch.py +11 -29
  16. kfinance/{tests → client/tests}/test_group_objects.py +1 -1
  17. kfinance/{tests → client/tests}/test_objects.py +111 -63
  18. kfinance/{tests/conftest.py → conftest.py} +14 -2
  19. kfinance/domains/README.md +14 -0
  20. kfinance/domains/__init__.py +0 -0
  21. kfinance/domains/business_relationships/__init__.py +0 -0
  22. kfinance/{models → domains/business_relationships}/business_relationship_models.py +10 -0
  23. kfinance/domains/business_relationships/business_relationship_tools.py +74 -0
  24. kfinance/domains/business_relationships/tests/__init__.py +0 -0
  25. kfinance/domains/business_relationships/tests/test_business_relationship_tools.py +55 -0
  26. kfinance/domains/capitalizations/__init__.py +0 -0
  27. kfinance/{models → domains/capitalizations}/capitalization_models.py +24 -17
  28. kfinance/domains/capitalizations/capitalization_tools.py +89 -0
  29. kfinance/domains/capitalizations/tests/__init__.py +0 -0
  30. kfinance/{tests/test_models → domains/capitalizations/tests}/test_capitalization_models.py +8 -10
  31. kfinance/domains/capitalizations/tests/test_capitalization_tools.py +85 -0
  32. kfinance/domains/companies/__init__.py +0 -0
  33. kfinance/domains/companies/company_identifiers.py +175 -0
  34. kfinance/domains/companies/company_models.py +27 -0
  35. kfinance/domains/companies/company_tools.py +66 -0
  36. kfinance/domains/companies/tests/__init__.py +0 -0
  37. kfinance/domains/companies/tests/test_company_tools.py +26 -0
  38. kfinance/domains/competitors/__init__.py +0 -0
  39. kfinance/{models → domains/competitors}/competitor_models.py +7 -0
  40. kfinance/domains/competitors/competitor_tools.py +62 -0
  41. kfinance/domains/competitors/tests/__init__.py +0 -0
  42. kfinance/domains/competitors/tests/test_competitor_tools.py +45 -0
  43. kfinance/domains/cusip_and_isin/__init__.py +0 -0
  44. kfinance/domains/cusip_and_isin/cusip_and_isin_tools.py +80 -0
  45. kfinance/domains/cusip_and_isin/tests/__init__.py +0 -0
  46. kfinance/domains/cusip_and_isin/tests/test_cusip_and_isin_tools.py +57 -0
  47. kfinance/domains/earnings/__init__.py +0 -0
  48. kfinance/domains/earnings/earning_models.py +41 -0
  49. kfinance/domains/earnings/earning_tools.py +174 -0
  50. kfinance/domains/earnings/tests/__init__.py +0 -0
  51. kfinance/domains/earnings/tests/test_earnings_tools.py +195 -0
  52. kfinance/domains/line_items/__init__.py +0 -0
  53. kfinance/domains/line_items/line_item_tools.py +114 -0
  54. kfinance/domains/line_items/tests/__init__.py +0 -0
  55. kfinance/domains/line_items/tests/test_line_item_tools.py +86 -0
  56. kfinance/domains/mergers_and_acquisitions/__init__.py +0 -0
  57. kfinance/domains/mergers_and_acquisitions/merger_and_acquisition_tools.py +176 -0
  58. kfinance/domains/mergers_and_acquisitions/tests/__init__.py +0 -0
  59. kfinance/domains/mergers_and_acquisitions/tests/test_merger_and_acquisition_tools.py +124 -0
  60. kfinance/domains/prices/__init__.py +0 -0
  61. kfinance/{models → domains/prices}/price_models.py +1 -1
  62. kfinance/domains/prices/price_tools.py +165 -0
  63. kfinance/domains/prices/tests/__init__.py +0 -0
  64. kfinance/{tests/test_models → domains/prices/tests}/test_price_models.py +2 -2
  65. kfinance/domains/prices/tests/test_price_tools.py +141 -0
  66. kfinance/domains/segments/__init__.py +0 -0
  67. kfinance/domains/segments/segment_tools.py +91 -0
  68. kfinance/domains/segments/tests/__init__.py +0 -0
  69. kfinance/domains/segments/tests/test_segment_tools.py +80 -0
  70. kfinance/domains/statements/__init__.py +0 -0
  71. kfinance/domains/statements/statement_tools.py +113 -0
  72. kfinance/domains/statements/tests/__init__.py +0 -0
  73. kfinance/domains/statements/tests/test_statement_tools.py +73 -0
  74. kfinance/integrations/README.md +8 -0
  75. kfinance/integrations/__init__.py +0 -0
  76. kfinance/integrations/mcp/__init__.py +0 -0
  77. kfinance/{mcp.py → integrations/mcp/mcp.py} +2 -2
  78. kfinance/integrations/tests/__init__.py +0 -0
  79. kfinance/{tests → integrations/tests}/test_example_notebook.py +4 -4
  80. kfinance/{tool_calling → integrations/tool_calling}/README.md +2 -2
  81. kfinance/integrations/tool_calling/__init__.py +0 -0
  82. kfinance/integrations/tool_calling/all_tools.py +55 -0
  83. kfinance/{tool_calling → integrations/tool_calling}/prompts.py +3 -2
  84. kfinance/integrations/tool_calling/static_tools/README.md +4 -0
  85. kfinance/integrations/tool_calling/static_tools/__init__.py +0 -0
  86. kfinance/{tool_calling → integrations/tool_calling/static_tools}/get_latest.py +3 -3
  87. kfinance/{tool_calling → integrations/tool_calling/static_tools}/get_n_quarters_ago.py +3 -3
  88. kfinance/integrations/tool_calling/static_tools/tests/__init__.py +0 -0
  89. kfinance/integrations/tool_calling/static_tools/tests/test_get_lastest.py +30 -0
  90. kfinance/integrations/tool_calling/static_tools/tests/test_get_n_quarters_ago.py +24 -0
  91. kfinance/integrations/tool_calling/tests/__init__.py +0 -0
  92. kfinance/integrations/tool_calling/tests/test_tool_calling_models.py +69 -0
  93. kfinance/{tool_calling/shared_models.py → integrations/tool_calling/tool_calling_models.py} +37 -7
  94. kfinance/version.py +2 -2
  95. kensho_kfinance-2.8.0.dist-info/RECORD +0 -70
  96. kfinance/models/id_models.py +0 -7
  97. kfinance/prompt.py +0 -526
  98. kfinance/pydantic_models.py +0 -33
  99. kfinance/tests/test_tools.py +0 -804
  100. kfinance/tool_calling/__init__.py +0 -53
  101. kfinance/tool_calling/get_advisors_for_company_in_transaction_from_identifier.py +0 -39
  102. kfinance/tool_calling/get_business_relationship_from_identifier.py +0 -30
  103. kfinance/tool_calling/get_capitalization_from_identifier.py +0 -35
  104. kfinance/tool_calling/get_competitors_from_identifier.py +0 -25
  105. kfinance/tool_calling/get_cusip_from_ticker.py +0 -20
  106. kfinance/tool_calling/get_earnings.py +0 -33
  107. kfinance/tool_calling/get_financial_line_item_from_identifier.py +0 -48
  108. kfinance/tool_calling/get_financial_statement_from_identifier.py +0 -44
  109. kfinance/tool_calling/get_history_metadata_from_identifier.py +0 -17
  110. kfinance/tool_calling/get_info_from_identifier.py +0 -16
  111. kfinance/tool_calling/get_isin_from_ticker.py +0 -20
  112. kfinance/tool_calling/get_latest_earnings.py +0 -30
  113. kfinance/tool_calling/get_merger_info_from_transaction_id.py +0 -68
  114. kfinance/tool_calling/get_mergers_from_identifier.py +0 -41
  115. kfinance/tool_calling/get_next_earnings.py +0 -30
  116. kfinance/tool_calling/get_prices_from_identifier.py +0 -46
  117. kfinance/tool_calling/get_segments_from_identifier.py +0 -44
  118. kfinance/tool_calling/get_transcript.py +0 -23
  119. kfinance/tool_calling/resolve_identifier.py +0 -18
  120. {kensho_kfinance-2.8.0.dist-info → kensho_kfinance-3.0.0.dist-info}/WHEEL +0 -0
  121. {kensho_kfinance-2.8.0.dist-info → kensho_kfinance-3.0.0.dist-info}/licenses/AUTHORS.md +0 -0
  122. {kensho_kfinance-2.8.0.dist-info → kensho_kfinance-3.0.0.dist-info}/licenses/LICENSE +0 -0
  123. {kensho_kfinance-2.8.0.dist-info → kensho_kfinance-3.0.0.dist-info}/top_level.txt +0 -0
  124. /kfinance/{models → client}/__init__.py +0 -0
  125. /kfinance/{models → client}/industry_models.py +0 -0
  126. /kfinance/{tests → client/models}/__init__.py +0 -0
  127. /kfinance/{models → client/models}/currency_models.py +0 -0
  128. /kfinance/{models → client/models}/date_and_period_models.py +0 -0
  129. /kfinance/{tests/test_models → client/models/tests}/__init__.py +0 -0
  130. /kfinance/{models → client}/permission_models.py +0 -0
  131. /kfinance/{server_thread.py → client/server_thread.py} +0 -0
  132. /kfinance/{models → domains/line_items}/line_item_models.py +0 -0
  133. /kfinance/{models → domains/segments}/segment_models.py +0 -0
  134. /kfinance/{models → domains/statements}/statement_models.py +0 -0
@@ -2,7 +2,6 @@ from __future__ import annotations
2
2
 
3
3
  from collections.abc import Sequence
4
4
  from concurrent.futures import ThreadPoolExecutor
5
- from copy import deepcopy
6
5
  from datetime import date, datetime, timezone
7
6
  from io import BytesIO
8
7
  import logging
@@ -18,29 +17,32 @@ from langchain_google_genai._function_utils import convert_to_genai_function_dec
18
17
  import pandas as pd
19
18
  from PIL.Image import Image, open as image_open
20
19
 
21
- from kfinance.batch_request_handling import add_methods_of_singular_class_to_iterable_class
22
- from kfinance.fetch import (
20
+ from kfinance.client.batch_request_handling import add_methods_of_singular_class_to_iterable_class
21
+ from kfinance.client.fetch import (
23
22
  DEFAULT_API_HOST,
24
23
  DEFAULT_API_VERSION,
25
24
  DEFAULT_OKTA_AUTH_SERVER,
26
25
  DEFAULT_OKTA_HOST,
27
26
  KFinanceApiClient,
28
27
  )
29
- from kfinance.meta_classes import (
28
+ from kfinance.client.industry_models import IndustryClassification
29
+ from kfinance.client.meta_classes import (
30
30
  CompanyFunctionsMetaClass,
31
31
  DelegatedCompanyFunctionsMetaClass,
32
32
  )
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
33
+ from kfinance.client.models.date_and_period_models import (
34
+ LatestPeriods,
35
+ Periodicity,
36
+ YearAndQuarter,
37
+ )
38
+ from kfinance.client.server_thread import ServerThread
39
+ from kfinance.domains.companies.company_models import IdentificationTriple
40
+ from kfinance.domains.earnings.earning_models import EarningsCall, TranscriptComponent
41
+ from kfinance.domains.prices.price_models import HistoryMetadata, PriceHistory
40
42
 
41
43
 
42
44
  if TYPE_CHECKING:
43
- from kfinance.tool_calling.shared_models import KfinanceTool
45
+ from kfinance.integrations.tool_calling.tool_calling_models import KfinanceTool
44
46
 
45
47
  logger = logging.getLogger(__name__)
46
48
 
@@ -266,6 +268,19 @@ class Earnings:
266
268
  self.key_dev_id = key_dev_id
267
269
  self._transcript: Transcript | None = None
268
270
 
271
+ @classmethod
272
+ def from_earnings_call(
273
+ cls, earnings_call: EarningsCall, kfinance_api_client: KFinanceApiClient
274
+ ) -> "Earnings":
275
+ """Generate an Earnings object from an EarningsCall pydantic model."""
276
+
277
+ return Earnings(
278
+ name=earnings_call.name,
279
+ datetime=earnings_call.datetime,
280
+ key_dev_id=earnings_call.key_dev_id,
281
+ kfinance_api_client=kfinance_api_client,
282
+ )
283
+
269
284
  def __str__(self) -> str:
270
285
  """String representation for the earnings object"""
271
286
  return f"{type(self).__module__}.{type(self).__qualname__} of {self.key_dev_id}"
@@ -513,24 +528,13 @@ class Company(CompanyFunctionsMetaClass):
513
528
  return self._all_earnings
514
529
 
515
530
  earnings_data = self.kfinance_api_client.fetch_earnings(self.company_id)
516
- self._all_earnings = []
517
-
518
- for earnings in earnings_data["earnings"]:
519
- if "keydevid" in earnings:
520
- earnings["key_dev_id"] = deepcopy(earnings["keydevid"])
521
- del earnings["keydevid"]
522
- earnings_datetime = datetime.fromisoformat(
523
- earnings["datetime"].replace("Z", "+00:00")
524
- ).replace(tzinfo=timezone.utc)
525
-
526
- self._all_earnings.append(
527
- Earnings(
528
- kfinance_api_client=self.kfinance_api_client,
529
- name=earnings["name"],
530
- datetime=earnings_datetime,
531
- key_dev_id=earnings["key_dev_id"],
532
- )
531
+
532
+ self._all_earnings = [
533
+ Earnings.from_earnings_call(
534
+ earnings_call=earnings_call, kfinance_api_client=self.kfinance_api_client
533
535
  )
536
+ for earnings_call in earnings_data.earnings_calls
537
+ ]
534
538
 
535
539
  return self._all_earnings
536
540
 
@@ -641,83 +645,77 @@ class Company(CompanyFunctionsMetaClass):
641
645
  return self._mergers_for_company
642
646
 
643
647
 
644
- class AdvisedCompany(Company):
648
+ class ParticipantInMerger:
645
649
  """A Company that has been involved in a transaction is a company that may have been advised."""
646
650
 
647
651
  def __init__(
648
- self,
649
- kfinance_api_client: KFinanceApiClient,
650
- company_id: int,
651
- transaction_id: int,
652
- company_name: str | None = None,
652
+ self, kfinance_api_client: KFinanceApiClient, transaction_id: int, company: Company
653
653
  ):
654
654
  """Initialize the AdvisedCompany object
655
655
 
656
656
  :param kfinance_api_client: The KFinanceApiClient used to fetch data
657
657
  :type kfinance_api_client: KFinanceApiClient
658
- :param company_id: The S&P Global CIQ Company Id
659
- :type company_id: int
660
658
  :param transaction_id: The S&P Global CIP Transaction Id
661
659
  :type transaction_id: int
660
+ :param company: The company object
661
+ :type company: Company
662
662
  """
663
-
664
- super().__init__(
665
- kfinance_api_client=kfinance_api_client,
666
- company_id=company_id,
667
- company_name=company_name,
668
- )
663
+ self.kfinance_api_client = kfinance_api_client
669
664
  self.transaction_id = transaction_id
665
+ self._company = company
670
666
 
671
667
  @property
672
- def advisors(self) -> Companies | None:
668
+ def company(self) -> Company:
669
+ """Get the specific Company object."""
670
+ return self._company
671
+
672
+ @property
673
+ def advisors(self) -> list[Advisor] | None:
673
674
  """Get the companies that advised this company during the current transaction."""
674
675
  advisors = self.kfinance_api_client.fetch_advisors_for_company_in_merger(
675
- transaction_id=self.transaction_id, advised_company_id=self.company_id
676
+ transaction_id=self.transaction_id, advised_company_id=self._company.company_id
676
677
  )["advisors"]
677
- companies = [
678
- AdvisorCompany(
679
- kfinance_api_client=self.kfinance_api_client,
680
- company_id=int(advisor["advisor_company_id"]),
681
- company_name=str(advisor["advisor_company_name"]),
678
+ return [
679
+ Advisor(
682
680
  advisor_type_name=str(advisor["advisor_type_name"]),
681
+ company=Company(
682
+ kfinance_api_client=self.kfinance_api_client,
683
+ company_id=int(advisor["advisor_company_id"]),
684
+ company_name=str(advisor["advisor_company_name"]),
685
+ ),
683
686
  )
684
687
  for advisor in advisors
685
688
  ]
686
- return Companies(kfinance_api_client=self.kfinance_api_client, companies=companies)
687
689
 
688
690
 
689
- class AdvisorCompany(Company):
691
+ class Advisor:
690
692
  """A company that advised another company during a transaction."""
691
693
 
692
694
  def __init__(
693
695
  self,
694
- kfinance_api_client: KFinanceApiClient,
695
- company_id: int,
696
- advisor_type_name: str,
697
- company_name: str | None = None,
696
+ advisor_type_name: str | None,
697
+ company: Company,
698
698
  ):
699
699
  """Initialize the AdvisorCompany object
700
700
 
701
- :param kfinance_api_client: The KFinanceApiClient used to fetch data
702
- :type kfinance_api_client: KFinanceApiClient
703
- :param company_id: The S&P Global CIQ Company Id
704
- :type company_id: int
701
+ :param company: The company that advised
702
+ :type company: Company
705
703
  :param advisor_type_name: The type of the advisor company
706
704
  :type advisor_type_name: str
707
705
  """
708
-
709
- super().__init__(
710
- kfinance_api_client=kfinance_api_client,
711
- company_id=company_id,
712
- company_name=company_name,
713
- )
714
706
  self._advisor_type_name = advisor_type_name
707
+ self._company = company
715
708
 
716
709
  @property
717
710
  def advisor_type_name(self) -> str | None:
718
711
  """When this company advised another during a transaction, get the advisor type name."""
719
712
  return self._advisor_type_name
720
713
 
714
+ @property
715
+ def company(self) -> Company:
716
+ """Get the Company object."""
717
+ return self._company
718
+
721
719
 
722
720
  class Security:
723
721
  """Security class
@@ -1253,7 +1251,11 @@ class MergerOrAcquisition:
1253
1251
  """An object that represents a merger or an acquisition of a company."""
1254
1252
 
1255
1253
  def __init__(
1256
- self, kfinance_api_client: KFinanceApiClient, transaction_id: int, merger_title: str | None
1254
+ self,
1255
+ kfinance_api_client: KFinanceApiClient,
1256
+ transaction_id: int,
1257
+ merger_title: str | None,
1258
+ closed_date: date | None,
1257
1259
  ) -> None:
1258
1260
  """MergerOrAcqusition initializer.
1259
1261
 
@@ -1264,6 +1266,7 @@ class MergerOrAcquisition:
1264
1266
  self.kfinance_api_client = kfinance_api_client
1265
1267
  self.transaction_id = transaction_id
1266
1268
  self.merger_title = merger_title
1269
+ self.closed_date = closed_date
1267
1270
  self._merger_info: dict | None = None
1268
1271
 
1269
1272
  @property
@@ -1301,36 +1304,39 @@ class MergerOrAcquisition:
1301
1304
  Each category is a single Company or a list of Companies.
1302
1305
  """
1303
1306
  return {
1304
- "target": AdvisedCompany(
1307
+ "target": ParticipantInMerger(
1305
1308
  kfinance_api_client=self.kfinance_api_client,
1306
- company_id=self.merger_info["participants"]["target"]["company_id"],
1307
- company_name=self.merger_info["participants"]["target"]["company_name"],
1308
1309
  transaction_id=self.transaction_id,
1310
+ company=Company(
1311
+ kfinance_api_client=self.kfinance_api_client,
1312
+ company_id=self.merger_info["participants"]["target"]["company_id"],
1313
+ company_name=self.merger_info["participants"]["target"]["company_name"],
1314
+ ),
1309
1315
  ),
1310
- "buyers": Companies(
1311
- kfinance_api_client=self.kfinance_api_client,
1312
- companies=[
1313
- AdvisedCompany(
1316
+ "buyers": [
1317
+ ParticipantInMerger(
1318
+ kfinance_api_client=self.kfinance_api_client,
1319
+ transaction_id=self.transaction_id,
1320
+ company=Company(
1314
1321
  kfinance_api_client=self.kfinance_api_client,
1315
1322
  company_id=company["company_id"],
1316
1323
  company_name=company["company_name"],
1317
- transaction_id=self.transaction_id,
1318
- )
1319
- for company in self.merger_info["participants"]["buyers"]
1320
- ],
1321
- ),
1322
- "sellers": Companies(
1323
- kfinance_api_client=self.kfinance_api_client,
1324
- companies=[
1325
- AdvisedCompany(
1324
+ ),
1325
+ )
1326
+ for company in self.merger_info["participants"]["buyers"]
1327
+ ],
1328
+ "sellers": [
1329
+ ParticipantInMerger(
1330
+ kfinance_api_client=self.kfinance_api_client,
1331
+ transaction_id=self.transaction_id,
1332
+ company=Company(
1326
1333
  kfinance_api_client=self.kfinance_api_client,
1327
1334
  company_id=company["company_id"],
1328
1335
  company_name=company["company_name"],
1329
- transaction_id=self.transaction_id,
1330
- )
1331
- for company in self.merger_info["participants"]["sellers"]
1332
- ],
1333
- ),
1336
+ ),
1337
+ )
1338
+ for company in self.merger_info["participants"]["sellers"]
1339
+ ],
1334
1340
  }
1335
1341
 
1336
1342
  @property
@@ -1360,7 +1366,6 @@ class Companies(set):
1360
1366
  self,
1361
1367
  kfinance_api_client: KFinanceApiClient,
1362
1368
  company_ids: Optional[Iterable[int]] = None,
1363
- transaction_id: Optional[int] = None,
1364
1369
  companies: Optional[Iterable[Company]] = None,
1365
1370
  ) -> None:
1366
1371
  """Initialize the Companies object
@@ -1369,8 +1374,6 @@ class Companies(set):
1369
1374
  :type kfinance_api_client: KFinanceApiClient
1370
1375
  :param company_ids: An iterable of S&P CIQ Company ids
1371
1376
  :type company_ids: Iterable[int]
1372
- :param transaction_id: If the companies were party to a transaction, the S&P CIQ Transaction Id
1373
- :type transaction_id: Optional[int]
1374
1377
  :param companies: If there's already an iterable of Company objects
1375
1378
  :type companies: Iterable[Company]
1376
1379
  """
@@ -1378,23 +1381,13 @@ class Companies(set):
1378
1381
  if companies is not None:
1379
1382
  super().__init__(company for company in companies)
1380
1383
  elif company_ids is not None:
1381
- if transaction_id is not None:
1382
- super().__init__(
1383
- AdvisedCompany(
1384
- kfinance_api_client=kfinance_api_client,
1385
- company_id=company_id,
1386
- transaction_id=transaction_id,
1387
- )
1388
- for company_id in company_ids
1389
- )
1390
- else:
1391
- super().__init__(
1392
- Company(
1393
- kfinance_api_client=kfinance_api_client,
1394
- company_id=company_id,
1395
- )
1396
- for company_id in company_ids
1384
+ super().__init__(
1385
+ Company(
1386
+ kfinance_api_client=kfinance_api_client,
1387
+ company_id=company_id,
1397
1388
  )
1389
+ for company_id in company_ids
1390
+ )
1398
1391
 
1399
1392
 
1400
1393
  @add_methods_of_singular_class_to_iterable_class(Security)
@@ -1409,6 +1402,7 @@ class Securities(set):
1409
1402
  :param security_ids: An iterable of S&P CIQ Security ids
1410
1403
  :type security_ids: Iterable[int]
1411
1404
  """
1405
+ self.kfinance_api_client = kfinance_api_client
1412
1406
  super().__init__(Security(kfinance_api_client, security_id) for security_id in security_ids)
1413
1407
 
1414
1408
 
@@ -1533,6 +1527,7 @@ class MergersAndAcquisitions(set):
1533
1527
  kfinance_api_client=kfinance_api_client,
1534
1528
  transaction_id=id_and_title["transaction_id"],
1535
1529
  merger_title=id_and_title["merger_title"],
1530
+ closed_date=id_and_title["closed_date"],
1536
1531
  )
1537
1532
  for id_and_title in ids_and_titles
1538
1533
  )
@@ -1551,8 +1546,6 @@ class Client:
1551
1546
  :type openai_tool_descriptions: list[dict]
1552
1547
  """
1553
1548
 
1554
- prompt = PROMPT
1555
-
1556
1549
  def __init__(
1557
1550
  self,
1558
1551
  refresh_token: Optional[str] = None,
@@ -1632,9 +1625,10 @@ class Client:
1632
1625
  @property
1633
1626
  def langchain_tools(self) -> list["KfinanceTool"]:
1634
1627
  """Return a list of all Kfinance tools for tool calling."""
1635
- if self._tools is None:
1636
- from kfinance.tool_calling import ALL_TOOLS
1637
1628
 
1629
+ from kfinance.integrations.tool_calling.all_tools import ALL_TOOLS
1630
+
1631
+ if self._tools is None:
1638
1632
  self._tools = []
1639
1633
  # Add tool to _tools if the user has permissions to use it.
1640
1634
  for tool_cls in ALL_TOOLS:
@@ -7,14 +7,15 @@ from cachetools import LRUCache, cached
7
7
  import numpy as np
8
8
  import pandas as pd
9
9
 
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
17
- from .pydantic_models import RelationshipResponse
10
+ from kfinance.client.fetch import KFinanceApiClient
11
+ from kfinance.client.models.date_and_period_models import PeriodType
12
+ from kfinance.domains.business_relationships.business_relationship_models import (
13
+ BusinessRelationshipType,
14
+ )
15
+ from kfinance.domains.capitalizations.capitalization_models import Capitalization
16
+ from kfinance.domains.competitors.competitor_models import CompetitorSource
17
+ from kfinance.domains.line_items.line_item_models import LINE_ITEMS
18
+ from kfinance.domains.segments.segment_models import SegmentType
18
19
 
19
20
 
20
21
  if TYPE_CHECKING:
@@ -234,28 +235,16 @@ class CompanyFunctionsMetaClass:
234
235
  company_id=self.company_id,
235
236
  relationship_type=relationship_type,
236
237
  )
237
- if isinstance(relationship_resp, RelationshipResponse):
238
- return BusinessRelationships(
239
- current=Companies(
240
- kfinance_api_client=self.kfinance_api_client,
241
- company_ids=[c.company_id for c in relationship_resp.current],
242
- ),
243
- previous=Companies(
244
- kfinance_api_client=self.kfinance_api_client,
245
- company_ids=[c.company_id for c in relationship_resp.previous],
246
- ),
247
- )
248
- else:
249
- return BusinessRelationships(
250
- current=Companies(
251
- kfinance_api_client=self.kfinance_api_client,
252
- company_ids=relationship_resp.current,
253
- ),
254
- previous=Companies(
255
- kfinance_api_client=self.kfinance_api_client,
256
- company_ids=relationship_resp.previous,
257
- ),
258
- )
238
+ return BusinessRelationships(
239
+ current=Companies(
240
+ kfinance_api_client=self.kfinance_api_client,
241
+ company_ids=[c.company_id for c in relationship_resp.current],
242
+ ),
243
+ previous=Companies(
244
+ kfinance_api_client=self.kfinance_api_client,
245
+ company_ids=[c.company_id for c in relationship_resp.previous],
246
+ ),
247
+ )
259
248
 
260
249
  def market_cap(
261
250
  self,
@@ -329,7 +318,9 @@ class CompanyFunctionsMetaClass:
329
318
  capitalizations = self.kfinance_api_client.fetch_market_caps_tevs_and_shares_outstanding(
330
319
  company_id=self.company_id, start_date=start_date, end_date=end_date
331
320
  )
332
- return capitalizations.jsonify_single_attribute(capitalization_to_extract)
321
+ return capitalizations.model_dump_json_single_metric(
322
+ capitalization_metric=capitalization_to_extract
323
+ )
333
324
 
334
325
  def _segments(
335
326
  self,
@@ -437,16 +428,16 @@ class CompanyFunctionsMetaClass:
437
428
 
438
429
  competitors_data = self.kfinance_api_client.fetch_competitors(
439
430
  company_id=self.company_id, competitor_source=competitor_source
440
- )["competitors"]
431
+ )
441
432
  return Companies(
442
433
  kfinance_api_client=self.kfinance_api_client,
443
434
  companies=[
444
435
  Company(
445
436
  kfinance_api_client=self.kfinance_api_client,
446
- company_id=company["company_id"],
447
- company_name=company["company_name"],
437
+ company_id=company.company_id,
438
+ company_name=company.company_name,
448
439
  )
449
- for company in competitors_data
440
+ for company in competitors_data.competitors
450
441
  ],
451
442
  )
452
443
 
@@ -5,7 +5,7 @@ from typing import Any
5
5
  from pydantic import BaseModel, Field, model_validator
6
6
  from typing_extensions import Self
7
7
 
8
- from kfinance.models.currency_models import ISO_CODE_TO_CURRENCY
8
+ from kfinance.client.models.currency_models import ISO_CODE_TO_CURRENCY
9
9
 
10
10
 
11
11
  class DecimalWithUnit(BaseModel):
@@ -3,7 +3,7 @@ from typing import Any
3
3
 
4
4
  import pytest
5
5
 
6
- from kfinance.decimal_with_unit import DecimalWithUnit, Money, Shares
6
+ from kfinance.client.models.decimal_with_unit import DecimalWithUnit, Money, Shares
7
7
 
8
8
 
9
9
  class TestDecimalWithUnit:
File without changes
@@ -9,16 +9,18 @@ import pytest
9
9
  import requests
10
10
  import requests_mock
11
11
 
12
- from kfinance.batch_request_handling import MAX_WORKERS_CAP
13
- from kfinance.decimal_with_unit import Money, Shares
14
- from kfinance.fetch import KFinanceApiClient
15
- from kfinance.kfinance import Companies, Company, Ticker, TradingItem, TradingItems
16
- from kfinance.models.price_models import PriceHistory, Prices
12
+ from kfinance.client.batch_request_handling import MAX_WORKERS_CAP
13
+ from kfinance.client.fetch import KFinanceApiClient
14
+ from kfinance.client.kfinance import Companies, Company, Ticker, TradingItem, TradingItems
15
+ from kfinance.client.models.decimal_with_unit import Money, Shares
16
+ from kfinance.domains.prices.price_models import PriceHistory, Prices
17
17
 
18
18
 
19
19
  @pytest.fixture(autouse=True)
20
20
  def mock_method():
21
- with patch("kfinance.fetch.KFinanceApiClient.access_token", return_value="fake_access_token"):
21
+ with patch(
22
+ "kfinance.client.fetch.KFinanceApiClient.access_token", return_value="fake_access_token"
23
+ ):
22
24
  yield
23
25
 
24
26
 
@@ -2,18 +2,20 @@ from unittest.mock import Mock
2
2
 
3
3
  import pytest
4
4
 
5
- from kfinance.kfinance import Client
6
- from kfinance.models.permission_models import Permission
7
- from kfinance.tool_calling import (
8
- GetBusinessRelationshipFromIdentifier,
9
- GetEarnings,
10
- GetFinancialStatementFromIdentifier,
11
- GetLatest,
12
- GetLatestEarnings,
13
- GetNextEarnings,
14
- GetTranscript,
5
+ from kfinance.client.kfinance import Client
6
+ from kfinance.client.permission_models import Permission
7
+ from kfinance.domains.business_relationships.business_relationship_tools import (
8
+ GetBusinessRelationshipFromIdentifiers,
15
9
  )
16
- from kfinance.tool_calling.shared_models import KfinanceTool
10
+ from kfinance.domains.earnings.earning_tools import (
11
+ GetEarningsFromIdentifiers,
12
+ GetLatestEarningsFromIdentifiers,
13
+ GetNextEarningsFromIdentifiers,
14
+ GetTranscriptFromKeyDevId,
15
+ )
16
+ from kfinance.domains.statements.statement_tools import GetFinancialStatementFromIdentifiers
17
+ from kfinance.integrations.tool_calling.static_tools.get_latest import GetLatest
18
+ from kfinance.integrations.tool_calling.tool_calling_models import KfinanceTool
17
19
 
18
20
 
19
21
  class TestLangchainTools:
@@ -52,19 +54,23 @@ class TestLangchainTools:
52
54
  mock_client.kfinance_api_client._user_permissions = {Permission.RelationshipPermission} # noqa: SLF001
53
55
  tool_classes = [type(t) for t in mock_client.langchain_tools]
54
56
  # User should have access to GetBusinessRelationshipFromIdentifier
55
- assert GetBusinessRelationshipFromIdentifier in tool_classes
57
+ assert GetBusinessRelationshipFromIdentifiers in tool_classes
56
58
  # User should have access to functions that don't require permissions
57
59
  assert GetLatest in tool_classes
58
60
  # User should not have access to functions that require statement permissions
59
- assert GetFinancialStatementFromIdentifier not in tool_classes
61
+ assert GetFinancialStatementFromIdentifiers not in tool_classes
60
62
 
61
63
  @pytest.mark.parametrize(
62
64
  "user_permission, expected_tool",
63
65
  [
64
66
  pytest.param(
65
- Permission.TranscriptsPermission, GetTranscript, id="Transcript Permissions"
67
+ Permission.TranscriptsPermission,
68
+ GetTranscriptFromKeyDevId,
69
+ id="Transcript Permissions",
70
+ ),
71
+ pytest.param(
72
+ Permission.EarningsPermission, GetEarningsFromIdentifiers, id="Earnings Permissions"
66
73
  ),
67
- pytest.param(Permission.EarningsPermission, GetEarnings, id="Earnings Permissions"),
68
74
  ],
69
75
  )
70
76
  def test_permission_set_handling(
@@ -79,11 +85,11 @@ class TestLangchainTools:
79
85
  mock_client.kfinance_api_client._user_permissions = {user_permission} # noqa: SLF001
80
86
  tool_classes = [type(t) for t in mock_client.langchain_tools]
81
87
  # User should have access to GetEarnings, GetNextEarnings, GetLatestEarnings, GetTranscript
82
- assert GetEarnings in tool_classes
83
- assert GetNextEarnings in tool_classes
84
- assert GetLatestEarnings in tool_classes
88
+ assert GetEarningsFromIdentifiers in tool_classes
89
+ assert GetNextEarningsFromIdentifiers in tool_classes
90
+ assert GetLatestEarningsFromIdentifiers in tool_classes
85
91
  assert expected_tool in tool_classes
86
92
  # User should have access to functions that don't require permissions
87
93
  assert GetLatest in tool_classes
88
94
  # User should not have access to functions that require statement permissions
89
- assert GetFinancialStatementFromIdentifier not in tool_classes
95
+ assert GetFinancialStatementFromIdentifiers not in tool_classes
@@ -5,16 +5,15 @@ from pydantic import ValidationError
5
5
  import pytest
6
6
  from requests_mock import Mocker
7
7
 
8
- from kfinance.fetch import KFinanceApiClient
9
- from kfinance.kfinance import Client
10
- from kfinance.models.business_relationship_models import BusinessRelationshipType
11
- from kfinance.models.date_and_period_models import Periodicity, PeriodType
12
- from kfinance.pydantic_models import (
13
- CompanyIdAndName,
8
+ from kfinance.client.fetch import KFinanceApiClient
9
+ from kfinance.client.kfinance import Client
10
+ from kfinance.client.models.date_and_period_models import Periodicity, PeriodType
11
+ from kfinance.conftest import SPGI_COMPANY_ID
12
+ from kfinance.domains.business_relationships.business_relationship_models import (
13
+ BusinessRelationshipType,
14
14
  RelationshipResponse,
15
- RelationshipResponseNoName,
16
15
  )
17
- from kfinance.tests.conftest import SPGI_COMPANY_ID
16
+ from kfinance.domains.companies.company_models import CompanyIdAndName
18
17
 
19
18
 
20
19
  def build_mock_api_client() -> KFinanceApiClient:
@@ -150,7 +149,8 @@ class TestFetchItem(TestCase):
150
149
  def test_fetch_earnings(self) -> None:
151
150
  company_id = 21719
152
151
  expected_fetch_url = f"{self.kfinance_api_client.url_base}earnings/{company_id}"
153
- self.kfinance_api_client.fetch_earnings(company_id=company_id)
152
+ with pytest.raises(ValidationError):
153
+ self.kfinance_api_client.fetch_earnings(company_id=company_id)
154
154
  self.kfinance_api_client.fetch.assert_called_once_with(expected_fetch_url)
155
155
 
156
156
  def test_fetch_transcript(self) -> None:
@@ -334,28 +334,10 @@ class TestMarketCap:
334
334
 
335
335
 
336
336
  class TestFetchCompaniesFromBusinessRelationship:
337
- def test_old_response_format(self, requests_mock: Mocker, mock_client: Client) -> None:
338
- """
339
- GIVEN a business relationship request
340
- WHEN the api returns a response in the old (no name) format
341
- THEN the response can successfully be parsed.
342
- """
343
- http_resp = {"current": [883103], "previous": [472898, 8182358]}
344
- expected_result = RelationshipResponseNoName(current=[883103], previous=[472898, 8182358])
345
- requests_mock.get(
346
- url=f"{mock_client.kfinance_api_client.url_base}relationship/{SPGI_COMPANY_ID}/{BusinessRelationshipType.supplier}",
347
- json=http_resp,
348
- )
349
-
350
- resp = mock_client.kfinance_api_client.fetch_companies_from_business_relationship(
351
- company_id=SPGI_COMPANY_ID, relationship_type=BusinessRelationshipType.supplier
352
- )
353
- assert resp == expected_result
354
-
355
- def test_new_response_format(self, requests_mock: Mocker, mock_client: Client) -> None:
337
+ def test_fetch_business_relationships(self, requests_mock: Mocker, mock_client: Client) -> None:
356
338
  """
357
339
  GIVEN a business relationship request
358
- WHEN the api returns a response in the new (with name) format
340
+ WHEN the api returns a response
359
341
  THEN the response can successfully be parsed.
360
342
  """
361
343