neurostats-API 0.0.21b0__py3-none-any.whl → 0.0.23b0__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.
Files changed (26) hide show
  1. neurostats_API/__init__.py +1 -1
  2. neurostats_API/fetchers/balance_sheet.py +138 -111
  3. neurostats_API/fetchers/base.py +89 -74
  4. neurostats_API/fetchers/cash_flow.py +120 -111
  5. neurostats_API/fetchers/finance_overview.py +2 -2
  6. neurostats_API/fetchers/month_revenue.py +1 -1
  7. neurostats_API/fetchers/profit_lose.py +188 -113
  8. neurostats_API/fetchers/tech.py +175 -42
  9. neurostats_API/fetchers/tej_finance_report.py +230 -335
  10. neurostats_API/tools/company_list/tw.json +2175 -0
  11. neurostats_API/tools/tej_db/tej_db_skip_index.yaml +3 -1
  12. neurostats_API/tools/tej_db/tej_db_thousand_index.yaml +0 -1
  13. neurostats_API/utils/__init__.py +0 -1
  14. neurostats_API/utils/calculate_value.py +99 -1
  15. neurostats_API/utils/data_process.py +43 -15
  16. {neurostats_API-0.0.21b0.dist-info → neurostats_API-0.0.23b0.dist-info}/METADATA +2 -2
  17. neurostats_API-0.0.23b0.dist-info/RECORD +34 -0
  18. neurostats_API/utils/fetcher.py +0 -1056
  19. neurostats_API-0.0.21b0.dist-info/RECORD +0 -34
  20. /neurostats_API/tools/{balance_sheet.yaml → twse/balance_sheet.yaml} +0 -0
  21. /neurostats_API/tools/{cash_flow_percentage.yaml → twse/cash_flow_percentage.yaml} +0 -0
  22. /neurostats_API/tools/{finance_overview_dict.yaml → twse/finance_overview_dict.yaml} +0 -0
  23. /neurostats_API/tools/{profit_lose.yaml → twse/profit_lose.yaml} +0 -0
  24. /neurostats_API/tools/{seasonal_data_field_dict.txt → twse/seasonal_data_field_dict.txt} +0 -0
  25. {neurostats_API-0.0.21b0.dist-info → neurostats_API-0.0.23b0.dist-info}/WHEEL +0 -0
  26. {neurostats_API-0.0.21b0.dist-info → neurostats_API-0.0.23b0.dist-info}/top_level.txt +0 -0
