neurostats-API 0.0.23b1__py3-none-any.whl → 0.0.24__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.
- neurostats_API/__init__.py +1 -1
- neurostats_API/fetchers/balance_sheet.py +63 -39
- neurostats_API/fetchers/base.py +6 -2
- neurostats_API/fetchers/cash_flow.py +91 -70
- neurostats_API/fetchers/finance_overview.py +26 -26
- neurostats_API/fetchers/institution.py +216 -97
- neurostats_API/fetchers/macro_daily_event.py +8 -0
- neurostats_API/fetchers/margin_trading.py +121 -94
- neurostats_API/fetchers/month_revenue.py +139 -105
- neurostats_API/fetchers/profit_lose.py +103 -83
- neurostats_API/fetchers/tech.py +25 -27
- neurostats_API/fetchers/tej_finance_report.py +101 -69
- neurostats_API/fetchers/value_invest.py +32 -12
- neurostats_API/tools/tej_db/tej_db_percent_index.yaml +0 -3
- neurostats_API/utils/calculate_value.py +5 -2
- neurostats_API/utils/data_process.py +12 -6
- neurostats_API/utils/logger.py +21 -0
- {neurostats_API-0.0.23b1.dist-info → neurostats_API-0.0.24.dist-info}/METADATA +2 -2
- neurostats_API-0.0.24.dist-info/RECORD +36 -0
- neurostats_API-0.0.23b1.dist-info/RECORD +0 -34
- {neurostats_API-0.0.23b1.dist-info → neurostats_API-0.0.24.dist-info}/WHEEL +0 -0
- {neurostats_API-0.0.23b1.dist-info → neurostats_API-0.0.24.dist-info}/top_level.txt +0 -0
@@ -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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
"
|
38
|
-
|
39
|
-
"
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
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[
|
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['
|
93
|
-
for data in
|
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
|
-
|
116
|
+
|
117
|
+
old_profit_lose_df = StatsProcessor.expand_value_percentage(
|
102
118
|
profit_lose_df
|
103
119
|
)
|
104
|
-
|
105
|
-
value_col =
|
106
|
-
percentage_col =
|
107
|
-
|
108
|
-
grand_total_value_col =
|
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 =
|
127
|
+
grand_total_percentage_col = old_profit_lose_df.columns.str.endswith(
|
112
128
|
f"grand_total_percentage"
|
113
129
|
)
|
114
130
|
|
115
|
-
|
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,
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
"
|
128
|
-
"
|
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 =
|
147
|
-
"_value"
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
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.
|
156
|
-
|
157
|
-
|
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.
|
164
|
-
|
165
|
-
|
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 |
|
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
|
-
|
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":
|
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
|
}
|
@@ -207,8 +222,8 @@ class ProfitLoseFetcher(StatsFetcher):
|
|
207
222
|
def process_data_us(self, fetched_data):
|
208
223
|
|
209
224
|
table_dict = {
|
210
|
-
f"{data['year']}Q{data['season']}": data['
|
211
|
-
for data in fetched_data
|
225
|
+
f"{data['year']}Q{data['season']}": data['data']
|
226
|
+
for data in fetched_data['seasonal_data']
|
212
227
|
}
|
213
228
|
|
214
229
|
table_dict = YoY_Calculator.cal_QoQ(table_dict)
|
@@ -218,16 +233,21 @@ 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
|
-
|
236
|
+
target_keys=["value", "growth"] +
|
237
|
+
[f"YoY_{i}" for i in [1, 3, 5, 10]]
|
223
238
|
)
|
224
239
|
|
225
240
|
# 計算QoQ
|
226
241
|
|
227
242
|
return_dict = {
|
228
243
|
"ticker": self.ticker,
|
229
|
-
"company_name": fetched_data[
|
244
|
+
"company_name": fetched_data['company_name'],
|
230
245
|
"profit_lose": pd.DataFrame.from_dict(table_dict)
|
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
|
+
}
|
neurostats_API/fetchers/tech.py
CHANGED
@@ -138,31 +138,6 @@ class TechFetcher(StatsFetcher):
|
|
138
138
|
)
|
139
139
|
|
140
140
|
return df
|
141
|
-
|
142
|
-
|
143
|
-
def conduct_db_search_twse(self):
|
144
|
-
required_cols = ['date', 'open', 'high', 'low', 'close', 'volume']
|
145
|
-
match_query = {"ticker" : self.ticker}
|
146
|
-
proj_query = {"_id": 0, "daily_data": 1}
|
147
|
-
|
148
|
-
full_data = self.twse_collection.find_one(match_query, proj_query)
|
149
|
-
|
150
|
-
if (not full_data):
|
151
|
-
raise ValueError("No ticker found in database twse_stats")
|
152
|
-
|
153
|
-
daily_data = full_data.get("daily_data", [])
|
154
|
-
|
155
|
-
if (not isinstance(daily_data, list)):
|
156
|
-
raise ValueError("No ticker found in database twse_stats")
|
157
|
-
|
158
|
-
df = pd.DataFrame(daily_data)
|
159
|
-
if not self.has_required_columns(df, required_cols):
|
160
|
-
raise KeyError(f"Missing required columns")
|
161
|
-
|
162
|
-
df = df[required_cols]
|
163
|
-
df = df.sort_values(by = 'date').drop_duplicates(subset=['date'])
|
164
|
-
|
165
|
-
return df
|
166
141
|
|
167
142
|
def conduct_db_search_tej(self):
|
168
143
|
# 再對TEJ search
|
@@ -177,7 +152,7 @@ class TechFetcher(StatsFetcher):
|
|
177
152
|
}
|
178
153
|
|
179
154
|
query = {'ticker': self.ticker}
|
180
|
-
ticker_full = self.
|
155
|
+
ticker_full = self.collection.find_one(query)
|
181
156
|
|
182
157
|
if not ticker_full:
|
183
158
|
raise ValueError("No ticker found in database")
|
@@ -215,10 +190,33 @@ class TechFetcher(StatsFetcher):
|
|
215
190
|
missing_cols = ",".join(missing_cols)
|
216
191
|
for col in missing_cols:
|
217
192
|
df[col] = pd.NA
|
218
|
-
# raise KeyError(f"Missing required columns")
|
219
193
|
|
220
194
|
return df[required_cols]
|
221
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
|
222
220
|
|
223
221
|
class TechProcessor:
|
224
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
|
@@ -127,6 +128,8 @@ class FinanceReportFetcher(BaseTEJFetcher):
|
|
127
128
|
lower_bound_year, lower_bound_season, report_type, indexes
|
128
129
|
)
|
129
130
|
fetched_data = self.collection.aggregate(pipeline).to_list()
|
131
|
+
fetched_data = fetched_data[0]
|
132
|
+
fetched_data = fetched_data.get('data', []) if isinstance(fetched_data, dict) else []
|
130
133
|
|
131
134
|
data_dict = self.transform_value(
|
132
135
|
StatsProcessor.list_of_dict_to_dict(
|
@@ -174,11 +177,14 @@ class FinanceReportFetcher(BaseTEJFetcher):
|
|
174
177
|
year_based=True
|
175
178
|
)
|
176
179
|
fetched_data = self.collection.aggregate(pipeline).to_list()
|
180
|
+
fetched_data = fetched_data[0]
|
181
|
+
fetched_data = fetched_data.get('data', []) if isinstance(fetched_data, dict) else []
|
182
|
+
|
177
183
|
data_dict = self.transform_value(
|
178
184
|
StatsProcessor.list_of_dict_to_dict(
|
179
|
-
data_list=fetched_data,
|
180
|
-
keys=["year", "season"],
|
181
|
-
delimeter="Q",
|
185
|
+
data_list=fetched_data,
|
186
|
+
keys=["year", "season"],
|
187
|
+
delimeter="Q",
|
182
188
|
data_key=report_type
|
183
189
|
)
|
184
190
|
)
|
@@ -198,9 +204,9 @@ class FinanceReportFetcher(BaseTEJFetcher):
|
|
198
204
|
(self.percent_index_list, "%")]:
|
199
205
|
process_list = list(set(data_df.index) & set(category))
|
200
206
|
if postfix == "%":
|
201
|
-
data_df
|
202
|
-
|
203
|
-
|
207
|
+
data_df = data_df.T
|
208
|
+
data_df[process_list] = data_df[process_list].map(lambda x: f"{x}%") # if (not np.isnan(x)) else None)
|
209
|
+
data_df = data_df.T
|
204
210
|
else:
|
205
211
|
data_df.loc[process_list] = data_df.loc[process_list].map(
|
206
212
|
lambda x: StatsProcessor.
|
@@ -221,67 +227,80 @@ class FinanceReportFetcher(BaseTEJFetcher):
|
|
221
227
|
indexes,
|
222
228
|
year_based=False
|
223
229
|
):
|
224
|
-
project_stage = {
|
225
|
-
"_id": 0,
|
226
|
-
"data.year": 1,
|
227
|
-
"data.season": 1,
|
228
|
-
**{
|
229
|
-
f"data.{report_type}.{idx}": 1
|
230
|
-
for idx in indexes
|
231
|
-
}
|
232
|
-
} if indexes else {
|
233
|
-
"_id": 0,
|
234
|
-
"data.year": 1,
|
235
|
-
"data.season": 1,
|
236
|
-
f"data.{report_type}": 1
|
237
|
-
}
|
238
230
|
|
239
|
-
if
|
240
|
-
|
241
|
-
"
|
242
|
-
"$in": start_year
|
243
|
-
|
244
|
-
|
245
|
-
"$lt": end_year
|
246
|
-
},
|
247
|
-
"data.season": start_season
|
231
|
+
if year_based:
|
232
|
+
filter_cond = {
|
233
|
+
"$and": [
|
234
|
+
{ "$in": ["$$item.year", start_year] },
|
235
|
+
{ "$eq": ["$$item.season", start_season] }
|
236
|
+
]
|
248
237
|
}
|
249
238
|
else:
|
250
|
-
|
239
|
+
filter_cond = {
|
251
240
|
"$or": [
|
252
241
|
{
|
253
|
-
"
|
254
|
-
"$gt": start_year,
|
255
|
-
"$lt": end_year
|
256
|
-
|
257
|
-
},
|
258
|
-
|
259
|
-
"
|
260
|
-
"$
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
242
|
+
"$and": [
|
243
|
+
{ "$gt": ["$$item.year", start_year] },
|
244
|
+
{ "$lt": ["$$item.year", end_year] }
|
245
|
+
]
|
246
|
+
},
|
247
|
+
{
|
248
|
+
"$and": [
|
249
|
+
{ "$eq": ["$$item.year", start_year] },
|
250
|
+
{ "$gte": ["$$item.season", start_season] }
|
251
|
+
]
|
252
|
+
},
|
253
|
+
{
|
254
|
+
"$and": [
|
255
|
+
{ "$eq": ["$$item.year", end_year] },
|
256
|
+
{ "$lte": ["$$item.season", end_season] }
|
257
|
+
]
|
258
|
+
},
|
259
|
+
{
|
260
|
+
"$and": [
|
261
|
+
{ "$eq": ["$$item.year", lower_bound_year] },
|
262
|
+
{ "$eq": ["$$item.season", lower_bound_season] }
|
263
|
+
]
|
270
264
|
}
|
271
265
|
]
|
272
266
|
}
|
273
267
|
|
268
|
+
# 每個 filtered item 要輸出哪些欄位
|
269
|
+
item_fields = {
|
270
|
+
"year": "$$item.year",
|
271
|
+
"season": "$$item.season"
|
272
|
+
}
|
273
|
+
|
274
|
+
if indexes:
|
275
|
+
for idx in indexes:
|
276
|
+
item_fields[idx] = f"$$item.{report_type}.{idx}"
|
277
|
+
else:
|
278
|
+
item_fields[report_type] = f"$$item.{report_type}"
|
279
|
+
|
274
280
|
return [
|
275
281
|
{
|
276
282
|
"$match": {
|
277
283
|
"ticker": ticker
|
278
284
|
}
|
279
|
-
},
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
+
},
|
286
|
+
{
|
287
|
+
"$project": {
|
288
|
+
"_id": 0,
|
289
|
+
"ticker": 1,
|
290
|
+
"data": {
|
291
|
+
"$map": {
|
292
|
+
"input": {
|
293
|
+
"$filter": {
|
294
|
+
"input": "$data",
|
295
|
+
"as": "item",
|
296
|
+
"cond": filter_cond
|
297
|
+
}
|
298
|
+
},
|
299
|
+
"as": "item",
|
300
|
+
"in": item_fields
|
301
|
+
}
|
302
|
+
}
|
303
|
+
}
|
285
304
|
}
|
286
305
|
]
|
287
306
|
|
@@ -330,7 +349,10 @@ class TEJStockPriceFetcher(BaseTEJFetcher):
|
|
330
349
|
|
331
350
|
if (period is not None):
|
332
351
|
latest_date = self.get_latest_data_time(ticker)
|
333
|
-
|
352
|
+
if (latest_date):
|
353
|
+
start_date = self.set_time_shift(date=latest_date, period=period)
|
354
|
+
else:
|
355
|
+
start_date = datetime.strptime(start_date, "%Y-%m-%d")
|
334
356
|
else:
|
335
357
|
start_date = datetime.strptime(start_date, "%Y-%m-%d")
|
336
358
|
|
@@ -339,26 +361,36 @@ class TEJStockPriceFetcher(BaseTEJFetcher):
|
|
339
361
|
"$match": {
|
340
362
|
"ticker": ticker
|
341
363
|
}
|
342
|
-
},
|
343
|
-
|
344
|
-
}, {
|
345
|
-
"$match": {
|
346
|
-
"data.mdate": {
|
347
|
-
"$gt": start_date
|
348
|
-
}
|
349
|
-
}
|
350
|
-
}, {
|
364
|
+
},
|
365
|
+
{
|
351
366
|
"$project": {
|
367
|
+
"_id": 0,
|
352
368
|
"ticker": 1,
|
353
|
-
"data":
|
354
|
-
|
369
|
+
"data": {
|
370
|
+
"$filter": {
|
371
|
+
"input": "$data",
|
372
|
+
"as": "item",
|
373
|
+
"cond": {
|
374
|
+
"$gte": ["$$item.mdate", start_date]
|
375
|
+
}
|
376
|
+
}
|
377
|
+
}
|
355
378
|
}
|
356
379
|
}
|
357
380
|
]
|
358
381
|
datas = self.collection.aggregate(pipeline).to_list()
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
382
|
+
datas = datas[0]
|
383
|
+
datas = datas.get('data', [])
|
384
|
+
elements = [element for element in datas]
|
385
|
+
try:
|
386
|
+
data_df = pd.DataFrame(elements).set_index('mdate')
|
387
|
+
except:
|
388
|
+
column_names = [
|
389
|
+
"coid", "mdate", "mkt", "open_d", "high_d", "low_d", "close_d",
|
390
|
+
"adjfac", "vol", "amt", "trn", "bid", "offer", "avgprc", "roi",
|
391
|
+
"hmlpct", "turnover", "shares", "mktcap", "mktcap_pct",
|
392
|
+
"amt_pct", "per", "pbr", "div_yid", "cdiv_yid"
|
393
|
+
]
|
394
|
+
data_df = pd.DataFrame(columns = column_names)
|
363
395
|
|
364
396
|
return data_df
|