kensho-kfinance 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kensho-kfinance might be problematic. Click here for more details.
- kensho_kfinance-1.0.0.dist-info/AUTHORS.md +9 -0
- kensho_kfinance-1.0.0.dist-info/LICENSE +1 -0
- kensho_kfinance-1.0.0.dist-info/METADATA +53 -0
- kensho_kfinance-1.0.0.dist-info/RECORD +21 -0
- kensho_kfinance-1.0.0.dist-info/WHEEL +5 -0
- kensho_kfinance-1.0.0.dist-info/top_level.txt +1 -0
- kfinance/CHANGELOG.md +10 -0
- kfinance/__init__.py +0 -0
- kfinance/constants.py +1689 -0
- kfinance/fetch.py +374 -0
- kfinance/kfinance.py +1224 -0
- kfinance/llm_tools.py +688 -0
- kfinance/meta_classes.py +367 -0
- kfinance/prompt.py +526 -0
- kfinance/py.typed +0 -0
- kfinance/server_thread.py +62 -0
- kfinance/tests/__init__.py +0 -0
- kfinance/tests/test_fetch.py +241 -0
- kfinance/tests/test_objects.py +551 -0
- kfinance/tool_schemas.py +132 -0
- kfinance/version.py +21 -0
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
from datetime import datetime, timezone
|
|
2
|
+
from io import BytesIO
|
|
3
|
+
import re
|
|
4
|
+
from typing import Optional
|
|
5
|
+
from unittest import TestCase
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
import pandas as pd
|
|
9
|
+
from PIL.Image import open as image_open
|
|
10
|
+
|
|
11
|
+
from kfinance.kfinance import Company, Security, Ticker, TradingItem
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
msft_company_id = "21835"
|
|
15
|
+
msft_security_id = "2630412"
|
|
16
|
+
msft_isin = "US5949181045"
|
|
17
|
+
msft_cusip = "594918104"
|
|
18
|
+
msft_trading_item_id = "2630413"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
MOCK_TRADING_ITEM_DB = {
|
|
22
|
+
msft_trading_item_id: {
|
|
23
|
+
"metadata": {
|
|
24
|
+
"currency": "USD",
|
|
25
|
+
"symbol": "MSFT",
|
|
26
|
+
"exchange_name": "NasdaqGS",
|
|
27
|
+
"instrument_type": "Equity",
|
|
28
|
+
"first_trade_date": "1986-03-13",
|
|
29
|
+
},
|
|
30
|
+
"price_chart": {
|
|
31
|
+
"2020-01-01": {
|
|
32
|
+
"2021-01-01": b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\x0cIDATx\x9cc\xf8\xcf\xc0\x00\x00\x03"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
MOCK_COMPANY_DB = {
|
|
40
|
+
msft_company_id: {
|
|
41
|
+
"info": {
|
|
42
|
+
"name": "Microsoft Corporation",
|
|
43
|
+
"status": "Operating",
|
|
44
|
+
"type": "Public Company",
|
|
45
|
+
"simple_industry": "Software",
|
|
46
|
+
"number_of_employees": "228000.0000",
|
|
47
|
+
"founding_date": "1975-01-01",
|
|
48
|
+
"webpage": "www.microsoft.com",
|
|
49
|
+
"address": "One Microsoft Way",
|
|
50
|
+
"city": "Redmond",
|
|
51
|
+
"zip_code": "98052-6399",
|
|
52
|
+
"state": "Washington",
|
|
53
|
+
"country": "United States",
|
|
54
|
+
"iso_country": "USA",
|
|
55
|
+
},
|
|
56
|
+
"earnings_call_dates": {"earnings": ["2004-07-22T21:30:00"]},
|
|
57
|
+
"statements": {
|
|
58
|
+
"income_statement": {
|
|
59
|
+
"statements": {
|
|
60
|
+
"2019": {
|
|
61
|
+
"Revenues": "125843000000.000000",
|
|
62
|
+
"Total Revenues": "125843000000.000000",
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"line_items": {
|
|
68
|
+
"revenue": {
|
|
69
|
+
"line_item": {
|
|
70
|
+
"2019": "125843000000.000000",
|
|
71
|
+
"2020": "143015000000.000000",
|
|
72
|
+
"2021": "168088000000.000000",
|
|
73
|
+
"2022": "198270000000.000000",
|
|
74
|
+
"2023": "211915000000.000000",
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
MOCK_SECURITY_DB = {msft_security_id: {"isin": msft_isin, "cusip": msft_cusip}}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
MOCK_TICKER_DB = {
|
|
86
|
+
"MSFT": {
|
|
87
|
+
"company_id": msft_company_id,
|
|
88
|
+
"security_id": msft_security_id,
|
|
89
|
+
"trading_item_id": msft_trading_item_id,
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
MOCK_ISIN_DB = {
|
|
94
|
+
msft_isin: {
|
|
95
|
+
"company_id": msft_company_id,
|
|
96
|
+
"security_id": msft_security_id,
|
|
97
|
+
"trading_item_id": msft_trading_item_id,
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
MOCK_CUSIP_DB = {
|
|
102
|
+
msft_cusip: {
|
|
103
|
+
"company_id": msft_company_id,
|
|
104
|
+
"security_id": msft_security_id,
|
|
105
|
+
"trading_item_id": msft_trading_item_id,
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class MockKFinanceApiClient:
|
|
111
|
+
def __init__(self):
|
|
112
|
+
"""Create a mock kfinance api client"""
|
|
113
|
+
pass
|
|
114
|
+
|
|
115
|
+
def fetch_id_triple(self, identifier: int | str, exchange_code: Optional[str] = None) -> dict:
|
|
116
|
+
"""Get the ID triple from ticker."""
|
|
117
|
+
if re.match("^[a-zA-Z]{2}[a-zA-Z0-9]{9}[0-9]{1}$", str(identifier)):
|
|
118
|
+
return MOCK_ISIN_DB[identifier]
|
|
119
|
+
elif re.match("^[a-zA-Z0-9]{9}$", str(identifier)):
|
|
120
|
+
return MOCK_CUSIP_DB[identifier]
|
|
121
|
+
else:
|
|
122
|
+
return MOCK_TICKER_DB[identifier]
|
|
123
|
+
|
|
124
|
+
def fetch_isin(self, security_id: int) -> dict:
|
|
125
|
+
"""Get the ISIN."""
|
|
126
|
+
return {"isin": MOCK_SECURITY_DB[security_id]["isin"]}
|
|
127
|
+
|
|
128
|
+
def fetch_cusip(self, security_id: int) -> dict:
|
|
129
|
+
"""Get the CUSIP."""
|
|
130
|
+
return {"cusip": MOCK_SECURITY_DB[security_id]["cusip"]}
|
|
131
|
+
|
|
132
|
+
def fetch_history_metadata(self, trading_item_id):
|
|
133
|
+
"""Get history metadata"""
|
|
134
|
+
return MOCK_TRADING_ITEM_DB[trading_item_id]["metadata"].copy()
|
|
135
|
+
|
|
136
|
+
def fetch_price_chart(
|
|
137
|
+
self, trading_item_id, is_adjusted, start_date, end_date, periodicity
|
|
138
|
+
) -> bytes:
|
|
139
|
+
"""Get price chart"""
|
|
140
|
+
return MOCK_TRADING_ITEM_DB[trading_item_id]["price_chart"][start_date][end_date]
|
|
141
|
+
|
|
142
|
+
def fetch_info(self, company_id: int) -> dict:
|
|
143
|
+
"""Get info"""
|
|
144
|
+
return MOCK_COMPANY_DB[company_id]["info"]
|
|
145
|
+
|
|
146
|
+
def fetch_earnings_dates(self, company_id: int):
|
|
147
|
+
"""Get the earnings dates"""
|
|
148
|
+
return MOCK_COMPANY_DB[company_id]["earnings_call_dates"]
|
|
149
|
+
|
|
150
|
+
def fetch_statement(
|
|
151
|
+
self,
|
|
152
|
+
company_id,
|
|
153
|
+
statement_type,
|
|
154
|
+
period_type,
|
|
155
|
+
start_year,
|
|
156
|
+
end_year,
|
|
157
|
+
start_quarter,
|
|
158
|
+
end_quarter,
|
|
159
|
+
):
|
|
160
|
+
"""Get a statement"""
|
|
161
|
+
return MOCK_COMPANY_DB[company_id]["statements"][statement_type]
|
|
162
|
+
|
|
163
|
+
def fetch_line_item(
|
|
164
|
+
self, company_id, line_item, period_type, start_year, end_year, start_quarter, end_quarter
|
|
165
|
+
):
|
|
166
|
+
"""Get a statement"""
|
|
167
|
+
return MOCK_COMPANY_DB[company_id]["line_items"][line_item]
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class TestTradingItem(TestCase):
|
|
171
|
+
def setUp(self):
|
|
172
|
+
"""setup tests"""
|
|
173
|
+
self.kfinance_api_client = MockKFinanceApiClient()
|
|
174
|
+
self.msft_trading_item_from_id = TradingItem(self.kfinance_api_client, msft_trading_item_id)
|
|
175
|
+
self.msft_trading_item_from_ticker = TradingItem.from_ticker(
|
|
176
|
+
self.kfinance_api_client, "MSFT"
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
def test_trading_item_id(self) -> None:
|
|
180
|
+
"""test trading item id"""
|
|
181
|
+
expected_trading_item_id = MOCK_TICKER_DB["MSFT"]["trading_item_id"]
|
|
182
|
+
trading_item_id = self.msft_trading_item_from_id.trading_item_id
|
|
183
|
+
self.assertEqual(expected_trading_item_id, trading_item_id)
|
|
184
|
+
|
|
185
|
+
trading_item_id = self.msft_trading_item_from_ticker.trading_item_id
|
|
186
|
+
self.assertEqual(expected_trading_item_id, trading_item_id)
|
|
187
|
+
|
|
188
|
+
def test_history_metadata(self) -> None:
|
|
189
|
+
"""test history metadata"""
|
|
190
|
+
expected_history_metadata = MOCK_TRADING_ITEM_DB[msft_trading_item_id]["metadata"].copy()
|
|
191
|
+
expected_history_metadata["first_trade_date"] = datetime.strptime(
|
|
192
|
+
expected_history_metadata["first_trade_date"], "%Y-%m-%d"
|
|
193
|
+
).date()
|
|
194
|
+
expected_exchange_code = "NasdaqGS"
|
|
195
|
+
history_metadata = self.msft_trading_item_from_id.history_metadata
|
|
196
|
+
self.assertEqual(expected_history_metadata, history_metadata)
|
|
197
|
+
self.assertEqual(expected_exchange_code, self.msft_trading_item_from_id.exchange_code)
|
|
198
|
+
|
|
199
|
+
history_metadata = self.msft_trading_item_from_ticker.history_metadata
|
|
200
|
+
self.assertEqual(expected_history_metadata, history_metadata)
|
|
201
|
+
self.assertEqual(expected_exchange_code, self.msft_trading_item_from_ticker.exchange_code)
|
|
202
|
+
|
|
203
|
+
def test_price_chart(self):
|
|
204
|
+
"""test price chart"""
|
|
205
|
+
expected_price_chart = image_open(
|
|
206
|
+
BytesIO(
|
|
207
|
+
MOCK_TRADING_ITEM_DB[msft_trading_item_id]["price_chart"]["2020-01-01"][
|
|
208
|
+
"2021-01-01"
|
|
209
|
+
]
|
|
210
|
+
)
|
|
211
|
+
)
|
|
212
|
+
price_chart = self.msft_trading_item_from_id.price_chart(
|
|
213
|
+
start_date="2020-01-01", end_date="2021-01-01"
|
|
214
|
+
)
|
|
215
|
+
self.assertEqual(expected_price_chart, price_chart)
|
|
216
|
+
|
|
217
|
+
price_chart = self.msft_trading_item_from_ticker.price_chart(
|
|
218
|
+
start_date="2020-01-01", end_date="2021-01-01"
|
|
219
|
+
)
|
|
220
|
+
self.assertEqual(expected_price_chart, price_chart)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class TestCompany(TestCase):
|
|
224
|
+
def setUp(self):
|
|
225
|
+
"""setup tests"""
|
|
226
|
+
self.kfinance_api_client = MockKFinanceApiClient()
|
|
227
|
+
self.msft_company = Company(self.kfinance_api_client, msft_company_id)
|
|
228
|
+
|
|
229
|
+
def test_company_id(self) -> None:
|
|
230
|
+
"""test company id"""
|
|
231
|
+
expected_company_id = msft_company_id
|
|
232
|
+
company_id = self.msft_company.company_id
|
|
233
|
+
self.assertEqual(expected_company_id, company_id)
|
|
234
|
+
|
|
235
|
+
def test_info(self) -> None:
|
|
236
|
+
"""test info"""
|
|
237
|
+
expected_info = MOCK_COMPANY_DB[msft_company_id]["info"]
|
|
238
|
+
info = self.msft_company.info
|
|
239
|
+
self.assertEqual(expected_info, info)
|
|
240
|
+
|
|
241
|
+
def test_name(self) -> None:
|
|
242
|
+
"""test name"""
|
|
243
|
+
expected_name = MOCK_COMPANY_DB[msft_company_id]["info"]["name"]
|
|
244
|
+
name = self.msft_company.name
|
|
245
|
+
self.assertEqual(expected_name, name)
|
|
246
|
+
|
|
247
|
+
def test_founding_date(self) -> None:
|
|
248
|
+
"""test founding date"""
|
|
249
|
+
expected_founding_date = datetime.strptime(
|
|
250
|
+
MOCK_COMPANY_DB[msft_company_id]["info"]["founding_date"], "%Y-%m-%d"
|
|
251
|
+
).date()
|
|
252
|
+
founding_date = self.msft_company.founding_date
|
|
253
|
+
self.assertEqual(expected_founding_date, founding_date)
|
|
254
|
+
|
|
255
|
+
def test_earnings_call_datetimes(self) -> None:
|
|
256
|
+
"""test earnings call datetimes"""
|
|
257
|
+
expected_earnings_call_datetimes = [
|
|
258
|
+
datetime.fromisoformat(
|
|
259
|
+
MOCK_COMPANY_DB[msft_company_id]["earnings_call_dates"]["earnings"][0]
|
|
260
|
+
).replace(tzinfo=timezone.utc)
|
|
261
|
+
]
|
|
262
|
+
earnings_call_datetimes = self.msft_company.earnings_call_datetimes
|
|
263
|
+
self.assertEqual(expected_earnings_call_datetimes, earnings_call_datetimes)
|
|
264
|
+
|
|
265
|
+
def test_income_statement(self) -> None:
|
|
266
|
+
"""test income statement"""
|
|
267
|
+
expected_income_statement = (
|
|
268
|
+
pd.DataFrame(
|
|
269
|
+
MOCK_COMPANY_DB[msft_company_id]["statements"]["income_statement"]["statements"]
|
|
270
|
+
)
|
|
271
|
+
.apply(pd.to_numeric)
|
|
272
|
+
.replace(np.nan, None)
|
|
273
|
+
)
|
|
274
|
+
income_statement = self.msft_company.income_statement()
|
|
275
|
+
pd.testing.assert_frame_equal(expected_income_statement, income_statement)
|
|
276
|
+
|
|
277
|
+
def test_revenue(self) -> None:
|
|
278
|
+
"""test revenue"""
|
|
279
|
+
expected_revenue = (
|
|
280
|
+
pd.DataFrame(MOCK_COMPANY_DB[msft_company_id]["line_items"]["revenue"])
|
|
281
|
+
.transpose()
|
|
282
|
+
.apply(pd.to_numeric)
|
|
283
|
+
.replace(np.nan, None)
|
|
284
|
+
.set_index(pd.Index(["revenue"]))
|
|
285
|
+
)
|
|
286
|
+
revenue = self.msft_company.revenue()
|
|
287
|
+
pd.testing.assert_frame_equal(expected_revenue, revenue)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
class TestSecurity(TestCase):
|
|
291
|
+
def setUp(self):
|
|
292
|
+
"""setup tests"""
|
|
293
|
+
self.kfinance_api_client = MockKFinanceApiClient()
|
|
294
|
+
self.msft_security = Security(self.kfinance_api_client, msft_security_id)
|
|
295
|
+
|
|
296
|
+
def test_security_id(self) -> None:
|
|
297
|
+
"""test security id"""
|
|
298
|
+
expected_security_id = msft_security_id
|
|
299
|
+
security_id = self.msft_security.security_id
|
|
300
|
+
self.assertEqual(expected_security_id, security_id)
|
|
301
|
+
|
|
302
|
+
def test_isin(self) -> None:
|
|
303
|
+
"""test isin"""
|
|
304
|
+
expected_isin = MOCK_SECURITY_DB[self.msft_security.security_id]["isin"]
|
|
305
|
+
isin = self.msft_security.isin
|
|
306
|
+
self.assertEqual(expected_isin, isin)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
class TestTicker(TestCase):
|
|
310
|
+
def setUp(self):
|
|
311
|
+
"""setup tests"""
|
|
312
|
+
self.kfinance_api_client = MockKFinanceApiClient()
|
|
313
|
+
self.msft_ticker_from_ticker = Ticker(self.kfinance_api_client, "MSFT")
|
|
314
|
+
self.msft_ticker_from_isin = Ticker(self.kfinance_api_client, msft_isin)
|
|
315
|
+
self.msft_ticker_from_cusip = Ticker(self.kfinance_api_client, msft_cusip)
|
|
316
|
+
self.msft_ticker_from_id_triple = Ticker(
|
|
317
|
+
self.kfinance_api_client,
|
|
318
|
+
company_id=msft_company_id,
|
|
319
|
+
security_id=msft_security_id,
|
|
320
|
+
trading_item_id=msft_trading_item_id,
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
def test_company_id(self) -> None:
|
|
324
|
+
"""test company id"""
|
|
325
|
+
expected_company_id = MOCK_TICKER_DB[self.msft_ticker_from_ticker.ticker]["company_id"]
|
|
326
|
+
company_id = self.msft_ticker_from_ticker.company_id
|
|
327
|
+
self.assertEqual(expected_company_id, company_id)
|
|
328
|
+
|
|
329
|
+
company_id = self.msft_ticker_from_isin.company_id
|
|
330
|
+
self.assertEqual(expected_company_id, company_id)
|
|
331
|
+
|
|
332
|
+
company_id = self.msft_ticker_from_cusip.company_id
|
|
333
|
+
self.assertEqual(expected_company_id, company_id)
|
|
334
|
+
|
|
335
|
+
company_id = self.msft_ticker_from_id_triple.company_id
|
|
336
|
+
self.assertEqual(expected_company_id, company_id)
|
|
337
|
+
|
|
338
|
+
def test_security_id(self) -> None:
|
|
339
|
+
"""test security id"""
|
|
340
|
+
expected_security_id = MOCK_TICKER_DB[self.msft_ticker_from_ticker.ticker]["security_id"]
|
|
341
|
+
security_id = self.msft_ticker_from_ticker.security_id
|
|
342
|
+
self.assertEqual(expected_security_id, security_id)
|
|
343
|
+
|
|
344
|
+
security_id = self.msft_ticker_from_isin.security_id
|
|
345
|
+
self.assertEqual(expected_security_id, security_id)
|
|
346
|
+
|
|
347
|
+
security_id = self.msft_ticker_from_cusip.security_id
|
|
348
|
+
self.assertEqual(expected_security_id, security_id)
|
|
349
|
+
|
|
350
|
+
security_id = self.msft_ticker_from_id_triple.security_id
|
|
351
|
+
self.assertEqual(expected_security_id, security_id)
|
|
352
|
+
|
|
353
|
+
def test_trading_item_id(self) -> None:
|
|
354
|
+
"""test trading item id"""
|
|
355
|
+
expected_trading_item_id = MOCK_TICKER_DB[self.msft_ticker_from_ticker.ticker][
|
|
356
|
+
"trading_item_id"
|
|
357
|
+
]
|
|
358
|
+
trading_item_id = self.msft_ticker_from_ticker.trading_item_id
|
|
359
|
+
self.assertEqual(expected_trading_item_id, trading_item_id)
|
|
360
|
+
|
|
361
|
+
trading_item_id = self.msft_ticker_from_isin.trading_item_id
|
|
362
|
+
self.assertEqual(expected_trading_item_id, trading_item_id)
|
|
363
|
+
|
|
364
|
+
trading_item_id = self.msft_ticker_from_cusip.trading_item_id
|
|
365
|
+
self.assertEqual(expected_trading_item_id, trading_item_id)
|
|
366
|
+
|
|
367
|
+
trading_item_id = self.msft_ticker_from_id_triple.trading_item_id
|
|
368
|
+
self.assertEqual(expected_trading_item_id, trading_item_id)
|
|
369
|
+
|
|
370
|
+
def test_cusip(self) -> None:
|
|
371
|
+
"""test cusip"""
|
|
372
|
+
expected_cusip = msft_cusip
|
|
373
|
+
cusip = self.msft_ticker_from_ticker.cusip
|
|
374
|
+
self.assertEqual(expected_cusip, cusip)
|
|
375
|
+
|
|
376
|
+
cusip = self.msft_ticker_from_isin.cusip
|
|
377
|
+
self.assertEqual(expected_cusip, cusip)
|
|
378
|
+
|
|
379
|
+
cusip = self.msft_ticker_from_cusip.cusip
|
|
380
|
+
self.assertEqual(expected_cusip, cusip)
|
|
381
|
+
|
|
382
|
+
cusip = self.msft_ticker_from_id_triple.cusip
|
|
383
|
+
self.assertEqual(expected_cusip, cusip)
|
|
384
|
+
|
|
385
|
+
def test_history_metadata(self) -> None:
|
|
386
|
+
"""test history metadata"""
|
|
387
|
+
expected_history_metadata = MOCK_TRADING_ITEM_DB[msft_trading_item_id]["metadata"].copy()
|
|
388
|
+
expected_history_metadata["first_trade_date"] = datetime.strptime(
|
|
389
|
+
expected_history_metadata["first_trade_date"], "%Y-%m-%d"
|
|
390
|
+
).date()
|
|
391
|
+
history_metadata = self.msft_ticker_from_ticker.history_metadata
|
|
392
|
+
expected_exchange_code = "NasdaqGS"
|
|
393
|
+
self.assertEqual(expected_history_metadata, history_metadata)
|
|
394
|
+
self.assertEqual(expected_exchange_code, self.msft_ticker_from_ticker.exchange_code)
|
|
395
|
+
|
|
396
|
+
history_metadata = self.msft_ticker_from_isin.history_metadata
|
|
397
|
+
self.assertEqual(expected_history_metadata, history_metadata)
|
|
398
|
+
self.assertEqual(expected_exchange_code, self.msft_ticker_from_isin.exchange_code)
|
|
399
|
+
|
|
400
|
+
history_metadata = self.msft_ticker_from_cusip.history_metadata
|
|
401
|
+
self.assertEqual(expected_history_metadata, history_metadata)
|
|
402
|
+
self.assertEqual(expected_exchange_code, self.msft_ticker_from_cusip.exchange_code)
|
|
403
|
+
|
|
404
|
+
history_metadata = self.msft_ticker_from_id_triple.history_metadata
|
|
405
|
+
self.assertEqual(expected_history_metadata, history_metadata)
|
|
406
|
+
self.assertEqual(expected_exchange_code, self.msft_ticker_from_id_triple.exchange_code)
|
|
407
|
+
|
|
408
|
+
def test_price_chart(self) -> None:
|
|
409
|
+
"""test price chart"""
|
|
410
|
+
expected_price_chart = image_open(
|
|
411
|
+
BytesIO(
|
|
412
|
+
MOCK_TRADING_ITEM_DB[msft_trading_item_id]["price_chart"]["2020-01-01"][
|
|
413
|
+
"2021-01-01"
|
|
414
|
+
]
|
|
415
|
+
)
|
|
416
|
+
)
|
|
417
|
+
price_chart = self.msft_ticker_from_ticker.price_chart(
|
|
418
|
+
start_date="2020-01-01", end_date="2021-01-01"
|
|
419
|
+
)
|
|
420
|
+
self.assertEqual(expected_price_chart, price_chart)
|
|
421
|
+
|
|
422
|
+
price_chart = self.msft_ticker_from_isin.price_chart(
|
|
423
|
+
start_date="2020-01-01", end_date="2021-01-01"
|
|
424
|
+
)
|
|
425
|
+
self.assertEqual(expected_price_chart, price_chart)
|
|
426
|
+
|
|
427
|
+
price_chart = self.msft_ticker_from_cusip.price_chart(
|
|
428
|
+
start_date="2020-01-01", end_date="2021-01-01"
|
|
429
|
+
)
|
|
430
|
+
self.assertEqual(expected_price_chart, price_chart)
|
|
431
|
+
|
|
432
|
+
price_chart = self.msft_ticker_from_id_triple.price_chart(
|
|
433
|
+
start_date="2020-01-01", end_date="2021-01-01"
|
|
434
|
+
)
|
|
435
|
+
self.assertEqual(expected_price_chart, price_chart)
|
|
436
|
+
|
|
437
|
+
def test_info(self) -> None:
|
|
438
|
+
"""test info"""
|
|
439
|
+
expected_info = MOCK_COMPANY_DB[msft_company_id]["info"]
|
|
440
|
+
info = self.msft_ticker_from_ticker.info
|
|
441
|
+
self.assertEqual(expected_info, info)
|
|
442
|
+
|
|
443
|
+
info = self.msft_ticker_from_isin.info
|
|
444
|
+
self.assertEqual(expected_info, info)
|
|
445
|
+
|
|
446
|
+
info = self.msft_ticker_from_cusip.info
|
|
447
|
+
self.assertEqual(expected_info, info)
|
|
448
|
+
|
|
449
|
+
info = self.msft_ticker_from_id_triple.info
|
|
450
|
+
self.assertEqual(expected_info, info)
|
|
451
|
+
|
|
452
|
+
def test_name(self) -> None:
|
|
453
|
+
"""test name"""
|
|
454
|
+
expected_name = MOCK_COMPANY_DB[msft_company_id]["info"]["name"]
|
|
455
|
+
name = self.msft_ticker_from_ticker.name
|
|
456
|
+
self.assertEqual(expected_name, name)
|
|
457
|
+
|
|
458
|
+
name = self.msft_ticker_from_isin.name
|
|
459
|
+
self.assertEqual(expected_name, name)
|
|
460
|
+
|
|
461
|
+
name = self.msft_ticker_from_cusip.name
|
|
462
|
+
self.assertEqual(expected_name, name)
|
|
463
|
+
|
|
464
|
+
name = self.msft_ticker_from_id_triple.name
|
|
465
|
+
self.assertEqual(expected_name, name)
|
|
466
|
+
|
|
467
|
+
def test_founding_date(self) -> None:
|
|
468
|
+
"""test founding date"""
|
|
469
|
+
expected_founding_date = datetime.strptime(
|
|
470
|
+
MOCK_COMPANY_DB[msft_company_id]["info"]["founding_date"], "%Y-%m-%d"
|
|
471
|
+
).date()
|
|
472
|
+
founding_date = self.msft_ticker_from_ticker.founding_date
|
|
473
|
+
self.assertEqual(expected_founding_date, founding_date)
|
|
474
|
+
|
|
475
|
+
founding_date = self.msft_ticker_from_cusip.founding_date
|
|
476
|
+
self.assertEqual(expected_founding_date, founding_date)
|
|
477
|
+
|
|
478
|
+
founding_date = self.msft_ticker_from_isin.founding_date
|
|
479
|
+
self.assertEqual(expected_founding_date, founding_date)
|
|
480
|
+
|
|
481
|
+
founding_date = self.msft_ticker_from_id_triple.founding_date
|
|
482
|
+
self.assertEqual(expected_founding_date, founding_date)
|
|
483
|
+
|
|
484
|
+
def test_earnings_call_datetimes(self) -> None:
|
|
485
|
+
"""test earnings call datetimes"""
|
|
486
|
+
expected_earnings_call_datetimes = [
|
|
487
|
+
datetime.fromisoformat(
|
|
488
|
+
MOCK_COMPANY_DB[msft_company_id]["earnings_call_dates"]["earnings"][0]
|
|
489
|
+
).replace(tzinfo=timezone.utc)
|
|
490
|
+
]
|
|
491
|
+
earnings_call_datetimes = self.msft_ticker_from_ticker.earnings_call_datetimes
|
|
492
|
+
self.assertEqual(expected_earnings_call_datetimes, earnings_call_datetimes)
|
|
493
|
+
|
|
494
|
+
earnings_call_datetimes = self.msft_ticker_from_isin.earnings_call_datetimes
|
|
495
|
+
self.assertEqual(expected_earnings_call_datetimes, earnings_call_datetimes)
|
|
496
|
+
|
|
497
|
+
earnings_call_datetimes = self.msft_ticker_from_cusip.earnings_call_datetimes
|
|
498
|
+
self.assertEqual(expected_earnings_call_datetimes, earnings_call_datetimes)
|
|
499
|
+
|
|
500
|
+
earnings_call_datetimes = self.msft_ticker_from_id_triple.earnings_call_datetimes
|
|
501
|
+
self.assertEqual(expected_earnings_call_datetimes, earnings_call_datetimes)
|
|
502
|
+
|
|
503
|
+
def test_income_statement(self) -> None:
|
|
504
|
+
"""test income statement"""
|
|
505
|
+
expected_income_statement = (
|
|
506
|
+
pd.DataFrame(
|
|
507
|
+
MOCK_COMPANY_DB[msft_company_id]["statements"]["income_statement"]["statements"]
|
|
508
|
+
)
|
|
509
|
+
.apply(pd.to_numeric)
|
|
510
|
+
.replace(np.nan, None)
|
|
511
|
+
)
|
|
512
|
+
income_statement = self.msft_ticker_from_ticker.income_statement()
|
|
513
|
+
pd.testing.assert_frame_equal(expected_income_statement, income_statement)
|
|
514
|
+
|
|
515
|
+
income_statement = self.msft_ticker_from_isin.income_statement()
|
|
516
|
+
pd.testing.assert_frame_equal(expected_income_statement, income_statement)
|
|
517
|
+
|
|
518
|
+
income_statement = self.msft_ticker_from_cusip.income_statement()
|
|
519
|
+
pd.testing.assert_frame_equal(expected_income_statement, income_statement)
|
|
520
|
+
|
|
521
|
+
income_statement = self.msft_ticker_from_id_triple.income_statement()
|
|
522
|
+
pd.testing.assert_frame_equal(expected_income_statement, income_statement)
|
|
523
|
+
|
|
524
|
+
def test_revenue(self) -> None:
|
|
525
|
+
"""test revenue"""
|
|
526
|
+
expected_revenue = (
|
|
527
|
+
pd.DataFrame(MOCK_COMPANY_DB[msft_company_id]["line_items"]["revenue"])
|
|
528
|
+
.transpose()
|
|
529
|
+
.apply(pd.to_numeric)
|
|
530
|
+
.replace(np.nan, None)
|
|
531
|
+
.set_index(pd.Index(["revenue"]))
|
|
532
|
+
)
|
|
533
|
+
revenue = self.msft_ticker_from_ticker.revenue()
|
|
534
|
+
pd.testing.assert_frame_equal(expected_revenue, revenue)
|
|
535
|
+
|
|
536
|
+
revenue = self.msft_ticker_from_isin.revenue()
|
|
537
|
+
pd.testing.assert_frame_equal(expected_revenue, revenue)
|
|
538
|
+
|
|
539
|
+
revenue = self.msft_ticker_from_cusip.revenue()
|
|
540
|
+
pd.testing.assert_frame_equal(expected_revenue, revenue)
|
|
541
|
+
|
|
542
|
+
revenue = self.msft_ticker_from_id_triple.revenue()
|
|
543
|
+
pd.testing.assert_frame_equal(expected_revenue, revenue)
|
|
544
|
+
|
|
545
|
+
def test_ticker_symbol(self):
|
|
546
|
+
"""test ticker symbol"""
|
|
547
|
+
expected_ticker_symbol = "MSFT"
|
|
548
|
+
self.assertEqual(expected_ticker_symbol, self.msft_ticker_from_ticker.ticker)
|
|
549
|
+
self.assertEqual(expected_ticker_symbol, self.msft_ticker_from_isin.ticker)
|
|
550
|
+
self.assertEqual(expected_ticker_symbol, self.msft_ticker_from_cusip.ticker)
|
|
551
|
+
self.assertEqual(expected_ticker_symbol, self.msft_ticker_from_id_triple.ticker)
|
kfinance/tool_schemas.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
from .constants import LINE_ITEM_NAMES_AND_ALIASES, BusinessRelationshipType
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class GetLatestInput(BaseModel):
|
|
9
|
+
use_local_timezone: bool = Field(
|
|
10
|
+
default=True, description="whether to use the local timezone of the user"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GetNQuartersAgoInput(BaseModel):
|
|
15
|
+
n: int = Field(description="number of quarters before the current quarter")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class GetCompanyIdFromIdentifier(BaseModel):
|
|
19
|
+
ticker_str: str = Field(description="The ticker")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class GetSecurityIdFromIdentifier(BaseModel):
|
|
23
|
+
identifier: str = Field(
|
|
24
|
+
description="The identifier, which can be a ticker symbol, ISIN, or CUSIP"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class GetTradingItemIdFromIdentifier(BaseModel):
|
|
29
|
+
identifier: str = Field(
|
|
30
|
+
description="The identifier, which can be a ticker symbol, ISIN, or CUSIP"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class GetIsinFromTicker(BaseModel):
|
|
35
|
+
ticker_str: str = Field(description="The ticker")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class GetCusipFromTicker(BaseModel):
|
|
39
|
+
ticker_str: str = Field(description="The ticker")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class GetInfoFromIdentifier(BaseModel):
|
|
43
|
+
identifier: str = Field(
|
|
44
|
+
description="The identifier, which can be a ticker symbol, ISIN, or CUSIP"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class GetEarningsCallDatetimesFromIdentifier(BaseModel):
|
|
49
|
+
identifier: str = Field(
|
|
50
|
+
description="The identifier, which can be a ticker symbol, ISIN, or CUSIP"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class GetHistoryMetadataFromIdentifier(BaseModel):
|
|
55
|
+
identifier: str = Field(
|
|
56
|
+
description="The identifier, which can be a ticker symbol, ISIN, or CUSIP"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class GetPricesFromIdentifier(BaseModel):
|
|
61
|
+
identifier: str = Field(
|
|
62
|
+
description="The identifier, which can be a ticker symbol, ISIN, or CUSIP"
|
|
63
|
+
)
|
|
64
|
+
periodicity: Literal["day", "week", "month", "year"] = Field(
|
|
65
|
+
default="day",
|
|
66
|
+
description="The frequency or interval at which the historical data points are sampled or aggregated. Periodicity is not the same as the date range. The date range specifies the time span over which the data is retrieved, while periodicity determines how the data within that date range is aggregated, valid inputs are ['day', 'week', 'month', 'year'].",
|
|
67
|
+
)
|
|
68
|
+
adjusted: bool = Field(
|
|
69
|
+
description="Whether to retrieve adjusted prices that account for corporate actions such as dividends and splits."
|
|
70
|
+
)
|
|
71
|
+
start_date: str | None = Field(
|
|
72
|
+
default=None,
|
|
73
|
+
description="The start date for historical price retrieval in format YYYY-MM-DD",
|
|
74
|
+
)
|
|
75
|
+
end_date: str | None = Field(
|
|
76
|
+
default=None, description="The end date for historical price retrieval in format YYYY-MM-DD"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class GetFinancialStatementFromIdentifier(BaseModel):
|
|
81
|
+
identifier: str = Field(
|
|
82
|
+
description="The identifier, which can be a ticker symbol, ISIN, or CUSIP"
|
|
83
|
+
)
|
|
84
|
+
statement: Literal["balance_sheet", "income_statement", "cashflow"] = Field(
|
|
85
|
+
description="The type of financial statement, valid inputs are ['balance_sheet', 'income_statement', 'cashflow']"
|
|
86
|
+
)
|
|
87
|
+
period_type: Literal["annual", "quarterly", "ltm", "ytd"] | None = Field(
|
|
88
|
+
default=None,
|
|
89
|
+
description="time period type, valid inputs are ['annual', 'quarterly', 'ltm', 'ytd'].",
|
|
90
|
+
)
|
|
91
|
+
start_year: int | None = Field(
|
|
92
|
+
default=None, description="The starting year for the data range."
|
|
93
|
+
)
|
|
94
|
+
end_year: int | None = Field(default=None, description="The ending year for the data range.")
|
|
95
|
+
start_quarter: Literal[1, 2, 3, 4] | None = Field(
|
|
96
|
+
default=None, description="starting quarter, valid inputs are [1, 2, 3, 4]"
|
|
97
|
+
)
|
|
98
|
+
end_quarter: Literal[1, 2, 3, 4] | None = Field(
|
|
99
|
+
default=None, description="ending quarter, valid inputs are [1, 2, 3, 4]"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class GetFinancialLineItemFromIdentifier(BaseModel):
|
|
104
|
+
identifier: str = Field(
|
|
105
|
+
description="The identifier, which can be a ticker symbol, ISIN, or CUSIP"
|
|
106
|
+
)
|
|
107
|
+
line_item: Literal[tuple(LINE_ITEM_NAMES_AND_ALIASES)] = Field( # type: ignore
|
|
108
|
+
description="The type of financial line_item requested"
|
|
109
|
+
)
|
|
110
|
+
period_type: Literal["annual", "quarterly", "ltm", "ytd"] | None = Field(
|
|
111
|
+
default=None,
|
|
112
|
+
description="time period type, valid inputs are ['annual', 'quarterly', 'ltm', 'ytd']",
|
|
113
|
+
)
|
|
114
|
+
start_year: int | None = Field(
|
|
115
|
+
default=None, description="The starting year for the data range."
|
|
116
|
+
)
|
|
117
|
+
end_year: int | None = Field(default=None, description="The ending year for the data range.")
|
|
118
|
+
start_quarter: Literal[1, 2, 3, 4] | None = Field(
|
|
119
|
+
default=None, description="starting quarter, valid inputs are [1, 2, 3, 4]"
|
|
120
|
+
)
|
|
121
|
+
end_quarter: Literal[1, 2, 3, 4] | None = Field(
|
|
122
|
+
default=None, description="ending quarter, valid inputs are [1, 2, 3, 4]"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class GetBusinessRelationshipFromIdentifier(BaseModel):
|
|
127
|
+
identifier: str = Field(
|
|
128
|
+
description="The identifier, which can be a ticker symbol, ISIN, or CUSIP"
|
|
129
|
+
)
|
|
130
|
+
business_relationship: BusinessRelationshipType = Field(
|
|
131
|
+
description="The type of business relationship requested"
|
|
132
|
+
)
|