@@ -1,1056 +0,0 @@
1
- import pandas as pd
2
- import json
3
- import pytz
4
- from datetime import datetime, timedelta, date
5
- from .data_process import StatsProcessor
6
- import yaml
7
-
8
-
9
- class StatsFetcher:
10
-
11
- def __init__(self, db_client):
12
- self.db = db_client["company"] # Replace with your database name
13
- self.collection = self.db["twse_stats"]
14
-
15
- self.timezone = pytz.timezone("Asia/Taipei")
16
-
17
- self.inverse_dict = StatsProcessor.load_txt("seasonal_data_field_dict.txt", json_load=True)
18
-
19
- self.seasons = ["01", "02", "03", "04"]
20
-
21
- self.pipeline = list()
22
-
23
- self.target_metric_dict = {
24
- 'value': ['value'],
25
- 'value_and_percentage': ['value', 'percentage'],
26
- 'percentage': ['percentage'],
27
- 'grand_total': ['grand_total'],
28
- 'grand_total_values':['grand_total', 'grand_total_percentage'],
29
- 'grand_total_percentage':['grand_total_percentage'],
30
- 'growth': [f'YoY_{i}' for i in [1,3,5,10]],
31
- 'grand_total_growth': [f"YoY_{i}" for i in [1,3,5,10]]
32
- }
33
-
34
- self.__return_dict = dict()
35
-
36
- def _flush_dict(self):
37
- self.__return_dict = dict()
38
-
39
- def _default_query(self, ticker, start_date, end_date):
40
-
41
- start_year, start_month, start_day = [
42
- int(num) for num in start_date.split("-")
43
- ]
44
- end_year, end_month, end_day = [
45
- int(num) for num in end_date.split("-")
46
- ]
47
-
48
- start_date = datetime.strptime(start_date, "%Y-%m-%d")
49
- end_date = datetime.strptime(end_date, "%Y-%m-%d")
50
- start_date = self.timezone.localize(start_date)
51
- end_date = self.timezone.localize(end_date)
52
-
53
- start_season = start_month // 3 + 1
54
- end_season = end_month // 3 + 1
55
-
56
- ticker = ticker.strip().split()[0]
57
-
58
- self.pipeline = [
59
- # 1. Find Ticker
60
- {
61
- "$match": {
62
- "ticker": ticker,
63
- }
64
- },
65
- # 2. Find by date
66
- {
67
- "$project": {
68
- "ticker": 1,
69
- "company_name": 1,
70
- # 2.1 Filter monthly_data
71
- "daily_data": {
72
- "$filter": {
73
- "input": "$daily_data",
74
- "as": "daily",
75
- "cond": {
76
- "$and": [
77
- {
78
- "$gte": ["$$daily.date", start_date]
79
- },
80
- {
81
- "$lte": ["$$daily.date", end_date]
82
- },
83
- ]
84
- },
85
- }
86
- },
87
- # 2.2 Filter monthly_data
88
- "monthly_data": {
89
- "$filter": {
90
- "input": "$monthly_data",
91
- "as": "monthly",
92
- "cond": {
93
- "$or": [
94
- {
95
- "$and": [
96
- {
97
- "$eq":
98
- ["$$monthly.year", start_year]
99
- },
100
- {
101
- "$gte": [
102
- "$$monthly.month",
103
- start_month
104
- ]
105
- },
106
- ]
107
- },
108
- {
109
- "$and": [
110
- {
111
- "$eq":
112
- ["$$monthly.year", end_year]
113
- },
114
- {
115
- "$lte":
116
- ["$$monthly.month", end_month]
117
- },
118
- ]
119
- },
120
- {
121
- "$and": [
122
- {
123
- "$gt":
124
- ["$$monthly.year", start_year]
125
- },
126
- {
127
- "$lt":
128
- ["$$monthly.year", end_year]
129
- },
130
- ]
131
- },
132
- ]
133
- },
134
- }
135
- },
136
- # 2.3 Filter seasonal_data
137
- "seasonal_data": {
138
- "$filter": {
139
- "input": "$seasonal_data",
140
- "as": "seasonal",
141
- "cond": {
142
- "$or": [
143
- {
144
- "$and": [
145
- {
146
- "$eq": [
147
- "$$seasonal.year",
148
- start_year
149
- ]
150
- },
151
- {
152
- "$gte": [
153
- "$$seasonal.season",
154
- start_season
155
- ]
156
- },
157
- ]
158
- },
159
- {
160
- "$and": [
161
- {
162
- "$eq":
163
- ["$$seasonal.year", end_year]
164
- },
165
- {
166
- "$lte": [
167
- "$$seasonal.season",
168
- end_season
169
- ]
170
- },
171
- ]
172
- },
173
- {
174
- "$and": [
175
- {
176
- "$gt": [
177
- "$$seasonal.year",
178
- start_year
179
- ]
180
- },
181
- {
182
- "$lt":
183
- ["$$seasonal.year", end_year]
184
- },
185
- ]
186
- },
187
- ]
188
- },
189
- }
190
- },
191
- "yearly_data": {
192
- "$filter": {
193
- "input": "$yearly_data",
194
- "as": "yearly",
195
- "cond": {
196
- "$and": [
197
- {
198
- "$gte": ["$$yearly.year", 107]
199
- },
200
- {
201
- "$lte": ["$$yearly.year", end_year]
202
- },
203
- ]
204
- },
205
- }
206
- }
207
- }
208
- },
209
- ]
210
-
211
- def query_data(self, ticker, start_date, end_date):
212
- """
213
- Return : Dict {
214
- 'ticker' : <ticker>,
215
- 'company_name': <company_name>,
216
- 'daily_data': List[Dict]
217
- 'monthly_data': List[Dict]
218
- 'seasonal_data': List[Dict]
219
- }
220
- """
221
-
222
- self._default_query(ticker, start_date, end_date)
223
-
224
- fetched_datas = list(self.collection.aggregate(self.pipeline))
225
-
226
- return fetched_datas[0]
227
-
228
- def query_values(self, ticker, start_date, end_date):
229
- self._default_query(ticker, start_date, end_date)
230
-
231
- self.pipeline.append({
232
- "$project": {
233
- "ticker": 1,
234
- "company_name": 1,
235
-
236
- # Transform daily_data to include only index and date
237
- "daily_data": {
238
- "$map": {
239
- "input": "$daily_data",
240
- "as": "daily_item",
241
- "in": {
242
- "date": "$$daily_item.date",
243
- "close": "$$daily_item.close",
244
- "P_B": "$$daily_item.P_B",
245
- "P_E": "$$daily_item.P_E",
246
- "P_FCF": "$$daily_item.P_FCF",
247
- "P_S": "$$daily_item.P_S",
248
- "EV_OPI": "$$daily_item.EV_OPI",
249
- "EV_EBIT": "$$daily_item.EV_EBIT",
250
- "EV_EBITDA": "$$daily_item.EV_EBITDA",
251
- "EV_S": "$$daily_item.EV_S"
252
- }
253
- }
254
- },
255
- "yearly_data": 1
256
- }
257
- })
258
-
259
- fetched_datas = list(self.collection.aggregate(self.pipeline))
260
-
261
- return fetched_datas[0]
262
-
263
- def query_stock_price(self, ticker, start_date, end_date):
264
-
265
- self.pipeline.append({
266
- "$project": {
267
- "ticker": 1,
268
- "company_name": 1,
269
-
270
- # Transform daily_data to include only index and date
271
- "daily_data": {
272
- "$map": {
273
- "input": "$daily_data",
274
- "as": "daily_item",
275
- "in": {
276
- "date": "$$daily_item.date",
277
- "open": "$$daily_item.open",
278
- "high": "$$daily_item.high",
279
- "los": "$$daily_item.low",
280
- "close": "$$daily_item.close",
281
- }
282
- }
283
- },
284
- }
285
- })
286
-
287
- fetched_datas = list(self.collection.aggregate(self.pipeline))
288
-
289
- return fetched_datas[0]
290
-
291
- def query_seasonal_data(self, ticker, start_date, end_date, sheet, target_season):
292
-
293
- self._default_query(ticker, start_date, end_date)
294
- self.pipeline.append({
295
- "$project": {
296
- "ticker": 1,
297
- "company_name": 1,
298
- "seasonal_data": {
299
- "$filter":{
300
- "input":{
301
- "$map": {
302
- "input": "$seasonal_data",
303
- "as": "seasonal_item",
304
- "in": {
305
- "year": "$$seasonal_item.year",
306
- "season": "$$seasonal_item.season",
307
- f"{sheet}": f"$$seasonal_item.{sheet}"
308
- }
309
- }
310
- },
311
- "as": "filtered_item",
312
- "cond": { "$eq": ["$$filtered_item.season", target_season] }
313
- }
314
- },
315
- }
316
- })
317
-
318
- self.pipeline.append({"$unwind": "$seasonal_data"})
319
-
320
- self.pipeline.append(
321
- {"$sort": {
322
- "seasonal_data.year": 1,
323
- "seasonal_data.season": 1
324
- }})
325
-
326
- fetched_datas = list(self.collection.aggregate(self.pipeline))
327
-
328
- return fetched_datas
329
-
330
- def query_month_revenue(self, ticker, start_date, end_date):
331
- self._default_query(ticker, start_date, end_date)
332
- self.pipeline.append(
333
- {
334
- "$project": {
335
- "ticker": 1,
336
- "company_name": 1,
337
- "monthly_data": {
338
- "$sortArray": {
339
- "input": "$monthly_data",
340
- "sortBy": {
341
- "year": 1,
342
- "month": 1
343
- }
344
- }
345
- },
346
- }
347
- }, )
348
-
349
- fetched_datas = list(self.collection.aggregate(self.pipeline))
350
-
351
- return fetched_datas[0]
352
-
353
- def query_latest_values(self, ticker):
354
- """
355
- 傳回最近一天的價值面
356
-
357
- return : Dict {
358
- "ticker": 股票代碼,
359
- "company_name": 公司中文名稱,
360
- ## 以下八個是iFa項目
361
- "P_E": 本益比,
362
- "P_B": 股價,
363
- "P_FCF": 股價自由現金流比,
364
- "P_S": 股價營收比,
365
- "EV_EBIT: ,
366
- "EV_EBITDA": ,
367
- "EV_OPI": ,
368
- "EV_S"; ,
369
- ## 以上八個是iFa項目
370
- "close": 收盤價,
371
- "EV": 市場價值
372
- }
373
- """
374
- today = date.today()
375
- yesterday = (today - timedelta(days=14)).strftime("%Y-%m-%d")
376
- today = today.strftime("%Y-%m-%d")
377
-
378
- fetched_datas = self.query_values(ticker, yesterday, today)
379
-
380
- daily_data = fetched_datas.pop('daily_data')
381
- fetched_datas.update(daily_data[-1])
382
- return fetched_datas
383
-
384
- def query_latest_month_revenue(self, ticker):
385
- """
386
- 傳回最新一期的月營收
387
- """
388
-
389
- today = date.today()
390
-
391
- last_month = (today - timedelta(days=30)).strftime("%Y-%m-%d")
392
- today = today.strftime("%Y-%m-%d")
393
-
394
- fetched_datas = self.query_month_revenue(ticker, last_month, today)
395
-
396
- print(fetched_datas)
397
-
398
- latest_month_revenue = fetched_datas['monthly_data']
399
- fetched_datas.pop('monthly_data')
400
-
401
- fetched_datas.update(latest_month_revenue[-1])
402
-
403
- return fetched_datas
404
-
405
- def query_latest_seasonal_data(self, ticker):
406
- """
407
- 傳回最新一期的季報
408
- """
409
- today = date.today()
410
-
411
- last_season = (today - timedelta(days=90)).strftime("%Y-%m-%d")
412
- today = today.strftime("%Y-%m-%d")
413
-
414
- fetched_datas = self.query_seasonal_data(ticker, last_season, today)
415
-
416
- print(fetched_datas)
417
-
418
- latest_seasonal_data = fetched_datas['seasonal_data']
419
- fetched_datas.pop('seasonal_data')
420
-
421
- fetched_datas.update(latest_seasonal_data[-1])
422
-
423
- return fetched_datas
424
-
425
- def get_value_sheet(self, ticker):
426
- """
427
- iFa.ai: 價值投資-> 市場指標
428
- """
429
- """
430
- 傳回最近一天的價值面
431
-
432
- return : Dict {
433
- "ticker": 股票代碼,
434
- "company_name": 公司中文名稱,
435
- "daily_data":{
436
- ## 以下八個是iFa項目
437
- "P_E": 本益比,
438
- "P_B": 股價,
439
- "P_FCF": 股價自由現金流比,
440
- "P_S": 股價營收比,
441
- "EV_EBIT: ,
442
- "EV_EBITDA": ,
443
- "EV_OPI": ,
444
- "EV_S";
445
- ## 以上八個是iFa項目
446
- "close": 收盤價,
447
- }
448
-
449
- "yearly_data": pd.DataFrame (下表格為範例)
450
- year P_E P_FCF P_B P_S EV_OPI EV_EBIT EV_EBITDA EV_S
451
- 0 107 16.68 29.155555 3.71 11.369868 29.837201 28.798274 187.647704 11.107886
452
- 1 108 26.06 67.269095 5.41 17.025721 50.145736 47.853790 302.526388 17.088863
453
- 2 109 27.98 95.650723 7.69 22.055379 53.346615 51.653834 205.847232 22.481951
454
- 3 110 27.83 149.512474 7.68 22.047422 55.398018 54.221387 257.091893 22.615355
455
- 4 111 13.11 48.562021 4.25 11.524975 24.683850 24.226554 66.953260 12.129333
456
- 5 112 17.17 216.371410 4.59 16.419533 40.017707 37.699267 105.980652 17.127656
457
- }
458
- """
459
- today = date.today()
460
- this_year = today.year - 1911
461
- yesterday = (today - timedelta(days=14)).strftime("%Y-%m-%d")
462
- today = today.strftime("%Y-%m-%d")
463
-
464
- fetched_datas = self.query_values(ticker, yesterday, today)
465
-
466
- fetched_datas['daily_data'] = fetched_datas['daily_data'][-1]
467
-
468
- latest_data = {"year": f"過去4季"}
469
-
470
- latest_data.update(fetched_datas['daily_data'])
471
- latest_data.pop("date")
472
- latest_data.pop("close")
473
-
474
- fetched_datas['yearly_data'].append(latest_data)
475
-
476
- fetched_datas['yearly_data'] = pd.DataFrame.from_dict(
477
- fetched_datas['yearly_data'])
478
-
479
- return fetched_datas
480
-
481
- def get_month_revenue_sheet(self, ticker):
482
- """
483
- iFa.ai: 財務分析 -> 每月營收
484
-
485
- return: Dict {
486
- 'ticker': str,
487
- 'company_name': str,
488
- 'month_revenue': pd.DataFrame (歷年的月營收以及到今年最新月份累計的月營收表格)
489
- 'this_month_revenue_over_years': pd.DataFrame (今年這個月的月營收與歷年同月份的營收比較)
490
- 'grand_total_over_years': pd.DataFrame (累計至今年這個月的月營收與歷年的比較)
491
- }
492
- """
493
-
494
- today = datetime.today()
495
- today = today.strftime("%Y-%m-%d")
496
-
497
- start_date = "2014-01-01"
498
-
499
- query_data = self.query_month_revenue(ticker, start_date, today)
500
-
501
- monthly_data = query_data['monthly_data']
502
-
503
- this_month = monthly_data[-1]["month"]
504
-
505
- month_dict = {i: None for i in range(1, 13)}
506
- month_dict[f"grand_total"] = None
507
-
508
- monthly_dict = dict()
509
-
510
- revenue_by_year = dict()
511
- single_month_revenue_dict = {
512
- "revenue": None,
513
- "MoM": None,
514
- "YoY": None,
515
- "YoY_3": None,
516
- "YoY_5": None,
517
- "YoY_10": None
518
- }
519
-
520
- grand_total_by_year = dict()
521
- grand_total_dict = {
522
- "revenue": None,
523
- "MoM": None,
524
- "YoY": None,
525
- "YoY_3": None,
526
- "YoY_5": None,
527
- "YoY_10": None
528
- }
529
-
530
- for data in monthly_data:
531
- try:
532
- monthly_dict[data['year']][data['month']] = data['revenue']
533
- except:
534
- monthly_dict[data['year']] = month_dict.copy()
535
- monthly_dict[data['year']][data['month']] = data['revenue']
536
-
537
- try:
538
- if (data['last_year_revenue']
539
- != monthly_dict[data['year'] - 1][data['month']]):
540
- monthly_dict[data['year'] -
541
- 1][data['month']] = data['last_year_revenue']
542
- except:
543
- pass
544
-
545
- if (data['month'] == this_month):
546
- monthly_dict[
547
- data['year']][f"grand_total"] = data['grand_total']
548
-
549
- single_month_revenue_dict['revenue'] = data["revenue"]
550
- single_month_revenue_dict['YoY'] = data[
551
- "revenue_increment_ratio"]
552
-
553
- grand_total_dict['revenue'] = data["grand_total"]
554
- grand_total_dict['YoY'] = data['grand_total_increment_ratio']
555
-
556
- revenue_by_year[
557
- data['year']] = single_month_revenue_dict.copy()
558
- grand_total_by_year[data['year']] = grand_total_dict.copy()
559
-
560
- query_data['month_revenue'] = pd.DataFrame(monthly_dict)
561
- query_data['this_month_revenue_over_years'] = pd.DataFrame(
562
- revenue_by_year)
563
- query_data['grand_total_over_years'] = pd.DataFrame(
564
- grand_total_by_year)
565
-
566
- query_data.pop("monthly_data")
567
-
568
- return query_data
569
-
570
- def _expand_value_percentage(self, dataframe):
571
-
572
- expanded_columns = {}
573
- for col in dataframe.columns:
574
- # Use json_normalize to split 'value' and 'percentage'
575
- expanded_df = pd.json_normalize(
576
- dataframe[col]).add_prefix(f"{col}_")
577
- expanded_df.index = dataframe.index
578
- # Append the expanded columns to the new DataFrame
579
- expanded_columns[col] = expanded_df
580
-
581
- expanded_df = pd.concat(expanded_columns.values(), axis=1)
582
-
583
- return expanded_df
584
-
585
- def _get_today(self):
586
- today = datetime.today()
587
- this_year = today.year
588
- this_month = today.month
589
- this_day = today.day
590
-
591
- return {
592
- "today": today,
593
- "year": this_year,
594
- "month": this_month,
595
- "day": this_day,
596
- }
597
-
598
- def get_balance_sheet(self, ticker):
599
- """
600
- iFa.ai: 財務分析 -> 資產負債表
601
-
602
- Return: Dict
603
- {
604
- 'ticker': 股票代碼,
605
- 'company_name': 公司名稱,
606
-
607
- 'balance_sheet': 歷年當季資場負債表"全表" (pd.DataFrame)
608
- 'total_asset': 歷年當季資產總額 (pd.DataFrame)
609
- 'current_asset': 歷年當季流動資產總額 (pd.DataFrame)
610
- 'non_current_asset': 歷年當季非流動資產 (pd.DataFrame)
611
- 'current_debt': 歷年當季流動負債 (pd.DataFrame)
612
- 'non_current_debt': 歷年當季非流動負債 (pd.DataFrame)
613
- 'equity': : 歷年當季權益 (pd.DataFrame)
614
- }
615
- """
616
- today_dict = self._get_today()
617
-
618
- today = today_dict['today']
619
- target_season = ((today.month - 1) // 3) + 1
620
-
621
- start_date = "2014-01-01"
622
- end_date = today.strftime("%Y-%m-%d")
623
-
624
- query_data = self.query_seasonal_data(ticker, start_date, end_date,
625
- "balance_sheet", target_season=target_season)
626
-
627
- return_dict = {
628
- "ticker": query_data[0]['ticker'],
629
- "company_name": query_data[0]['company_name'],
630
- }
631
-
632
- index_names = []
633
-
634
- table_dict = dict()
635
- total_asset_dict = dict()
636
- current_asset_dict = dict()
637
- non_current_asset_dict = dict()
638
- current_debt_dict = dict()
639
- non_current_debt_dict = dict()
640
- equity_dict = dict()
641
-
642
- this_season = query_data[-1]['seasonal_data']['season']
643
-
644
- value_type_list = ['value', 'percentage']
645
-
646
- for data in query_data:
647
- year = data['seasonal_data']['year']
648
- season = data['seasonal_data']['season']
649
-
650
- time_index = f"{year}Q{season}"
651
-
652
- if (season == this_season):
653
- try:
654
- table_dict[time_index] = data['seasonal_data'][
655
- 'balance_sheet']
656
- except:
657
- table_dict[time_index] = dict()
658
- table_dict[time_index] = data['seasonal_data'][
659
- 'balance_sheet']
660
-
661
- try:
662
- total_asset_dict[time_index] = {
663
- "total_asset":
664
- data['seasonal_data']['balance_sheet']['資產總額'],
665
- "total_debt":
666
- data['seasonal_data']['balance_sheet']['負債總額'],
667
- "total_equity":
668
- data['seasonal_data']['balance_sheet']['權益總額'],
669
- }
670
- except:
671
- total_asset_dict[time_index] = {
672
- "total_asset": None,
673
- "total_debt": None,
674
- "total_equity": None,
675
- }
676
-
677
- for value_type in value_type_list:
678
- try:
679
- current_asset_dict[
680
- f"{time_index}_{value_type}"] = data[
681
- 'seasonal_data']['balance_sheet']['流動資產合計'][
682
- value_type]
683
- except:
684
- if (time_index not in current_asset_dict.keys()):
685
- current_asset_dict[
686
- f"{time_index}_{value_type}"] = None
687
-
688
- try:
689
- non_current_asset_dict[
690
- f"{time_index}_{value_type}"] = data[
691
- 'seasonal_data']['balance_sheet']['非流動資產合計'][
692
- value_type]
693
- except:
694
- non_current_asset_dict[
695
- f"{time_index}_{value_type}"] = None
696
-
697
- try:
698
- current_debt_dict[f"{time_index}_{value_type}"] = data[
699
- 'seasonal_data']['balance_sheet']['流動負債合計'][
700
- value_type]
701
- except:
702
- current_debt_dict[f"{time_index}_{value_type}"] = None
703
-
704
- try:
705
- non_current_debt_dict[
706
- f"{time_index}_{value_type}"] = data[
707
- 'seasonal_data']['balance_sheet']['非流動負債合計'][
708
- value_type]
709
- except:
710
- non_current_debt_dict[
711
- f"{time_index}_{value_type}"] = None
712
-
713
- try:
714
- equity_dict[f"{time_index}_{value_type}"] = data[
715
- 'seasonal_data']['balance_sheet']['權益合計'][
716
- value_type]
717
- except:
718
- equity_dict[f"{time_index}_{value_type}"] = None
719
-
720
- index_names += list(
721
- data['seasonal_data']['balance_sheet'].keys())
722
-
723
- index_names = list(dict.fromkeys(index_names))
724
-
725
- balance_sheet_table = pd.DataFrame(table_dict)
726
- balance_sheet_table = self._expand_value_percentage(
727
- balance_sheet_table)
728
-
729
- total_asset_table = pd.DataFrame(total_asset_dict)
730
- total_asset_table = self._expand_value_percentage(total_asset_table)
731
-
732
- current_asset_table = pd.DataFrame(current_asset_dict,
733
- index=['current_asset'])
734
- non_current_asset_table = pd.DataFrame(non_current_asset_dict,
735
- index=['non_current_asset'])
736
- current_debt_table = pd.DataFrame(non_current_asset_dict,
737
- index=['current_debt'])
738
- non_current_debt_table = pd.DataFrame(non_current_asset_dict,
739
- index=['non_current_debt'])
740
- equity_table = pd.DataFrame(non_current_asset_dict, index=['equity'])
741
-
742
- return_dict['balance_sheet'] = balance_sheet_table
743
- return_dict['total_asset'] = total_asset_table
744
- return_dict['current_asset'] = current_asset_table
745
- return_dict['non_current_asset'] = non_current_asset_table
746
- return_dict['current_debt'] = current_debt_table
747
- return_dict['non_current_debt'] = non_current_debt_table
748
- return_dict['equity'] = equity_table
749
- return return_dict
750
-
751
- def _gen_dict(self,
752
- query_data,
753
- target_season,
754
- keys,
755
- calculate_type='value',
756
- calculate_grand_total=False):
757
- """
758
- Will be deprecated
759
- """
760
- assert(calculate_type in ['growth_rate', 'value', 'percentage']), "args: calculate_type Error"
761
- table_dict = dict()
762
- grand_total_dict = dict() if (calculate_grand_total) else None
763
-
764
- for data in query_data:
765
- if (calculate_grand_total
766
- and data['seasonal_data']['season'] <= target_season):
767
- time_index = f"{data['seasonal_data']['year']}Q{target_season}"
768
- profit_lose = data['seasonal_data']['profit_lose']
769
-
770
- for key in keys:
771
- try:
772
- if (calculate_type in ['growth_rate']):
773
- for growth_rate in ['YoY_1', 'YoY_3', 'YoY_5', 'YoY_10']:
774
- try:
775
- grand_total_dict[time_index][
776
- growth_rate] += profit_lose[key][growth_rate]
777
- except Exception:
778
- if (time_index not in
779
- grand_total_dict.keys()):
780
- grand_total_dict[time_index] = {
781
- "YoY": None,
782
- "YoY_3": None,
783
- "YoY_5": None,
784
- "YoY_10": None,
785
- }
786
- grand_total_dict[time_index][
787
- growth_rate] = profit_lose[key][growth_rate]
788
-
789
- elif (calculate_type in ['percentage']):
790
- grand_total_dict[time_index] += profit_lose[key][
791
- calculate_type] / target_season
792
- else:
793
- grand_total_dict[time_index] += profit_lose[key][
794
- calculate_type]
795
- break
796
- except KeyError:
797
- try:
798
- if (calculate_type
799
- in ['percentage']):
800
- grand_total_dict[time_index] = profit_lose[
801
- key][calculate_type] / target_season
802
- else:
803
- grand_total_dict[time_index] = profit_lose[
804
- key][calculate_type]
805
- break
806
- except: # key in profit_lose not found or not growth_rate not implemented
807
- continue
808
- except Exception: # Other exceotion
809
- continue
810
- else: # All keys not found
811
- grand_total_dict[time_index] = None
812
-
813
- if (data['seasonal_data']['season'] == target_season):
814
- time_index = f"{data['seasonal_data']['year']}Q{target_season}"
815
- profit_lose = data['seasonal_data']['profit_lose']
816
-
817
- for key in keys:
818
- try:
819
- if (calculate_type in ['growth_rate']):
820
- for item in items:
821
- table_dict[time_index][item] = profit_dict[key][item]
822
- else:
823
- table_dict[time_index] = profit_lose[key][
824
- calculate_type]
825
- break
826
- except Exception:
827
- continue
828
- else:
829
- if (calculate_type == 'growth_rate'):
830
- table_dict[time_index] = {
831
- "YoY_1": None,
832
- "YoY_3": None,
833
- "YoY_5": None,
834
- "YoY_10": None
835
- }
836
- else:
837
- table_dict[time_index] = None
838
- return table_dict, grand_total_dict
839
-
840
- def _slice_multi_col_table(
841
- self,
842
- total_table,
843
- mode='value',
844
- target_index=None, # None or Str, 要特別抓哪個index
845
- ):
846
- times = total_table.columns.get_level_values(0).unique()
847
- try:
848
- target_metrics = self.target_metric_dict[mode]
849
- except KeyError as e:
850
- return f"mode Error: Get mode should be {list(self.target_metric_dict.keys())} but get {mode}"
851
-
852
- desired_order = [
853
- (time, value_name) for time in times for value_name in target_metrics
854
- ]
855
-
856
- if (target_index):
857
- sliced_table = total_table.loc[[target_index], pd.IndexSlice[:, target_metrics]][desired_order].T
858
- sliced_table = sliced_table.reset_index()
859
- sliced_table = sliced_table.pivot(index='level_1', columns='level_0', values=target_index)
860
- sliced_table.columns.name = None
861
- sliced_table.index.name = None
862
- return sliced_table.reindex(target_metrics)
863
-
864
- else:
865
- return total_table.loc[:, pd.IndexSlice[:, target_metrics]][desired_order]
866
-
867
- def get_profit_lose(self, ticker):
868
- """
869
- ticker: str
870
- iFa.ai: 財務分析 -> 損益表
871
- """
872
- today_dict = self._get_today()
873
-
874
- table_settings = StatsProcessor.load_yaml("profit_lose.yaml")
875
- today = today_dict['today'].strftime("%Y-%m-%d")
876
- start_date = "2014-01-01"
877
-
878
- this_season = ((today_dict['month'] - 1) // 3)
879
- this_season = 4 if (this_season == 0) else this_season - 1
880
- # TODO: 將這裡改成根據每公司的最後更新季度
881
-
882
- query_data = self.query_seasonal_data(ticker,
883
- start_date=start_date,
884
- end_date=today,
885
- sheet='profit_lose',
886
- target_season=this_season)
887
-
888
- index_names = []
889
-
890
- return_dict = {
891
- "ticker": query_data[0]['ticker'],
892
- "company_name": query_data[0]['company_name'],
893
- }
894
-
895
- table_dict = dict()
896
- grand_total_dict = dict()
897
-
898
- column_names = []
899
-
900
- for data in query_data:
901
- year = data['seasonal_data']['year']
902
- season = data['seasonal_data']['season']
903
-
904
- time_index = f"{year}Q{season}"
905
-
906
- index_names += list(
907
- data['seasonal_data']['profit_lose'].keys())
908
-
909
- profit_lose = data['seasonal_data']['profit_lose']
910
-
911
- for index_name, value_dict in profit_lose.items():
912
- column_names += [
913
- (time_index, index_name, item_name)
914
- for item_name in value_dict.keys()
915
- ]
916
- for item_name, item in value_dict.items():
917
- try:
918
- table_dict[index_name][(time_index, item_name)] = item
919
- #[time_index][index_name][item_name] = item
920
-
921
- except KeyError:
922
- if (index_name not in table_dict.keys()):
923
- table_dict[index_name] = dict()
924
- grand_total_dict[index_name] = dict()
925
-
926
- table_dict[index_name][(time_index, item_name)] = item
927
-
928
- columns = pd.MultiIndex.from_tuples(table_dict.keys())
929
- total_table = pd.DataFrame.from_dict(
930
- table_dict,
931
- orient='index'
932
- )
933
-
934
- total_table.columns = pd.MultiIndex.from_tuples(total_table.columns)
935
-
936
- for name, setting in table_settings.items():
937
- return_dict[name] = self._slice_multi_col_table(
938
- total_table=total_table,
939
- mode=setting['mode'],
940
- target_index=setting['target_index'] if "target_index" in setting.keys() else None
941
- )
942
-
943
- return return_dict
944
-
945
- def get_cash_flow(self, ticker):
946
- """
947
- iFa.ai: 財務分析 -> 現金金流表
948
- """
949
- today_dict = self._get_today()
950
-
951
- today = today_dict['today']
952
- this_season = (today.month - 1) // 3 + 1
953
- start_date = "2014-01-01"
954
- end_date = today.strftime("%Y-%m-%d")
955
-
956
- query_data = self.query_seasonal_data(ticker,
957
- start_date=start_date,
958
- end_date=end_date,
959
- sheet='cash_flow',
960
- target_season=this_season)
961
-
962
- index_names = []
963
-
964
- return_dict = {
965
- "ticker": query_data[0]['ticker'],
966
- "company_name": query_data[0]['company_name'],
967
- }
968
-
969
- table_dict = dict()
970
- CASHO_dict = dict() # 營業活動
971
- CASHI_dict = dict() # 投資活動
972
- CASHF_dict = dict() # 籌資活動
973
-
974
- this_season = query_data[-1]['seasonal_data']['season']
975
-
976
- checkpoints = ["營業活動之現金流量-間接法", "投資活動之現金流量", "籌資活動之現金流量"]
977
- main_cash_flows = [
978
- "營業活動之淨現金流入(流出)", "投資活動之淨現金流入(流出)", "籌資活動之淨現金流入(流出)"
979
- ]
980
-
981
- partial_cash_flows = [CASHO_dict, CASHI_dict, CASHF_dict]
982
-
983
- for data in query_data:
984
- year = data['seasonal_data']['year']
985
- season = data['seasonal_data']['season']
986
-
987
- time_index = f"{year}Q{season}"
988
-
989
- if (season == this_season):
990
- cash_flow = data['seasonal_data']['cash_flow']
991
- main_cash_flow_name = None
992
- partial_cash_flow = None
993
- next_checkpoint = 0
994
-
995
- for index_name, value in cash_flow.items():
996
- if (next_checkpoint < 3
997
- and index_name == checkpoints[next_checkpoint]):
998
- main_cash_flow_name = main_cash_flows[next_checkpoint]
999
- partial_cash_flow = partial_cash_flows[next_checkpoint]
1000
- next_checkpoint += 1
1001
- try:
1002
- table_dict[time_index][index_name]['value'] = value[
1003
- 'value']
1004
- if (value['value']):
1005
- table_dict[time_index][index_name][
1006
- 'percentage'] = value['value'] / cash_flow[
1007
- main_cash_flow_name]['value']
1008
- else:
1009
- table_dict[time_index][index_name][
1010
- 'percentage'] = None
1011
- except:
1012
- if (time_index not in table_dict.keys()):
1013
- table_dict[time_index] = dict()
1014
- table_dict[time_index][index_name] = dict()
1015
-
1016
- table_dict[time_index][index_name]['value'] = value[
1017
- 'value']
1018
- if (value['value']):
1019
- table_dict[time_index][index_name][
1020
- 'percentage'] = value['value'] / cash_flow[
1021
- main_cash_flow_name]['value']
1022
- else:
1023
- table_dict[time_index][index_name][
1024
- 'percentage'] = None
1025
-
1026
- try:
1027
- partial_cash_flow[time_index][index_name] = table_dict[
1028
- time_index][index_name]
1029
- except:
1030
- if (time_index not in partial_cash_flow.keys()):
1031
- partial_cash_flow[time_index] = dict()
1032
- partial_cash_flow[time_index][index_name] = table_dict[
1033
- time_index][index_name]
1034
-
1035
- index_names += list(cash_flow.keys())
1036
-
1037
- index_names = list(dict.fromkeys(index_names))
1038
-
1039
- cash_flow_table = pd.DataFrame(table_dict)
1040
- cash_flow_table = self._expand_value_percentage(cash_flow_table)
1041
-
1042
- CASHO_table = pd.DataFrame(CASHO_dict)
1043
- CASHO_table = self._expand_value_percentage(CASHO_table)
1044
-
1045
- CASHI_table = pd.DataFrame(CASHI_dict)
1046
- CASHI_table = self._expand_value_percentage(CASHI_table)
1047
-
1048
- CASHF_table = pd.DataFrame(CASHF_dict)
1049
- CASHF_table = self._expand_value_percentage(CASHF_table)
1050
-
1051
- return_dict['cash_flow'] = cash_flow_table
1052
- return_dict['CASHO'] = CASHO_table
1053
- return_dict['CASHI'] = CASHI_table
1054
- return_dict['CASHF'] = CASHF_table
1055
-
1056
- return return_dict