kensho-kfinance 2.7.0__py3-none-any.whl → 2.8.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.8.0.dist-info}/METADATA +1 -1
- kensho_kfinance-2.8.0.dist-info/RECORD +70 -0
- kfinance/CHANGELOG.md +3 -0
- kfinance/decimal_with_unit.py +78 -0
- kfinance/fetch.py +15 -16
- kfinance/kfinance.py +40 -44
- 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 +29 -23
- kfinance/tests/test_tools.py +35 -11
- kfinance/tool_calling/get_advisors_for_company_in_transaction_from_identifier.py +1 -1
- 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 +1 -1
- kfinance/tool_calling/get_mergers_from_identifier.py +1 -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.8.0.dist-info}/WHEEL +0 -0
- {kensho_kfinance-2.7.0.dist-info → kensho_kfinance-2.8.0.dist-info}/licenses/AUTHORS.md +0 -0
- {kensho_kfinance-2.7.0.dist-info → kensho_kfinance-2.8.0.dist-info}/licenses/LICENSE +0 -0
- {kensho_kfinance-2.7.0.dist-info → kensho_kfinance-2.8.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.8.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
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
kensho_kfinance-2.8.0.dist-info/licenses/AUTHORS.md,sha256=0h9ClbI0pu1oKj1M28ROUsaxrbZg-6ukQGl6X4y9noI,68
|
|
2
|
+
kensho_kfinance-2.8.0.dist-info/licenses/LICENSE,sha256=bsY4blvSgq6o0FMQ3RXa2NCgco--nHCCchLXzxr6kms,83
|
|
3
|
+
kfinance/CHANGELOG.md,sha256=JaR2dyTzhmue8dt6LVbt8ckSFTuqnJuqP5mYaePs85A,2245
|
|
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=qa8oOFBayT2zIdvDh4Gizg1aF_PNWJrsvoAd1q5Oflg,73225
|
|
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=xvvCLYZzL60ATcl_e94czeUS6YcsWE3LumbDKAyCfWM,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=Fd5iYy-Ofzil3xJr5hLflsvvNVwoWfEDDiRZb_-LVKc,41082
|
|
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=kL8cnTIsnPBAZcTriKJKL2uFfFFNUuV0PMKVitD2MXQ,1630
|
|
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=zzAg79hbUmwcdg-hJ2wGjcTQ_YHWXw85eOt48lmlZLs,3165
|
|
58
|
+
kfinance/tool_calling/get_mergers_from_identifier.py,sha256=2w8bA-FQMdbmleuoWTbMPHK1K4IRxwHciZQPeQGGIVw,1725
|
|
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.8.0.dist-info/METADATA,sha256=XiOVL58W7LuXqyo76ASS9XESdvFUCHVYHXigOl11YTY,6197
|
|
68
|
+
kensho_kfinance-2.8.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
69
|
+
kensho_kfinance-2.8.0.dist-info/top_level.txt,sha256=kT_kNwVhfQoOAecY8W7uYah5xaHMoHoAdBIvXh6DaKM,9
|
|
70
|
+
kensho_kfinance-2.8.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(
|
|
@@ -815,7 +803,11 @@ class Security:
|
|
|
815
803
|
"trading_items"
|
|
816
804
|
]
|
|
817
805
|
self._trading_items = TradingItems(
|
|
818
|
-
kfinance_api_client=self.kfinance_api_client,
|
|
806
|
+
kfinance_api_client=self.kfinance_api_client,
|
|
807
|
+
trading_items=[
|
|
808
|
+
TradingItem(kfinance_api_client=self.kfinance_api_client, trading_item_id=tii)
|
|
809
|
+
for tii in trading_item_ids
|
|
810
|
+
],
|
|
819
811
|
)
|
|
820
812
|
return self._trading_items
|
|
821
813
|
|
|
@@ -1194,7 +1186,7 @@ class Ticker(DelegatedCompanyFunctionsMetaClass):
|
|
|
1194
1186
|
adjusted: bool = True,
|
|
1195
1187
|
start_date: Optional[str] = None,
|
|
1196
1188
|
end_date: Optional[str] = None,
|
|
1197
|
-
) ->
|
|
1189
|
+
) -> PriceHistory:
|
|
1198
1190
|
"""Retrieves the historical price data for a given asset over a specified date range.
|
|
1199
1191
|
|
|
1200
1192
|
:param periodicity: Determines the frequency of the historical data returned. Defaults to Periodicity.day.
|
|
@@ -1205,8 +1197,8 @@ class Ticker(DelegatedCompanyFunctionsMetaClass):
|
|
|
1205
1197
|
:type start_date: str, optional
|
|
1206
1198
|
:param end_date: The end date for historical price retrieval in format "YYYY-MM-DD", default to None
|
|
1207
1199
|
:type end_date: str, optional
|
|
1208
|
-
:return: A
|
|
1209
|
-
:rtype:
|
|
1200
|
+
: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".
|
|
1201
|
+
:rtype: PriceHistory
|
|
1210
1202
|
"""
|
|
1211
1203
|
return self.primary_trading_item.history(
|
|
1212
1204
|
periodicity,
|
|
@@ -1425,20 +1417,17 @@ class TradingItems(set):
|
|
|
1425
1417
|
"""Base class for representing a set of Trading Items"""
|
|
1426
1418
|
|
|
1427
1419
|
def __init__(
|
|
1428
|
-
self, kfinance_api_client: KFinanceApiClient,
|
|
1420
|
+
self, kfinance_api_client: KFinanceApiClient, trading_items: Iterable[TradingItem]
|
|
1429
1421
|
) -> None:
|
|
1430
1422
|
"""Initialize the Trading Items
|
|
1431
1423
|
|
|
1432
1424
|
:param kfinance_api_client: The KFinanceApiClient used to fetch data
|
|
1433
1425
|
:type kfinance_api_client: KFinanceApiClient
|
|
1434
|
-
:param
|
|
1435
|
-
:type
|
|
1426
|
+
:param trading_items: An iterable of TradingItem
|
|
1427
|
+
:type trading_items: Iterable[TradingItem]
|
|
1436
1428
|
"""
|
|
1437
1429
|
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
|
-
)
|
|
1430
|
+
super().__init__(trading_items)
|
|
1442
1431
|
|
|
1443
1432
|
|
|
1444
1433
|
@add_methods_of_singular_class_to_iterable_class(Ticker)
|
|
@@ -1515,7 +1504,14 @@ class Tickers(set):
|
|
|
1515
1504
|
:rtype: TradingItems
|
|
1516
1505
|
"""
|
|
1517
1506
|
return TradingItems(
|
|
1518
|
-
self.kfinance_api_client,
|
|
1507
|
+
self.kfinance_api_client,
|
|
1508
|
+
[
|
|
1509
|
+
TradingItem(
|
|
1510
|
+
kfinance_api_client=self.kfinance_api_client,
|
|
1511
|
+
trading_item_id=ticker.trading_item_id,
|
|
1512
|
+
)
|
|
1513
|
+
for ticker in self.__iter__()
|
|
1514
|
+
],
|
|
1519
1515
|
)
|
|
1520
1516
|
|
|
1521
1517
|
|
kfinance/meta_classes.py
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
2
|
from datetime import datetime
|
|
3
3
|
import logging
|
|
4
|
-
from typing import TYPE_CHECKING, Any, Callable,
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional
|
|
5
5
|
|
|
6
6
|
from cachetools import LRUCache, cached
|
|
7
7
|
import numpy as np
|
|
8
8
|
import pandas as pd
|
|
9
9
|
|
|
10
|
-
from .constants import (
|
|
11
|
-
LINE_ITEMS,
|
|
12
|
-
BusinessRelationshipType,
|
|
13
|
-
CompetitorSource,
|
|
14
|
-
PeriodType,
|
|
15
|
-
SegmentType,
|
|
16
|
-
)
|
|
17
10
|
from .fetch import KFinanceApiClient
|
|
11
|
+
from .models.business_relationship_models import BusinessRelationshipType
|
|
12
|
+
from .models.capitalization_models import Capitalization
|
|
13
|
+
from .models.competitor_models import CompetitorSource
|
|
14
|
+
from .models.date_and_period_models import PeriodType
|
|
15
|
+
from .models.line_item_models import LINE_ITEMS
|
|
16
|
+
from .models.segment_models import SegmentType
|
|
18
17
|
from .pydantic_models import RelationshipResponse
|
|
19
18
|
|
|
20
19
|
|
|
@@ -262,73 +261,75 @@ class CompanyFunctionsMetaClass:
|
|
|
262
261
|
self,
|
|
263
262
|
start_date: Optional[str] = None,
|
|
264
263
|
end_date: Optional[str] = None,
|
|
265
|
-
) ->
|
|
264
|
+
) -> dict:
|
|
266
265
|
"""Retrieves market caps for a company between start and end date.
|
|
267
266
|
|
|
268
267
|
:param start_date: The start date in format "YYYY-MM-DD", default to None
|
|
269
268
|
:type start_date: str, optional
|
|
270
269
|
:param end_date: The end date in format "YYYY-MM-DD", default to None
|
|
271
270
|
:type end_date: str, optional
|
|
272
|
-
:return: A
|
|
273
|
-
:rtype:
|
|
271
|
+
:return: A dict with market_cap
|
|
272
|
+
:rtype: dict
|
|
274
273
|
"""
|
|
275
274
|
|
|
276
275
|
return self._fetch_market_cap_tev_or_shares_outstanding(
|
|
277
|
-
|
|
276
|
+
capitalization_to_extract=Capitalization.market_cap,
|
|
277
|
+
start_date=start_date,
|
|
278
|
+
end_date=end_date,
|
|
278
279
|
)
|
|
279
280
|
|
|
280
281
|
def tev(
|
|
281
282
|
self,
|
|
282
283
|
start_date: Optional[str] = None,
|
|
283
284
|
end_date: Optional[str] = None,
|
|
284
|
-
) ->
|
|
285
|
+
) -> dict:
|
|
285
286
|
"""Retrieves TEV (total enterprise value) for a company between start and end date.
|
|
286
287
|
|
|
287
288
|
:param start_date: The start date in format "YYYY-MM-DD", default to None
|
|
288
289
|
:type start_date: str, optional
|
|
289
290
|
:param end_date: The end date in format "YYYY-MM-DD", default to None
|
|
290
291
|
:type end_date: str, optional
|
|
291
|
-
:return: A
|
|
292
|
-
:rtype:
|
|
292
|
+
:return: A dict with TEV
|
|
293
|
+
:rtype: dict
|
|
293
294
|
"""
|
|
294
295
|
|
|
295
296
|
return self._fetch_market_cap_tev_or_shares_outstanding(
|
|
296
|
-
|
|
297
|
+
capitalization_to_extract=Capitalization.tev, start_date=start_date, end_date=end_date
|
|
297
298
|
)
|
|
298
299
|
|
|
299
300
|
def shares_outstanding(
|
|
300
301
|
self,
|
|
301
302
|
start_date: Optional[str] = None,
|
|
302
303
|
end_date: Optional[str] = None,
|
|
303
|
-
) ->
|
|
304
|
+
) -> dict:
|
|
304
305
|
"""Retrieves shares outstanding for a company between start and end date.
|
|
305
306
|
|
|
306
307
|
:param start_date: The start date in format "YYYY-MM-DD", default to None
|
|
307
308
|
:type start_date: str, optional
|
|
308
309
|
:param end_date: The end date in format "YYYY-MM-DD", default to None
|
|
309
310
|
:type end_date: str, optional
|
|
310
|
-
:return: A
|
|
311
|
-
:rtype:
|
|
311
|
+
:return: A dict with outstanding shares
|
|
312
|
+
:rtype: dict
|
|
312
313
|
"""
|
|
313
314
|
|
|
314
315
|
return self._fetch_market_cap_tev_or_shares_outstanding(
|
|
315
|
-
|
|
316
|
+
capitalization_to_extract=Capitalization.shares_outstanding,
|
|
317
|
+
start_date=start_date,
|
|
318
|
+
end_date=end_date,
|
|
316
319
|
)
|
|
317
320
|
|
|
318
321
|
def _fetch_market_cap_tev_or_shares_outstanding(
|
|
319
322
|
self,
|
|
320
|
-
|
|
323
|
+
capitalization_to_extract: Capitalization,
|
|
321
324
|
start_date: str | None,
|
|
322
325
|
end_date: str | None,
|
|
323
|
-
) ->
|
|
326
|
+
) -> dict:
|
|
324
327
|
"""Helper function to fetch market cap, TEV, and shares outstanding."""
|
|
325
328
|
|
|
326
|
-
|
|
327
|
-
self.
|
|
328
|
-
company_id=self.company_id, start_date=start_date, end_date=end_date
|
|
329
|
-
)["market_caps"]
|
|
329
|
+
capitalizations = self.kfinance_api_client.fetch_market_caps_tevs_and_shares_outstanding(
|
|
330
|
+
company_id=self.company_id, start_date=start_date, end_date=end_date
|
|
330
331
|
)
|
|
331
|
-
return
|
|
332
|
+
return capitalizations.jsonify_single_attribute(capitalization_to_extract)
|
|
332
333
|
|
|
333
334
|
def _segments(
|
|
334
335
|
self,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from strenum import StrEnum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class BusinessRelationshipType(StrEnum):
|
|
5
|
+
"""The type of business relationship"""
|
|
6
|
+
|
|
7
|
+
supplier = "supplier"
|
|
8
|
+
customer = "customer"
|
|
9
|
+
distributor = "distributor"
|
|
10
|
+
franchisor = "franchisor"
|
|
11
|
+
franchisee = "franchisee"
|
|
12
|
+
landlord = "landlord"
|
|
13
|
+
tenant = "tenant"
|
|
14
|
+
licensor = "licensor"
|
|
15
|
+
licensee = "licensee"
|
|
16
|
+
creditor = "creditor"
|
|
17
|
+
borrower = "borrower"
|
|
18
|
+
lessor = "lessor"
|
|
19
|
+
lessee = "lessee"
|
|
20
|
+
strategic_alliance = "strategic_alliance"
|
|
21
|
+
investor_relations_firm = "investor_relations_firm"
|
|
22
|
+
investor_relations_client = "investor_relations_client"
|
|
23
|
+
transfer_agent = "transfer_agent"
|
|
24
|
+
transfer_agent_client = "transfer_agent_client"
|
|
25
|
+
vendor = "vendor"
|
|
26
|
+
client_services = "client_services"
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
2
|
+
from datetime import date
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field, model_validator
|
|
6
|
+
from strenum import StrEnum
|
|
7
|
+
|
|
8
|
+
from kfinance.decimal_with_unit import Money, Shares
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Capitalization(StrEnum):
|
|
12
|
+
"""The capitalization type"""
|
|
13
|
+
|
|
14
|
+
market_cap = "market_cap"
|
|
15
|
+
tev = "tev"
|
|
16
|
+
shares_outstanding = "shares_outstanding"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DailyCapitalization(BaseModel):
|
|
20
|
+
"""DailyCapitalization represents market cap, TEV, and shares outstanding for a day"""
|
|
21
|
+
|
|
22
|
+
date: date
|
|
23
|
+
market_cap: Money
|
|
24
|
+
tev: Money
|
|
25
|
+
shares_outstanding: Shares
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Capitalizations(BaseModel):
|
|
29
|
+
"""Capitalizations represents market cap, TEV, and shares outstanding for a date range"""
|
|
30
|
+
|
|
31
|
+
capitalizations: list[DailyCapitalization] = Field(validation_alias="market_caps")
|
|
32
|
+
|
|
33
|
+
@model_validator(mode="before")
|
|
34
|
+
@classmethod
|
|
35
|
+
def inject_currency_into_data(cls, data: Any) -> Any:
|
|
36
|
+
"""Inject the currency into each market_cap and TEV.
|
|
37
|
+
|
|
38
|
+
The capitalization response only includes the currency as a top level element.
|
|
39
|
+
However, the capitalizations model expects the unit to be included with each market cap
|
|
40
|
+
and tev.
|
|
41
|
+
Before:
|
|
42
|
+
"market_caps": [
|
|
43
|
+
{
|
|
44
|
+
"date": "2024-06-24",
|
|
45
|
+
"market_cap": "139231113000.000000",
|
|
46
|
+
"tev": "153942113000.000000",
|
|
47
|
+
"shares_outstanding": 312900000
|
|
48
|
+
},
|
|
49
|
+
]
|
|
50
|
+
After:
|
|
51
|
+
"market_caps": [
|
|
52
|
+
{
|
|
53
|
+
"date": "2024-06-24",
|
|
54
|
+
"market_cap": {"value": "139231113000.000000", "unit": "USD"},
|
|
55
|
+
"tev": {"value": "153942113000.000000", "unit": "USD"},
|
|
56
|
+
"shares_outstanding": 312900000
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
Note: shares_outstanding does not need the unit injected because the Shares class
|
|
60
|
+
already has "Shares" encoded. However, currencies differ between companies,
|
|
61
|
+
so we need to inject that information.
|
|
62
|
+
"""
|
|
63
|
+
if isinstance(data, dict) and "currency" in data:
|
|
64
|
+
data = deepcopy(data)
|
|
65
|
+
currency = data["currency"]
|
|
66
|
+
for capitalization in data["market_caps"]:
|
|
67
|
+
for key in ["market_cap", "tev"]:
|
|
68
|
+
capitalization[key] = dict(unit=currency, value=capitalization[key])
|
|
69
|
+
return data
|
|
70
|
+
|
|
71
|
+
def jsonify_single_attribute(self, capitalization_to_extract: Capitalization) -> dict:
|
|
72
|
+
"""Return a json representation of a single attribute like "market_cap".
|
|
73
|
+
|
|
74
|
+
Example response:
|
|
75
|
+
{
|
|
76
|
+
"market_cap": [
|
|
77
|
+
{'2024-06-24': {'unit': 'USD', 'value': '139231113000.00'}},
|
|
78
|
+
{'2024-06-25': {'unit': 'USD', 'value': '140423262000.00'}}
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
capitalizations = []
|
|
85
|
+
for capitalization in self.capitalizations:
|
|
86
|
+
attribute_val = getattr(capitalization, capitalization_to_extract.value)
|
|
87
|
+
capitalizations.append(
|
|
88
|
+
{capitalization.date.isoformat(): attribute_val.model_dump(mode="json")}
|
|
89
|
+
)
|
|
90
|
+
return {capitalization_to_extract: capitalizations}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from strenum import StrEnum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class CompetitorSource(StrEnum):
|
|
5
|
+
"""The source type of the competitor information: 'filing' (from SEC filings), 'key_dev' (from key developments), 'contact' (from contact relationships), 'third_party' (from third-party sources), 'self_identified' (self-identified), 'named_by_competitor' (from competitor's perspective)."""
|
|
6
|
+
|
|
7
|
+
all = "all"
|
|
8
|
+
filing = "filing"
|
|
9
|
+
key_dev = "key_dev"
|
|
10
|
+
contact = "contact"
|
|
11
|
+
third_party = "third_party"
|
|
12
|
+
self_identified = "self_identified"
|
|
13
|
+
named_by_competitor = "named_by_competitor"
|