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
@@ -11,81 +11,69 @@ class CashFlowFetcher(StatsFetcher):
11
11
  super().__init__(ticker, db_client)
12
12
 
13
13
  self.cash_flow_dict = StatsProcessor.load_yaml(
14
- "cash_flow_percentage.yaml"
14
+ "twse/cash_flow_percentage.yaml"
15
15
  ) # 計算子表格用
16
+
17
+ self.process_function_map = {
18
+ "twse_stats": self.process_data_twse,
19
+ "us_stats": self.process_data_us
20
+ }
16
21
 
17
- def prepare_query(self, target_season):
22
+ def prepare_query(self):
18
23
  pipeline = super().prepare_query()
19
24
 
20
- pipeline.append({
21
- "$project": {
22
- "_id": 0,
23
- "ticker": 1,
24
- "company_name": 1,
25
- "cash_flows": {
26
- "$sortArray": {
27
- "input": {
28
- "$map": {
29
- "input": {
30
- "$filter": {
31
- "input": "$seasonal_data",
32
- "as": "season",
33
- "cond": {
34
- "$eq":
35
- ["$$season.season", target_season]
36
- }
37
- }
38
- },
39
- "as": "target_season_data",
40
- "in": {
41
- "year":
42
- "$$target_season_data.year",
43
- "season":
44
- "$$target_season_data.season",
45
- "cash_flow":
46
- "$$target_season_data.cash_flow"
47
- }
48
- }
49
- },
50
- "sortBy": {
51
- "year": -1
52
- } # 按 year 降序排序
53
- }
25
+ name_map = {
26
+ "twse_stats": "cash_flow",
27
+ "us_stats": "cash_flow"
28
+ }
29
+
30
+
31
+ chart_name = name_map.get(self.collection_name, "cash_flow")
32
+
33
+ append_pipeline = [
34
+ {
35
+ "$unwind": "$seasonal_data" # 展開 seasonal_data 陣列
36
+ },
37
+ {
38
+ "$project": {
39
+ "_id": 0,
40
+ "ticker": 1,
41
+ "company_name": 1,
42
+ "year": "$seasonal_data.year",
43
+ "season": "$seasonal_data.season",
44
+ "cash_flow": {
45
+ "$ifNull": [f"$seasonal_data.{chart_name}", []]
46
+ } # 避免 null
47
+ }
48
+ },
49
+ {
50
+ "$sort": {
51
+ "year": -1,
52
+ "season": -1
54
53
  }
55
54
  }
56
- })
55
+ ]
56
+
57
+ pipeline = pipeline + append_pipeline
57
58
 
58
59
  return pipeline
59
60
 
60
- def collect_data(self, target_season):
61
- pipeline = self.prepare_query(target_season)
62
-
63
- fetched_data = self.collection.aggregate(pipeline)
64
-
65
- return list(fetched_data)[0]
61
+ def collect_data(self):
62
+ return super().collect_data()
66
63
 
67
64
  def query_data(self):
68
-
69
- try:
70
- latest_time = StatsDateTime.get_latest_time(self.ticker, self.collection)['last_update_time']
71
- target_season = latest_time['seasonal_data']['latest_season']
72
-
73
- except:
74
- today = StatsDateTime.get_today()
75
- target_season = today.season - 1 if (today.season > 1) else 4
76
-
77
- fetched_data = self.collect_data(target_season)
78
-
79
- return self.process_data(fetched_data, target_season)
65
+ fetched_data = self.collect_data()
66
+
67
+ process_fn = self.process_function_map.get(self.collection_name, self.process_data_us)
68
+ return process_fn(fetched_data)
80
69
 
81
- def process_data(self, fetched_data, target_season):
70
+ def process_data_twse(self, fetched_data):
82
71
  """
83
72
  處理現金流量表頁面的所有表格
84
73
  金流表本身沒有比例 但是Ifa有算,
85
74
  項目所屬的情況也不一(分別所屬營業,投資,籌資三個活動)
86
75
  所以這裡選擇不用slicing處理
87
76
  """
88
- cash_flows = fetched_data['cash_flows']
89
77
 
90
78
  index_names = []
91
79
  column_names = []
@@ -95,15 +83,7 @@ class CashFlowFetcher(StatsFetcher):
95
83
  CASHI_dict = dict()
96
84
  CASHF_dict = dict()
97
85
 
98
- return_dict = {
99
- "ticker": fetched_data['ticker'],
100
- "company_name": fetched_data['company_name'],
101
- "cash_flow": dict(),
102
- "CASHO": dict(),
103
- "CASHI": dict(),
104
- "CASHF": dict()
105
- }
106
-
86
+ # 處理cash_flow 比例
107
87
  checkpoints = ["營業活動之現金流量-間接法", "投資活動之現金流量", "籌資活動之現金流量", "匯率變動對現金及約當現金之影響"]
108
88
  main_cash_flows = [
109
89
  "營業活動之淨現金流入(流出)", "投資活動之淨現金流入(流出)", "籌資活動之淨現金流入(流出)", None
@@ -112,67 +92,61 @@ class CashFlowFetcher(StatsFetcher):
112
92
 
113
93
  # 作法: dictionary中也有checkpoints,如果出現了就換下一個index去計算
114
94
 
115
- for data in cash_flows:
116
- year = data['year']
95
+ for data in fetched_data:
96
+ year, season, cash_flow = data['year'], data['season'], data['cash_flow']
117
97
 
118
- time_index = f"{year}Q{target_season}"
98
+ time_index = f"{year}Q{season}"
119
99
 
120
- cash_flow = data['cash_flow']
121
100
  main_cash_flow_name = None
122
101
  partial_cash_flow = None
123
102
  next_checkpoint = 0
124
103
 
125
- for index_name, value in cash_flow.items():
104
+ temp_dict = {}
105
+
106
+ for index_name, cash_flow_value in cash_flow.items():
126
107
  if (next_checkpoint < 3
127
108
  and index_name == checkpoints[next_checkpoint]): # 找到了主要的變動點
128
109
  main_cash_flow_name = main_cash_flows[next_checkpoint]
129
110
  partial_cash_flow = partial_cash_flows[next_checkpoint]
111
+ partial_cash_flow[time_index] = {}
130
112
  next_checkpoint += 1
113
+
114
+ if (isinstance(cash_flow_value, dict)):
115
+ value = cash_flow_value.get('value', None)
116
+ else:
117
+ value = cash_flow_value
118
+
119
+
120
+ main_value = cash_flow.get(main_cash_flow_name, None)
121
+ if (isinstance(main_value, dict)):
122
+ main_value = main_value.get('value', None)
123
+ else:
124
+ pass
125
+
131
126
  try:
132
- table_dict[time_index][index_name]['value'] = value[
133
- 'value']
134
- if (value['value']):
135
- ratio = np.round(
136
- (value['value'] / cash_flow[
137
- main_cash_flow_name]['value']) * 100, 2)
138
- table_dict[time_index][index_name][
139
- 'percentage'] = f"{ratio}%"
140
- else:
141
- table_dict[time_index][index_name][
142
- 'percentage'] = None
143
- except: # 新增index再做一次
144
- if (time_index not in table_dict.keys()):
145
- table_dict[time_index] = dict()
146
- table_dict[time_index][index_name] = dict()
147
-
148
- table_dict[time_index][index_name]['value'] = value[
149
- 'value']
150
- if (value['value']):
151
- ratio = np.round(
152
- (value['value'] / cash_flow[
153
- main_cash_flow_name]['value']) * 100, 2)
154
- table_dict[time_index][index_name][
155
- 'percentage'] = f"{ratio}%"
156
- else:
157
- table_dict[time_index][index_name][
158
- 'percentage'] = None
159
- table_dict[time_index][index_name]['value'] = StatsProcessor.cal_non_percentage(value['value'], postfix="千元")
160
- try:
161
- partial_cash_flow[time_index][index_name] = table_dict[
162
- time_index][index_name]
127
+ ratio = np.round(
128
+ (value / main_value) * 100, 2
129
+ )
130
+ ratio = f"{ratio}%"
163
131
  except:
164
- if (time_index not in partial_cash_flow.keys()):
165
- partial_cash_flow[time_index] = dict()
166
- partial_cash_flow[time_index][index_name] = table_dict[
167
- time_index][index_name]
132
+ ratio = None
133
+
134
+ value = StatsProcessor.cal_non_percentage(value, postfix="千元")
135
+ temp_dict[index_name] = {
136
+ "value" : value,
137
+ "percentage": ratio
138
+ }
139
+
140
+ partial_cash_flow[time_index][index_name] = temp_dict[index_name]
168
141
 
142
+ table_dict[time_index] = temp_dict
169
143
  index_names += list(cash_flow.keys())
170
144
 
171
145
  # 轉成dictionary keys
172
146
  index_names = list(dict.fromkeys(index_names))
173
147
 
174
148
  cash_flow_table = pd.DataFrame(table_dict)
175
- cash_flow_table = StatsProcessor.expand_value_percentage(cash_flow_table)
149
+ cash_flow_table_stats = StatsProcessor.expand_value_percentage(cash_flow_table)
176
150
 
177
151
  CASHO_table = pd.DataFrame(CASHO_dict)
178
152
  CASHO_table = StatsProcessor.expand_value_percentage(CASHO_table)
@@ -183,9 +157,44 @@ class CashFlowFetcher(StatsFetcher):
183
157
  CASHF_table = pd.DataFrame(CASHF_dict)
184
158
  CASHF_table = StatsProcessor.expand_value_percentage(CASHF_table)
185
159
 
186
- return_dict['cash_flow'] = cash_flow_table
187
- return_dict['CASHO'] = CASHO_table
188
- return_dict['CASHI'] = CASHI_table
189
- return_dict['CASHF'] = CASHF_table
160
+ for time_index in table_dict.keys():
161
+ table_dict[time_index] = self.flatten_dict(table_dict[time_index], index_names, target_keys=['value', 'percentage'])
162
+ cash_flow_flatten = pd.DataFrame.from_dict(table_dict)
163
+
164
+ target_season = fetched_data[0]['season']
165
+ target_season_column = cash_flow_flatten.columns.str.endswith(f"Q{target_season}")
166
+
167
+ return_dict = {
168
+ "ticker": self.ticker,
169
+ "company_name": fetched_data[-1]['company_name'],
170
+ "cash_flow": cash_flow_table_stats,
171
+ "CASHO": CASHO_table,
172
+ "CASHI": CASHI_table,
173
+ "CASHF": CASHF_table,
174
+ "cash_flow_all": cash_flow_flatten,
175
+ "cash_flow_YoY": cash_flow_flatten.loc[:, target_season_column]
176
+ }
177
+ return return_dict
178
+
179
+ def process_data_us(self, fetched_data):
180
+
181
+ table_dict = {
182
+ f"{data['year']}Q{data['season']}": data['cash_flow']
183
+ for data in fetched_data
184
+ }
185
+
186
+ cash_flow_df = pd.DataFrame.from_dict(table_dict)
190
187
 
188
+ latest_season = fetched_data[0]['season']
189
+ target_season_columns = cash_flow_df.columns.str.endswith(
190
+ f"Q{latest_season}"
191
+ )
192
+ cash_flow_df_YoY = cash_flow_df.loc[:, target_season_columns]
193
+
194
+ return_dict = {
195
+ "ticker": self.ticker,
196
+ "company_name": fetched_data[-1]['company_name'],
197
+ "cash_flow": cash_flow_df,
198
+ "cash_flow_YoY": cash_flow_df_YoY
199
+ }
191
200
  return return_dict
@@ -16,9 +16,9 @@ class FinanceOverviewFetcher(StatsFetcher):
16
16
  super().__init__(ticker, db_client)
17
17
 
18
18
  self.target_fields = StatsProcessor.load_yaml(
19
- "finance_overview_dict.yaml")
19
+ "twse/finance_overview_dict.yaml")
20
20
  self.inverse_dict = StatsProcessor.load_txt(
21
- "seasonal_data_field_dict.txt", json_load=True)
21
+ "twse/seasonal_data_field_dict.txt", json_load=True)
22
22
 
23
23
  def prepare_query(self, target_year, target_season):
24
24
 
@@ -149,7 +149,7 @@ class MonthRevenueFetcher(StatsFetcher):
149
149
  try:
150
150
  year = data['year'] - 1
151
151
  total = grand_total_dict[year][12]
152
- accum_YoY = round((data['grand_total'] / total) * 100, 2)
152
+ accum_YoY = round(((data['grand_total'] - total) / total) * 100, 2)
153
153
  accum_YoYs.append(f"{accum_YoY}%")
154
154
  except Exception as e:
155
155
  accum_YoYs.append(None)
@@ -3,11 +3,10 @@ import importlib.resources as pkg_resources
3
3
  import json
4
4
  import numpy as np
5
5
  import pandas as pd
6
- from ..utils import StatsDateTime, StatsProcessor
6
+ from ..utils import StatsDateTime, StatsProcessor, YoY_Calculator
7
7
  import yaml
8
8
 
9
9
 
10
-
11
10
  class ProfitLoseFetcher(StatsFetcher):
12
11
  """
13
12
  iFa.ai: 財務分析 -> 損益表
@@ -16,143 +15,219 @@ class ProfitLoseFetcher(StatsFetcher):
16
15
  def __init__(self, ticker, db_client):
17
16
  super().__init__(ticker, db_client)
18
17
 
19
- self.table_settings = StatsProcessor.load_yaml("profit_lose.yaml")
18
+ self.table_settings = StatsProcessor.load_yaml("twse/profit_lose.yaml")
19
+
20
+ self.process_function_map = {
21
+ "twse_stats": self.process_data_twse,
22
+ "us_stats": self.process_data_us
23
+ }
20
24
 
21
- def prepare_query(self, target_season):
25
+ def prepare_query(self):
22
26
  pipeline = super().prepare_query()
23
27
 
24
- pipeline.append({
25
- "$project": {
26
- "_id": 0,
27
- "ticker": 1,
28
- "company_name": 1,
29
- "profit_loses": {
30
- "$sortArray": {
31
- "input": {
32
- "$map": {
33
- "input": {
34
- "$filter": {
35
- "input": "$seasonal_data",
36
- "as": "season",
37
- "cond": {
38
- "$eq":
39
- ["$$season.season", target_season]
40
- }
41
- }
42
- },
43
- "as": "target_season_data",
44
- "in": {
45
- "year":
46
- "$$target_season_data.year",
47
- "season":
48
- "$$target_season_data.season",
49
- "profit_lose":
50
- "$$target_season_data.profit_lose"
51
- }
52
- }
53
- },
54
- "sortBy": {
55
- "year": -1
56
- } # 按 year 降序排序
57
- }
28
+ name_map = {"twse_stats": "profit_lose", "us_stats": "income_statement"}
29
+
30
+ chart_name = name_map.get(self.collection_name, "income_statement")
31
+
32
+ append_pipeline = [
33
+ {
34
+ "$unwind": "$seasonal_data" # 展開 seasonal_data 陣列
35
+ },
36
+ {
37
+ "$project": {
38
+ "_id": 0,
39
+ "ticker": 1,
40
+ "company_name": 1,
41
+ "year": "$seasonal_data.year",
42
+ "season": "$seasonal_data.season",
43
+ "profit_lose": {
44
+ "$ifNull": [f"$seasonal_data.{chart_name}", []]
45
+ } # 避免 null
46
+ }
47
+ },
48
+ {
49
+ "$sort": {
50
+ "year": -1,
51
+ "season": -1
58
52
  }
59
53
  }
60
- })
61
-
62
- return pipeline
54
+ ]
63
55
 
64
- def collect_data(self, target_season):
65
- pipeline = self.prepare_query(target_season)
56
+ pipeline = pipeline + append_pipeline
66
57
 
67
- fetched_data = self.collection.aggregate(pipeline)
58
+ return pipeline
68
59
 
69
- return list(fetched_data)[-1]
60
+ def collect_data(self):
61
+ return super().collect_data()
70
62
 
71
63
  def query_data(self):
72
- try:
73
- latest_time = StatsDateTime.get_latest_time(
74
- self.ticker, self.collection)['last_update_time']
75
- target_season = latest_time['seasonal_data']['latest_season']
76
- except Exception as e:
77
- today = StatsDateTime.get_today()
78
-
79
- target_season = today.season
80
- target_season = target_season - 1 if target_season > 1 else 4
81
-
82
- fetched_data = self.collect_data(target_season)
64
+ fetched_data = self.collect_data()
83
65
 
84
- return self.process_data(fetched_data, target_season)
66
+ process_fn = self.process_function_map.get(
67
+ self.collection_name, self.process_data_us
68
+ )
69
+ return process_fn(fetched_data)
85
70
 
86
- def process_data(self, fetched_data, target_season):
71
+ def process_data_twse(self, fetched_data):
87
72
 
88
- profit_loses = fetched_data['profit_loses']
73
+ latest_time = StatsDateTime.get_latest_time(
74
+ self.ticker, self.collection
75
+ ).get('last_update_time', {})
89
76
 
90
- index_names = []
91
-
92
- table_dict = dict()
93
- grand_total_dict = dict()
77
+ # 取最新時間資料時間,沒取到就預設去年年底
78
+ target_year = latest_time.get('seasonal_data', {}).get(
79
+ 'latest_target_year',
80
+ StatsDateTime.get_today().year - 1
81
+ )
82
+ target_season = latest_time.get(
83
+ 'seasonal_data',{}
84
+ ).get('latest_season', 4)
94
85
 
95
86
  return_dict = {
96
- "ticker": fetched_data['ticker'],
97
- "company_name": fetched_data['company_name'],
87
+ "ticker": self.ticker,
88
+ "company_name": fetched_data[-1]['company_name'],
98
89
  }
99
90
 
100
- for data in profit_loses:
101
- year = data['year']
102
-
103
- time_index = f"{year}Q{target_season}"
91
+ profit_lose_dict = {
92
+ f"{data['year']}Q{data['season']}": data['profit_lose']
93
+ for data in fetched_data
94
+ }
104
95
 
96
+ profit_lose_df = pd.DataFrame.from_dict(profit_lose_dict)
97
+ target_season_col = profit_lose_df.columns.str.endswith(
98
+ f"Q{target_season}"
99
+ )
100
+ profit_lose_df = profit_lose_df.loc[:, target_season_col]
101
+ profit_lose_df = StatsProcessor.expand_value_percentage(
102
+ profit_lose_df
103
+ )
104
+
105
+ value_col = profit_lose_df.columns.str.endswith(f"_value")
106
+ percentage_col = profit_lose_df.columns.str.endswith(f"_percentage")
107
+
108
+ grand_total_value_col = profit_lose_df.columns.str.endswith(
109
+ f"grand_total_value"
110
+ )
111
+ grand_total_percentage_col = profit_lose_df.columns.str.endswith(
112
+ f"grand_total_percentage"
113
+ )
114
+
115
+ profit_lose_stats_df = profit_lose_df.loc[:, (
116
+ (value_col & ~grand_total_value_col) |
117
+ (percentage_col & ~grand_total_percentage_col)
118
+ )]
119
+
120
+ for time_index, profit_lose in profit_lose_dict.items():
105
121
  # 蒐集整體的keys
106
- index_names += list(data['profit_lose'].keys())
107
- profit_lose = data['profit_lose']
108
-
109
- for index_name, value_dict in profit_lose.items():
110
- # (2020Q1, 項目, 金額或%)
111
- for item_name, item in value_dict.items():
112
- if ('percentage' in item_name):
113
- if (isinstance(item, (float, int))):
114
- item = StatsProcessor.cal_non_percentage(item, to_str=True, postfix="%")
115
- elif ('YoY' in item_name):
116
- if (isinstance(item, (float, int))):
117
- item = StatsProcessor.cal_percentage(item)
118
- elif ('每股盈餘' in index_name):
119
- if (isinstance(item, (float, int))):
120
- item = StatsProcessor.cal_non_percentage(item, postfix="元")
121
- else:
122
- if (isinstance(item, (float, int))):
123
- item = StatsProcessor.cal_non_percentage(item, postfix="千元")
124
- try:
125
- table_dict[index_name][(time_index, item_name)] = item
126
-
127
- except KeyError:
128
- if (index_name not in table_dict.keys()):
129
- table_dict[index_name] = dict()
130
- grand_total_dict[index_name] = dict()
131
-
132
- table_dict[index_name][(time_index, item_name)] = item
133
-
134
- total_table = pd.DataFrame.from_dict(table_dict, orient='index')
135
- total_table.columns = pd.MultiIndex.from_tuples(total_table.columns)
136
-
137
- total_table = total_table.replace("N/A", None)
122
+ index_names = list(profit_lose.keys())
123
+ target_keys = [
124
+ "value",
125
+ "percentage",
126
+ "grand_total",
127
+ "grand_total_percentage",
128
+ "YoY_1",
129
+ "YoY_3",
130
+ "YoY_5",
131
+ "YoY_10",
132
+ "grand_total_YoY_1",
133
+ "grand_total_YoY_3",
134
+ "grand_total_YoY_5",
135
+ "grand_total_YoY_10",
136
+ ]
137
+ # flatten dict
138
+ new_profit_lose = self.flatten_dict(
139
+ profit_lose, index_names, target_keys
140
+ )
141
+ profit_lose_dict[time_index] = new_profit_lose
142
+
143
+ profit_lose_df = pd.DataFrame.from_dict(profit_lose_dict)
144
+
145
+ # EPS的value用元計算
146
+ eps_index = profit_lose_df.index.str.endswith(
147
+ "_value"
148
+ ) & profit_lose_df.index.str.contains("每股盈餘")
149
+ profit_lose_df.loc[eps_index] = profit_lose_df.loc[
150
+ eps_index].apply(
151
+ lambda x: StatsProcessor.cal_non_percentage(x, postfix="元")
152
+ )
153
+
154
+ # percentage處理
155
+ percentage_index = profit_lose_df.index.str.endswith("percentage")
156
+ profit_lose_df.loc[percentage_index] = profit_lose_df.loc[
157
+ percentage_index].apply(
158
+ lambda x: StatsProcessor.
159
+ cal_non_percentage(x, to_str=True, postfix="%")
160
+ )
161
+
162
+ # YoY處理: 乘以100
163
+ YoY_index = profit_lose_df.index.str.contains("YoY")
164
+ profit_lose_df.loc[YoY_index] = profit_lose_df.loc[
165
+ YoY_index].apply(lambda x: StatsProcessor.cal_percentage(x))
166
+
167
+ # 剩下的處理: 乘以千元
168
+ value_index = ~(
169
+ percentage_index | YoY_index | profit_lose_df.index.isin(eps_index)
170
+ ) # 除了上述以外的 index
171
+ profit_lose_df.loc[value_index] = profit_lose_df.loc[
172
+ value_index].apply(
173
+ lambda x: StatsProcessor.cal_non_percentage(x, postfix="千元")
174
+ )
175
+
176
+ total_table = profit_lose_df.replace("N/A", None)
177
+
178
+ # 取特定季度
179
+ target_season_columns = total_table.columns.str.endswith(
180
+ f"Q{target_season}"
181
+ )
182
+ total_table_YoY = total_table.loc[:, target_season_columns]
138
183
 
139
184
  for name, setting in self.table_settings.items():
140
- if ('target_index' in setting.keys()):
141
- target_indexes = [target.strip() for target in setting['target_index']]
142
- else:
143
- target_indexes = [None]
144
-
185
+ target_indexes = setting.get('target_index', [None])
145
186
  for target_index in target_indexes:
146
187
  try:
147
- return_dict[name] = StatsProcessor.slice_multi_col_table(
148
- total_table=total_table,
188
+ return_dict[name] = StatsProcessor.slice_table(
189
+ total_table=total_table_YoY,
149
190
  mode=setting['mode'],
150
- target_index=target_index)
191
+ target_index=target_index
192
+ )
151
193
  break
152
194
  except Exception as e:
153
- return_dict[name] = StatsProcessor.slice_multi_col_table(
154
- total_table=total_table,
155
- mode=setting['mode'],
156
- target_index=target_index)
195
+ print(str(e))
196
+ continue
197
+
198
+ return_dict.update(
199
+ {
200
+ "profit_lose": profit_lose_stats_df,
201
+ "profit_lose_all": total_table.copy(),
202
+ "profit_lose_YoY": total_table_YoY
203
+ }
204
+ )
205
+ return return_dict
206
+
207
+ def process_data_us(self, fetched_data):
208
+
209
+ table_dict = {
210
+ f"{data['year']}Q{data['season']}": data['profit_lose']
211
+ for data in fetched_data
212
+ }
213
+
214
+ table_dict = YoY_Calculator.cal_QoQ(table_dict)
215
+ table_dict = YoY_Calculator.cal_YoY(table_dict)
216
+
217
+ for time_index, data_dict in table_dict.items():
218
+ table_dict[time_index] = self.flatten_dict(
219
+ value_dict=data_dict,
220
+ indexes=list(data_dict.keys()),
221
+ target_keys=["value", "growth"] +
222
+ [f"YoY_{i}" for i in [1, 3, 5, 10]]
223
+ )
224
+
225
+ # 計算QoQ
226
+
227
+ return_dict = {
228
+ "ticker": self.ticker,
229
+ "company_name": fetched_data[-1]['company_name'],
230
+ "profit_lose": pd.DataFrame.from_dict(table_dict)
231
+ }
157
232
 
158
233
  return return_dict