neurostats-API 0.0.22__py3-none-any.whl → 0.0.23__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.
@@ -22,6 +22,16 @@ class ProfitLoseFetcher(StatsFetcher):
22
22
  "us_stats": self.process_data_us
23
23
  }
24
24
 
25
+ self.return_keys = [
26
+ 'profit_lose', 'grand_total_profit_lose', 'revenue', 'grand_total_revenue',
27
+ 'gross_profit', 'grand_total_gross_profit', 'gross_profit_percentage',
28
+ 'grand_total_gross_profit_percentage', 'operating_income', 'grand_total_operating_income', 'operating_income_percentage',
29
+ 'grand_total_operating_income_percentage', 'net_income_before_tax', 'grand_total_net_income_before_tax', 'net_income_before_tax_percentage',
30
+ 'grand_total_net_income_before_tax_percentage', 'net_income', 'grand_total_net_income', 'net_income_percentage',
31
+ 'grand_total_income_percentage', 'EPS', 'EPS_growth', 'grand_total_EPS',
32
+ 'grand_total_EPS_growth', 'profit_lose_all', 'profit_lose_YoY'
33
+ ]
34
+
25
35
  def prepare_query(self):
26
36
  pipeline = super().prepare_query()
27
37
 
@@ -30,28 +40,25 @@ class ProfitLoseFetcher(StatsFetcher):
30
40
  chart_name = name_map.get(self.collection_name, "income_statement")
31
41
 
32
42
  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
43
+ {
44
+ "$project": {
45
+ "_id": 0,
46
+ "ticker": 1,
47
+ "company_name": 1,
48
+ "seasonal_data": {
49
+ "$map": {
50
+ "input": {"$ifNull": ["$seasonal_data", []]},
51
+ "as": "season",
52
+ "in": {
53
+ "year": "$$season.year",
54
+ "season": "$$season.season",
55
+ "data": {"$ifNull": [f"$$season.{chart_name}", []]}
56
+ }
57
+ }
52
58
  }
53
59
  }
54
- ]
60
+ }
61
+ ]
55
62
 
56
63
  pipeline = pipeline + append_pipeline
57
64
 
@@ -63,6 +70,8 @@ class ProfitLoseFetcher(StatsFetcher):
63
70
  def query_data(self):
64
71
  fetched_data = self.collect_data()
65
72
 
73
+ fetched_data = fetched_data[0]
74
+
66
75
  process_fn = self.process_function_map.get(
67
76
  self.collection_name, self.process_data_us
68
77
  )
@@ -79,101 +88,108 @@ class ProfitLoseFetcher(StatsFetcher):
79
88
  'latest_target_year',
80
89
  StatsDateTime.get_today().year - 1
81
90
  )
82
- target_season = latest_time.get(
83
- 'seasonal_data',{}
84
- ).get('latest_season', 4)
91
+ target_season = latest_time.get('seasonal_data',
92
+ {}).get('latest_season', 4)
85
93
 
86
94
  return_dict = {
87
95
  "ticker": self.ticker,
88
- "company_name": fetched_data[-1]['company_name'],
96
+ "company_name": fetched_data['company_name'],
89
97
  }
90
98
 
99
+ seasonal_data = fetched_data.get('seasonal_data', [])
100
+
101
+ if (not seasonal_data):
102
+ return_dict.update(self._get_empty_structure())
103
+ return return_dict
104
+
91
105
  profit_lose_dict = {
92
- f"{data['year']}Q{data['season']}": data['profit_lose']
93
- for data in fetched_data
106
+ f"{data['year']}Q{data['season']}": data['data']
107
+ for data in seasonal_data
94
108
  }
95
109
 
110
+ profit_lose_dict = YoY_Calculator.cal_QoQ(profit_lose_dict)
96
111
  profit_lose_df = pd.DataFrame.from_dict(profit_lose_dict)
97
112
  target_season_col = profit_lose_df.columns.str.endswith(
98
113
  f"Q{target_season}"
99
114
  )
100
115
  profit_lose_df = profit_lose_df.loc[:, target_season_col]
