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.
- {kensho_kfinance-3.2.4.dist-info → kensho_kfinance-4.0.0.dist-info}/METADATA +3 -3
- {kensho_kfinance-3.2.4.dist-info → kensho_kfinance-4.0.0.dist-info}/RECORD +57 -56
- kfinance/CHANGELOG.md +51 -0
- kfinance/client/batch_request_handling.py +3 -1
- kfinance/client/fetch.py +127 -54
- kfinance/client/kfinance.py +38 -39
- kfinance/client/meta_classes.py +50 -20
- kfinance/client/models/date_and_period_models.py +32 -7
- kfinance/client/models/decimal_with_unit.py +14 -2
- kfinance/client/models/response_models.py +33 -0
- kfinance/client/models/tests/test_decimal_with_unit.py +9 -0
- kfinance/client/tests/test_batch_requests.py +5 -4
- kfinance/client/tests/test_fetch.py +134 -58
- kfinance/client/tests/test_objects.py +207 -145
- kfinance/conftest.py +10 -0
- kfinance/domains/business_relationships/business_relationship_tools.py +17 -8
- kfinance/domains/business_relationships/tests/test_business_relationship_tools.py +18 -16
- kfinance/domains/capitalizations/capitalization_models.py +7 -5
- kfinance/domains/capitalizations/capitalization_tools.py +38 -20
- kfinance/domains/capitalizations/tests/test_capitalization_tools.py +66 -36
- kfinance/domains/companies/company_models.py +22 -2
- kfinance/domains/companies/company_tools.py +49 -16
- kfinance/domains/companies/tests/test_company_tools.py +27 -9
- kfinance/domains/competitors/competitor_tools.py +19 -5
- kfinance/domains/competitors/tests/test_competitor_tools.py +22 -19
- kfinance/domains/cusip_and_isin/cusip_and_isin_tools.py +29 -8
- kfinance/domains/cusip_and_isin/tests/test_cusip_and_isin_tools.py +13 -8
- kfinance/domains/earnings/earning_tools.py +73 -29
- kfinance/domains/earnings/tests/test_earnings_tools.py +52 -43
- kfinance/domains/line_items/line_item_models.py +372 -16
- kfinance/domains/line_items/line_item_tools.py +198 -46
- kfinance/domains/line_items/tests/test_line_item_tools.py +305 -39
- kfinance/domains/mergers_and_acquisitions/merger_and_acquisition_models.py +46 -2
- kfinance/domains/mergers_and_acquisitions/merger_and_acquisition_tools.py +55 -74
- kfinance/domains/mergers_and_acquisitions/tests/test_merger_and_acquisition_tools.py +61 -59
- kfinance/domains/prices/price_models.py +7 -6
- kfinance/domains/prices/price_tools.py +24 -16
- kfinance/domains/prices/tests/test_price_tools.py +47 -39
- kfinance/domains/segments/segment_models.py +17 -3
- kfinance/domains/segments/segment_tools.py +102 -42
- kfinance/domains/segments/tests/test_segment_tools.py +166 -37
- kfinance/domains/statements/statement_models.py +17 -3
- kfinance/domains/statements/statement_tools.py +130 -46
- kfinance/domains/statements/tests/test_statement_tools.py +251 -49
- kfinance/integrations/local_mcp/kfinance_mcp.py +1 -1
- kfinance/integrations/tests/test_example_notebook.py +57 -16
- kfinance/integrations/tool_calling/all_tools.py +5 -1
- kfinance/integrations/tool_calling/static_tools/get_n_quarters_ago.py +5 -0
- kfinance/integrations/tool_calling/static_tools/tests/test_get_lastest.py +13 -10
- kfinance/integrations/tool_calling/static_tools/tests/test_get_n_quarters_ago.py +2 -1
- kfinance/integrations/tool_calling/tests/test_tool_calling_models.py +15 -4
- kfinance/integrations/tool_calling/tool_calling_models.py +18 -6
- kfinance/version.py +2 -2
- {kensho_kfinance-3.2.4.dist-info → kensho_kfinance-4.0.0.dist-info}/WHEEL +0 -0
- {kensho_kfinance-3.2.4.dist-info → kensho_kfinance-4.0.0.dist-info}/licenses/AUTHORS.md +0 -0
- {kensho_kfinance-3.2.4.dist-info → kensho_kfinance-4.0.0.dist-info}/licenses/LICENSE +0 -0
- {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
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
"
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
json=
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
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", "
|
|
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
|
|
64
|
-
THEN we only get back the most recent
|
|
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
|
-
|
|
70
|
-
"
|
|
71
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
|
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
|
|
16
|
-
|
|
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.
|
|
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(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
57
|
+
Get a financial statement (balance_sheet, income_statement, or cashflow) for a group of identifiers.
|
|
36
58
|
|
|
37
|
-
-
|
|
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
|
-
|
|
40
|
-
Query: "Fetch the balance sheets of
|
|
41
|
-
Function:
|
|
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
|
-
|
|
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
|
-
'
|
|
65
|
-
|
|
66
|
-
'
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
|
188
|
+
and num_periods is None
|
|
189
|
+
and num_periods_back is None
|
|
190
|
+
and len(identifier_to_results) > 1
|
|
106
191
|
):
|
|
107
|
-
for
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
113
|
-
results=
|
|
197
|
+
return GetFinancialStatementFromIdentifiersResp(
|
|
198
|
+
results=identifier_to_results, errors=all_errors
|
|
114
199
|
)
|
|
115
|
-
return output_model.model_dump(mode="json")
|