neurostats-API 0.0.19__tar.gz → 0.0.21__tar.gz
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.
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/PKG-INFO +26 -15
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/README.md +24 -13
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/__init__.py +1 -1
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/fetchers/base.py +78 -88
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/fetchers/month_revenue.py +48 -1
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/fetchers/tech.py +1 -1
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/fetchers/tej_finance_report.py +36 -2
- neurostats_api-0.0.21/neurostats_API/tools/tej_db/tej_db_percent_index.yaml +44 -0
- neurostats_api-0.0.21/neurostats_API/tools/tej_db/tej_db_skip_index.yaml +20 -0
- neurostats_api-0.0.21/neurostats_API/tools/tej_db/tej_db_thousand_index.yaml +71 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/utils/data_process.py +8 -2
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API.egg-info/PKG-INFO +26 -15
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API.egg-info/SOURCES.txt +4 -5
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API.egg-info/requires.txt +1 -1
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/setup.py +3 -3
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/test/test_tej.py +7 -7
- neurostats_api-0.0.19/neurostats_API/tools/balance_sheet.yaml +0 -35
- neurostats_api-0.0.19/neurostats_API/tools/cash_flow_percentage.yaml +0 -39
- neurostats_api-0.0.19/neurostats_API/tools/finance_overview_dict.yaml +0 -185
- neurostats_api-0.0.19/neurostats_API/tools/profit_lose.yaml +0 -143
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/MANIFEST.in +0 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/cli.py +0 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/fetchers/__init__.py +0 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/fetchers/balance_sheet.py +0 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/fetchers/cash_flow.py +0 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/fetchers/finance_overview.py +0 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/fetchers/institution.py +0 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/fetchers/margin_trading.py +0 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/fetchers/profit_lose.py +0 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/fetchers/value_invest.py +0 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/main.py +0 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/tools/seasonal_data_field_dict.txt +0 -0
- {neurostats_api-0.0.19/neurostats_API/tools → neurostats_api-0.0.21/neurostats_API/tools/tej_db}/tej_db_index.yaml +0 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/utils/__init__.py +0 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/utils/calculate_value.py +0 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/utils/datetime.py +0 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/utils/db_client.py +0 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/utils/fetcher.py +0 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API.egg-info/dependency_links.txt +0 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API.egg-info/top_level.txt +0 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/setup.cfg +0 -0
- {neurostats_api-0.0.19 → neurostats_api-0.0.21}/test/test_fetchers.py +0 -0
@@ -1,13 +1,13 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: neurostats_API
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.21
|
4
4
|
Summary: The service of NeuroStats website
|
5
5
|
Home-page: https://github.com/NeurowattStats/NeuroStats_API.git
|
6
6
|
Author: JasonWang@Neurowatt
|
7
7
|
Author-email: jason@neurowatt.ai
|
8
8
|
Requires-Python: >=3.6
|
9
9
|
Description-Content-Type: text/markdown
|
10
|
-
Requires-Dist: numpy
|
10
|
+
Requires-Dist: numpy
|
11
11
|
Requires-Dist: pandas>=2.2.0
|
12
12
|
Requires-Dist: pymongo
|
13
13
|
Requires-Dist: pytz
|
@@ -89,7 +89,7 @@ pip install neurostats-API
|
|
89
89
|
```Python
|
90
90
|
>>> import neurostats_API
|
91
91
|
>>> print(neurostats_API.__version__)
|
92
|
-
0.0.
|
92
|
+
0.0.21
|
93
93
|
```
|
94
94
|
|
95
95
|
### 得到最新一期的評價資料與歷年評價
|
@@ -236,6 +236,15 @@ data = fetcher.query_data()
|
|
236
236
|
grand_total_YoY_5 1.691300e+02 ... NaN
|
237
237
|
grand_total_YoY_10 NaN ... NaN
|
238
238
|
|
239
|
+
"recent_month_revenue":
|
240
|
+
date 2024/11 2024/10 ... 2024/1 2023/12
|
241
|
+
revenue 51243946000 45451116000 ... 56451418000 55329015000
|
242
|
+
MoM 12.75% -14.77% ... 2.03% -0.52%
|
243
|
+
YoY -7.87% -30.00% ... -7.36% -11.13%
|
244
|
+
total_YoY -6.93% -6.84% ... -7.36% -15.97%
|
245
|
+
accum_YoY 85.84% 78.65% ... 7.92% 84.03%
|
246
|
+
# total_YoY為當月累計營收 / 上一年的當月累計營收
|
247
|
+
# accum_YoY為當月累計營收 / 上一年的總營收
|
239
248
|
|
240
249
|
}
|
241
250
|
```
|
@@ -728,6 +737,7 @@ data = fetcher.get(
|
|
728
737
|
#### 回傳資料
|
729
738
|
##### `YOY_NOCAL` 與 `QOQ_NOCAL`
|
730
739
|
為回傳`pd.DataFrame`,column名稱為<年份>Q<季>, row名稱為指定財報項目
|
740
|
+
|
731
741
|
```Python
|
732
742
|
# fetch_mode = fetcher.FetchMode.QOQ_NOCAL
|
733
743
|
2024Q3 2024Q2 2024Q1
|
@@ -742,17 +752,18 @@ bp51 3.111298e+09 3.173919e+09 2.453840e+09
|
|
742
752
|
|
743
753
|
##### `YOY` 與 `QOQ`
|
744
754
|
回傳為`Dict[pd.DataFrame]`, key 為指定的index, DataFrame中則是該index歷年的數值與成長率
|
755
|
+
成長率單位為`%`
|
745
756
|
```Python
|
746
757
|
# fetch_mode = fetcher.FetchMode.QOQ
|
747
758
|
{
|
748
|
-
|
759
|
+
'bp41':
|
749
760
|
2024Q3 2024Q2 2024Q1
|
750
761
|
value 7.082005e+07 6.394707e+07 5.761001e+07
|
751
|
-
growth
|
752
|
-
|
762
|
+
growth 10.75% 11.00% 0.55%,
|
763
|
+
'bp51':
|
753
764
|
2024Q3 2024Q2 2024Q1
|
754
765
|
value 3.111298e+09 3.145373e+09 3.091985e+09
|
755
|
-
growth
|
766
|
+
growth -1.08% 1.73% -0.42%
|
756
767
|
}
|
757
768
|
|
758
769
|
# fetch_mode = fetcher.FetchMode.YOY
|
@@ -760,17 +771,17 @@ growth -1.083335e-02 1.726663e-02 -4.159542e-03
|
|
760
771
|
'bp41':
|
761
772
|
2024Q3 2023Q3 2022Q3
|
762
773
|
value 7.082005e+07 5.377231e+07 6.201822e+07
|
763
|
-
YoY_1
|
764
|
-
YoY_3
|
765
|
-
YoY_5
|
766
|
-
YoY_10
|
774
|
+
YoY_1 31.70% -13.30% 41.31%
|
775
|
+
YoY_3 17.29% 9.56% 18.83%
|
776
|
+
YoY_5 13.89% 12.15% 16.43%
|
777
|
+
YoY_10 12.55% 13.56% 15.60% ,
|
767
778
|
'bp51':
|
768
779
|
2024Q3 2023Q3 2022Q3
|
769
780
|
value 3.111298e+09 3.173919e+09 2.453840e+09
|
770
|
-
YoY_1
|
771
|
-
YoY_3
|
772
|
-
YoY_5
|
773
|
-
YoY_10
|
781
|
+
YoY_1 -1.97% 29.34% 31.80%
|
782
|
+
YoY_3 18.67% 27.67% 26.39%
|
783
|
+
YoY_5 20.68% 24.80% 18.15%
|
784
|
+
YoY_10 14.20% 15.87% 15.51%
|
774
785
|
}
|
775
786
|
```
|
776
787
|
|
@@ -73,7 +73,7 @@ pip install neurostats-API
|
|
73
73
|
```Python
|
74
74
|
>>> import neurostats_API
|
75
75
|
>>> print(neurostats_API.__version__)
|
76
|
-
0.0.
|
76
|
+
0.0.21
|
77
77
|
```
|
78
78
|
|
79
79
|
### 得到最新一期的評價資料與歷年評價
|
@@ -220,6 +220,15 @@ data = fetcher.query_data()
|
|
220
220
|
grand_total_YoY_5 1.691300e+02 ... NaN
|
221
221
|
grand_total_YoY_10 NaN ... NaN
|
222
222
|
|
223
|
+
"recent_month_revenue":
|
224
|
+
date 2024/11 2024/10 ... 2024/1 2023/12
|
225
|
+
revenue 51243946000 45451116000 ... 56451418000 55329015000
|
226
|
+
MoM 12.75% -14.77% ... 2.03% -0.52%
|
227
|
+
YoY -7.87% -30.00% ... -7.36% -11.13%
|
228
|
+
total_YoY -6.93% -6.84% ... -7.36% -15.97%
|
229
|
+
accum_YoY 85.84% 78.65% ... 7.92% 84.03%
|
230
|
+
# total_YoY為當月累計營收 / 上一年的當月累計營收
|
231
|
+
# accum_YoY為當月累計營收 / 上一年的總營收
|
223
232
|
|
224
233
|
}
|
225
234
|
```
|
@@ -712,6 +721,7 @@ data = fetcher.get(
|
|
712
721
|
#### 回傳資料
|
713
722
|
##### `YOY_NOCAL` 與 `QOQ_NOCAL`
|
714
723
|
為回傳`pd.DataFrame`,column名稱為<年份>Q<季>, row名稱為指定財報項目
|
724
|
+
|
715
725
|
```Python
|
716
726
|
# fetch_mode = fetcher.FetchMode.QOQ_NOCAL
|
717
727
|
2024Q3 2024Q2 2024Q1
|
@@ -726,17 +736,18 @@ bp51 3.111298e+09 3.173919e+09 2.453840e+09
|
|
726
736
|
|
727
737
|
##### `YOY` 與 `QOQ`
|
728
738
|
回傳為`Dict[pd.DataFrame]`, key 為指定的index, DataFrame中則是該index歷年的數值與成長率
|
739
|
+
成長率單位為`%`
|
729
740
|
```Python
|
730
741
|
# fetch_mode = fetcher.FetchMode.QOQ
|
731
742
|
{
|
732
|
-
|
743
|
+
'bp41':
|
733
744
|
2024Q3 2024Q2 2024Q1
|
734
745
|
value 7.082005e+07 6.394707e+07 5.761001e+07
|
735
|
-
growth
|
736
|
-
|
746
|
+
growth 10.75% 11.00% 0.55%,
|
747
|
+
'bp51':
|
737
748
|
2024Q3 2024Q2 2024Q1
|
738
749
|
value 3.111298e+09 3.145373e+09 3.091985e+09
|
739
|
-
growth
|
750
|
+
growth -1.08% 1.73% -0.42%
|
740
751
|
}
|
741
752
|
|
742
753
|
# fetch_mode = fetcher.FetchMode.YOY
|
@@ -744,17 +755,17 @@ growth -1.083335e-02 1.726663e-02 -4.159542e-03
|
|
744
755
|
'bp41':
|
745
756
|
2024Q3 2023Q3 2022Q3
|
746
757
|
value 7.082005e+07 5.377231e+07 6.201822e+07
|
747
|
-
YoY_1
|
748
|
-
YoY_3
|
749
|
-
YoY_5
|
750
|
-
YoY_10
|
758
|
+
YoY_1 31.70% -13.30% 41.31%
|
759
|
+
YoY_3 17.29% 9.56% 18.83%
|
760
|
+
YoY_5 13.89% 12.15% 16.43%
|
761
|
+
YoY_10 12.55% 13.56% 15.60% ,
|
751
762
|
'bp51':
|
752
763
|
2024Q3 2023Q3 2022Q3
|
753
764
|
value 3.111298e+09 3.173919e+09 2.453840e+09
|
754
|
-
YoY_1
|
755
|
-
YoY_3
|
756
|
-
YoY_5
|
757
|
-
YoY_10
|
765
|
+
YoY_1 -1.97% 29.34% 31.80%
|
766
|
+
YoY_3 18.67% 27.67% 26.39%
|
767
|
+
YoY_5 20.68% 24.80% 18.15%
|
768
|
+
YoY_10 14.20% 15.87% 15.51%
|
758
769
|
}
|
759
770
|
```
|
760
771
|
|
@@ -81,10 +81,14 @@ class BaseTEJFetcher(abc.ABC):
|
|
81
81
|
|
82
82
|
def get_latest_data_time(self, ticker):
|
83
83
|
latest_data = self.collection.find_one(
|
84
|
-
{
|
84
|
+
{
|
85
|
+
"ticker": ticker
|
86
|
+
},
|
87
|
+
{
|
85
88
|
"last_update": 1,
|
86
89
|
"_id": 0
|
87
|
-
}
|
90
|
+
}
|
91
|
+
)
|
88
92
|
|
89
93
|
try:
|
90
94
|
latest_date = latest_data['last_update']["latest_data_date"]
|
@@ -93,82 +97,78 @@ class BaseTEJFetcher(abc.ABC):
|
|
93
97
|
|
94
98
|
return latest_date
|
95
99
|
|
96
|
-
def
|
97
|
-
|
100
|
+
def process_value(self, value):
|
101
|
+
if isinstance(value, str) and "%" in value:
|
102
|
+
value = value.replace("%", "")
|
103
|
+
try:
|
104
|
+
return float(value)
|
105
|
+
except (ValueError, TypeError):
|
106
|
+
return None
|
107
|
+
|
108
|
+
def calculate_growth(self, this_value, last_value, delta):
|
109
|
+
try:
|
110
|
+
return YoY_Calculator.cal_growth(this_value, last_value, delta) * 100
|
111
|
+
except Exception:
|
112
|
+
return None
|
113
|
+
|
114
|
+
def cal_YoY(self, data_dict: dict, start_year: int, end_year: int, season: int):
|
98
115
|
year_shifts = [1, 3, 5, 10]
|
99
116
|
return_dict = {}
|
117
|
+
|
100
118
|
for year in range(start_year, end_year + 1):
|
101
|
-
|
102
|
-
|
103
|
-
except KeyError as e:
|
119
|
+
year_data = data_dict.get(f"{year}Q{season}", {}).copy()
|
120
|
+
if not year_data:
|
104
121
|
continue
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
if (key in 'season'):
|
122
|
+
|
123
|
+
for key in list(year_data.keys()):
|
124
|
+
if key == "season":
|
109
125
|
continue
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
for shift in year_shifts:
|
115
|
-
this_value = year_data[key]
|
116
|
-
try:
|
117
|
-
past_year = year - shift
|
118
|
-
last_value = data_dict[f"{past_year}Q{season}"][key]
|
119
|
-
temp_dict[
|
120
|
-
f"YoY_{shift}"] = YoY_Calculator.cal_growth(
|
121
|
-
this_value, last_value, delta=shift)
|
122
|
-
except Exception as e:
|
123
|
-
temp_dict[f"YoY_{shift}"] = None
|
124
|
-
|
125
|
-
year_data[key] = temp_dict
|
126
|
-
|
127
|
-
else:
|
126
|
+
|
127
|
+
this_value = self.process_value(year_data[key])
|
128
|
+
if this_value is None:
|
128
129
|
year_data.pop(key)
|
129
|
-
|
130
|
+
continue
|
131
|
+
|
132
|
+
temp_dict = {"value": year_data[key]}
|
133
|
+
for shift in year_shifts:
|
134
|
+
past_year = year - shift
|
135
|
+
last_value = data_dict.get(f"{past_year}Q{season}", {}).get(key)
|
136
|
+
last_value = self.process_value(last_value)
|
137
|
+
growth = self.calculate_growth(this_value, last_value, shift) if last_value is not None else None
|
138
|
+
|
139
|
+
temp_dict[f"YoY_{shift}"] = (f"{growth:.2f}%" if growth else None)
|
140
|
+
year_data[key] = temp_dict
|
141
|
+
|
130
142
|
return_dict[f"{year}Q{season}"] = year_data
|
131
|
-
|
143
|
+
|
132
144
|
return return_dict
|
133
145
|
|
134
146
|
def cal_QoQ(self, data_dict):
|
135
147
|
return_dict = {}
|
136
|
-
|
137
|
-
|
138
|
-
year = int(
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
else:
|
144
|
-
last_year = year
|
145
|
-
last_season = season - 1
|
146
|
-
|
147
|
-
this_data = data_dict[time_index]
|
148
|
-
this_keys = list(this_data.keys())
|
149
|
-
for key in this_keys:
|
150
|
-
if (key in 'season'):
|
148
|
+
|
149
|
+
for time_index, this_data in data_dict.items():
|
150
|
+
year, season = map(int, time_index.split("Q"))
|
151
|
+
last_year, last_season = (year - 1, 4) if season == 1 else (year, season - 1)
|
152
|
+
|
153
|
+
for key in list(this_data.keys()):
|
154
|
+
if key == "season":
|
151
155
|
continue
|
152
|
-
|
153
|
-
this_value = this_data[key]
|
154
|
-
|
155
|
-
if (isinstance(this_value, (int, float))):
|
156
|
-
temp_dict = {"value": this_value}
|
157
|
-
|
158
|
-
try:
|
159
|
-
last_value = data_dict[f"{last_year}Q{last_season}"][
|
160
|
-
key]['value']
|
161
|
-
|
162
|
-
temp_dict['growth'] = YoY_Calculator.cal_growth(
|
163
|
-
this_value, last_value, delta=1)
|
164
|
-
except Exception as e:
|
165
|
-
temp_dict['growth'] = None
|
166
|
-
|
167
|
-
this_data[key] = temp_dict
|
168
|
-
|
169
|
-
else:
|
156
|
+
|
157
|
+
this_value = self.process_value(this_data[key])
|
158
|
+
if this_value is None:
|
170
159
|
this_data.pop(key)
|
160
|
+
continue
|
161
|
+
|
162
|
+
temp_dict = {"value": this_data[key]}
|
163
|
+
last_value = data_dict.get(f"{last_year}Q{last_season}", {}).get(key, {}).get('value')
|
164
|
+
last_value = self.process_value(last_value)
|
165
|
+
growth = self.calculate_growth(this_value, last_value, 1) if last_value is not None else None
|
166
|
+
temp_dict['growth'] = (f"{growth:.2f}%" if growth else None)
|
167
|
+
|
168
|
+
this_data[key] = temp_dict
|
169
|
+
|
171
170
|
return_dict[time_index] = this_data
|
171
|
+
|
172
172
|
return return_dict
|
173
173
|
|
174
174
|
def get_dict_of_df(self, data_dict):
|
@@ -180,31 +180,21 @@ class BaseTEJFetcher(abc.ABC):
|
|
180
180
|
return data_dict
|
181
181
|
|
182
182
|
def set_time_shift(self, date: Union[str, datetime], period: str):
|
183
|
-
if
|
183
|
+
if isinstance(date, str):
|
184
184
|
date = datetime.strptime(date, "%Y-%m-%d")
|
185
|
-
if (period == '1d'):
|
186
|
-
return date - timedelta(days=1)
|
187
|
-
|
188
|
-
elif (period == '7d'):
|
189
|
-
return date - timedelta(days=7)
|
190
185
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
return date - timedelta(days=365 * 3)
|
202
|
-
|
203
|
-
elif (period == '5y'):
|
204
|
-
return date - timedelta(days=365 * 5)
|
205
|
-
|
206
|
-
elif (period == '10y'):
|
207
|
-
return date - timedelta(days=365 * 10)
|
186
|
+
period_mapping = {
|
187
|
+
"1d": timedelta(days=1),
|
188
|
+
"7d": timedelta(days=7),
|
189
|
+
"1m": timedelta(days=30),
|
190
|
+
"3m": timedelta(days=90),
|
191
|
+
"1y": timedelta(days=365),
|
192
|
+
"3y": timedelta(days=365 * 3),
|
193
|
+
"5y": timedelta(days=365 * 5),
|
194
|
+
"10y": timedelta(days=365 * 10),
|
195
|
+
}
|
208
196
|
|
209
|
-
|
197
|
+
if period == "all":
|
210
198
|
return datetime.strptime("1991-01-01", "%Y-%m-%d")
|
199
|
+
|
200
|
+
return date - period_mapping.get(period, timedelta(days=0)) # 預設為不變"
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from .base import StatsFetcher, StatsDateTime
|
2
2
|
import json
|
3
3
|
import pandas as pd
|
4
|
-
from ..utils import StatsDateTime, StatsProcessor
|
4
|
+
from ..utils import StatsDateTime, StatsProcessor, YoY_Calculator
|
5
5
|
import importlib.resources as pkg_resources
|
6
6
|
import yaml
|
7
7
|
|
@@ -77,7 +77,9 @@ class MonthRevenueFetcher(StatsFetcher):
|
|
77
77
|
postfix="千元")
|
78
78
|
target_month = monthly_data[0]['month']
|
79
79
|
monthly_df = pd.DataFrame(monthly_data)
|
80
|
+
|
80
81
|
target_month_df = monthly_df[monthly_df['month'] == target_month]
|
82
|
+
annual_month_df = monthly_df[monthly_df['month'] == 12]
|
81
83
|
month_revenue_df = monthly_df.pivot(index='month',
|
82
84
|
columns='year',
|
83
85
|
values='revenue')
|
@@ -86,6 +88,10 @@ class MonthRevenueFetcher(StatsFetcher):
|
|
86
88
|
columns='year',
|
87
89
|
values='grand_total')
|
88
90
|
|
91
|
+
annual_total_df = annual_month_df.pivot(index='month',
|
92
|
+
columns='year',
|
93
|
+
values='grand_total')
|
94
|
+
|
89
95
|
grand_total_df.rename(index={target_month: f"grand_total"},
|
90
96
|
inplace=True)
|
91
97
|
month_revenue_df = month_revenue_df.sort_index(ascending=False)
|
@@ -111,4 +117,45 @@ class MonthRevenueFetcher(StatsFetcher):
|
|
111
117
|
|
112
118
|
fetched_data.pop("monthly_data")
|
113
119
|
|
120
|
+
fetched_data['recent_month_revenue'] = self.get_recent_revenue_grwoth(
|
121
|
+
monthly_data, grand_total_dict=annual_total_df.to_dict(), interval = 12
|
122
|
+
)
|
123
|
+
|
114
124
|
return fetched_data
|
125
|
+
|
126
|
+
def get_recent_revenue_grwoth(self, monthly_data, grand_total_dict, interval: int = 12):
|
127
|
+
recent_month_data = monthly_data[:interval + 1]
|
128
|
+
|
129
|
+
MoMs = [
|
130
|
+
YoY_Calculator.cal_growth(this_value['revenue'], last_value['revenue'], delta = 1)
|
131
|
+
for this_value, last_value in zip(
|
132
|
+
recent_month_data[:12], recent_month_data[1:13]
|
133
|
+
)
|
134
|
+
]
|
135
|
+
|
136
|
+
recent_month_data = {
|
137
|
+
"date" : [f"{data['year']}/{data['month']}" for data in recent_month_data[:interval]],
|
138
|
+
"revenue" : [data['revenue'] for data in recent_month_data[:interval]],
|
139
|
+
"MoM" : [f"{(data * 100):.2f}%" for data in MoMs],
|
140
|
+
"YoY" : [f"{data['revenue_increment_ratio']}" for data in recent_month_data[:interval]],
|
141
|
+
"total_YoY": [f"{data['grand_total_increment_ratio']}" for data in recent_month_data[:interval]],
|
142
|
+
}
|
143
|
+
|
144
|
+
# accum_YoY
|
145
|
+
# accum_YoY 為 Davis提出的定義
|
146
|
+
# 2024/6的累計YoY(accum_YoY) 為 2024累計到6月為止的總營收/2023年度總營收
|
147
|
+
accum_YoYs = []
|
148
|
+
for data in monthly_data[:interval]:
|
149
|
+
try:
|
150
|
+
year = data['year'] - 1
|
151
|
+
total = grand_total_dict[year][12]
|
152
|
+
accum_YoY = round((data['grand_total'] / total) * 100, 2)
|
153
|
+
accum_YoYs.append(f"{accum_YoY}%")
|
154
|
+
except Exception as e:
|
155
|
+
accum_YoYs.append(None)
|
156
|
+
|
157
|
+
recent_month_data['accum_YoY'] = accum_YoYs
|
158
|
+
|
159
|
+
recent_month_df = pd.DataFrame(recent_month_data).set_index('date').T
|
160
|
+
|
161
|
+
return recent_month_df
|
{neurostats_api-0.0.19 → neurostats_api-0.0.21}/neurostats_API/fetchers/tej_finance_report.py
RENAMED
@@ -20,15 +20,23 @@ class FinanceReportFetcher(BaseTEJFetcher):
|
|
20
20
|
def __init__(
|
21
21
|
self,
|
22
22
|
mongo_uri,
|
23
|
-
db_name="
|
23
|
+
db_name="company",
|
24
24
|
collection_name="TWN/AINVFQ1"
|
25
25
|
):
|
26
26
|
self.client = MongoClient(mongo_uri)
|
27
27
|
self.db = self.client[db_name]
|
28
28
|
self.collection = self.db[collection_name]
|
29
29
|
|
30
|
-
index_dict = StatsProcessor.load_yaml("tej_db_index.yaml")
|
30
|
+
index_dict = StatsProcessor.load_yaml("tej_db/tej_db_index.yaml")
|
31
|
+
thousand_dict = StatsProcessor.load_yaml("tej_db/tej_db_thousand_index.yaml")
|
32
|
+
percent_dict = StatsProcessor.load_yaml("tej_db/tej_db_percent_index.yaml")
|
33
|
+
skip_dict = StatsProcessor.load_yaml("tej_db/tej_db_percent_index.yaml")
|
31
34
|
self.check_index = set(index_dict[collection_name])
|
35
|
+
self.skip_index = set(skip_dict[collection_name])
|
36
|
+
|
37
|
+
self.thousand_index_list = list(thousand_dict[collection_name])
|
38
|
+
self.percent_index_list = list(percent_dict[collection_name])
|
39
|
+
|
32
40
|
|
33
41
|
def get(
|
34
42
|
self,
|
@@ -234,6 +242,8 @@ class FinanceReportFetcher(BaseTEJFetcher):
|
|
234
242
|
keys=["year", "season"],
|
235
243
|
delimeter="Q",
|
236
244
|
data_key=report_type)
|
245
|
+
|
246
|
+
data_dict = self.transform_value(data_dict)
|
237
247
|
|
238
248
|
if (use_cal):
|
239
249
|
data_with_QoQ = self.cal_QoQ(data_dict)
|
@@ -347,6 +357,8 @@ class FinanceReportFetcher(BaseTEJFetcher):
|
|
347
357
|
keys=['year', 'season'],
|
348
358
|
data_key=report_type,
|
349
359
|
delimeter='Q')
|
360
|
+
|
361
|
+
data_dict = self.transform_value(data_dict)
|
350
362
|
|
351
363
|
if (use_cal):
|
352
364
|
data_with_YoY = self.cal_YoY(
|
@@ -360,7 +372,29 @@ class FinanceReportFetcher(BaseTEJFetcher):
|
|
360
372
|
data_df = pd.DataFrame.from_dict(data_dict)
|
361
373
|
data_df = data_df.iloc[:, ::-1]
|
362
374
|
return data_df
|
375
|
+
|
376
|
+
def transform_value(self, data_dict):
|
377
|
+
"""
|
378
|
+
處理千元, %等單位
|
379
|
+
"""
|
363
380
|
|
381
|
+
data_df = pd.DataFrame.from_dict(data_dict)
|
382
|
+
|
383
|
+
process_set = set(data_df.index).intersection(set(self.thousand_index_list))
|
384
|
+
process_list = list(process_set)
|
385
|
+
data_df.loc[process_list] = data_df.loc[process_list].map(
|
386
|
+
lambda x : StatsProcessor.cal_non_percentage(x, postfix="千元")
|
387
|
+
)
|
388
|
+
|
389
|
+
process_set = set(data_df.index).intersection(set(self.percent_index_list))
|
390
|
+
process_list = list(process_set)
|
391
|
+
data_df.loc[process_list] = data_df.loc[process_list].map(
|
392
|
+
lambda x : f"{x}%"
|
393
|
+
)
|
394
|
+
|
395
|
+
data_dict = data_df.to_dict()
|
396
|
+
|
397
|
+
return data_dict
|
364
398
|
|
365
399
|
class TEJStockPriceFetcher(BaseTEJFetcher):
|
366
400
|
|
@@ -0,0 +1,44 @@
|
|
1
|
+
TWN/AINVFQ1:
|
2
|
+
- taxrate
|
3
|
+
- r104
|
4
|
+
- r115
|
5
|
+
- r105
|
6
|
+
- r106
|
7
|
+
- r107
|
8
|
+
- r108
|
9
|
+
- r201
|
10
|
+
- r112
|
11
|
+
- r401
|
12
|
+
- r402
|
13
|
+
- r403
|
14
|
+
- r404
|
15
|
+
- r405
|
16
|
+
- r408
|
17
|
+
- r409
|
18
|
+
- r410
|
19
|
+
- r502
|
20
|
+
- r501
|
21
|
+
- r205
|
22
|
+
- r505
|
23
|
+
- r517
|
24
|
+
- r512
|
25
|
+
- r509
|
26
|
+
- r608
|
27
|
+
- r616
|
28
|
+
- r610
|
29
|
+
- r607
|
30
|
+
- r613
|
31
|
+
- r612
|
32
|
+
- r609
|
33
|
+
- r614
|
34
|
+
- r611
|
35
|
+
TWN/AFESTM1:
|
36
|
+
- r105
|
37
|
+
- r106
|
38
|
+
- r107
|
39
|
+
- r108
|
40
|
+
- r401
|
41
|
+
- r402
|
42
|
+
- r403
|
43
|
+
- r404
|
44
|
+
- r405
|
@@ -0,0 +1,71 @@
|
|
1
|
+
TWN/AINVFQ1:
|
2
|
+
- bp11
|
3
|
+
- bp21
|
4
|
+
- bp22
|
5
|
+
- bp31
|
6
|
+
- bp41
|
7
|
+
- bp51
|
8
|
+
- bp53
|
9
|
+
- bp61
|
10
|
+
- bp62
|
11
|
+
- bp63
|
12
|
+
- bp64
|
13
|
+
- bp65
|
14
|
+
- bf11
|
15
|
+
- bf12
|
16
|
+
- bf21
|
17
|
+
- bf22
|
18
|
+
- bf41
|
19
|
+
- bf42
|
20
|
+
- bf43
|
21
|
+
- bf44
|
22
|
+
- bf45
|
23
|
+
- bf99
|
24
|
+
- bsca
|
25
|
+
- bsnca
|
26
|
+
- bsta
|
27
|
+
- bscl
|
28
|
+
- bsncl
|
29
|
+
- bstl
|
30
|
+
- bsse
|
31
|
+
- bslse
|
32
|
+
- debt
|
33
|
+
- quick
|
34
|
+
- ppe
|
35
|
+
- ar
|
36
|
+
- ip12
|
37
|
+
- ip22
|
38
|
+
- ip31
|
39
|
+
- ip51
|
40
|
+
- iv41
|
41
|
+
- if11
|
42
|
+
- isibt
|
43
|
+
- isni
|
44
|
+
- isnip
|
45
|
+
- eps
|
46
|
+
- ispsd
|
47
|
+
- gm
|
48
|
+
- opi
|
49
|
+
- nri
|
50
|
+
- ri
|
51
|
+
- nopi
|
52
|
+
- ebit
|
53
|
+
- cip31
|
54
|
+
- cscfo
|
55
|
+
- cscfi
|
56
|
+
- cscff
|
57
|
+
- person
|
58
|
+
- shares
|
59
|
+
- wavg
|
60
|
+
- r304
|
61
|
+
- r305
|
62
|
+
- r306
|
63
|
+
- r316
|
64
|
+
- r834
|
65
|
+
TWN/AFESTM1:
|
66
|
+
- ip12
|
67
|
+
- gm
|
68
|
+
- opi
|
69
|
+
- isibt
|
70
|
+
- isni
|
71
|
+
- isnip
|