kensho-kfinance 3.0.3__py3-none-any.whl → 3.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kensho-kfinance might be problematic. Click here for more details.
- {kensho_kfinance-3.0.3.dist-info → kensho_kfinance-3.1.0.dist-info}/METADATA +1 -1
- {kensho_kfinance-3.0.3.dist-info → kensho_kfinance-3.1.0.dist-info}/RECORD +45 -44
- kfinance/CHANGELOG.md +3 -0
- kfinance/client/fetch.py +26 -17
- kfinance/client/kfinance.py +17 -18
- kfinance/client/meta_classes.py +2 -2
- kfinance/client/tests/test_fetch.py +36 -24
- kfinance/client/tests/test_objects.py +112 -120
- kfinance/conftest.py +49 -5
- kfinance/domains/business_relationships/business_relationship_tools.py +30 -19
- kfinance/domains/business_relationships/tests/test_business_relationship_tools.py +18 -15
- kfinance/domains/capitalizations/capitalization_models.py +1 -1
- kfinance/domains/capitalizations/capitalization_tools.py +41 -24
- kfinance/domains/capitalizations/tests/test_capitalization_tools.py +38 -13
- kfinance/domains/companies/company_identifiers.py +0 -175
- kfinance/domains/companies/company_models.py +98 -5
- kfinance/domains/companies/company_tools.py +33 -29
- kfinance/domains/companies/tests/test_company_tools.py +11 -4
- kfinance/domains/competitors/competitor_tools.py +21 -21
- kfinance/domains/competitors/tests/test_competitor_tools.py +21 -7
- kfinance/domains/cusip_and_isin/cusip_and_isin_tools.py +38 -26
- kfinance/domains/cusip_and_isin/tests/test_cusip_and_isin_tools.py +27 -26
- kfinance/domains/earnings/earning_tools.py +54 -47
- kfinance/domains/earnings/tests/test_earnings_tools.py +58 -63
- kfinance/domains/line_items/line_item_tools.py +29 -36
- kfinance/domains/line_items/tests/test_line_item_tools.py +23 -5
- kfinance/domains/mergers_and_acquisitions/merger_and_acquisition_models.py +15 -0
- kfinance/domains/mergers_and_acquisitions/merger_and_acquisition_tools.py +55 -38
- kfinance/domains/mergers_and_acquisitions/tests/test_merger_and_acquisition_tools.py +22 -9
- kfinance/domains/prices/price_models.py +9 -9
- kfinance/domains/prices/price_tools.py +49 -38
- kfinance/domains/prices/tests/test_price_tools.py +52 -36
- kfinance/domains/segments/segment_models.py +7 -0
- kfinance/domains/segments/segment_tools.py +37 -20
- kfinance/domains/segments/tests/test_segment_tools.py +13 -6
- kfinance/domains/statements/statement_models.py +7 -0
- kfinance/domains/statements/statement_tools.py +38 -40
- kfinance/domains/statements/tests/test_statement_tools.py +39 -10
- kfinance/integrations/tool_calling/tests/test_tool_calling_models.py +2 -2
- kfinance/integrations/tool_calling/tool_calling_models.py +24 -5
- kfinance/version.py +2 -2
- {kensho_kfinance-3.0.3.dist-info → kensho_kfinance-3.1.0.dist-info}/WHEEL +0 -0
- {kensho_kfinance-3.0.3.dist-info → kensho_kfinance-3.1.0.dist-info}/licenses/AUTHORS.md +0 -0
- {kensho_kfinance-3.0.3.dist-info → kensho_kfinance-3.1.0.dist-info}/licenses/LICENSE +0 -0
- {kensho_kfinance-3.0.3.dist-info → kensho_kfinance-3.1.0.dist-info}/top_level.txt +0 -0
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
from textwrap import dedent
|
|
2
2
|
from typing import Literal, Type
|
|
3
3
|
|
|
4
|
-
import numpy as np
|
|
5
|
-
import pandas as pd
|
|
6
4
|
from pydantic import BaseModel, Field
|
|
7
5
|
|
|
8
6
|
from kfinance.client.batch_request_handling import Task, process_tasks_in_thread_pool_executor
|
|
9
7
|
from kfinance.client.models.date_and_period_models import PeriodType
|
|
10
8
|
from kfinance.client.permission_models import Permission
|
|
11
|
-
from kfinance.domains.companies.company_identifiers import (
|
|
12
|
-
Identifier,
|
|
13
|
-
fetch_company_ids_from_identifiers,
|
|
14
|
-
parse_identifiers,
|
|
15
|
-
)
|
|
16
9
|
from kfinance.domains.line_items.line_item_models import (
|
|
17
10
|
LINE_ITEM_NAMES_AND_ALIASES,
|
|
18
11
|
LineItemResponse,
|
|
@@ -20,6 +13,7 @@ from kfinance.domains.line_items.line_item_models import (
|
|
|
20
13
|
from kfinance.integrations.tool_calling.tool_calling_models import (
|
|
21
14
|
KfinanceTool,
|
|
22
15
|
ToolArgsWithIdentifiers,
|
|
16
|
+
ToolRespWithErrors,
|
|
23
17
|
)
|
|
24
18
|
|
|
25
19
|
|
|
@@ -37,6 +31,10 @@ class GetFinancialLineItemFromIdentifiersArgs(ToolArgsWithIdentifiers):
|
|
|
37
31
|
end_quarter: Literal[1, 2, 3, 4] | None = Field(default=None, description="Ending quarter")
|
|
38
32
|
|
|
39
33
|
|
|
34
|
+
class GetFinancialLineItemFromIdentifiersResp(ToolRespWithErrors):
|
|
35
|
+
results: dict[str, LineItemResponse]
|
|
36
|
+
|
|
37
|
+
|
|
40
38
|
class GetFinancialLineItemFromIdentifiers(KfinanceTool):
|
|
41
39
|
name: str = "get_financial_line_item_from_identifiers"
|
|
42
40
|
description: str = dedent("""
|
|
@@ -76,16 +74,13 @@ class GetFinancialLineItemFromIdentifiers(KfinanceTool):
|
|
|
76
74
|
}
|
|
77
75
|
"""
|
|
78
76
|
api_client = self.kfinance_client.kfinance_api_client
|
|
79
|
-
|
|
80
|
-
identifiers_to_company_ids = fetch_company_ids_from_identifiers(
|
|
81
|
-
identifiers=parsed_identifiers, api_client=api_client
|
|
82
|
-
)
|
|
77
|
+
id_triple_resp = api_client.unified_fetch_id_triples(identifiers=identifiers)
|
|
83
78
|
|
|
84
79
|
tasks = [
|
|
85
80
|
Task(
|
|
86
81
|
func=api_client.fetch_line_item,
|
|
87
82
|
kwargs=dict(
|
|
88
|
-
company_id=company_id,
|
|
83
|
+
company_id=id_triple.company_id,
|
|
89
84
|
line_item=line_item,
|
|
90
85
|
period_type=period_type,
|
|
91
86
|
start_year=start_year,
|
|
@@ -95,31 +90,29 @@ class GetFinancialLineItemFromIdentifiers(KfinanceTool):
|
|
|
95
90
|
),
|
|
96
91
|
result_key=identifier,
|
|
97
92
|
)
|
|
98
|
-
for identifier,
|
|
93
|
+
for identifier, id_triple in id_triple_resp.identifiers_to_id_triples.items()
|
|
99
94
|
]
|
|
100
95
|
|
|
101
|
-
line_item_responses: dict[
|
|
102
|
-
|
|
96
|
+
line_item_responses: dict[str, LineItemResponse] = process_tasks_in_thread_pool_executor(
|
|
97
|
+
api_client=api_client, tasks=tasks
|
|
103
98
|
)
|
|
104
99
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
return output
|
|
100
|
+
# If no date and multiple companies, only return the most recent value.
|
|
101
|
+
# By default, we return 5 years of data, which can be too much when
|
|
102
|
+
# returning data for many companies.
|
|
103
|
+
if (
|
|
104
|
+
start_year is None
|
|
105
|
+
and end_year is None
|
|
106
|
+
and start_quarter is None
|
|
107
|
+
and end_quarter is None
|
|
108
|
+
and len(line_item_responses) > 1
|
|
109
|
+
):
|
|
110
|
+
for line_item_response in line_item_responses.values():
|
|
111
|
+
most_recent_year = max(line_item_response.line_item.keys())
|
|
112
|
+
most_recent_year_data = line_item_response.line_item[most_recent_year]
|
|
113
|
+
line_item_response.line_item = {most_recent_year: most_recent_year_data}
|
|
114
|
+
|
|
115
|
+
output_model = GetFinancialLineItemFromIdentifiersResp(
|
|
116
|
+
results=line_item_responses, errors=list(id_triple_resp.errors.values())
|
|
117
|
+
)
|
|
118
|
+
return output_model.model_dump(mode="json")
|
|
@@ -24,8 +24,8 @@ class TestGetFinancialLineItemFromCompanyIds:
|
|
|
24
24
|
):
|
|
25
25
|
"""
|
|
26
26
|
GIVEN the GetFinancialLineItemFromCompanyId tool
|
|
27
|
-
WHEN we request SPGI
|
|
28
|
-
THEN we get back the SPGI revenue
|
|
27
|
+
WHEN we request revenue for SPGI and a non-existent company
|
|
28
|
+
THEN we get back the SPGI revenue and an error for the non-existent company
|
|
29
29
|
"""
|
|
30
30
|
|
|
31
31
|
expected_response = {
|
|
@@ -35,6 +35,20 @@ class TestGetFinancialLineItemFromCompanyIds:
|
|
|
35
35
|
"2024": {"revenue": 14208000000.0},
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
+
expected_response = {
|
|
39
|
+
"results": {
|
|
40
|
+
"SPGI": {
|
|
41
|
+
"line_item": {
|
|
42
|
+
"2022": "11181000000.000000",
|
|
43
|
+
"2023": "12497000000.000000",
|
|
44
|
+
"2024": "14208000000.000000",
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"errors": [
|
|
49
|
+
"No identification triple found for the provided identifier: NON-EXISTENT of type: ticker"
|
|
50
|
+
],
|
|
51
|
+
}
|
|
38
52
|
|
|
39
53
|
requests_mock.get(
|
|
40
54
|
url=f"https://kfinance.kensho.com/api/v1/line_item/{SPGI_COMPANY_ID}/revenue/none/none/none/none/none",
|
|
@@ -42,7 +56,9 @@ class TestGetFinancialLineItemFromCompanyIds:
|
|
|
42
56
|
)
|
|
43
57
|
|
|
44
58
|
tool = GetFinancialLineItemFromIdentifiers(kfinance_client=mock_client)
|
|
45
|
-
args = GetFinancialLineItemFromIdentifiersArgs(
|
|
59
|
+
args = GetFinancialLineItemFromIdentifiersArgs(
|
|
60
|
+
identifiers=["SPGI", "non-existent"], line_item="revenue"
|
|
61
|
+
)
|
|
46
62
|
response = tool.run(args.model_dump(mode="json"))
|
|
47
63
|
assert response == expected_response
|
|
48
64
|
|
|
@@ -55,8 +71,10 @@ class TestGetFinancialLineItemFromCompanyIds:
|
|
|
55
71
|
|
|
56
72
|
company_ids = [1, 2]
|
|
57
73
|
expected_response = {
|
|
58
|
-
"
|
|
59
|
-
|
|
74
|
+
"results": {
|
|
75
|
+
"C_1": {"line_item": {"2024": "14208000000.000000"}},
|
|
76
|
+
"C_2": {"line_item": {"2024": "14208000000.000000"}},
|
|
77
|
+
}
|
|
60
78
|
}
|
|
61
79
|
for company_id in company_ids:
|
|
62
80
|
requests_mock.get(
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MergerSummary(BaseModel):
|
|
7
|
+
transaction_id: int
|
|
8
|
+
merger_title: str
|
|
9
|
+
closed_date: date
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MergersResp(BaseModel):
|
|
13
|
+
target: list[MergerSummary]
|
|
14
|
+
buyer: list[MergerSummary]
|
|
15
|
+
seller: list[MergerSummary]
|
|
@@ -6,19 +6,21 @@ from pydantic import BaseModel, Field
|
|
|
6
6
|
from kfinance.client.batch_request_handling import Task, process_tasks_in_thread_pool_executor
|
|
7
7
|
from kfinance.client.kfinance import Company, MergerOrAcquisition, ParticipantInMerger
|
|
8
8
|
from kfinance.client.permission_models import Permission
|
|
9
|
-
from kfinance.domains.companies.
|
|
10
|
-
|
|
11
|
-
fetch_company_ids_from_identifiers,
|
|
12
|
-
parse_identifiers,
|
|
13
|
-
)
|
|
9
|
+
from kfinance.domains.companies.company_models import prefix_company_id
|
|
10
|
+
from kfinance.domains.mergers_and_acquisitions.merger_and_acquisition_models import MergersResp
|
|
14
11
|
from kfinance.integrations.tool_calling.tool_calling_models import (
|
|
15
12
|
KfinanceTool,
|
|
16
13
|
ToolArgsWithIdentifier,
|
|
17
14
|
ToolArgsWithIdentifiers,
|
|
15
|
+
ToolRespWithErrors,
|
|
18
16
|
)
|
|
19
17
|
|
|
20
18
|
|
|
21
|
-
class
|
|
19
|
+
class GetMergersFromIdentifiersResp(ToolRespWithErrors):
|
|
20
|
+
results: dict[str, MergersResp]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class GetMergersFromIdentifiers(KfinanceTool):
|
|
22
24
|
name: str = "get_mergers_from_identifiers"
|
|
23
25
|
description: str = dedent("""
|
|
24
26
|
Get the transaction IDs that involve the given identifiers.
|
|
@@ -29,24 +31,57 @@ class GetMergersFromIdentifier(KfinanceTool):
|
|
|
29
31
|
accepted_permissions: set[Permission] | None = {Permission.MergersPermission}
|
|
30
32
|
|
|
31
33
|
def _run(self, identifiers: list[str]) -> dict:
|
|
34
|
+
"""Sample Response:
|
|
35
|
+
|
|
36
|
+
{
|
|
37
|
+
'results': {
|
|
38
|
+
'SPGI': {
|
|
39
|
+
'target': [
|
|
40
|
+
{
|
|
41
|
+
'transaction_id': 10998717,
|
|
42
|
+
'merger_title': 'Closed M/A of Microsoft Corporation',
|
|
43
|
+
'closed_date': '2021-01-01'
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
'buyer': [
|
|
47
|
+
{
|
|
48
|
+
'transaction_id': 517414,
|
|
49
|
+
'merger_title': 'Closed M/A of MongoMusic, Inc.',
|
|
50
|
+
'closed_date': '2023-01-01'
|
|
51
|
+
},
|
|
52
|
+
'seller': [
|
|
53
|
+
{
|
|
54
|
+
'transaction_id': 455551,
|
|
55
|
+
'merger_title': 'Closed M/A of VacationSpot.com, Inc.',
|
|
56
|
+
'closed_date': '2024-01-01'
|
|
57
|
+
},
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
'errors': ['No identification triple found for the provided identifier: NON-EXISTENT of type: ticker']
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
"""
|
|
65
|
+
|
|
32
66
|
api_client = self.kfinance_client.kfinance_api_client
|
|
33
|
-
|
|
34
|
-
identifiers_to_company_ids = fetch_company_ids_from_identifiers(
|
|
35
|
-
identifiers=parsed_identifiers, api_client=api_client
|
|
36
|
-
)
|
|
67
|
+
id_triple_resp = api_client.unified_fetch_id_triples(identifiers=identifiers)
|
|
37
68
|
|
|
38
69
|
tasks = [
|
|
39
70
|
Task(
|
|
40
71
|
func=api_client.fetch_mergers_for_company,
|
|
41
|
-
kwargs=dict(company_id=company_id),
|
|
72
|
+
kwargs=dict(company_id=id_triple.company_id),
|
|
42
73
|
result_key=identifier,
|
|
43
74
|
)
|
|
44
|
-
for identifier,
|
|
75
|
+
for identifier, id_triple in id_triple_resp.identifiers_to_id_triples.items()
|
|
45
76
|
]
|
|
46
77
|
|
|
47
|
-
merger_responses = process_tasks_in_thread_pool_executor(
|
|
48
|
-
|
|
49
|
-
|
|
78
|
+
merger_responses: dict[str, MergersResp] = process_tasks_in_thread_pool_executor(
|
|
79
|
+
api_client=api_client, tasks=tasks
|
|
80
|
+
)
|
|
81
|
+
output_model = GetMergersFromIdentifiersResp(
|
|
82
|
+
results=merger_responses, errors=list(id_triple_resp.errors.values())
|
|
83
|
+
)
|
|
84
|
+
return output_model.model_dump(mode="json")
|
|
50
85
|
|
|
51
86
|
|
|
52
87
|
class GetMergerInfoFromTransactionIdArgs(BaseModel):
|
|
@@ -83,34 +118,21 @@ class GetMergerInfoFromTransactionId(KfinanceTool):
|
|
|
83
118
|
else None,
|
|
84
119
|
"participants": {
|
|
85
120
|
"target": {
|
|
86
|
-
"company_id":
|
|
87
|
-
|
|
88
|
-
company_id=merger_participants["target"].company.company_id,
|
|
89
|
-
api_client=self.kfinance_client.kfinance_api_client,
|
|
90
|
-
)
|
|
121
|
+
"company_id": prefix_company_id(
|
|
122
|
+
merger_participants["target"].company.company_id
|
|
91
123
|
),
|
|
92
124
|
"company_name": merger_participants["target"].company.name,
|
|
93
125
|
},
|
|
94
126
|
"buyers": [
|
|
95
127
|
{
|
|
96
|
-
"company_id":
|
|
97
|
-
CompanyId(
|
|
98
|
-
buyer.company.company_id,
|
|
99
|
-
api_client=self.kfinance_client.kfinance_api_client,
|
|
100
|
-
)
|
|
101
|
-
),
|
|
128
|
+
"company_id": prefix_company_id(buyer.company.company_id),
|
|
102
129
|
"company_name": buyer.company.name,
|
|
103
130
|
}
|
|
104
131
|
for buyer in merger_participants["buyers"]
|
|
105
132
|
],
|
|
106
133
|
"sellers": [
|
|
107
134
|
{
|
|
108
|
-
"company_id":
|
|
109
|
-
CompanyId(
|
|
110
|
-
seller.company.company_id,
|
|
111
|
-
api_client=self.kfinance_client.kfinance_api_client,
|
|
112
|
-
)
|
|
113
|
-
),
|
|
135
|
+
"company_id": prefix_company_id(seller.company.company_id),
|
|
114
136
|
"company_name": seller.company.name,
|
|
115
137
|
}
|
|
116
138
|
for seller in merger_participants["sellers"]
|
|
@@ -161,12 +183,7 @@ class GetAdvisorsForCompanyInTransactionFromIdentifier(KfinanceTool):
|
|
|
161
183
|
if advisors:
|
|
162
184
|
return [
|
|
163
185
|
{
|
|
164
|
-
"advisor_company_id":
|
|
165
|
-
CompanyId(
|
|
166
|
-
company_id=advisor.company.company_id,
|
|
167
|
-
api_client=self.kfinance_client.kfinance_api_client,
|
|
168
|
-
)
|
|
169
|
-
),
|
|
186
|
+
"advisor_company_id": prefix_company_id(advisor.company.company_id),
|
|
170
187
|
"advisor_company_name": advisor.company.name,
|
|
171
188
|
"advisor_type_name": advisor.advisor_type_name,
|
|
172
189
|
}
|
|
@@ -3,30 +3,43 @@ from copy import deepcopy
|
|
|
3
3
|
from requests_mock import Mocker
|
|
4
4
|
|
|
5
5
|
from kfinance.client.kfinance import Client
|
|
6
|
-
from kfinance.client.tests.test_objects import
|
|
6
|
+
from kfinance.client.tests.test_objects import (
|
|
7
|
+
MERGERS_RESP,
|
|
8
|
+
ordered,
|
|
9
|
+
)
|
|
10
|
+
from kfinance.conftest import SPGI_COMPANY_ID
|
|
7
11
|
from kfinance.domains.companies.company_models import COMPANY_ID_PREFIX
|
|
8
12
|
from kfinance.domains.mergers_and_acquisitions.merger_and_acquisition_tools import (
|
|
9
13
|
GetAdvisorsForCompanyInTransactionFromIdentifier,
|
|
10
14
|
GetAdvisorsForCompanyInTransactionFromIdentifierArgs,
|
|
11
15
|
GetMergerInfoFromTransactionId,
|
|
12
16
|
GetMergerInfoFromTransactionIdArgs,
|
|
13
|
-
|
|
17
|
+
GetMergersFromIdentifiers,
|
|
14
18
|
)
|
|
15
19
|
from kfinance.integrations.tool_calling.tool_calling_models import ToolArgsWithIdentifiers
|
|
16
20
|
|
|
17
21
|
|
|
18
22
|
class TestGetMergersFromIdentifiers:
|
|
19
23
|
def test_get_mergers_from_identifiers(self, requests_mock: Mocker, mock_client: Client):
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
"""
|
|
25
|
+
GIVEN the GetMergersFromIdentifiers tool
|
|
26
|
+
WHEN we request mergers for SPGI and a non-existent company
|
|
27
|
+
THEN we get back the SPGI mergers and an error for the non-existent company"""
|
|
28
|
+
|
|
29
|
+
merger_data = MERGERS_RESP.model_dump(mode="json")
|
|
30
|
+
expected_response = {
|
|
31
|
+
"results": {"SPGI": merger_data},
|
|
32
|
+
"errors": [
|
|
33
|
+
"No identification triple found for the provided identifier: NON-EXISTENT of type: ticker"
|
|
34
|
+
],
|
|
35
|
+
}
|
|
23
36
|
requests_mock.get(
|
|
24
|
-
url=f"https://kfinance.kensho.com/api/v1/mergers/{
|
|
37
|
+
url=f"https://kfinance.kensho.com/api/v1/mergers/{SPGI_COMPANY_ID}", json=merger_data
|
|
25
38
|
)
|
|
26
|
-
tool =
|
|
27
|
-
args = ToolArgsWithIdentifiers(identifiers=["
|
|
39
|
+
tool = GetMergersFromIdentifiers(kfinance_client=mock_client)
|
|
40
|
+
args = ToolArgsWithIdentifiers(identifiers=["SPGI", "non-existent"])
|
|
28
41
|
response = tool.run(args.model_dump(mode="json"))
|
|
29
|
-
assert
|
|
42
|
+
assert response == expected_response
|
|
30
43
|
|
|
31
44
|
|
|
32
45
|
class TestGetCompaniesAdvisingCompanyInTransactionFromIdentifier:
|
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
from copy import deepcopy
|
|
2
2
|
from datetime import date
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel, model_validator
|
|
6
6
|
|
|
7
7
|
from kfinance.client.models.decimal_with_unit import Money, Shares
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
class HistoryMetadata(TypedDict):
|
|
11
|
-
currency: str
|
|
12
|
-
symbol: str
|
|
13
|
-
exchange_name: str
|
|
14
|
-
instrument_type: str
|
|
15
|
-
first_trade_date: date
|
|
16
|
-
|
|
17
|
-
|
|
18
10
|
class Prices(BaseModel):
|
|
19
11
|
"""Prices represents prices for a stock for a specific "date".
|
|
20
12
|
|
|
@@ -68,3 +60,11 @@ class PriceHistory(BaseModel):
|
|
|
68
60
|
for key in ["open", "high", "low", "close"]:
|
|
69
61
|
capitalization[key] = dict(unit=currency, value=capitalization[key])
|
|
70
62
|
return data
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class HistoryMetadataResp(BaseModel):
|
|
66
|
+
currency: str
|
|
67
|
+
symbol: str
|
|
68
|
+
exchange_name: str
|
|
69
|
+
instrument_type: str
|
|
70
|
+
first_trade_date: date
|
|
@@ -7,13 +7,11 @@ from pydantic import BaseModel, Field
|
|
|
7
7
|
from kfinance.client.batch_request_handling import Task, process_tasks_in_thread_pool_executor
|
|
8
8
|
from kfinance.client.models.date_and_period_models import Periodicity
|
|
9
9
|
from kfinance.client.permission_models import Permission
|
|
10
|
-
from kfinance.domains.
|
|
11
|
-
fetch_trading_item_ids_from_identifiers,
|
|
12
|
-
parse_identifiers,
|
|
13
|
-
)
|
|
10
|
+
from kfinance.domains.prices.price_models import HistoryMetadataResp, PriceHistory
|
|
14
11
|
from kfinance.integrations.tool_calling.tool_calling_models import (
|
|
15
12
|
KfinanceTool,
|
|
16
13
|
ToolArgsWithIdentifiers,
|
|
14
|
+
ToolRespWithErrors,
|
|
17
15
|
)
|
|
18
16
|
|
|
19
17
|
|
|
@@ -32,6 +30,10 @@ class GetPricesFromIdentifiersArgs(ToolArgsWithIdentifiers):
|
|
|
32
30
|
)
|
|
33
31
|
|
|
34
32
|
|
|
33
|
+
class GetPricesFromIdentifiersResp(ToolRespWithErrors):
|
|
34
|
+
results: dict[str, PriceHistory]
|
|
35
|
+
|
|
36
|
+
|
|
35
37
|
class GetPricesFromIdentifiers(KfinanceTool):
|
|
36
38
|
name: str = "get_prices_from_identifiers"
|
|
37
39
|
description: str = dedent("""
|
|
@@ -81,20 +83,20 @@ class GetPricesFromIdentifiers(KfinanceTool):
|
|
|
81
83
|
'volume': {'value': '1182229', 'unit': 'Shares'}
|
|
82
84
|
}
|
|
83
85
|
]
|
|
84
|
-
}
|
|
86
|
+
},
|
|
87
|
+
'errors': ['No identification triple found for the provided identifier: NON-EXISTENT of type: ticker']
|
|
85
88
|
}
|
|
86
89
|
"""
|
|
90
|
+
|
|
87
91
|
api_client = self.kfinance_client.kfinance_api_client
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
identifiers=parsed_identifiers, api_client=api_client
|
|
91
|
-
)
|
|
92
|
+
id_triple_resp = api_client.unified_fetch_id_triples(identifiers=identifiers)
|
|
93
|
+
id_triple_resp.filter_out_companies_without_trading_item_ids()
|
|
92
94
|
|
|
93
95
|
tasks = [
|
|
94
96
|
Task(
|
|
95
97
|
func=api_client.fetch_history,
|
|
96
98
|
kwargs=dict(
|
|
97
|
-
trading_item_id=trading_item_id,
|
|
99
|
+
trading_item_id=id_triple.trading_item_id,
|
|
98
100
|
start_date=start_date,
|
|
99
101
|
end_date=end_date,
|
|
100
102
|
periodicity=periodicity,
|
|
@@ -102,20 +104,26 @@ class GetPricesFromIdentifiers(KfinanceTool):
|
|
|
102
104
|
),
|
|
103
105
|
result_key=identifier,
|
|
104
106
|
)
|
|
105
|
-
for identifier,
|
|
107
|
+
for identifier, id_triple in id_triple_resp.identifiers_to_id_triples.items()
|
|
106
108
|
]
|
|
107
109
|
|
|
108
|
-
price_responses = process_tasks_in_thread_pool_executor(
|
|
110
|
+
price_responses: dict[str, PriceHistory] = process_tasks_in_thread_pool_executor(
|
|
111
|
+
api_client=api_client, tasks=tasks
|
|
112
|
+
)
|
|
113
|
+
# If we return results for more than one company and the start and end dates are unset,
|
|
114
|
+
# truncate data to only return the most recent datapoint.
|
|
115
|
+
if len(price_responses) > 1 and start_date is None and end_date is None:
|
|
116
|
+
for price_response in price_responses.values():
|
|
117
|
+
price_response.prices = price_response.prices[-1:]
|
|
118
|
+
|
|
119
|
+
output_model = GetPricesFromIdentifiersResp(
|
|
120
|
+
results=price_responses, errors=list(id_triple_resp.errors.values())
|
|
121
|
+
)
|
|
122
|
+
return output_model.model_dump(mode="json")
|
|
109
123
|
|
|
110
|
-
# Only include most recent price if more than one identifier passed and start_date == end_date == None
|
|
111
|
-
dump_include_filter = None
|
|
112
|
-
if len(identifiers) > 1 and start_date == end_date is None:
|
|
113
|
-
dump_include_filter = {"prices": {0: True}}
|
|
114
124
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
for identifier, prices in price_responses.items()
|
|
118
|
-
}
|
|
125
|
+
class GetHistoryMetadataFromIdentifiersResp(ToolRespWithErrors):
|
|
126
|
+
results: dict[str, HistoryMetadataResp]
|
|
119
127
|
|
|
120
128
|
|
|
121
129
|
class GetHistoryMetadataFromIdentifiers(KfinanceTool):
|
|
@@ -132,34 +140,37 @@ class GetHistoryMetadataFromIdentifiers(KfinanceTool):
|
|
|
132
140
|
"""Sample response:
|
|
133
141
|
|
|
134
142
|
{
|
|
135
|
-
'
|
|
136
|
-
'
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
143
|
+
'results': {
|
|
144
|
+
'SPGI': {
|
|
145
|
+
'currency': 'USD',
|
|
146
|
+
'exchange_name': 'NYSE',
|
|
147
|
+
'first_trade_date': '1968-01-02',
|
|
148
|
+
'instrument_type': 'Equity',
|
|
149
|
+
'symbol': 'SPGI'
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
'errors': ['No identification triple found for the provided identifier: NON-EXISTENT of type: ticker']
|
|
142
153
|
}
|
|
143
154
|
"""
|
|
155
|
+
|
|
144
156
|
api_client = self.kfinance_client.kfinance_api_client
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
identifiers=parsed_identifiers, api_client=api_client
|
|
148
|
-
)
|
|
157
|
+
id_triple_resp = api_client.unified_fetch_id_triples(identifiers=identifiers)
|
|
158
|
+
id_triple_resp.filter_out_companies_without_trading_item_ids()
|
|
149
159
|
|
|
150
160
|
tasks = [
|
|
151
161
|
Task(
|
|
152
162
|
func=api_client.fetch_history_metadata,
|
|
153
|
-
kwargs=dict(trading_item_id=trading_item_id),
|
|
163
|
+
kwargs=dict(trading_item_id=id_triple.trading_item_id),
|
|
154
164
|
result_key=identifier,
|
|
155
165
|
)
|
|
156
|
-
for identifier,
|
|
166
|
+
for identifier, id_triple in id_triple_resp.identifiers_to_id_triples.items()
|
|
157
167
|
]
|
|
158
168
|
|
|
159
|
-
history_metadata_responses =
|
|
160
|
-
api_client=api_client, tasks=tasks
|
|
169
|
+
history_metadata_responses: dict[str, HistoryMetadataResp] = (
|
|
170
|
+
process_tasks_in_thread_pool_executor(api_client=api_client, tasks=tasks)
|
|
171
|
+
)
|
|
172
|
+
output_model = GetHistoryMetadataFromIdentifiersResp(
|
|
173
|
+
results=history_metadata_responses, errors=list(id_triple_resp.errors.values())
|
|
161
174
|
)
|
|
162
175
|
|
|
163
|
-
return
|
|
164
|
-
str(identifier): result for identifier, result in history_metadata_responses.items()
|
|
165
|
-
}
|
|
176
|
+
return output_model.model_dump(mode="json")
|