neurostats-API 0.0.3__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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