kensho-kfinance 2.7.0__py3-none-any.whl → 2.9.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-2.7.0.dist-info → kensho_kfinance-2.9.0.dist-info}/METADATA +2 -2
- kensho_kfinance-2.9.0.dist-info/RECORD +70 -0
- kfinance/CHANGELOG.md +6 -0
- kfinance/decimal_with_unit.py +78 -0
- kfinance/fetch.py +15 -16
- kfinance/kfinance.py +108 -122
- kfinance/meta_classes.py +28 -27
- kfinance/models/business_relationship_models.py +26 -0
- kfinance/models/capitalization_models.py +90 -0
- kfinance/models/competitor_models.py +13 -0
- kfinance/models/currency_models.py +345 -0
- kfinance/models/date_and_period_models.py +48 -0
- kfinance/models/id_models.py +7 -0
- kfinance/models/industry_models.py +12 -0
- kfinance/{constants.py → models/line_item_models.py} +1 -165
- kfinance/models/permission_models.py +15 -0
- kfinance/models/price_models.py +70 -0
- kfinance/models/segment_models.py +8 -0
- kfinance/models/statement_models.py +9 -0
- kfinance/tests/test_batch_requests.py +61 -31
- kfinance/tests/test_client.py +1 -1
- kfinance/tests/test_decimal_with_unit.py +60 -0
- kfinance/tests/test_example_notebook.py +1 -0
- kfinance/tests/test_fetch.py +23 -12
- kfinance/tests/test_models/__init__.py +0 -0
- kfinance/tests/test_models/test_capitalization_models.py +83 -0
- kfinance/tests/test_models/test_price_models.py +58 -0
- kfinance/tests/test_objects.py +107 -57
- kfinance/tests/test_tools.py +35 -11
- kfinance/tool_calling/get_advisors_for_company_in_transaction_from_identifier.py +10 -7
- kfinance/tool_calling/get_business_relationship_from_identifier.py +2 -1
- kfinance/tool_calling/get_capitalization_from_identifier.py +4 -5
- kfinance/tool_calling/get_competitors_from_identifier.py +2 -1
- kfinance/tool_calling/get_cusip_from_ticker.py +1 -1
- kfinance/tool_calling/get_earnings.py +1 -1
- kfinance/tool_calling/get_financial_line_item_from_identifier.py +3 -1
- kfinance/tool_calling/get_financial_statement_from_identifier.py +3 -1
- kfinance/tool_calling/get_history_metadata_from_identifier.py +2 -1
- kfinance/tool_calling/get_info_from_identifier.py +1 -1
- kfinance/tool_calling/get_isin_from_ticker.py +1 -1
- kfinance/tool_calling/get_latest.py +2 -1
- kfinance/tool_calling/get_latest_earnings.py +1 -1
- kfinance/tool_calling/get_merger_info_from_transaction_id.py +6 -5
- kfinance/tool_calling/get_mergers_from_identifier.py +4 -1
- kfinance/tool_calling/get_n_quarters_ago.py +2 -1
- kfinance/tool_calling/get_next_earnings.py +1 -1
- kfinance/tool_calling/get_prices_from_identifier.py +4 -3
- kfinance/tool_calling/get_segments_from_identifier.py +3 -1
- kfinance/tool_calling/get_transcript.py +1 -1
- kfinance/tool_calling/resolve_identifier.py +1 -1
- kfinance/tool_calling/shared_models.py +1 -1
- kfinance/version.py +2 -2
- kensho_kfinance-2.7.0.dist-info/RECORD +0 -54
- {kensho_kfinance-2.7.0.dist-info → kensho_kfinance-2.9.0.dist-info}/WHEEL +0 -0
- {kensho_kfinance-2.7.0.dist-info → kensho_kfinance-2.9.0.dist-info}/licenses/AUTHORS.md +0 -0
- {kensho_kfinance-2.7.0.dist-info → kensho_kfinance-2.9.0.dist-info}/licenses/LICENSE +0 -0
- {kensho_kfinance-2.7.0.dist-info → kensho_kfinance-2.9.0.dist-info}/top_level.txt +0 -0
- /kfinance/{tests/scratch.py → models/__init__.py} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kensho-kfinance
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.9.0
|
|
4
4
|
Summary: Python CLI for kFinance
|
|
5
5
|
Author-email: Luke Brown <luke.brown@kensho.com>, Michelle Keoy <michelle.keoy@kensho.com>, Keith Page <keith.page@kensho.com>, Matthew Rosen <matthew.rosen@kensho.com>, Nick Roshdieh <nick.roshdieh@kensho.com>
|
|
6
6
|
Project-URL: source, https://github.com/kensho-technologies/kfinance
|
|
@@ -14,7 +14,7 @@ License-File: LICENSE
|
|
|
14
14
|
License-File: AUTHORS.md
|
|
15
15
|
Requires-Dist: cachetools<6,>=5.5
|
|
16
16
|
Requires-Dist: click<=9,>=8.2.1
|
|
17
|
-
Requires-Dist: fastmcp<
|
|
17
|
+
Requires-Dist: fastmcp<2.10,>=2.9
|
|
18
18
|
Requires-Dist: langchain-core>=0.3.15
|
|
19
19
|
Requires-Dist: langchain-google-genai<3,>=2.1.5
|
|
20
20
|
Requires-Dist: numpy>=1.22.4
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
kensho_kfinance-2.9.0.dist-info/licenses/AUTHORS.md,sha256=0h9ClbI0pu1oKj1M28ROUsaxrbZg-6ukQGl6X4y9noI,68
|
|
2
|
+
kensho_kfinance-2.9.0.dist-info/licenses/LICENSE,sha256=bsY4blvSgq6o0FMQ3RXa2NCgco--nHCCchLXzxr6kms,83
|
|
3
|
+
kfinance/CHANGELOG.md,sha256=vwLoxhyrR2AGSJbkfkvWYjjZQ6UBrfjOPSZq4eM9nok,2280
|
|
4
|
+
kfinance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
kfinance/batch_request_handling.py,sha256=G9rhpgQaCdb5C1dsfuJip8383iszibcOJ5k8gxld-tQ,5001
|
|
6
|
+
kfinance/decimal_with_unit.py,sha256=U1eJSJ-4zPHkdtHcrfaW9CmmaLPE13MiWJy6odRyeXo,2904
|
|
7
|
+
kfinance/fetch.py,sha256=MSdDddZ_1YdmzCuXqCXwY_vfFLdbdQeCJ6YRQrwCUCI,28157
|
|
8
|
+
kfinance/kfinance.py,sha256=ob5jNO5-aTSfBwknsSVCMLQJD5mWZkPTAZlvsBJ_Z2Y,72584
|
|
9
|
+
kfinance/mcp.py,sha256=MbktclVfBOEwfe-eR7kPaTXopMJmn_8RMlf4Jx5CXKU,3689
|
|
10
|
+
kfinance/meta_classes.py,sha256=0wI70PXfQzNbRUW2nYVfxJjqCqqI5EukUIKjaMcrzVA,21804
|
|
11
|
+
kfinance/prompt.py,sha256=PtVB8c_FcSlVdyGgByAnIFGzuUuBaEjciCqnBJl1hSQ,25133
|
|
12
|
+
kfinance/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
kfinance/pydantic_models.py,sha256=avpbPqwrAyLqsCbrmFpK_B8_fj1nPlBHrnPxRcBaSkE,774
|
|
14
|
+
kfinance/server_thread.py,sha256=jUnt1YGoYDkqqz1MbCwd44zJs1T_Z2BCgvj75bdtLgA,2574
|
|
15
|
+
kfinance/version.py,sha256=pqhLPO2SP7VnEU-lJpoO0qS9x0Kgz8BD6Rgr6J_aK-o,511
|
|
16
|
+
kfinance/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
kfinance/models/business_relationship_models.py,sha256=XlkYI_XUegORH4U4oLXjdFIX9Pr9FvQUnFAc67D2M4s,770
|
|
18
|
+
kfinance/models/capitalization_models.py,sha256=jB0MIZ94UOPfVckuS2asED9ARjisrStC_O4KgfHmoJw,3193
|
|
19
|
+
kfinance/models/competitor_models.py,sha256=0yDu49Pe28ErpCMLX-BBeUnjjxqerb49ToVKSTOivNk,565
|
|
20
|
+
kfinance/models/currency_models.py,sha256=JfDZf-nATCyjThBw-Ky2BNvM-n2oHepBLIHMCIyCRUE,19201
|
|
21
|
+
kfinance/models/date_and_period_models.py,sha256=B508v1SLy_mbSSuSbPz41xeFNRAeBFv41gJSlaXyHnU,1062
|
|
22
|
+
kfinance/models/id_models.py,sha256=xhFKmDddBfqkQZsbOP8za8VnxIFHiwvGThKjeGKC2Hg,138
|
|
23
|
+
kfinance/models/industry_models.py,sha256=ydB_cTIsNsfCUeaO7K2qqLUuHdG6WfUCoAzBigKVV8M,243
|
|
24
|
+
kfinance/models/line_item_models.py,sha256=JIdoSYxoJDQVkmqUK6_ABpb6lULXRvrCDJGiKL6kRhE,45399
|
|
25
|
+
kfinance/models/permission_models.py,sha256=z6MKY7Zf7StPKwsucsoVqltUesicU6MC6rxRGmlRwZI,557
|
|
26
|
+
kfinance/models/price_models.py,sha256=saiJk1ZIs02cHtY54-5YvS7NgH3EABR2RGT6wpzRauY,2149
|
|
27
|
+
kfinance/models/segment_models.py,sha256=GSJhK1HmbMmwRDFmrDTZzx8aYrAaHN13IJ0J9GTBn9Y,145
|
|
28
|
+
kfinance/models/statement_models.py,sha256=RmclTeyahCODUP0-KgZZZequFgQ2pUQ8cdfHI96c9vA,207
|
|
29
|
+
kfinance/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
+
kfinance/tests/conftest.py,sha256=dhL_RSc-af3j-2_UrAGRE9mxgcbjuIRtj08DTx79pQc,1123
|
|
31
|
+
kfinance/tests/test_batch_requests.py,sha256=JjP5OOKyxzYqmVmTR2hzM4Q6wyYDCEDHINH4vNFd6ag,11597
|
|
32
|
+
kfinance/tests/test_client.py,sha256=YZT-AXpFsVHpdoZj01d2TkmUkNP-8f3cIOgeXtP_05U,3870
|
|
33
|
+
kfinance/tests/test_decimal_with_unit.py,sha256=98zZmSLojWq3FS0e5AuNxv9eluqGVEoqM3r_agkmt_Y,2188
|
|
34
|
+
kfinance/tests/test_example_notebook.py,sha256=VFS2W_T0yEtd74YiRxj3KA7eEL_fJuCpWwUAkiFqzkc,6501
|
|
35
|
+
kfinance/tests/test_fetch.py,sha256=s8fSkXDtNjDcK_jWQYUO1stx9ElX1I54qG_48WxfBAc,18558
|
|
36
|
+
kfinance/tests/test_group_objects.py,sha256=SoMEZmkG4RYdgWOAwxLHHtzIQho92KM01YbQXPUg578,1689
|
|
37
|
+
kfinance/tests/test_objects.py,sha256=cpTE22omZATe-2j9jnuchgEiZtNM-wler95ainYoh_o,42471
|
|
38
|
+
kfinance/tests/test_tools.py,sha256=jLrugYTSdK6Cw4SmJxsxdYdJ1S3X7wDcBC7kQAWe75A,31024
|
|
39
|
+
kfinance/tests/test_models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
40
|
+
kfinance/tests/test_models/test_capitalization_models.py,sha256=E-5eZX0ufdFsmcWATt5agyH4Ks-q8TNO-W6cnYIMOcE,3116
|
|
41
|
+
kfinance/tests/test_models/test_price_models.py,sha256=DHBMAFJ8axat1s_C0tElqcCjw6YhmzF9WWKpKmySwv0,2378
|
|
42
|
+
kfinance/tool_calling/README.md,sha256=omJq7Us6r4U45QB7hRpLjRJ5BMalCkZkh4uXBjTbJXc,2022
|
|
43
|
+
kfinance/tool_calling/__init__.py,sha256=UmtbtG6PvQHB1fInEL-K5q0kPHL__zTY9wzaPRSp1wg,2174
|
|
44
|
+
kfinance/tool_calling/get_advisors_for_company_in_transaction_from_identifier.py,sha256=IYpImoYfeq7Ep_IrUZgsSHC0oLSmI1mwuTR_1ftTPMY,1789
|
|
45
|
+
kfinance/tool_calling/get_business_relationship_from_identifier.py,sha256=oPlVpDy4AY8nQFZnHRp9DhpL8SecIlyEY6aiklHtVDg,1551
|
|
46
|
+
kfinance/tool_calling/get_capitalization_from_identifier.py,sha256=qciQI9gSJh4gGatnjOkid-GJSlzXkvt7-2VcK2yBfrg,1558
|
|
47
|
+
kfinance/tool_calling/get_competitors_from_identifier.py,sha256=J9TVWNU_6d6ltJLIJTdCtkhbc0MqoCbqdWUxrwBkOUs,1156
|
|
48
|
+
kfinance/tool_calling/get_cusip_from_ticker.py,sha256=ebzFmjZFkcuydntQXmuxfh8mhPiXmmzUTb2ncHy00ao,667
|
|
49
|
+
kfinance/tool_calling/get_earnings.py,sha256=d7UvVsSAcrVlveWyUpua35vdloA_QgJnQ4Nmg7wT7m0,1245
|
|
50
|
+
kfinance/tool_calling/get_financial_line_item_from_identifier.py,sha256=hsKmObT2TJ4HMusVtQXU6FMDmyY7bKg2V-x-IXuU71c,2270
|
|
51
|
+
kfinance/tool_calling/get_financial_statement_from_identifier.py,sha256=ba8Z378YrQ_J0-A6KVLOtxe-hYMP68780g0xXZfAyT8,2023
|
|
52
|
+
kfinance/tool_calling/get_history_metadata_from_identifier.py,sha256=hPC-RwwcnJ9kkhxTUJLE-xvCqfIq5kJcKM4deiL8zlI,789
|
|
53
|
+
kfinance/tool_calling/get_info_from_identifier.py,sha256=2-NmGvFpYXASGV_lJaYhhdXMxQpyvi30bXY9mGWwbBw,787
|
|
54
|
+
kfinance/tool_calling/get_isin_from_ticker.py,sha256=tp79lowBUdkv7fm-8QX4vJqeDZiq5M_jkkp8XKC1KY4,661
|
|
55
|
+
kfinance/tool_calling/get_latest.py,sha256=m6nBA8Bo51Q85kGbGnP59IUz4-lm_CRo1kSuNM26iXg,857
|
|
56
|
+
kfinance/tool_calling/get_latest_earnings.py,sha256=R98ImaONP3AjVshygkRLwcAHKscvVRkVoPw117O9nuw,1197
|
|
57
|
+
kfinance/tool_calling/get_merger_info_from_transaction_id.py,sha256=gB6VUmCAKS0OBclpcdp2gQq7cdxKbWTXt5Z8aMH_-ys,3243
|
|
58
|
+
kfinance/tool_calling/get_mergers_from_identifier.py,sha256=tgpcLQM1CPvyk4_xyUhcZh01kAzwdkQCV6gq-bEsFhA,1935
|
|
59
|
+
kfinance/tool_calling/get_n_quarters_ago.py,sha256=VYUBbQIowHXmNuln1oKoDKeWDbnXLg4p6X0d1HoY_NY,784
|
|
60
|
+
kfinance/tool_calling/get_next_earnings.py,sha256=vL3sCoocPhHDRawVRu9IyQPLXVAgODrZgKSqkP3Scs8,1177
|
|
61
|
+
kfinance/tool_calling/get_prices_from_identifier.py,sha256=uXD9fiLFI0vUM_YY8DI_9ifC0oeeC3KCyaTQQ0IjuSA,1966
|
|
62
|
+
kfinance/tool_calling/get_segments_from_identifier.py,sha256=1Ux9ZWEU3xmOSuAPGedo2r8wdRa_glv8roghp08MJ-c,1980
|
|
63
|
+
kfinance/tool_calling/get_transcript.py,sha256=urZgpQXigmahy-vj6OjkGI9O6HJcPIpd-5CRU7NI3YQ,782
|
|
64
|
+
kfinance/tool_calling/prompts.py,sha256=Yw1DJIMh90cjL-8q6_RMRiSjCtFDXvJAy7QiV5_uAU8,911
|
|
65
|
+
kfinance/tool_calling/resolve_identifier.py,sha256=Rkhwi6m3hffE19qaxRkrsu6slRKyiAXjfLr8L41N2zc,647
|
|
66
|
+
kfinance/tool_calling/shared_models.py,sha256=Sx_zlwk5YLu533406Pmh499NH5oTBWrZf8Ua8zoUSRk,3746
|
|
67
|
+
kensho_kfinance-2.9.0.dist-info/METADATA,sha256=NhJXLeMBY0A1QP2PaA2BWB7-m-cUxY1wqp7TlDRqPAY,6202
|
|
68
|
+
kensho_kfinance-2.9.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
69
|
+
kensho_kfinance-2.9.0.dist-info/top_level.txt,sha256=kT_kNwVhfQoOAecY8W7uYah5xaHMoHoAdBIvXh6DaKM,9
|
|
70
|
+
kensho_kfinance-2.9.0.dist-info/RECORD,,
|
kfinance/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field, model_validator
|
|
6
|
+
from typing_extensions import Self
|
|
7
|
+
|
|
8
|
+
from kfinance.models.currency_models import ISO_CODE_TO_CURRENCY
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DecimalWithUnit(BaseModel):
|
|
12
|
+
"""DecimalWithUnit (DWU) represents a decimal with a corresponding unit like $100 or 20 shares.
|
|
13
|
+
|
|
14
|
+
In addition to a value and unit, each DWU has a `conventional_decimals` attribute,
|
|
15
|
+
which indicates the number of decimals that should be represented.
|
|
16
|
+
For example, for USD, conventional_decimals is 2, which will display as "1.00".
|
|
17
|
+
For shares, conventional_decimals is 0, which will display as "1"
|
|
18
|
+
|
|
19
|
+
Usually, rather than initializing a DWU directly, you'll likely want to use an
|
|
20
|
+
existing subclass like `Money` or `Shares` or create a new one.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
value: Decimal
|
|
24
|
+
unit: str
|
|
25
|
+
# exclude conventional_decimals from serialization
|
|
26
|
+
conventional_decimals: int = Field(exclude=True)
|
|
27
|
+
|
|
28
|
+
@model_validator(mode="after")
|
|
29
|
+
def quantize_value(self) -> Self:
|
|
30
|
+
"""Quantize the value at the end of the deserialization.
|
|
31
|
+
|
|
32
|
+
The value gets adjusted so that it always has the expected number of decimals defined in
|
|
33
|
+
conventional_decimals.
|
|
34
|
+
For USD with conventional_decimals=2, it will show values like "1.00"
|
|
35
|
+
For Shares with conventional_decimals=0, it will show values like "1"
|
|
36
|
+
"""
|
|
37
|
+
exponent = Decimal("10") ** Decimal(-self.conventional_decimals)
|
|
38
|
+
self.value = self.value.quantize(exp=exponent)
|
|
39
|
+
return self
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class Money(DecimalWithUnit):
|
|
43
|
+
@model_validator(mode="before")
|
|
44
|
+
@classmethod
|
|
45
|
+
def inject_conventional_decimals_into_data(cls, data: Any) -> Any:
|
|
46
|
+
"""Inject conventional_decimals into data dict.
|
|
47
|
+
|
|
48
|
+
Each currency has an associated conventional_decimals defined in the
|
|
49
|
+
CURRENCIES list. This validator fetches that number and injects it into the
|
|
50
|
+
data dict.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
if isinstance(data, dict) and "conventional_decimals" not in data:
|
|
54
|
+
data = deepcopy(data)
|
|
55
|
+
currency = ISO_CODE_TO_CURRENCY[data["unit"]]
|
|
56
|
+
data["conventional_decimals"] = currency.conventional_decimals
|
|
57
|
+
|
|
58
|
+
return data
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class Shares(DecimalWithUnit):
|
|
62
|
+
unit: str = "Shares"
|
|
63
|
+
conventional_decimals: int = Field(exclude=True, default=0)
|
|
64
|
+
|
|
65
|
+
@model_validator(mode="before")
|
|
66
|
+
@classmethod
|
|
67
|
+
def convert_numbers_to_dicts(cls, data: Any) -> Any:
|
|
68
|
+
"""Convert numbers into dicts.
|
|
69
|
+
|
|
70
|
+
The shares class can be built from a single number because unit and
|
|
71
|
+
conventional_decimals are always the same. However, the parser expects a
|
|
72
|
+
dict instead of a number, so we have to convert any number ("10") into a
|
|
73
|
+
dict {"value": "10"}.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
if isinstance(data, (str, int, float, Decimal)):
|
|
77
|
+
data = {"value": data}
|
|
78
|
+
return data
|
kfinance/fetch.py
CHANGED
|
@@ -10,23 +10,22 @@ import jwt
|
|
|
10
10
|
from pydantic import ValidationError
|
|
11
11
|
import requests
|
|
12
12
|
|
|
13
|
-
from .
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
from .pydantic_models import RelationshipResponse, RelationshipResponseNoName
|
|
13
|
+
from kfinance.models.business_relationship_models import BusinessRelationshipType
|
|
14
|
+
from kfinance.models.capitalization_models import Capitalizations
|
|
15
|
+
from kfinance.models.competitor_models import CompetitorSource
|
|
16
|
+
from kfinance.models.date_and_period_models import Periodicity, PeriodType
|
|
17
|
+
from kfinance.models.id_models import IdentificationTriple
|
|
18
|
+
from kfinance.models.industry_models import IndustryClassification
|
|
19
|
+
from kfinance.models.permission_models import Permission
|
|
20
|
+
from kfinance.models.price_models import PriceHistory
|
|
21
|
+
from kfinance.models.segment_models import SegmentType
|
|
22
|
+
from kfinance.pydantic_models import RelationshipResponse, RelationshipResponseNoName
|
|
24
23
|
|
|
25
24
|
|
|
26
25
|
# version.py gets autogenerated by setuptools-scm and is not available
|
|
27
26
|
# during local development.
|
|
28
27
|
try:
|
|
29
|
-
from .version import __version__ as kfinance_version
|
|
28
|
+
from kfinance.version import __version__ as kfinance_version
|
|
30
29
|
except ImportError:
|
|
31
30
|
kfinance_version = "dev"
|
|
32
31
|
|
|
@@ -300,7 +299,7 @@ class KFinanceApiClient:
|
|
|
300
299
|
start_date: Optional[str] = None,
|
|
301
300
|
end_date: Optional[str] = None,
|
|
302
301
|
periodicity: Optional[Periodicity] = None,
|
|
303
|
-
) ->
|
|
302
|
+
) -> PriceHistory:
|
|
304
303
|
"""Get the pricing history."""
|
|
305
304
|
url = (
|
|
306
305
|
f"{self.url_base}pricing/{trading_item_id}/"
|
|
@@ -309,7 +308,7 @@ class KFinanceApiClient:
|
|
|
309
308
|
f"{periodicity if periodicity else 'none'}/"
|
|
310
309
|
f"{'adjusted' if is_adjusted else 'unadjusted'}"
|
|
311
310
|
)
|
|
312
|
-
return self.fetch(url)
|
|
311
|
+
return PriceHistory.model_validate(self.fetch(url))
|
|
313
312
|
|
|
314
313
|
def fetch_history_metadata(self, trading_item_id: int) -> dict[str, str]:
|
|
315
314
|
"""Get the pricing history metadata."""
|
|
@@ -321,14 +320,14 @@ class KFinanceApiClient:
|
|
|
321
320
|
company_id: int,
|
|
322
321
|
start_date: Optional[str] = None,
|
|
323
322
|
end_date: Optional[str] = None,
|
|
324
|
-
) ->
|
|
323
|
+
) -> Capitalizations:
|
|
325
324
|
"""Get the market cap, TEV, and shares outstanding for a company."""
|
|
326
325
|
url = (
|
|
327
326
|
f"{self.url_base}market_cap/{company_id}/"
|
|
328
327
|
f"{start_date if start_date is not None else 'none'}/"
|
|
329
328
|
f"{end_date if end_date is not None else 'none'}"
|
|
330
329
|
)
|
|
331
|
-
return self.fetch(url)
|
|
330
|
+
return Capitalizations.model_validate(self.fetch(url))
|
|
332
331
|
|
|
333
332
|
def fetch_segments(
|
|
334
333
|
self,
|
kfinance/kfinance.py
CHANGED
|
@@ -15,33 +15,28 @@ import webbrowser
|
|
|
15
15
|
import google.ai.generativelanguage_v1beta.types as gapic
|
|
16
16
|
from langchain_core.utils.function_calling import convert_to_openai_tool
|
|
17
17
|
from langchain_google_genai._function_utils import convert_to_genai_function_declarations
|
|
18
|
-
import numpy as np
|
|
19
18
|
import pandas as pd
|
|
20
19
|
from PIL.Image import Image, open as image_open
|
|
21
20
|
|
|
22
|
-
from .batch_request_handling import add_methods_of_singular_class_to_iterable_class
|
|
23
|
-
from .
|
|
24
|
-
HistoryMetadata,
|
|
25
|
-
IdentificationTriple,
|
|
26
|
-
IndustryClassification,
|
|
27
|
-
LatestPeriods,
|
|
28
|
-
Periodicity,
|
|
29
|
-
YearAndQuarter,
|
|
30
|
-
)
|
|
31
|
-
from .fetch import (
|
|
21
|
+
from kfinance.batch_request_handling import add_methods_of_singular_class_to_iterable_class
|
|
22
|
+
from kfinance.fetch import (
|
|
32
23
|
DEFAULT_API_HOST,
|
|
33
24
|
DEFAULT_API_VERSION,
|
|
34
25
|
DEFAULT_OKTA_AUTH_SERVER,
|
|
35
26
|
DEFAULT_OKTA_HOST,
|
|
36
27
|
KFinanceApiClient,
|
|
37
28
|
)
|
|
38
|
-
from .meta_classes import (
|
|
29
|
+
from kfinance.meta_classes import (
|
|
39
30
|
CompanyFunctionsMetaClass,
|
|
40
31
|
DelegatedCompanyFunctionsMetaClass,
|
|
41
32
|
)
|
|
42
|
-
from .
|
|
43
|
-
from .
|
|
44
|
-
from .
|
|
33
|
+
from kfinance.models.date_and_period_models import LatestPeriods, Periodicity, YearAndQuarter
|
|
34
|
+
from kfinance.models.id_models import IdentificationTriple
|
|
35
|
+
from kfinance.models.industry_models import IndustryClassification
|
|
36
|
+
from kfinance.models.price_models import HistoryMetadata, PriceHistory
|
|
37
|
+
from kfinance.prompt import PROMPT
|
|
38
|
+
from kfinance.pydantic_models import TranscriptComponent
|
|
39
|
+
from kfinance.server_thread import ServerThread
|
|
45
40
|
|
|
46
41
|
|
|
47
42
|
if TYPE_CHECKING:
|
|
@@ -140,36 +135,29 @@ class TradingItem:
|
|
|
140
135
|
adjusted: bool = True,
|
|
141
136
|
start_date: Optional[str] = None,
|
|
142
137
|
end_date: Optional[str] = None,
|
|
143
|
-
) ->
|
|
138
|
+
) -> PriceHistory:
|
|
144
139
|
"""Retrieves the historical price data for a given asset over a specified date range.
|
|
145
140
|
|
|
146
141
|
:param periodicity: Determines the frequency of the historical data returned. Defaults to Periodicity.day.
|
|
147
142
|
:param Optional[bool] adjusted: Whether to retrieve adjusted prices that account for corporate actions such as dividends and splits, it defaults True
|
|
148
143
|
:param Optional[str] start_date: The start date for historical price retrieval in format "YYYY-MM-DD", default to None
|
|
149
144
|
:param Optional[str] end_date: The end date for historical price retrieval in format "YYYY-MM-DD", default to None
|
|
150
|
-
:return: A
|
|
151
|
-
:rtype:
|
|
145
|
+
:return: A PriceHistory containing historical price data including "open", "high", "low", "close", "volume" in type Money. The date value is a string that depends on the periodicity. If Periodicity.day, the Date index is the day in format "YYYY-MM-DD", eg "2024-05-13" If Periodicity.week, the Date index is the week number of the year in format "YYYY Week ##", eg "2024 Week 2" If Periodicity.month, the Date index is the month name of the year in format "<Month> YYYY", eg "January 2024". If Periodicity.year, the Date index is the year in format "YYYY", eg "2024".
|
|
146
|
+
:rtype: PriceHistory
|
|
152
147
|
"""
|
|
153
148
|
if start_date and end_date:
|
|
154
149
|
if (
|
|
155
150
|
datetime.strptime(start_date, "%Y-%m-%d").date()
|
|
156
151
|
> datetime.strptime(end_date, "%Y-%m-%d").date()
|
|
157
152
|
):
|
|
158
|
-
return
|
|
153
|
+
return PriceHistory(prices=[])
|
|
159
154
|
|
|
160
|
-
return (
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
end_date=end_date,
|
|
167
|
-
periodicity=periodicity,
|
|
168
|
-
)["prices"]
|
|
169
|
-
)
|
|
170
|
-
.set_index("date")
|
|
171
|
-
.apply(pd.to_numeric)
|
|
172
|
-
.replace(np.nan, None)
|
|
155
|
+
return self.kfinance_api_client.fetch_history(
|
|
156
|
+
trading_item_id=self.trading_item_id,
|
|
157
|
+
is_adjusted=adjusted,
|
|
158
|
+
start_date=start_date,
|
|
159
|
+
end_date=end_date,
|
|
160
|
+
periodicity=periodicity,
|
|
173
161
|
)
|
|
174
162
|
|
|
175
163
|
def price_chart(
|
|
@@ -653,83 +641,77 @@ class Company(CompanyFunctionsMetaClass):
|
|
|
653
641
|
return self._mergers_for_company
|
|
654
642
|
|
|
655
643
|
|
|
656
|
-
class
|
|
644
|
+
class ParticipantInMerger:
|
|
657
645
|
"""A Company that has been involved in a transaction is a company that may have been advised."""
|
|
658
646
|
|
|
659
647
|
def __init__(
|
|
660
|
-
self,
|
|
661
|
-
kfinance_api_client: KFinanceApiClient,
|
|
662
|
-
company_id: int,
|
|
663
|
-
transaction_id: int,
|
|
664
|
-
company_name: str | None = None,
|
|
648
|
+
self, kfinance_api_client: KFinanceApiClient, transaction_id: int, company: Company
|
|
665
649
|
):
|
|
666
650
|
"""Initialize the AdvisedCompany object
|
|
667
651
|
|
|
668
652
|
:param kfinance_api_client: The KFinanceApiClient used to fetch data
|
|
669
653
|
:type kfinance_api_client: KFinanceApiClient
|
|
670
|
-
:param company_id: The S&P Global CIQ Company Id
|
|
671
|
-
:type company_id: int
|
|
672
654
|
:param transaction_id: The S&P Global CIP Transaction Id
|
|
673
655
|
:type transaction_id: int
|
|
656
|
+
:param company: The company object
|
|
657
|
+
:type company: Company
|
|
674
658
|
"""
|
|
675
|
-
|
|
676
|
-
super().__init__(
|
|
677
|
-
kfinance_api_client=kfinance_api_client,
|
|
678
|
-
company_id=company_id,
|
|
679
|
-
company_name=company_name,
|
|
680
|
-
)
|
|
659
|
+
self.kfinance_api_client = kfinance_api_client
|
|
681
660
|
self.transaction_id = transaction_id
|
|
661
|
+
self._company = company
|
|
662
|
+
|
|
663
|
+
@property
|
|
664
|
+
def company(self) -> Company:
|
|
665
|
+
"""Get the specific Company object."""
|
|
666
|
+
return self._company
|
|
682
667
|
|
|
683
668
|
@property
|
|
684
|
-
def advisors(self) ->
|
|
669
|
+
def advisors(self) -> list[Advisor] | None:
|
|
685
670
|
"""Get the companies that advised this company during the current transaction."""
|
|
686
671
|
advisors = self.kfinance_api_client.fetch_advisors_for_company_in_merger(
|
|
687
|
-
transaction_id=self.transaction_id, advised_company_id=self.company_id
|
|
672
|
+
transaction_id=self.transaction_id, advised_company_id=self._company.company_id
|
|
688
673
|
)["advisors"]
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
kfinance_api_client=self.kfinance_api_client,
|
|
692
|
-
company_id=int(advisor["advisor_company_id"]),
|
|
693
|
-
company_name=str(advisor["advisor_company_name"]),
|
|
674
|
+
return [
|
|
675
|
+
Advisor(
|
|
694
676
|
advisor_type_name=str(advisor["advisor_type_name"]),
|
|
677
|
+
company=Company(
|
|
678
|
+
kfinance_api_client=self.kfinance_api_client,
|
|
679
|
+
company_id=int(advisor["advisor_company_id"]),
|
|
680
|
+
company_name=str(advisor["advisor_company_name"]),
|
|
681
|
+
),
|
|
695
682
|
)
|
|
696
683
|
for advisor in advisors
|
|
697
684
|
]
|
|
698
|
-
return Companies(kfinance_api_client=self.kfinance_api_client, companies=companies)
|
|
699
685
|
|
|
700
686
|
|
|
701
|
-
class
|
|
687
|
+
class Advisor:
|
|
702
688
|
"""A company that advised another company during a transaction."""
|
|
703
689
|
|
|
704
690
|
def __init__(
|
|
705
691
|
self,
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
advisor_type_name: str,
|
|
709
|
-
company_name: str | None = None,
|
|
692
|
+
advisor_type_name: str | None,
|
|
693
|
+
company: Company,
|
|
710
694
|
):
|
|
711
695
|
"""Initialize the AdvisorCompany object
|
|
712
696
|
|
|
713
|
-
:param
|
|
714
|
-
:type
|
|
715
|
-
:param company_id: The S&P Global CIQ Company Id
|
|
716
|
-
:type company_id: int
|
|
697
|
+
:param company: The company that advised
|
|
698
|
+
:type company: Company
|
|
717
699
|
:param advisor_type_name: The type of the advisor company
|
|
718
700
|
:type advisor_type_name: str
|
|
719
701
|
"""
|
|
720
|
-
|
|
721
|
-
super().__init__(
|
|
722
|
-
kfinance_api_client=kfinance_api_client,
|
|
723
|
-
company_id=company_id,
|
|
724
|
-
company_name=company_name,
|
|
725
|
-
)
|
|
726
702
|
self._advisor_type_name = advisor_type_name
|
|
703
|
+
self._company = company
|
|
727
704
|
|
|
728
705
|
@property
|
|
729
706
|
def advisor_type_name(self) -> str | None:
|
|
730
707
|
"""When this company advised another during a transaction, get the advisor type name."""
|
|
731
708
|
return self._advisor_type_name
|
|
732
709
|
|
|
710
|
+
@property
|
|
711
|
+
def company(self) -> Company:
|
|
712
|
+
"""Get the Company object."""
|
|
713
|
+
return self._company
|
|
714
|
+
|
|
733
715
|
|
|
734
716
|
class Security:
|
|
735
717
|
"""Security class
|
|
@@ -815,7 +797,11 @@ class Security:
|
|
|
815
797
|
"trading_items"
|
|
816
798
|
]
|
|
817
799
|
self._trading_items = TradingItems(
|
|
818
|
-
kfinance_api_client=self.kfinance_api_client,
|
|
800
|
+
kfinance_api_client=self.kfinance_api_client,
|
|
801
|
+
trading_items=[
|
|
802
|
+
TradingItem(kfinance_api_client=self.kfinance_api_client, trading_item_id=tii)
|
|
803
|
+
for tii in trading_item_ids
|
|
804
|
+
],
|
|
819
805
|
)
|
|
820
806
|
return self._trading_items
|
|
821
807
|
|
|
@@ -1194,7 +1180,7 @@ class Ticker(DelegatedCompanyFunctionsMetaClass):
|
|
|
1194
1180
|
adjusted: bool = True,
|
|
1195
1181
|
start_date: Optional[str] = None,
|
|
1196
1182
|
end_date: Optional[str] = None,
|
|
1197
|
-
) ->
|
|
1183
|
+
) -> PriceHistory:
|
|
1198
1184
|
"""Retrieves the historical price data for a given asset over a specified date range.
|
|
1199
1185
|
|
|
1200
1186
|
:param periodicity: Determines the frequency of the historical data returned. Defaults to Periodicity.day.
|
|
@@ -1205,8 +1191,8 @@ class Ticker(DelegatedCompanyFunctionsMetaClass):
|
|
|
1205
1191
|
:type start_date: str, optional
|
|
1206
1192
|
:param end_date: The end date for historical price retrieval in format "YYYY-MM-DD", default to None
|
|
1207
1193
|
:type end_date: str, optional
|
|
1208
|
-
:return: A
|
|
1209
|
-
:rtype:
|
|
1194
|
+
:return: A PriceHistory containing historical price data including "open", "high", "low", "close", "volume" in type Money. The date value is a string that depends on the periodicity. If Periodicity.day, the Date index is the day in format "YYYY-MM-DD", eg "2024-05-13" If Periodicity.week, the Date index is the week number of the year in format "YYYY Week ##", eg "2024 Week 2" If Periodicity.month, the Date index is the month name of the year in format "<Month> YYYY", eg "January 2024". If Periodicity.year, the Date index is the year in format "YYYY", eg "2024".
|
|
1195
|
+
:rtype: PriceHistory
|
|
1210
1196
|
"""
|
|
1211
1197
|
return self.primary_trading_item.history(
|
|
1212
1198
|
periodicity,
|
|
@@ -1261,7 +1247,11 @@ class MergerOrAcquisition:
|
|
|
1261
1247
|
"""An object that represents a merger or an acquisition of a company."""
|
|
1262
1248
|
|
|
1263
1249
|
def __init__(
|
|
1264
|
-
self,
|
|
1250
|
+
self,
|
|
1251
|
+
kfinance_api_client: KFinanceApiClient,
|
|
1252
|
+
transaction_id: int,
|
|
1253
|
+
merger_title: str | None,
|
|
1254
|
+
closed_date: date | None,
|
|
1265
1255
|
) -> None:
|
|
1266
1256
|
"""MergerOrAcqusition initializer.
|
|
1267
1257
|
|
|
@@ -1272,6 +1262,7 @@ class MergerOrAcquisition:
|
|
|
1272
1262
|
self.kfinance_api_client = kfinance_api_client
|
|
1273
1263
|
self.transaction_id = transaction_id
|
|
1274
1264
|
self.merger_title = merger_title
|
|
1265
|
+
self.closed_date = closed_date
|
|
1275
1266
|
self._merger_info: dict | None = None
|
|
1276
1267
|
|
|
1277
1268
|
@property
|
|
@@ -1309,36 +1300,39 @@ class MergerOrAcquisition:
|
|
|
1309
1300
|
Each category is a single Company or a list of Companies.
|
|
1310
1301
|
"""
|
|
1311
1302
|
return {
|
|
1312
|
-
"target":
|
|
1303
|
+
"target": ParticipantInMerger(
|
|
1313
1304
|
kfinance_api_client=self.kfinance_api_client,
|
|
1314
|
-
company_id=self.merger_info["participants"]["target"]["company_id"],
|
|
1315
|
-
company_name=self.merger_info["participants"]["target"]["company_name"],
|
|
1316
1305
|
transaction_id=self.transaction_id,
|
|
1306
|
+
company=Company(
|
|
1307
|
+
kfinance_api_client=self.kfinance_api_client,
|
|
1308
|
+
company_id=self.merger_info["participants"]["target"]["company_id"],
|
|
1309
|
+
company_name=self.merger_info["participants"]["target"]["company_name"],
|
|
1310
|
+
),
|
|
1317
1311
|
),
|
|
1318
|
-
"buyers":
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1312
|
+
"buyers": [
|
|
1313
|
+
ParticipantInMerger(
|
|
1314
|
+
kfinance_api_client=self.kfinance_api_client,
|
|
1315
|
+
transaction_id=self.transaction_id,
|
|
1316
|
+
company=Company(
|
|
1322
1317
|
kfinance_api_client=self.kfinance_api_client,
|
|
1323
1318
|
company_id=company["company_id"],
|
|
1324
1319
|
company_name=company["company_name"],
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1320
|
+
),
|
|
1321
|
+
)
|
|
1322
|
+
for company in self.merger_info["participants"]["buyers"]
|
|
1323
|
+
],
|
|
1324
|
+
"sellers": [
|
|
1325
|
+
ParticipantInMerger(
|
|
1326
|
+
kfinance_api_client=self.kfinance_api_client,
|
|
1327
|
+
transaction_id=self.transaction_id,
|
|
1328
|
+
company=Company(
|
|
1334
1329
|
kfinance_api_client=self.kfinance_api_client,
|
|
1335
1330
|
company_id=company["company_id"],
|
|
1336
1331
|
company_name=company["company_name"],
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
),
|
|
1332
|
+
),
|
|
1333
|
+
)
|
|
1334
|
+
for company in self.merger_info["participants"]["sellers"]
|
|
1335
|
+
],
|
|
1342
1336
|
}
|
|
1343
1337
|
|
|
1344
1338
|
@property
|
|
@@ -1368,7 +1362,6 @@ class Companies(set):
|
|
|
1368
1362
|
self,
|
|
1369
1363
|
kfinance_api_client: KFinanceApiClient,
|
|
1370
1364
|
company_ids: Optional[Iterable[int]] = None,
|
|
1371
|
-
transaction_id: Optional[int] = None,
|
|
1372
1365
|
companies: Optional[Iterable[Company]] = None,
|
|
1373
1366
|
) -> None:
|
|
1374
1367
|
"""Initialize the Companies object
|
|
@@ -1377,8 +1370,6 @@ class Companies(set):
|
|
|
1377
1370
|
:type kfinance_api_client: KFinanceApiClient
|
|
1378
1371
|
:param company_ids: An iterable of S&P CIQ Company ids
|
|
1379
1372
|
:type company_ids: Iterable[int]
|
|
1380
|
-
:param transaction_id: If the companies were party to a transaction, the S&P CIQ Transaction Id
|
|
1381
|
-
:type transaction_id: Optional[int]
|
|
1382
1373
|
:param companies: If there's already an iterable of Company objects
|
|
1383
1374
|
:type companies: Iterable[Company]
|
|
1384
1375
|
"""
|
|
@@ -1386,23 +1377,13 @@ class Companies(set):
|
|
|
1386
1377
|
if companies is not None:
|
|
1387
1378
|
super().__init__(company for company in companies)
|
|
1388
1379
|
elif company_ids is not None:
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
company_id=company_id,
|
|
1394
|
-
transaction_id=transaction_id,
|
|
1395
|
-
)
|
|
1396
|
-
for company_id in company_ids
|
|
1397
|
-
)
|
|
1398
|
-
else:
|
|
1399
|
-
super().__init__(
|
|
1400
|
-
Company(
|
|
1401
|
-
kfinance_api_client=kfinance_api_client,
|
|
1402
|
-
company_id=company_id,
|
|
1403
|
-
)
|
|
1404
|
-
for company_id in company_ids
|
|
1380
|
+
super().__init__(
|
|
1381
|
+
Company(
|
|
1382
|
+
kfinance_api_client=kfinance_api_client,
|
|
1383
|
+
company_id=company_id,
|
|
1405
1384
|
)
|
|
1385
|
+
for company_id in company_ids
|
|
1386
|
+
)
|
|
1406
1387
|
|
|
1407
1388
|
|
|
1408
1389
|
@add_methods_of_singular_class_to_iterable_class(Security)
|
|
@@ -1425,20 +1406,17 @@ class TradingItems(set):
|
|
|
1425
1406
|
"""Base class for representing a set of Trading Items"""
|
|
1426
1407
|
|
|
1427
1408
|
def __init__(
|
|
1428
|
-
self, kfinance_api_client: KFinanceApiClient,
|
|
1409
|
+
self, kfinance_api_client: KFinanceApiClient, trading_items: Iterable[TradingItem]
|
|
1429
1410
|
) -> None:
|
|
1430
1411
|
"""Initialize the Trading Items
|
|
1431
1412
|
|
|
1432
1413
|
:param kfinance_api_client: The KFinanceApiClient used to fetch data
|
|
1433
1414
|
:type kfinance_api_client: KFinanceApiClient
|
|
1434
|
-
:param
|
|
1435
|
-
:type
|
|
1415
|
+
:param trading_items: An iterable of TradingItem
|
|
1416
|
+
:type trading_items: Iterable[TradingItem]
|
|
1436
1417
|
"""
|
|
1437
1418
|
self.kfinance_api_client = kfinance_api_client
|
|
1438
|
-
super().__init__(
|
|
1439
|
-
TradingItem(kfinance_api_client, trading_item_id)
|
|
1440
|
-
for trading_item_id in trading_item_ids
|
|
1441
|
-
)
|
|
1419
|
+
super().__init__(trading_items)
|
|
1442
1420
|
|
|
1443
1421
|
|
|
1444
1422
|
@add_methods_of_singular_class_to_iterable_class(Ticker)
|
|
@@ -1515,7 +1493,14 @@ class Tickers(set):
|
|
|
1515
1493
|
:rtype: TradingItems
|
|
1516
1494
|
"""
|
|
1517
1495
|
return TradingItems(
|
|
1518
|
-
self.kfinance_api_client,
|
|
1496
|
+
self.kfinance_api_client,
|
|
1497
|
+
[
|
|
1498
|
+
TradingItem(
|
|
1499
|
+
kfinance_api_client=self.kfinance_api_client,
|
|
1500
|
+
trading_item_id=ticker.trading_item_id,
|
|
1501
|
+
)
|
|
1502
|
+
for ticker in self.__iter__()
|
|
1503
|
+
],
|
|
1519
1504
|
)
|
|
1520
1505
|
|
|
1521
1506
|
|
|
@@ -1537,6 +1522,7 @@ class MergersAndAcquisitions(set):
|
|
|
1537
1522
|
kfinance_api_client=kfinance_api_client,
|
|
1538
1523
|
transaction_id=id_and_title["transaction_id"],
|
|
1539
1524
|
merger_title=id_and_title["merger_title"],
|
|
1525
|
+
closed_date=id_and_title["closed_date"],
|
|
1540
1526
|
)
|
|
1541
1527
|
for id_and_title in ids_and_titles
|
|
1542
1528
|
)
|