kensho-kfinance 3.2.4__py3-none-any.whl → 4.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.
Files changed (57) hide show
  1. {kensho_kfinance-3.2.4.dist-info → kensho_kfinance-4.0.0.dist-info}/METADATA +3 -3
  2. {kensho_kfinance-3.2.4.dist-info → kensho_kfinance-4.0.0.dist-info}/RECORD +57 -56
  3. kfinance/CHANGELOG.md +51 -0
  4. kfinance/client/batch_request_handling.py +3 -1
  5. kfinance/client/fetch.py +127 -54
  6. kfinance/client/kfinance.py +38 -39
  7. kfinance/client/meta_classes.py +50 -20
  8. kfinance/client/models/date_and_period_models.py +32 -7
  9. kfinance/client/models/decimal_with_unit.py +14 -2
  10. kfinance/client/models/response_models.py +33 -0
  11. kfinance/client/models/tests/test_decimal_with_unit.py +9 -0
  12. kfinance/client/tests/test_batch_requests.py +5 -4
  13. kfinance/client/tests/test_fetch.py +134 -58
  14. kfinance/client/tests/test_objects.py +207 -145
  15. kfinance/conftest.py +10 -0
  16. kfinance/domains/business_relationships/business_relationship_tools.py +17 -8
  17. kfinance/domains/business_relationships/tests/test_business_relationship_tools.py +18 -16
  18. kfinance/domains/capitalizations/capitalization_models.py +7 -5
  19. kfinance/domains/capitalizations/capitalization_tools.py +38 -20
  20. kfinance/domains/capitalizations/tests/test_capitalization_tools.py +66 -36
  21. kfinance/domains/companies/company_models.py +22 -2
  22. kfinance/domains/companies/company_tools.py +49 -16
  23. kfinance/domains/companies/tests/test_company_tools.py +27 -9
  24. kfinance/domains/competitors/competitor_tools.py +19 -5
  25. kfinance/domains/competitors/tests/test_competitor_tools.py +22 -19
  26. kfinance/domains/cusip_and_isin/cusip_and_isin_tools.py +29 -8
  27. kfinance/domains/cusip_and_isin/tests/test_cusip_and_isin_tools.py +13 -8
  28. kfinance/domains/earnings/earning_tools.py +73 -29
  29. kfinance/domains/earnings/tests/test_earnings_tools.py +52 -43
  30. kfinance/domains/line_items/line_item_models.py +372 -16
  31. kfinance/domains/line_items/line_item_tools.py +198 -46
  32. kfinance/domains/line_items/tests/test_line_item_tools.py +305 -39
  33. kfinance/domains/mergers_and_acquisitions/merger_and_acquisition_models.py +46 -2
  34. kfinance/domains/mergers_and_acquisitions/merger_and_acquisition_tools.py +55 -74
  35. kfinance/domains/mergers_and_acquisitions/tests/test_merger_and_acquisition_tools.py +61 -59
  36. kfinance/domains/prices/price_models.py +7 -6
  37. kfinance/domains/prices/price_tools.py +24 -16
  38. kfinance/domains/prices/tests/test_price_tools.py +47 -39
  39. kfinance/domains/segments/segment_models.py +17 -3
  40. kfinance/domains/segments/segment_tools.py +102 -42
  41. kfinance/domains/segments/tests/test_segment_tools.py +166 -37
  42. kfinance/domains/statements/statement_models.py +17 -3
  43. kfinance/domains/statements/statement_tools.py +130 -46
  44. kfinance/domains/statements/tests/test_statement_tools.py +251 -49
  45. kfinance/integrations/local_mcp/kfinance_mcp.py +1 -1
  46. kfinance/integrations/tests/test_example_notebook.py +57 -16
  47. kfinance/integrations/tool_calling/all_tools.py +5 -1
  48. kfinance/integrations/tool_calling/static_tools/get_n_quarters_ago.py +5 -0
  49. kfinance/integrations/tool_calling/static_tools/tests/test_get_lastest.py +13 -10
  50. kfinance/integrations/tool_calling/static_tools/tests/test_get_n_quarters_ago.py +2 -1
  51. kfinance/integrations/tool_calling/tests/test_tool_calling_models.py +15 -4
  52. kfinance/integrations/tool_calling/tool_calling_models.py +18 -6
  53. kfinance/version.py +2 -2
  54. {kensho_kfinance-3.2.4.dist-info → kensho_kfinance-4.0.0.dist-info}/WHEEL +0 -0
  55. {kensho_kfinance-3.2.4.dist-info → kensho_kfinance-4.0.0.dist-info}/licenses/AUTHORS.md +0 -0
  56. {kensho_kfinance-3.2.4.dist-info → kensho_kfinance-4.0.0.dist-info}/licenses/LICENSE +0 -0
  57. {kensho_kfinance-3.2.4.dist-info → kensho_kfinance-4.0.0.dist-info}/top_level.txt +0 -0