101
- profit_lose_df = StatsProcessor.expand_value_percentage(
116
+
117
+ old_profit_lose_df = StatsProcessor.expand_value_percentage(
102
118
  profit_lose_df
103
119
  )
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(
120
+ # OLD: 回傳包含value & percentage
121
+ value_col = old_profit_lose_df.columns.str.endswith(f"_value")
122
+ percentage_col = old_profit_lose_df.columns.str.endswith(f"_percentage")
123
+ # OLD: 回傳剔除grand_total
124
+ grand_total_value_col = old_profit_lose_df.columns.str.endswith(
109
125
  f"grand_total_value"
110
126
  )
111
- grand_total_percentage_col = profit_lose_df.columns.str.endswith(
127
+ grand_total_percentage_col = old_profit_lose_df.columns.str.endswith(
112
128
  f"grand_total_percentage"
113
129
  )
114
130
 
115
- profit_lose_stats_df = profit_lose_df.loc[:, (
131
+ old_profit_lose_df = old_profit_lose_df.loc[:, (
116
132
  (value_col & ~grand_total_value_col) |
117
133
  (percentage_col & ~grand_total_percentage_col)
118
134
  )]
119
135
 
120
- for time_index, profit_lose in profit_lose_dict.items():
121
- # 蒐集整體的keys
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
136
+ for time_index, data_dict in profit_lose_dict.items():
137
+ profit_lose_dict[time_index] = self.flatten_dict(
138
+ value_dict=data_dict,
139
+ indexes=list(data_dict.keys()),
140
+ target_keys=[
141
+ "value", "growth", "percentage", "grand_total",
142
+ "grand_total_percentage"
143
+ ] + [f"YoY_{i}" for i in [1, 3, 5, 10]] +
144
+ [f"grand_total_YoY_{i}" for i in [1, 3, 5, 10]]
140
145
  )
141
- profit_lose_dict[time_index] = new_profit_lose
142
-
143
- profit_lose_df = pd.DataFrame.from_dict(profit_lose_dict)
144
146
 
147
+ profit_lose_df = pd.DataFrame.from_dict(profit_lose_dict).T
145
148
  # 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
- )
149
+ eps_index = (
150
+ profit_lose_df.columns.str.endswith("_value")
151
+ & profit_lose_df.columns.str.contains("每股盈餘")
152
+ )
153
+ eps_copy = profit_lose_df.loc[:, eps_index].copy()
154
+ eps_mask_index = eps_copy.columns
155
+ profit_lose_df[eps_mask_index] = profit_lose_df[eps_mask_index].map(
156
+ lambda x: StatsProcessor.cal_non_percentage(x, postfix="元")
157
+ )
153
158
 
154
159
  # 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(
160
+ percentage_index = profit_lose_df.columns.str.endswith("percentage")
161
+ growth_index = profit_lose_df.columns.str.endswith("growth")
162
+ percentage_mask = (percentage_index | growth_index)
163
+ percentage_copy = profit_lose_df.loc[:, percentage_mask]
164
+ percentage_mask_index = percentage_copy.columns
165
+
166
+ profit_lose_df[percentage_mask_index] = profit_lose_df[
167
+ percentage_mask_index].map(
158
168
  lambda x: StatsProcessor.
159
169
  cal_non_percentage(x, to_str=True, postfix="%")
160
170
  )
161
171
 
162
172
  # 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))
173
+ YoY_index = profit_lose_df.columns.str.contains("YoY")
174
+ YoY_mask = YoY_index
175
+ YoY_copy = profit_lose_df.loc[:, YoY_mask]
176
+ YoY_mask_cols = YoY_copy.columns
177
+
178
+ profit_lose_df[YoY_mask_cols] = profit_lose_df[YoY_mask_cols].map(
179
+ lambda x: StatsProcessor.cal_percentage(x)
180
+ )
166
181
 
167
182
  # 剩下的處理: 乘以千元
168
183
  value_index = ~(
169
- percentage_index | YoY_index | profit_lose_df.index.isin(eps_index)
184
+ percentage_index | growth_index | YoY_index | eps_index
170
185
  ) # 除了上述以外的 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
186
 
176
- total_table = profit_lose_df.replace("N/A", None)
187
+ value_col = profit_lose_df.loc[:, value_index].columns
188
+ profit_lose_df[value_col] = profit_lose_df[value_col].map(
189
+ lambda x: StatsProcessor.cal_non_percentage(x, postfix="千元")
190
+ )
191
+
192
+ total_table = profit_lose_df.replace("N/A", None).T
177
193
 
178
194
  # 取特定季度
179
195
  target_season_columns = total_table.columns.str.endswith(
@@ -192,12 +208,11 @@ class ProfitLoseFetcher(StatsFetcher):
192
208
  )
193
209
  break
194
210
  except Exception as e:
195
- print(str(e))
196
211
  continue
197
212
 
198
213
  return_dict.update(
199
214
  {
200
- "profit_lose": profit_lose_stats_df,
215
+ "profit_lose": old_profit_lose_df,
201
216
  "profit_lose_all": total_table.copy(),
202
217
  "profit_lose_YoY": total_table_YoY
203
218
  }
@@ -218,8 +233,8 @@ class ProfitLoseFetcher(StatsFetcher):
218
233
  table_dict[time_index] = self.flatten_dict(
219
234
  value_dict=data_dict,
220
235
  indexes=list(data_dict.keys()),
221
- target_keys=["value", "growth"] +
222
- [f"YoY_{i}" for i in [1, 3, 5, 10]]
236
+ target_keys=["value", "growth"] +
237
+ [f"YoY_{i}" for i in [1, 3, 5, 10]]
223
238
  )
224
239
 
225
240
  # 計算QoQ
@@ -231,3 +246,8 @@ class ProfitLoseFetcher(StatsFetcher):
231
246
  }
232
247
 
233
248
  return return_dict
249
+
250
+ def _get_empty_structure(self):
251
+ return {
252
+ key: pd.DataFrame(columns= pd.Index([], name = 'date')) for key in self.return_keys
253
+ }
@@ -13,7 +13,8 @@ class TechFetcher(StatsFetcher):
13
13
 
14
14
  super().__init__(ticker, db_client)
15
15
  if (ticker in self.tw_company_list.keys()):
16
- self.collection = self.db["TWN/APIPRCD"]
16
+ self.twse_collection = self.db['twse_stats']
17
+ self.tej_collection = self.db["TWN/APIPRCD"]
17
18
 
18
19
  self.full_ohlcv = self._get_ohlcv()
19
20
  self.basic_indexes = [
@@ -53,35 +54,47 @@ class TechFetcher(StatsFetcher):
53
54
 
54
55
  required_cols = ['date', 'open', 'high', 'low', 'close', 'volume']
55
56
 
56
- try:
57
- # 先對yf search
58
- if self.ticker in ['GSPC', 'IXIC', 'DJI', 'TWII']:
59
- full_tick = f'^{self.ticker}'
60
- elif(self.ticker in self.tw_company_list.keys()):
61
- full_tick = f'{self.ticker}.tw'
62
- else:
63
- full_tick = f"{self.ticker}"
64
-
57
+ if self.ticker in ['GSPC', 'IXIC', 'DJI', 'TWII']:
58
+ full_tick = f'^{self.ticker}'
65
59
  df = self.conduct_yf_search(full_tick)
66
60
 
67
- if (
68
- self.ticker in self.tw_company_list.keys() and
69
- not self.has_required_columns(df, required_cols)
70
- ):
71
- full_tick = f'{self.ticker}.two'
61
+ return df[required_cols]
72
62
 
73
- df = self.conduct_yf_search(full_tick)
63
+ elif(self.ticker in self.tw_company_list.keys()):
64
+ search_fns = [
65
+ self.conduct_db_search_twse,
66
+ self.conduct_db_search_tej,
67
+ lambda: self.conduct_yf_search(f'{self.ticker}.tw'),
68
+ lambda: self.conduct_yf_search(f'{self.ticker}.two')
69
+ ]
70
+
71
+ for search_method in search_fns:
72
+ try:
73
+ df = search_method()
74
+ break
75
+ except (KeyError, ValueError, TypeError):
76
+ continue
77
+ else:
78
+ return pd.DataFrame(columns=required_cols)
74
79
 
75
- if (df.empty):
76
- raise ValueError(f"No data found for ticker: {self.ticker}")\
77
-
80
+ # break跳出後
78
81
  return df[required_cols]
82
+ else: # 美股
83
+ search_fns = [
84
+ self.conduct_db_search_us,
85
+ lambda : self.conduct_yf_search(f"{self.ticker}")
86
+ ]
87
+ for search_method in search_fns:
88
+ try:
89
+ df = search_method()
90
+ break
91
+ except (KeyError, ValueError, TypeError):
92
+ continue
93
+ else:
94
+ df = pd.DataFrame()
79
95
 
80
- except (KeyError, ValueError, TypeError) as e:
81
- if (self.collection_name in ['TWN/APIPRCD']):
82
- return self.conduct_db_search_tej()
83
- elif (self.collection_name == 'us_stats'):
84
- return self.conduct_db_search_us()
96
+ return df
97
+
85
98
 
86
99
  def get_daily(self):
87
100
 
@@ -175,13 +188,35 @@ class TechFetcher(StatsFetcher):
175
188
  if not self.has_required_columns(df, required_cols):
176
189
  missing_cols = [col for col in required_cols if col not in df.columns]
177
190
  missing_cols = ",".join(missing_cols)
178
- print(Warning(f"{missing_cols} not in columns"))
179
191
  for col in missing_cols:
180
192
  df[col] = pd.NA
181
- # raise KeyError(f"Missing required columns")
182
193
 
183
194
  return df[required_cols]
184
195
 
196
+
197
+ def conduct_db_search_twse(self):
198
+ required_cols = ['date', 'open', 'high', 'low', 'close', 'volume']
199
+ match_query = {"ticker" : self.ticker}
200
+ proj_query = {"_id": 0, "daily_data": 1}
201
+
202
+ full_data = self.twse_collection.find_one(match_query, proj_query)
203
+
204
+ if (not full_data):
205
+ raise ValueError("No ticker found in database twse_stats")
206
+
207
+ daily_data = full_data.get("daily_data", [])
208
+
209
+ if (not isinstance(daily_data, list)):
210
+ raise ValueError("No ticker found in database twse_stats")
211
+
212
+ df = pd.DataFrame(daily_data)
213
+ if not self.has_required_columns(df, required_cols):
214
+ raise KeyError(f"Missing required columns")
215
+
216
+ df = df[required_cols]
217
+ df = df.sort_values(by = 'date').drop_duplicates(subset=['date'])
218
+
219
+ return df
185
220
 
186
221
  class TechProcessor:
187
222
 
@@ -1,6 +1,7 @@
1
1
  from .base import BaseTEJFetcher
2
2
  from datetime import datetime
3
3
  from enum import Enum
4
+ import numpy as np
4
5
  import pandas as pd
5
6
  from pymongo import MongoClient
6
7
  from .tech import TechProcessor
@@ -176,7 +177,10 @@ class FinanceReportFetcher(BaseTEJFetcher):
176
177
  fetched_data = self.collection.aggregate(pipeline).to_list()
177
178
  data_dict = self.transform_value(
178
179
  StatsProcessor.list_of_dict_to_dict(
179
- fetched_data, ["year", "season"], "Q", report_type
180
+ data_list=fetched_data,
181
+ keys=["year", "season"],
182
+ delimeter="Q",
183
+ data_key=report_type
180
184
  )
181
185
  )
182
186
 
@@ -195,9 +199,9 @@ class FinanceReportFetcher(BaseTEJFetcher):
195
199
  (self.percent_index_list, "%")]:
196
200
  process_list = list(set(data_df.index) & set(category))
197
201
  if postfix == "%":
198
- data_df.loc[process_list] = data_df.loc[process_list].astype(
199
- str
200
- ).map(lambda x: f"{x}%")
202
+ data_df = data_df.T
203
+ data_df[process_list] = data_df[process_list].map(lambda x: f"{x}%") # if (not np.isnan(x)) else None)
204
+ data_df = data_df.T
201
205
  else:
202
206
  data_df.loc[process_list] = data_df.loc[process_list].map(
203
207
  lambda x: StatsProcessor.
@@ -241,7 +245,7 @@ class FinanceReportFetcher(BaseTEJFetcher):
241
245
  "$gt": start_year,
242
246
  "$lt": end_year
243
247
  },
244
- "data.season": end_season
248
+ "data.season": start_season
245
249
  }
246
250
  else:
247
251
  match_stage = {
@@ -327,7 +331,10 @@ class TEJStockPriceFetcher(BaseTEJFetcher):
327
331
 
328
332
  if (period is not None):
329
333
  latest_date = self.get_latest_data_time(ticker)
330
- start_date = self.set_time_shift(date=latest_date, period=period)
334
+ if (latest_date):
335
+ start_date = self.set_time_shift(date=latest_date, period=period)
336
+ else:
337
+ start_date = datetime.strptime(start_date, "%Y-%m-%d")
331
338
  else:
332
339
  start_date = datetime.strptime(start_date, "%Y-%m-%d")
333
340
 
@@ -355,7 +362,15 @@ class TEJStockPriceFetcher(BaseTEJFetcher):
355
362
  datas = self.collection.aggregate(pipeline).to_list()
356
363
 
357
364
  elements = [element['data'] for element in datas]
358
-
359
- data_df = pd.DataFrame(elements).set_index('mdate')
365
+ try:
366
+ data_df = pd.DataFrame(elements).set_index('mdate')
367
+ except:
368
+ column_names = [
369
+ "coid", "mdate", "mkt", "open_d", "high_d", "low_d", "close_d",
370
+ "adjfac", "vol", "amt", "trn", "bid", "offer", "avgprc", "roi",
371
+ "hmlpct", "turnover", "shares", "mktcap", "mktcap_pct",
372
+ "amt_pct", "per", "pbr", "div_yid", "cdiv_yid"
373
+ ]
374
+ data_df = pd.DataFrame(columns = column_names)
360
375
 
361
376
  return data_df
@@ -8,6 +8,11 @@ class ValueFetcher(StatsFetcher):
8
8
 
9
9
  def __init__(self, ticker: str, db_client):
10
10
  super().__init__(ticker, db_client)
11
+ self.value_keys = [
12
+ "P_E", "P_FCF", "P_B", "P_S",
13
+ "EV_OPI", "EV_EBIT", "EV_EBITDA", "EV_S",
14
+ "Yield"
15
+ ]
11
16
 
12
17
  def prepare_query(self, start_date, end_date):
13
18
  pipeline = super().prepare_query()
@@ -54,7 +59,8 @@ class ValueFetcher(StatsFetcher):
54
59
  "EV_OPI": "$$daily_item.EV_OPI",
55
60
  "EV_EBIT": "$$daily_item.EV_EBIT",
56
61
  "EV_EBITDA": "$$daily_item.EV_EBITDA",
57
- "EV_S": "$$daily_item.EV_S"
62
+ "EV_S": "$$daily_item.EV_S",
63
+ "Yield": "$$daily_item.Yield"
58
64
  }
59
65
  }
60
66
  },
@@ -89,10 +95,13 @@ class ValueFetcher(StatsFetcher):
89
95
  fetched_data = self.collect_data(start_date, end_date)
90
96
 
91
97
  fetched_data['daily_data'] = fetched_data['daily_data'][-1]
98
+ for key in self.value_keys:
99
+ fetched_data['daily_data'][key] = fetched_data['daily_data'].get(key, None)
92
100
 
93
101
  fetched_data['yearly_data'] = ValueProcessor.transform_to_df(
94
102
  fetched_data['daily_data'],
95
103
  fetched_data['yearly_data'],
104
+ required_keys=self.value_keys
96
105
  )
97
106
 
98
107
  return fetched_data
@@ -110,7 +119,8 @@ class ValueFetcher(StatsFetcher):
110
119
  EV_OPI,
111
120
  EV_EBIT,
112
121
  EV_EBITDA,
113
- EV_S
122
+ EV_S,
123
+ Yield
114
124
  }
