neurostats-API 0.0.3__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.
@@ -0,0 +1 @@
1
+ __version__='0.0.3'
neurostats_API/cli.py ADDED
@@ -0,0 +1,35 @@
1
+ import argparse
2
+ import pprint
3
+ from fetchers.value_invest import ValueFetcher
4
+ import pandas as pd
5
+ pp = pprint.PrettyPrinter(
6
+ indent = 2
7
+ )
8
+
9
+ pd.options.display.max_rows = 4
10
+ pd.options.display.max_columns = 4
11
+
12
+ def main():
13
+ parser = argparse.ArgumentParser(
14
+ prog="Neurostats Datasets fetching",
15
+ description="Main function of fetching Neurostat Dataset",
16
+ )
17
+
18
+ parser.add_argument("--ticker", type=str, default="2330")
19
+ parser.add_argument("--start_date", type=str, default="2018-01-01")
20
+ parser.add_argument("--end_date", type=str, default="2199-12-31")
21
+
22
+ args = parser.parse_args()
23
+
24
+ fetcher = ValueFetcher(args.ticker)
25
+
26
+ query_data = fetcher.query_data()
27
+ print("{")
28
+ for key, value in query_data.items():
29
+ print(f"\t\"{key}\":")
30
+
31
+ print(f"{value}")
32
+ print("}")
33
+
34
+ if (__name__ == "__main__"):
35
+ main()
@@ -0,0 +1 @@
1
+ from .finance_overview import FinanceOverviewFetcher
File without changes
@@ -0,0 +1,59 @@
1
+ from pymongo import MongoClient
2
+ import pandas as pd
3
+ import json
4
+ import pytz
5
+ from datetime import datetime, timedelta, date
6
+ from utils import StatsDateTime
7
+ import yaml
8
+ from .utils.db_client import shared_client
9
+
10
+ class StatsFetcher:
11
+
12
+ def __init__(self, ticker):
13
+ self.ticker = ticker
14
+ self.db = shared_client[
15
+ "company"] # Replace with your database name
16
+ self.collection = self.db["twse_stats"]
17
+
18
+ self.timezone = pytz.timezone("Asia/Taipei")
19
+
20
+ self.target_metric_dict = {
21
+ 'value': ['value'],
22
+ 'value_and_percentage': ['value', 'percentage'],
23
+ 'percentage': ['percentage'],
24
+ 'grand_total': ['grand_total'],
25
+ 'grand_total_values': ['grand_total', 'grand_total_percentage'],
26
+ 'grand_total_percentage': ['grand_total_percentage'],
27
+ 'growth': [f'YoY_{i}' for i in [1, 3, 5, 10]],
28
+ 'grand_total_growth': [f"YoY_{i}" for i in [1, 3, 5, 10]]
29
+ }
30
+
31
+ def prepare_query(self):
32
+ return [
33
+ {
34
+ "$match": {
35
+ "ticker": self.ticker,
36
+ }
37
+ },
38
+ ]
39
+
40
+ def collect_data(self, start_date, end_date):
41
+ pipeline = self.prepare_query(start_date, end_date)
42
+
43
+ fetched_data = list(self.collection.aggregate(pipeline))
44
+
45
+ return fetched_data[0]
46
+
47
+ def str_to_datetime(self, date_str):
48
+ year, month, day = [int(num) for num in date_str.split("-")]
49
+
50
+ date = datetime.strptime(date_str, "%Y-%m-%d")
51
+ date = self.timezone.localize(date)
52
+
53
+ season = (month - 1) // 3 + 1
54
+
55
+ return StatsDateTime(date, year, month, day, season)
56
+
57
+
58
+ class StatsProcessor:
59
+ pass
@@ -0,0 +1,501 @@
1
+ from .base import StatsFetcher, StatsProcessor, StatsDateTime
2
+ import json
3
+ import pandas as pd
4
+ from utils import StatsDateTime
5
+ import yaml
6
+
7
+
8
+ class FinanceOverviewFetcher(StatsFetcher):
9
+ """
10
+ 對應iFa.ai -> 財務分析 -> 重要指標(finance_overview)
11
+ """
12
+
13
+ def __init__(self, ticker):
14
+ super().__init__(ticker)
15
+
16
+ with open("./tools/finance_overview_dict.yaml") as f:
17
+ self.target_fields = yaml.safe_load(f)
18
+
19
+ with open("./tools/seasonal_data_field_dict.txt") as f:
20
+ self.inverse_dict = json.load(f)
21
+
22
+ def prepare_query(self, target_year, target_season):
23
+
24
+ pipeline = super().prepare_query()
25
+
26
+ target_query = {
27
+ "year": "$$target_season_data.year",
28
+ "season": "$$target_season_data.season",
29
+ }
30
+
31
+ for key, target_sets in self.target_fields.items():
32
+ try:
33
+ small_target = target_sets['field']
34
+ big_target = self.inverse_dict[
35
+ small_target] # balance_sheet/profit_lose/cash_flow
36
+ value_index = target_sets['value'] # "金額" or "%"
37
+
38
+ target_query.update({
39
+ f"{key}":
40
+ f"$$target_season_data.{big_target}.{small_target}.{value_index}"
41
+ })
42
+ except Exception:
43
+ continue
44
+
45
+ pipeline.append({
46
+ "$project": {
47
+ "_id": 0,
48
+ "ticker": 1,
49
+ "company_name": 1,
50
+ "seasonal_data": {
51
+ "$map": {
52
+ "input": {
53
+ "$filter": {
54
+ "input": "$seasonal_data",
55
+ "as": "season",
56
+ "cond": {
57
+ "$and": [{
58
+ "$eq": ["$$season.year", target_year]
59
+ }, {
60
+ "$eq":
61
+ ["$$season.season", target_season]
62
+ }]
63
+ }
64
+ }
65
+ },
66
+ "as": "target_season_data",
67
+ "in": target_query
68
+ }
69
+ }
70
+ }
71
+ })
72
+
73
+ return pipeline
74
+
75
+ def collect_data(self, target_year, target_season):
76
+ pipeline = self.prepare_query(target_year, target_season)
77
+
78
+ fetched_data = self.collection.aggregate(pipeline)
79
+
80
+ fetched_data = list(fetched_data)
81
+
82
+ return fetched_data[0]
83
+
84
+ def query_data(self):
85
+ today = StatsDateTime.get_today()
86
+
87
+ fetched_data = self.collect_data(2024, 2)
88
+ finance_dict = fetched_data['seasonal_data'][0]
89
+ FinanceOverviewProcessor.process_all(finance_dict)
90
+ fetched_data['seasonal_data'] = finance_dict
91
+ return fetched_data
92
+
93
+
94
+ class FinanceOverviewProcessor(StatsProcessor):
95
+
96
+ @classmethod
97
+ def process_all(cls, finance_dict):
98
+ methods = [
99
+ cls.cal_EBIT,
100
+ cls.cal_share_outstanding,
101
+ cls.cal_fcf,
102
+ cls.cal_revenue_per_share,
103
+ cls.cal_gross_per_share,
104
+ cls.cal_operating_income_per_share,
105
+ cls.cal_operating_cash_flow_per_share,
106
+ cls.fcf_per_share,
107
+ cls.cal_roa,
108
+ cls.cal_roe,
109
+ cls.cal_gross_over_asset,
110
+ cls.cal_roce,
111
+ cls.cal_gross_profit_marginal,
112
+ cls.cal_operation_profit_rate,
113
+ cls.cal_operating_cash_flow_profit_rate,
114
+ cls.cal_dso,
115
+ cls.cal_account_receive_over_revenue,
116
+ cls.cal_dpo,
117
+ cls.cal_inventories_cycle_ratio,
118
+ cls.cal_dio,
119
+ cls.cal_inventories_revenue_ratio,
120
+ cls.cal_cash_of_conversion_cycle,
121
+ cls.cal_asset_turnover,
122
+ cls.cal_application_turnover,
123
+ cls.cal_current_ratio,
124
+ cls.cal_quick_ratio,
125
+ cls.cal_debt_to_equity_ratio,
126
+ cls.cal_net_debt_to_equity_ratio,
127
+ cls.cal_interest_coverage_ratio,
128
+ cls.cal_debt_to_operating_cash_flow,
129
+ cls.cal_debt_to_free_cash_flow,
130
+ cls.cal_cash_flow_ratio
131
+ ]
132
+
133
+ for method in methods:
134
+ method(finance_dict)
135
+
136
+ @classmethod
137
+ def cal_EBIT(cls, finance_dict):
138
+ """
139
+ 計算EBIT
140
+ EBIT = 營業收入 - 營業成本 - 營業費用
141
+ """
142
+ try:
143
+ finance_dict['EBIT'] = (finance_dict['revenue'] -
144
+ finance_dict['operating_cost'] -
145
+ finance_dict['operating_expenses'])
146
+ except (KeyError, TypeError) as e:
147
+ finance_dict['EBIT'] = None
148
+ print(f"Error calculating EBIT: {e}")
149
+
150
+ @classmethod
151
+ def cal_fcf(cls, finance_dict):
152
+ """
153
+ 計算自由現金流(FCF):
154
+ 自由現金流 = 營業現金流 + 投資現金流
155
+ """
156
+ try:
157
+ finance_dict["fcf"] = (finance_dict["operating_cash_flow"] +
158
+ finance_dict["financing_cash_flow"])
159
+ except Exception as e:
160
+ finance_dict['fcf'] = None
161
+ print(f"Error calculating FCF: {e}")
162
+
163
+ @classmethod
164
+ def cal_share_outstanding(cls, finance_dict):
165
+ """
166
+ 計算流通股數
167
+ 流通股數 = 本期淨利 ÷ 基本每股盈餘
168
+ """
169
+ try:
170
+ finance_dict["share_outstanding"] = (finance_dict['net_income'] /
171
+ finance_dict['eps'])
172
+ except KeyError as e:
173
+ finance_dict['share_outstanding'] = None
174
+ print(f"share_outstanding failed because of {str(e)}")
175
+
176
+ @classmethod
177
+ def cal_revenue_per_share(cls, finance_dict):
178
+ """
179
+ 計算每股營收
180
+ 每股營收 = 營業收入 / 在外流通股數
181
+ """
182
+ try:
183
+ finance_dict['revenue_per_share'] = (
184
+ finance_dict['revenue'] / finance_dict['share_outstanding'])
185
+ except KeyError as e:
186
+ finance_dict['revenue_per_share'] = None
187
+ print(f"revenue_per_share failed because of {str(e)}")
188
+
189
+ @classmethod
190
+ def cal_gross_per_share(cls, finance_dict):
191
+ """
192
+ 計算每股毛利
193
+ = (當期營業毛利)÷(當期在外流通股數)
194
+ """
195
+
196
+ try:
197
+ finance_dict['gross_per_share'] = (
198
+ finance_dict['gross_profit'] /
199
+ finance_dict['share_outstanding'])
200
+ except KeyError as e:
201
+ finance_dict['gross_per_share'] = None
202
+ print(f"gross_per_share failed because of {str(e)}")
203
+
204
+ @classmethod
205
+ def cal_operating_income_per_share(cls, finance_dict):
206
+ """
207
+ 計算每股營業利益
208
+ 每股營業利益= (當期營業利益)÷(當期在外流通股數)
209
+ """
210
+ try:
211
+ finance_dict['operating_income_per_share'] = (
212
+ finance_dict['operating_income'] /
213
+ finance_dict['share_outstanding'])
214
+ except KeyError as e:
215
+ finance_dict['operating_income_per_share'] = None
216
+ print(f"operating_income_per_share failed because of {str(e)}")
217
+
218
+ @classmethod
219
+ def cal_operating_cash_flow_per_share(cls, finance_dict):
220
+ """
221
+ 計算每股營業現金流
222
+ = (當期營業現金流) ÷(當期在外流通股數)
223
+ """
224
+ try:
225
+ finance_dict["operating_cash_flow_per_share"] = (
226
+ finance_dict["operating_cash_flow"] /
227
+ finance_dict['share_outstanding'])
228
+ except KeyError as e:
229
+ finance_dict['operating_cash_flow_per_share'] = None
230
+ print(f'operating_cash_flow_per_share because of {str(e)}')
231
+
232
+ @classmethod
233
+ def fcf_per_share(cls, finance_dict):
234
+ """
235
+ 計算每股自由現金流
236
+ 每股自由現金流 = (當期自由現金流) ÷(當期在外流通股數)
237
+ """
238
+ try:
239
+ finance_dict['fcf_per_share'] = (finance_dict['fcf'] /
240
+ finance_dict['share_outstanding'])
241
+ except KeyError as e:
242
+ finance_dict['fcf_per_share'] = None
243
+ print(f"fcf_per_share failed because of {str(e)}")
244
+
245
+ # 盈利能力
246
+
247
+ @classmethod
248
+ def cal_roa(cls, finance_dict):
249
+ """
250
+ 計算資產報酬率(ROA)
251
+ ROA = [ 本期淨利 + 利息費用 × (1-有效稅率) ] ÷(資產總額)
252
+ """
253
+ finance_dict["roa"] = (
254
+ finance_dict['net_income'] + finance_dict['interest'] +
255
+ (1 * 0.1) # 有效稅率需要改,這裡先設0.1
256
+ ) / finance_dict['inventories']
257
+
258
+ @classmethod
259
+ def cal_roe(cls, finance_dict):
260
+ """
261
+ 計算股東權益報酬率(ROE)
262
+ ROE = (本期淨利) ÷(權益總額)
263
+ """
264
+ finance_dict['roe'] = (finance_dict['net_income'] /
265
+ finance_dict['equity'])
266
+
267
+ @classmethod
268
+ def cal_gross_over_asset(cls, finance_dict):
269
+ """
270
+ 計算營業毛利/總資產
271
+ """
272
+ finance_dict['gross_over_asset'] = (finance_dict['gross_profit'] /
273
+ finance_dict['total_asset'])
274
+
275
+ @classmethod
276
+ def cal_roce(cls, finance_dict):
277
+ """
278
+ 計算資本運用報酬率(ROCE):
279
+ ROCE = (稅前淨利+利息費用) / (資產總額-流動負債)
280
+ """
281
+ try:
282
+ finance_dict['roce'] = (
283
+ (finance_dict['net_income_before_tax'] +
284
+ finance_dict['interest']) /
285
+ (finance_dict['asset'] - finance_dict['current_liabilities']))
286
+ except KeyError as e:
287
+ finance_dict['roce'] = None
288
+ print(f"ROCE failed because of {str(e)}")
289
+
290
+ @classmethod
291
+ def cal_gross_profit_marginal(cls, finance_dict):
292
+ """
293
+ 計算營業毛利率(gross profit margin)
294
+ 營業毛利率 = 營業毛利 ÷ 營業收入
295
+ """
296
+ try:
297
+ finance_dict['gross_profit_margin'] = (
298
+ finance_dict['gross_profit'] / finance_dict['revenue'])
299
+ except:
300
+ finance_dict['gross_profit_margin'] = None
301
+ print(f"gross_profit_margin failed because of {str(e)}")
302
+
303
+ @classmethod
304
+ def cal_operation_profit_rate(cls, finance_dict):
305
+ """
306
+ 計算營業利益率
307
+ 營業利益率 = ( 營業收入-營業成本-營業費用)÷ 營業收入
308
+ """
309
+ try:
310
+ finance_dict["operation_profit_rate"] = (
311
+ finance_dict['revenue'] - finance_dict['operating_cost'] -
312
+ finance_dict['operating_price']) / finance_dict['revenue']
313
+ except KeyError as e:
314
+ finance_dict["operation_profit_rate"] = None
315
+ print(f"operation_profit failed because of {str(e)}")
316
+
317
+ @classmethod
318
+ def cal_operating_cash_flow_profit_rate(cls, finance_dict):
319
+ """
320
+ 計算營業現金流利潤率
321
+ 營業現金流利潤率 = 營業活動現金流 ÷ 營業收入
322
+ """
323
+ try:
324
+ finance_dict["operating_cash_flow_profit_rate"] = (
325
+ finance_dict["operating_cash_flow"] / finance_dict["revenue"])
326
+ except KeyError:
327
+ finance_dict["operating_cash_flow_profit_rate"] = None
328
+
329
+ print(
330
+ f"operating_cash_flow_profit_rate failed because of {str(e)}")
331
+
332
+
333
+ # 成長動能
334
+
335
+ """
336
+ 前四個已經有了 revenue_YoY, gross_prof_YoY, operating_income_YoY, net_income_YoY:
337
+ 後四個在金流,還需要處理
338
+ """
339
+ # 營運指標
340
+
341
+ @classmethod
342
+ def cal_dso(cls, finance_dict):
343
+ """
344
+ 計算應收帳款收現天數(DSO)
345
+ DSO = 365 × (營業收入 ÷ 應收帳款平均餘額)
346
+ """
347
+ finance_dict['dso'] = (
348
+ 365 * (finance_dict['revenue'] / finance_dict['account_pay']))
349
+
350
+ @classmethod
351
+ def cal_account_receive_over_revenue(cls, finance_dict):
352
+ """
353
+ 計算應收帳款佔營收比率
354
+ = 應收帳款平均餘額 ÷ 營業收入
355
+ """
356
+ finance_dict["account_receive_over_revenue"] = (
357
+ finance_dict['account_receive'] / finance_dict['revenue'])
358
+
359
+ @classmethod
360
+ def cal_dpo(cls, finance_dict):
361
+ """
362
+ 計算應付帳款週轉天數
363
+ DPO = 365天 ÷ (銷貨成本÷平均應付帳款)
364
+ """
365
+ finance_dict["dpo"] = (
366
+ 365 *
367
+ (finance_dict['operating_cost'] / finance_dict['account_pay']))
368
+
369
+ @classmethod
370
+ def cal_inventories_cycle_ratio(cls, finance_dict):
371
+ """
372
+ 計算存貨周轉率
373
+ = 銷貨成本 ÷ 存貨
374
+ """
375
+
376
+ finance_dict["inventories_cycle_ratio"] = (
377
+ finance_dict['operating_cost'] / finance_dict['inventories'])
378
+
379
+ @classmethod
380
+ def cal_dio(cls, finance_dict):
381
+ """
382
+ 計算 存貨週轉天數
383
+ DIO = 365天 ÷ (銷貨成本 ÷ 存貨)
384
+ MUDA MUDA MUDA MUDA !!!
385
+ """
386
+ finance_dict["dio"] = (finance_dict["operating_cost"] /
387
+ finance_dict["inventories"])
388
+
389
+ @classmethod
390
+ def cal_inventories_revenue_ratio(cls, finance_dict):
391
+ """
392
+ 計算存貨佔營收比率
393
+ 存貨佔營收比= 存貨 ÷ 營業收入
394
+ """
395
+ finance_dict["inventories_revenue_ratio"] = (
396
+ finance_dict['inventories'] / finance_dict['revenue'])
397
+
398
+ @classmethod
399
+ def cal_cash_of_conversion_cycle(cls, finance_dict):
400
+ """
401
+ 計算現金循環週期
402
+ 存貨週轉天數 + 應收帳款週轉天數 - 應付帳款週轉天數
403
+ """
404
+ finance_dict["cash_of_conversion_cycle"] = (finance_dict["dio"] +
405
+ finance_dict["dso"] -
406
+ finance_dict['dpo'])
407
+
408
+ @classmethod
409
+ def cal_asset_turnover(cls, finance_dict):
410
+ finance_dict["asset_turnover"] = (finance_dict["revenue"] /
411
+ finance_dict["inventories"])
412
+
413
+ @classmethod
414
+ def cal_application_turnover(cls, finance_dict):
415
+ finance_dict['applcation_turnover'] = (finance_dict['revenue'] /
416
+ finance_dict["application"])
417
+
418
+ @classmethod
419
+ def cal_current_ratio(cls, finance_dict):
420
+ """
421
+ 計算流動比率 = 流動資產 / 流動負債
422
+ """
423
+ try:
424
+ finance_dict['current_ratio'] = finance_dict[
425
+ 'current_assets'] / finance_dict['current_liabilities']
426
+ except (KeyError, ZeroDivisionError, TypeError) as e:
427
+ finance_dict['current_ratio'] = None
428
+ print(f"Error calculating current ratio: {e}")
429
+
430
+ @classmethod
431
+ def cal_quick_ratio(cls, finance_dict):
432
+ try:
433
+ # 速動比率 = (流動資產 - 存貨) / 流動負債
434
+ finance_dict['quick_ratio'] = (
435
+ finance_dict['current_assets'] - finance_dict['inventories']
436
+ ) / finance_dict['current_liabilities']
437
+ except (KeyError, ZeroDivisionError, TypeError) as e:
438
+ finance_dict['quick_ratio'] = None
439
+ print(f"Error calculating quick ratio: {e}")
440
+
441
+ @classmethod
442
+ def cal_debt_to_equity_ratio(cls, finance_dict):
443
+ try:
444
+ # 負債權益比率 = 總負債 / 股東權益
445
+ finance_dict['debt_to_equity_ratio'] = finance_dict[
446
+ 'total_liabilities'] / finance_dict['equity']
447
+ except (KeyError, ZeroDivisionError, TypeError) as e:
448
+ finance_dict['debt_to_equity_ratio'] = None
449
+ print(f"Error calculating debt to equity ratio: {e}")
450
+
451
+ @classmethod
452
+ def cal_net_debt_to_equity_ratio(cls, finance_dict):
453
+ try:
454
+ # 淨負債權益比率 = (總負債 - 現金及約當現金) / 股東權益
455
+ finance_dict['net_debt_to_equity_ratio'] = (
456
+ finance_dict['total_liabilities'] -
457
+ finance_dict['cash_and_cash_equivalents']
458
+ ) / finance_dict['equity']
459
+ except (KeyError, ZeroDivisionError, TypeError) as e:
460
+ finance_dict['net_debt_to_equity_ratio'] = None
461
+ print(f"Error calculating net debt to equity ratio: {e}")
462
+
463
+ @classmethod
464
+ def cal_interest_coverage_ratio(cls, finance_dict):
465
+ try:
466
+ # 利息保障倍數 = EBIT / 利息費用
467
+ finance_dict['interest_coverage_ratio'] = finance_dict[
468
+ 'EBIT'] / finance_dict['interest_expense']
469
+ except (KeyError, ZeroDivisionError, TypeError) as e:
470
+ finance_dict['interest_coverage_ratio'] = None
471
+ print(f"Error calculating interest coverage ratio: {e}")
472
+
473
+ @classmethod
474
+ def cal_debt_to_operating_cash_flow(cls, finance_dict):
475
+ try:
476
+ # 有息負債 / 營業活動現金流
477
+ finance_dict['debt_to_operating_cash_flow'] = finance_dict[
478
+ 'interest_bearing_debt'] / finance_dict['operating_cash_flow']
479
+ except (KeyError, ZeroDivisionError, TypeError) as e:
480
+ finance_dict['debt_to_operating_cash_flow'] = None
481
+ print(f"Error calculating debt to operating cash flow: {e}")
482
+
483
+ @classmethod
484
+ def cal_debt_to_free_cash_flow(cls, finance_dict):
485
+ try:
486
+ # 有息負債 / 自由現金流
487
+ finance_dict['debt_to_free_cash_flow'] = finance_dict[
488
+ 'interest_bearing_debt'] / finance_dict['fcf']
489
+ except (KeyError, ZeroDivisionError, TypeError) as e:
490
+ finance_dict['debt_to_free_cash_flow'] = None
491
+ print(f"Error calculating debt to free cash flow: {e}")
492
+
493
+ @classmethod
494
+ def cal_cash_flow_ratio(cls, finance_dict):
495
+ try:
496
+ # 現金流量比率 = 營業活動現金流 / 流動負債
497
+ finance_dict['cash_flow_ratio'] = finance_dict[
498
+ 'operating_cash_flow'] / finance_dict['current_liabilities']
499
+ except (KeyError, ZeroDivisionError, TypeError) as e:
500
+ finance_dict['cash_flow_ratio'] = None
501
+ print(f"Error calculating cash flow ratio: {e}")
File without changes
File without changes