@@ -1,31 +1,50 @@
1
1
  from requests_mock import Mocker
2
2
 
3
3
  from kfinance.client.kfinance import Client
4
- from kfinance.conftest import SPGI_COMPANY_ID
5
4
  from kfinance.domains.companies.company_models import COMPANY_ID_PREFIX
6
5
  from kfinance.domains.segments.segment_models import SegmentType
7
6
  from kfinance.domains.segments.segment_tools import (
8
7
  GetSegmentsFromIdentifiers,
9
8
  GetSegmentsFromIdentifiersArgs,
9
+ GetSegmentsFromIdentifiersResp,
10
10
  )
11
11
 
12
12
 
13
13
  class TestGetSegmentsFromIdentifier:
14
14
  segments_response = {
15
- "segments": {
16
- "2020": {
17
- "Commodity Insights": {
18
- "CAPEX": -7000000.0,
19
- "D&A": 17000000.0,
20
- },
21
- "Unallocated Assets Held for Sale": None,
15
+ "currency": "USD",
16
+ "periods": {
17
+ "CY2020": {
18
+ "period_end_date": "2020-12-31",
19
+ "num_months": 12,
20
+ "segments": [
21
+ {
22
+ "name": "Commodity Insights",
23
+ "line_items": [
24
+ {"name": "CAPEX", "value": "-7000000.0", "sources": []},
25
+ {"name": "D&A", "value": "17000000.0", "sources": []},
26
+ ],
27
+ }
28
+ ],
22
29
  },
23
- "2021": {
24
- "Commodity Insights": {
25
- "CAPEX": -2000000.0,
26
- "D&A": 12000000.0,
27
- },
28
- "Unallocated Assets Held for Sale": {"Total Assets": 321000000.0},
30
+ "CY2021": {
31
+ "period_end_date": "2021-12-31",
32
+ "num_months": 12,
33
+ "segments": [
34
+ {
35
+ "name": "Commodity Insights",
36
+ "line_items": [
37
+ {"name": "CAPEX", "value": "-2000000.0", "sources": []},
38
+ {"name": "D&A", "value": "12000000.0", "sources": []},
39
+ ],
40
+ },
41
+ {
42
+ "name": "Unallocated Assets Held for Sale",
43
+ "line_items": [
44
+ {"name": "Total Assets", "value": "321000000.0", "sources": []},
45
+ ],
46
+ },
47
+ ],
29
48
  },
30
49
  },
31
50
  }
@@ -37,22 +56,41 @@ class TestGetSegmentsFromIdentifier:
37
56
  THEN we get back the SPGI business segment and an error for the non-existent company.
38
57
  """
39
58
 
40
- requests_mock.get(
41
- url=f"https://kfinance.kensho.com/api/v1/segments/{SPGI_COMPANY_ID}/business/none/none/none/none/none",
42
- # truncated from the original API response
43
- json=self.segments_response,
59
+ # Mock the unified_fetch_id_triples response
60
+ requests_mock.post(
61
+ url="https://kfinance.kensho.com/api/v1/ids",
62
+ json={
63
+ "identifiers_to_id_triples": {
64
+ "SPGI": {
65
+ "company_id": 21719,
66
+ "security_id": 2629107,
67
+ "trading_item_id": 2629108,
68
+ }
69
+ },
70
+ "errors": {
71
+ "NON-EXISTENT": "No identification triple found for the provided identifier: NON-EXISTENT of type: ticker"
72
+ },
73
+ },
74
+ )
75
+
76
+ # Mock the fetch_segments response
77
+ requests_mock.post(
78
+ url="https://kfinance.kensho.com/api/v1/segments/",
79
+ json={"results": {"21719": self.segments_response}, "errors": {}},
44
80
  )
45
81
 
46
- expected_response = {
47
- "results": {"SPGI": self.segments_response},
48
- "errors": [
49
- "No identification triple found for the provided identifier: NON-EXISTENT of type: ticker"
50
- ],
51
- }
82
+ expected_response = GetSegmentsFromIdentifiersResp.model_validate(
83
+ {
84
+ "results": {"SPGI": self.segments_response},
85
+ "errors": [
86
+ "No identification triple found for the provided identifier: NON-EXISTENT of type: ticker"
87
+ ],
88
+ }
89
+ )
52
90
 
53
91
  tool = GetSegmentsFromIdentifiers(kfinance_client=mock_client)
54
92
  args = GetSegmentsFromIdentifiersArgs(
55
- identifiers=["SPGI", "non-existent"], segment_type=SegmentType.business
93
+ identifiers=["SPGI", "NON-EXISTENT"], segment_type=SegmentType.business
56
94
  )
57
95
  response = tool.run(args.model_dump(mode="json"))
58
96
  assert response == expected_response
@@ -60,23 +98,114 @@ class TestGetSegmentsFromIdentifier:
60
98
  def test_most_recent_request(self, requests_mock: Mocker, mock_client: Client) -> None:
61
99
  """
62
100
  GIVEN the GetFinancialLineItemFromIdentifiers tool
63
- WHEN we request most recent statement for multiple companies
64
- THEN we only get back the most recent statement for each company
101
+ WHEN we request most recent segment for multiple companies
102
+ THEN we only get back the most recent segment for each company
65
103
  """
66
104
 
67
105
  company_ids = [1, 2]
68
- expected_response = {
69
- "results": {
70
- "C_1": {"segments": {"2021": self.segments_response["segments"]["2021"]}},
71
- "C_2": {"segments": {"2021": self.segments_response["segments"]["2021"]}},
106
+ expected_response = GetSegmentsFromIdentifiersResp.model_validate(
107
+ {
108
+ "results": {
109
+ "C_1": {
110
+ "currency": "USD",
111
+ "periods": {
112
+ "CY2021": {
113
+ "period_end_date": "2021-12-31",
114
+ "num_months": 12,
115
+ "segments": self.segments_response["periods"]["CY2021"]["segments"],
116
+ }
117
+ },
118
+ },
119
+ "C_2": {
120
+ "currency": "USD",
121
+ "periods": {
122
+ "CY2021": {
123
+ "period_end_date": "2021-12-31",
124
+ "num_months": 12,
125
+ "segments": self.segments_response["periods"]["CY2021"]["segments"],
126
+ }
127
+ },
128
+ },
129
+ }
72
130
  }
73
- }
131
+ )
74
132
 
75
- for company_id in company_ids:
76
- requests_mock.get(
77
- url=f"https://kfinance.kensho.com/api/v1/segments/{company_id}/business/none/none/none/none/none",
78
- json=self.segments_response,
79
- )
133
+ # Mock the unified_fetch_id_triples response
134
+ requests_mock.post(
135
+ url="https://kfinance.kensho.com/api/v1/ids",
136
+ json={
137
+ "identifiers_to_id_triples": {
138
+ "C_1": {"company_id": 1, "security_id": 101, "trading_item_id": 201},
139
+ "C_2": {"company_id": 2, "security_id": 102, "trading_item_id": 202},
140
+ },
141
+ "errors": {},
142
+ },
143
+ )
144
+
145
+ # Mock the fetch_segments response
146
+ requests_mock.post(
147
+ url="https://kfinance.kensho.com/api/v1/segments/",
148
+ json={
149
+ "results": {"1": self.segments_response, "2": self.segments_response},
150
+ "errors": {},
151
+ },
152
+ )
153
+
154
+ tool = GetSegmentsFromIdentifiers(kfinance_client=mock_client)
155
+ args = GetSegmentsFromIdentifiersArgs(
156
+ identifiers=[f"{COMPANY_ID_PREFIX}{company_id}" for company_id in company_ids],
157
+ segment_type=SegmentType.business,
158
+ )
159
+ response = tool.run(args.model_dump(mode="json"))
160
+ assert response == expected_response
161
+
162
+ def test_empty_most_recent_request(self, requests_mock: Mocker, mock_client: Client) -> None:
163
+ """
164
+ GIVEN the GetFinancialLineItemFromIdentifiers tool
165
+ WHEN we request most recent segment for multiple companies
166
+ THEN we only get back the most recent segment for each company
167
+ UNLESS no segments exist
168
+ """
169
+
170
+ company_ids = [1, 2]
171
+ expected_response = GetSegmentsFromIdentifiersResp.model_validate(
172
+ {
173
+ "results": {
174
+ "C_1": {"currency": "USD", "periods": {}},
175
+ "C_2": {
176
+ "currency": "USD",
177
+ "periods": {
178
+ "CY2021": {
179
+ "period_end_date": "2021-12-31",
180
+ "num_months": 12,
181
+ "segments": self.segments_response["periods"]["CY2021"]["segments"],
182
+ }
183
+ },
184
+ },
185
+ }
186
+ }
187
+ )
188
+
189
+ # Mock the unified_fetch_id_triples response
190
+ requests_mock.post(
191
+ url="https://kfinance.kensho.com/api/v1/ids",
192
+ json={
193
+ "identifiers_to_id_triples": {
194
+ "C_1": {"company_id": 1, "security_id": 101, "trading_item_id": 201},
195
+ "C_2": {"company_id": 2, "security_id": 102, "trading_item_id": 202},
196
+ },
197
+ "errors": {},
198
+ },
199
+ )
200
+
201
+ # Mock the fetch_segments response with different data for different companies
202
+ requests_mock.post(
203
+ url="https://kfinance.kensho.com/api/v1/segments/",
204
+ json={
205
+ "results": {"1": {"currency": "USD", "periods": {}}, "2": self.segments_response},
206
+ "errors": {},
207
+ },
208
+ )
80
209
 
81
210
  tool = GetSegmentsFromIdentifiers(kfinance_client=mock_client)
82
211
  args = GetSegmentsFromIdentifiersArgs(
@@ -1,8 +1,10 @@
1
- from typing import Any
1
+ from datetime import date
2
2
 
3
3
  from pydantic import BaseModel
4
4
  from strenum import StrEnum
5
5
 
6
+ from kfinance.domains.line_items.line_item_models import BasePeriodsResp, LineItem
7
+
6
8
 
7
9
  class StatementType(StrEnum):
8
10
  """The type of financial statement"""
@@ -12,5 +14,17 @@ class StatementType(StrEnum):
12
14
  cashflow = "cashflow"
13
15
 
14
16
 
15
- class StatementsResp(BaseModel):
16
- statements: dict[str, Any]
17
+ class Statement(BaseModel):
18
+ name: str
19
+ line_items: list[LineItem]
20
+
21
+
22
+ class StatementPeriodData(BaseModel):
23
+ period_end_date: date
24
+ num_months: int
25
+ statements: list[Statement]
26
+
27
+
28
+ class StatementsResp(BasePeriodsResp):
29
+ currency: str | None
30
+ periods: dict[str, StatementPeriodData] # period -> statement and period data
@@ -3,9 +3,9 @@ from typing import Literal, Type
3
3
 
4
4
  from pydantic import BaseModel, Field
5
5
 
6
- from kfinance.client.batch_request_handling import Task, process_tasks_in_thread_pool_executor
7
- from kfinance.client.models.date_and_period_models import PeriodType
6
+ from kfinance.client.models.date_and_period_models import NumPeriods, NumPeriodsBack, PeriodType
8
7
  from kfinance.client.permission_models import Permission
8
+ from kfinance.domains.line_items.line_item_models import CalendarType
9
9
  from kfinance.domains.statements.statement_models import StatementsResp, StatementType
10
10
  from kfinance.integrations.tool_calling.tool_calling_models import (
11
11
  KfinanceTool,
@@ -18,27 +18,64 @@ from kfinance.integrations.tool_calling.tool_calling_models import (
18
18
  class GetFinancialStatementFromIdentifiersArgs(ToolArgsWithIdentifiers):
19
19
  # no description because the description for enum fields comes from the enum docstring.
20
20
  statement: StatementType
21
- period_type: PeriodType | None = Field(default=None, description="The period type")
22
- start_year: int | None = Field(default=None, description="The starting year for the data range")
23
- end_year: int | None = Field(default=None, description="The ending year for the data range")
24
- start_quarter: ValidQuarter | None = Field(default=None, description="Starting quarter")
25
- end_quarter: ValidQuarter | None = Field(default=None, description="Ending quarter")
21
+ period_type: PeriodType | None = Field(
22
+ default=None, description="The period type (annual or quarterly)"
23
+ )
24
+ start_year: int | None = Field(
25
+ default=None,
26
+ description="The starting year for the data range. Use null for the most recent data.",
27
+ )
28
+ end_year: int | None = Field(
29
+ default=None,
30
+ description="The ending year for the data range. Use null for the most recent data.",
31
+ )
32
+ start_quarter: ValidQuarter | None = Field(
33
+ default=None, description="Starting quarter (1-4). Only used when period_type is quarterly."
34
+ )
35
+ end_quarter: ValidQuarter | None = Field(
36
+ default=None, description="Ending quarter (1-4). Only used when period_type is quarterly."
37
+ )
38
+ calendar_type: CalendarType | None = Field(
39
+ default=None, description="Fiscal year or calendar year"
40
+ )
41
+ num_periods: NumPeriods | None = Field(
42
+ default=None, description="The number of periods to retrieve data for (1-99)"
43
+ )
44
+ num_periods_back: NumPeriodsBack | None = Field(
45
+ default=None,
46
+ description="The end period of the data range expressed as number of periods back relative to the present period (0-99)",
47
+ )
26
48
 
27
49
 
28
50
  class GetFinancialStatementFromIdentifiersResp(ToolRespWithErrors):
29
- results: dict[str, StatementsResp]
51
+ results: dict[str, StatementsResp] # identifier -> response
30
52
 
31
53
 
32
54
  class GetFinancialStatementFromIdentifiers(KfinanceTool):
33
55
  name: str = "get_financial_statement_from_identifiers"
34
56
  description: str = dedent("""
35
- Get a financial statement associated with a group of identifiers.
57
+ Get a financial statement (balance_sheet, income_statement, or cashflow) for a group of identifiers.
36
58
 
37
- - To fetch the most recent value for the statement, leave start_year, start_quarter, end_year, and end_quarter as None.
59
+ - When possible, pass multiple identifiers in a single call rather than making multiple calls.
60
+ - To fetch the most recent statement, leave start_year, start_quarter, end_year, end_quarter, num_periods, and num_periods_back as null.
61
+ - The tool accepts an optional calendar_type argument, which can either be 'calendar' or 'fiscal'. If 'calendar' is chosen, then start_year and end_year will filter on calendar year, and the output returned will be in calendar years. If 'fiscal' is chosen (which is the default), then start_year and end_year will filter on fiscal year, and the output returned will be in fiscal years.
62
+ - To filter by time, use either absolute (start_year, end_year, start_quarter, end_quarter) for specific dates like "in 2023" or "Q2 2021", OR relative (num_periods, num_periods_back) for phrases like "last 3 quarters" or "past five years"—but not both.
38
63
 
39
- Example:
40
- Query: "Fetch the balance sheets of BAC and GS for 2024"
41
- Function: get_financial_statement_from_company_ids(identifiers=["BAC", "GS"], statement=StatementType.balance_sheet, start_year=2024, end_year=2024)
64
+ Examples:
65
+ Query: "Fetch the balance sheets of Bank of America and Goldman Sachs for 2024"
66
+ Function: get_financial_statement_from_identifiers(identifiers=["Bank of America", "Goldman Sachs"], statement="balance_sheet", period_type="annual", start_year=2024, end_year=2024)
67
+
68
+ Query: "Get income statements for NEE and DUK"
69
+ Function: get_financial_statement_from_identifiers(identifiers=["NEE", "DUK"], statement="income_statement")
70
+
71
+ Query: "Q2 2023 cashflow for XOM"
72
+ Function: get_financial_statement_from_identifiers(identifiers=["XOM"], statement="cashflow", period_type="quarterly", start_year=2023, end_year=2023, start_quarter=2, end_quarter=2)
73
+
74
+ Query: "What is the balance sheet for The New York Times for the past 7 years except for the most recent 2 years?"
75
+ Function: get_financial_statement_from_identifiers(statement_type="balance_sheet", num_periods=7, num_periods_back=2, identifiers=["NYT"])
76
+
77
+ Query: "What are the annual income statement for the calendar years between 2013 and 2016 for Alibaba and Wayfair?"
78
+ Function: get_financial_statement_from_identifiers(statement_type="income_statement", period_type="annual", calendar_type="calendar", start_year=2013, end_year=2016, identifiers=["BABA", "W"])
42
79
  """).strip()
43
80
  args_schema: Type[BaseModel] = GetFinancialStatementFromIdentifiersArgs
44
81
  accepted_permissions: set[Permission] | None = {
@@ -55,15 +92,61 @@ class GetFinancialStatementFromIdentifiers(KfinanceTool):
55
92
  end_year: int | None = None,
56
93
  start_quarter: Literal[1, 2, 3, 4] | None = None,
57
94
  end_quarter: Literal[1, 2, 3, 4] | None = None,
58
- ) -> dict:
95
+ calendar_type: CalendarType | None = None,
96
+ num_periods: int | None = None,
97
+ num_periods_back: int | None = None,
98
+ ) -> GetFinancialStatementFromIdentifiersResp:
59
99
  """Sample response:
60
100
 
61
101
  {
62
102
  'results': {
63
103
  'SPGI': {
64
- 'statements': {
65
- '2020': {'Revenues': '7442000000.000000', 'Total Revenues': '7442000000.000000'},
66
- '2021': {'Revenues': '8243000000.000000', 'Total Revenues': '8243000000.000000'}
104
+ 'currency': 'USD',
105
+ 'periods': {
106
+ 'CY2020': {
107
+ 'period_end_date': '2020-12-31',
108
+ 'num_months': 12,
109
+ 'statements': [
110
+ {
111
+ 'name': 'Income Statement',
112
+ 'line_items': [
113
+ {
114
+ 'name': 'Revenues',
115
+ 'value': 7442000000.0,
116
+ 'sources': [
117
+ {
118
+ 'type': 'doc-viewer statement',
119
+ 'url': 'https://www.capitaliq.spglobal.com/...'
120
+ }
121
+ ]
122
+ },
123
+ {
124
+ 'name': 'Total Revenues',
125
+ 'value': 7442000000.0
126
+ }
127
+ ]
128
+ }
129
+ ]
130
+ },
131
+ 'CY2021': {
132
+ 'period_end_date': '2021-12-31',
133
+ 'num_months': 12,
134
+ 'statements': [
135
+ {
136
+ 'name': 'Income Statement',
137
+ 'line_items': [
138
+ {
139
+ 'name': 'Revenues',
140
+ 'value': 8243000000.0
141
+ },
142
+ {
143
+ 'name': 'Total Revenues',
144
+ 'value': 8243000000.0
145
+ }
146
+ ]
147
+ }
148
+ ]
149
+ }
67
150
  }
68
151
  }
69
152
  },
@@ -71,29 +154,29 @@ class GetFinancialStatementFromIdentifiers(KfinanceTool):
71
154
  }
72
155
  """
73
156
  api_client = self.kfinance_client.kfinance_api_client
74
- id_triple_resp = api_client.unified_fetch_id_triples(identifiers=identifiers)
75
-
76
- tasks = [
77
- Task(
78
- func=api_client.fetch_statement,
79
- kwargs=dict(
80
- company_id=id_triple.company_id,
81
- statement_type=statement.value,
82
- period_type=period_type,
83
- start_year=start_year,
84
- end_year=end_year,
85
- start_quarter=start_quarter,
86
- end_quarter=end_quarter,
87
- ),
88
- result_key=identifier,
89
- )
90
- for identifier, id_triple in id_triple_resp.identifiers_to_id_triples.items()
91
- ]
92
-
93
- statement_responses: dict[str, StatementsResp] = process_tasks_in_thread_pool_executor(
94
- api_client=api_client, tasks=tasks
157
+
158
+ # First resolve identifiers to company IDs
159
+ ids_response = api_client.unified_fetch_id_triples(identifiers)
160
+
161
+ # Call the simplified fetch_statement API with company IDs
162
+ response = api_client.fetch_statement(
163
+ company_ids=ids_response.company_ids,
164
+ statement_type=statement.value,
165
+ period_type=period_type,
166
+ start_year=start_year,
167
+ end_year=end_year,
168
+ start_quarter=start_quarter,
169
+ end_quarter=end_quarter,
170
+ calendar_type=calendar_type,
171
+ num_periods=num_periods,
172
+ num_periods_back=num_periods_back,
95
173
  )
96
174
 
175
+ identifier_to_results = {}
176
+ for company_id_str, statement_resp in response.results.items():
177
+ original_identifier = ids_response.get_identifier_from_company_id(int(company_id_str))
178
+ identifier_to_results[original_identifier] = statement_resp
179
+
97
180
  # If no date and multiple companies, only return the most recent value.
98
181
  # By default, we return 5 years of data, which can be too much when
99
182
  # returning data for many companies.
@@ -102,14 +185,15 @@ class GetFinancialStatementFromIdentifiers(KfinanceTool):
102
185
  and end_year is None
103
186
  and start_quarter is None
104
187
  and end_quarter is None
105
- and len(statement_responses) > 1
188
+ and num_periods is None
189
+ and num_periods_back is None
190
+ and len(identifier_to_results) > 1
106
191
  ):
107
- for statement_response in statement_responses.values():
108
- most_recent_year = max(statement_response.statements.keys())
109
- most_recent_year_data = statement_response.statements[most_recent_year]
110
- statement_response.statements = {most_recent_year: most_recent_year_data}
192
+ for result in identifier_to_results.values():
193
+ result.remove_all_periods_other_than_the_most_recent_one()
194
+
195
+ all_errors = list(ids_response.errors.values()) + list(response.errors.values())
111
196
 
112
- output_model = GetFinancialStatementFromIdentifiersResp(
113
- results=statement_responses, errors=list(id_triple_resp.errors.values())
197
+ return GetFinancialStatementFromIdentifiersResp(
198
+ results=identifier_to_results, errors=all_errors
114
199
  )
115
- return output_model.model_dump(mode="json")