115
125
  """
116
126
 
@@ -142,7 +152,8 @@ class ValueFetcher(StatsFetcher):
142
152
  "EV_OPI": "$$daily.EV_OPI",
143
153
  "EV_EBIT": "$$daily.EV_EBIT",
144
154
  "EV_EBITDA": "$$daily.EV_EBITDA",
145
- "EV_S": "$$daily.EV_S"
155
+ "EV_S": "$$daily.EV_S",
156
+ "Yield": "$$daily.Yield"
146
157
  }
147
158
  }
148
159
  }
@@ -153,15 +164,12 @@ class ValueFetcher(StatsFetcher):
153
164
  fetched_data = self.collection.aggregate(pipeline).to_list()
154
165
  fetched_data = fetched_data[0]
155
166
 
156
- value_keys = ["P_E", "P_FCF", "P_B", "P_S", "EV_OPI", "EV_EBIT", "EV_EVITDA", "EV_S"]
157
- return_dict = {value_key: dict() for value_key in value_keys}
167
+ return_dict = {value_key: dict() for value_key in self.value_keys}
168
+
169
+ for data in fetched_data['daily_data']:
170
+ for value_key in self.value_keys:
171
+ return_dict[value_key].update({data['date']: data.get(value_key, None)})
158
172
 
159
- for value_key in value_keys:
160
- for data in fetched_data['daily_data']:
161
- if (value_key not in data.keys()):
162
- continue
163
- else:
164
- return_dict[value_key].update({data['date']: data[value_key]})
165
173
 
166
174
  return_dict = {
167
175
  value_key: pd.DataFrame.from_dict(value_dict, orient='index', columns=[value_key])
@@ -173,7 +181,7 @@ class ValueFetcher(StatsFetcher):
173
181
  class ValueProcessor(StatsProcessor):
174
182
 
175
183
  @staticmethod
176
- def transform_to_df(daily_dict, yearly_dict):
184
+ def transform_to_df(daily_dict: dict, yearly_dict: dict, required_keys: list):
177
185
  latest_data = {"year": f"過去4季"}
178
186
 
179
187
  latest_data.update(daily_dict)
@@ -184,4 +192,16 @@ class ValueProcessor(StatsProcessor):
184
192
 
185
193
  yearly_dict = pd.DataFrame.from_dict(yearly_dict)
186
194
 
195
+ total_keys = [
196
+ # 年度評價
197
+ 'year', 'P_E', 'P_FCF', 'P_B', 'P_S',
198
+ 'EV_OPI', 'EV_EBIT', 'EV_EBITDA','EV_S',
199
+
200
+ # 最新一日交易
201
+ 'open', 'high', 'low', 'close', 'volume', 'dividends',
202
+ 'stock splits', 'Yield', 'dividend_year', 'finance_report_time'
203
+ ]
204
+
205
+ total_keys_list = list(total_keys)
206
+ yearly_dict = yearly_dict.reindex(columns=total_keys, fill_value=None)
187
207
  return yearly_dict
@@ -29,9 +29,6 @@ TWN/AINVFQ1:
29
29
  - r607
30
30
  - r613
31
31
  - r612
32
- - r609
33
- - r614
34
- - r611
35
32
  TWN/AFESTM1:
36
33
  - r105
37
34
  - r106
@@ -9,6 +9,13 @@ TWN/AINVFQ1:
9
9
  - annd
10
10
  - fin_ind
11
11
  - eps
12
+ - r307
13
+ - r305
14
+ - r306
15
+ - r316
16
+ - r609
17
+ - r614
18
+ - r611
12
19
  TWN/AFESTM1:
13
20
  - coid
14
21
  - mdate
@@ -19,4 +26,8 @@ TWN/AFESTM1:
19
26
  - curr
20
27
  - annd
21
28
  - fin_ind
22
- - eps
29
+ - eps
30
+ - r307
31
+ - r305
32
+ - r306
33
+ - r316
@@ -56,10 +56,6 @@ TWN/AINVFQ1:
56
56
  - person
57
57
  - shares
58
58
  - wavg
59
- - r304
60
- - r305
61
- - r306
62
- - r316
63
59
  - r834
64
60
  TWN/AFESTM1:
65
61
  - ip12
@@ -1,4 +1,5 @@
1
1
  from .data_process import StatsProcessor
2
+ import numpy as np
2
3
  class YoY_Calculator:
3
4
  def __init__(self):
4
5
  pass
@@ -23,6 +24,9 @@ class YoY_Calculator:
23
24
 
24
25
  if (isinstance(YoY, complex)): # 年化成長率有複數問題
25
26
  return None
27
+
28
+ if YoY != YoY: # 確認是否為nan (Python 中 nan不等於自己)
29
+ return None
26
30
 
27
31
  return YoY
28
32
  @classmethod
@@ -91,7 +95,6 @@ class YoY_Calculator:
91
95
  for key in list(this_data.keys()):
92
96
  if key == "season":
93
97
  continue
94
-
95
98
 
96
99
  value = this_data.get(key, None)
97
100
 
@@ -102,7 +105,7 @@ class YoY_Calculator:
102
105
  temp_dict = {"value": value}
103
106
  this_value = value
104
107
 
105
- this_value = StatsProcessor.process_str_to_float(this_data[key])
108
+ this_value = StatsProcessor.process_str_to_float(this_value)
106
109
 
107
110
  last_value = data_dict.get(
108
111
  f"{last_year}Q{last_season}",{}