neurostats-API 0.0.6__py3-none-any.whl → 0.0.7__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- neurostats_API/__init__.py +1 -1
- neurostats_API/fetchers/__init__.py +4 -0
- neurostats_API/fetchers/balance_sheet.py +122 -0
- neurostats_API/fetchers/cash_flow.py +181 -0
- neurostats_API/fetchers/finance_overview.py +3 -1
- neurostats_API/fetchers/month_revenue.py +92 -0
- neurostats_API/fetchers/profit_lose.py +131 -0
- neurostats_API/tools/balance_sheet.yaml +26 -0
- neurostats_API/tools/cash_flow_percentage.yaml +39 -0
- neurostats_API/tools/profit_lose.yaml +1 -1
- neurostats_API/utils/data_process.py +123 -3
- {neurostats_API-0.0.6.dist-info → neurostats_API-0.0.7.dist-info}/METADATA +135 -187
- neurostats_API-0.0.7.dist-info/RECORD +26 -0
- neurostats_API-0.0.6.dist-info/RECORD +0 -23
- {neurostats_API-0.0.6.dist-info → neurostats_API-0.0.7.dist-info}/WHEEL +0 -0
- {neurostats_API-0.0.6.dist-info → neurostats_API-0.0.7.dist-info}/top_level.txt +0 -0
neurostats_API/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__='0.0.
|
1
|
+
__version__='0.0.7'
|
@@ -1,3 +1,7 @@
|
|
1
1
|
from .base import StatsDateTime, StatsFetcher
|
2
|
+
from .balance_sheet import BalanceSheetFetcher
|
3
|
+
from .cash_flow import CashFlowFetcher
|
2
4
|
from .finance_overview import FinanceOverviewFetcher
|
5
|
+
from .month_revenue import MonthRevenueFetcher
|
6
|
+
from .profit_lose import ProfitLoseFetcher
|
3
7
|
from .value_invest import ValueFetcher
|
@@ -0,0 +1,122 @@
|
|
1
|
+
from .base import StatsFetcher, StatsDateTime
|
2
|
+
import json
|
3
|
+
import pandas as pd
|
4
|
+
from ..utils import StatsDateTime, StatsProcessor
|
5
|
+
import importlib.resources as pkg_resources
|
6
|
+
import yaml
|
7
|
+
|
8
|
+
class BalanceSheetFetcher(StatsFetcher):
|
9
|
+
"""
|
10
|
+
對應iFa.ai -> 財務分析 -> 資產負債表
|
11
|
+
"""
|
12
|
+
|
13
|
+
def __init__(self, ticker, db_client):
|
14
|
+
super().__init__(ticker, db_client)
|
15
|
+
self.table_settings = StatsProcessor.load_yaml("balance_sheet.yaml")
|
16
|
+
|
17
|
+
def prepare_query(self, target_year, target_season):
|
18
|
+
pipeline = super().prepare_query()
|
19
|
+
|
20
|
+
target_query = {
|
21
|
+
"year": "$$target_season_data.year",
|
22
|
+
"season": "$$target_season_data.season",
|
23
|
+
"balance_sheet": "$$$$target_season_data.balance_sheet"
|
24
|
+
}
|
25
|
+
|
26
|
+
pipeline.append({
|
27
|
+
"$project": {
|
28
|
+
"_id": 0,
|
29
|
+
"ticker": 1,
|
30
|
+
"company_name": 1,
|
31
|
+
"balance_sheets": {
|
32
|
+
"$sortArray": {
|
33
|
+
"input": {
|
34
|
+
"$map": {
|
35
|
+
"input": {
|
36
|
+
"$filter": {
|
37
|
+
"input": "$seasonal_data",
|
38
|
+
"as": "season",
|
39
|
+
"cond": {
|
40
|
+
"$eq": ["$$season.season", target_season]
|
41
|
+
}
|
42
|
+
}
|
43
|
+
},
|
44
|
+
"as": "target_season_data",
|
45
|
+
"in": {
|
46
|
+
"year": "$$target_season_data.year",
|
47
|
+
"season": "$$target_season_data.season",
|
48
|
+
"balance_sheet": "$$target_season_data.balance_sheet"
|
49
|
+
}
|
50
|
+
}
|
51
|
+
},
|
52
|
+
"sortBy": { "year": -1 } # 按 year 降序排序
|
53
|
+
}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
})
|
57
|
+
|
58
|
+
return pipeline
|
59
|
+
|
60
|
+
def collect_data(self, target_year, target_season):
|
61
|
+
pipeline = self.prepare_query(target_year, target_season)
|
62
|
+
|
63
|
+
fetched_data = self.collection.aggregate(pipeline)
|
64
|
+
|
65
|
+
fetched_data = list(fetched_data)
|
66
|
+
|
67
|
+
return fetched_data[-1]
|
68
|
+
|
69
|
+
def query_data(self):
|
70
|
+
today = StatsDateTime.get_today()
|
71
|
+
|
72
|
+
year = today.year - 1 if (today.season == 1) else today.year
|
73
|
+
season = 4 if (today.season == 1) else today.season - 2
|
74
|
+
fetched_data = self.collect_data(year, season)
|
75
|
+
|
76
|
+
return self.process_data(season, fetched_data)
|
77
|
+
|
78
|
+
def process_data(self, target_season, fetched_data):
|
79
|
+
return_dict = {
|
80
|
+
"ticker": self.ticker,
|
81
|
+
"company_name": fetched_data['company_name']
|
82
|
+
}
|
83
|
+
|
84
|
+
index_names = []
|
85
|
+
|
86
|
+
table_dict = dict()
|
87
|
+
|
88
|
+
balance_sheets = fetched_data['balance_sheets']
|
89
|
+
|
90
|
+
# 將value與percentage跟著年分季度一筆筆取出
|
91
|
+
for data in balance_sheets:
|
92
|
+
year = data['year']
|
93
|
+
|
94
|
+
time_index = f"{year}Q{target_season}"
|
95
|
+
|
96
|
+
# 蒐集整體的keys
|
97
|
+
index_names += list(data['balance_sheet'].keys())
|
98
|
+
balance_sheet = data['balance_sheet']
|
99
|
+
|
100
|
+
for index_name, value_dict in balance_sheet.items():
|
101
|
+
for item_name, item in value_dict.items():
|
102
|
+
try: # table_dict[項目][(2020Q1, '%')]
|
103
|
+
table_dict[index_name][(time_index,item_name)] = item
|
104
|
+
|
105
|
+
except KeyError:
|
106
|
+
if (index_name not in table_dict.keys()):
|
107
|
+
table_dict[index_name] = dict()
|
108
|
+
|
109
|
+
table_dict[index_name][(time_index,item_name)] = item
|
110
|
+
|
111
|
+
total_table = pd.DataFrame.from_dict(table_dict, orient='index')
|
112
|
+
total_table.columns = pd.MultiIndex.from_tuples(total_table.columns)
|
113
|
+
|
114
|
+
for name, setting in self.table_settings.items():
|
115
|
+
return_dict[name] = StatsProcessor.slice_multi_col_table(
|
116
|
+
total_table=total_table,
|
117
|
+
mode=setting['mode'],
|
118
|
+
target_index=setting['target_index']
|
119
|
+
if "target_index" in setting.keys() else None)
|
120
|
+
|
121
|
+
print(f"{name}: {return_dict[name].columns}")
|
122
|
+
return return_dict
|
@@ -0,0 +1,181 @@
|
|
1
|
+
from .base import StatsFetcher, StatsDateTime
|
2
|
+
import json
|
3
|
+
import pandas as pd
|
4
|
+
from ..utils import StatsDateTime, StatsProcessor
|
5
|
+
import importlib.resources as pkg_resources
|
6
|
+
import yaml
|
7
|
+
|
8
|
+
class CashFlowFetcher(StatsFetcher):
|
9
|
+
def __init__(self, ticker, db_client):
|
10
|
+
super().__init__(ticker, db_client)
|
11
|
+
|
12
|
+
self.cash_flow_dict = StatsProcessor.load_yaml(
|
13
|
+
"cash_flow_percentage.yaml"
|
14
|
+
) # 計算子表格用
|
15
|
+
|
16
|
+
def prepare_query(self, target_season):
|
17
|
+
pipeline = super().prepare_query()
|
18
|
+
|
19
|
+
pipeline.append({
|
20
|
+
"$project": {
|
21
|
+
"_id": 0,
|
22
|
+
"ticker": 1,
|
23
|
+
"company_name": 1,
|
24
|
+
"cash_flows": {
|
25
|
+
"$sortArray": {
|
26
|
+
"input": {
|
27
|
+
"$map": {
|
28
|
+
"input": {
|
29
|
+
"$filter": {
|
30
|
+
"input": "$seasonal_data",
|
31
|
+
"as": "season",
|
32
|
+
"cond": {
|
33
|
+
"$eq":
|
34
|
+
["$$season.season", target_season]
|
35
|
+
}
|
36
|
+
}
|
37
|
+
},
|
38
|
+
"as": "target_season_data",
|
39
|
+
"in": {
|
40
|
+
"year":
|
41
|
+
"$$target_season_data.year",
|
42
|
+
"season":
|
43
|
+
"$$target_season_data.season",
|
44
|
+
"cash_flow":
|
45
|
+
"$$target_season_data.cash_flow"
|
46
|
+
}
|
47
|
+
}
|
48
|
+
},
|
49
|
+
"sortBy": {
|
50
|
+
"year": -1
|
51
|
+
} # 按 year 降序排序
|
52
|
+
}
|
53
|
+
}
|
54
|
+
}
|
55
|
+
})
|
56
|
+
|
57
|
+
return pipeline
|
58
|
+
|
59
|
+
def collect_data(self, target_season):
|
60
|
+
pipeline = self.prepare_query(target_season)
|
61
|
+
|
62
|
+
fetched_data = self.collection.aggregate(pipeline)
|
63
|
+
|
64
|
+
return list(fetched_data)[0]
|
65
|
+
|
66
|
+
def query_data(self):
|
67
|
+
today = StatsDateTime.get_today()
|
68
|
+
|
69
|
+
target_season = today.season - 1 if (today.season > 1) else 4
|
70
|
+
|
71
|
+
fetched_data = self.collect_data(target_season)
|
72
|
+
|
73
|
+
return self.process_data(fetched_data, target_season)
|
74
|
+
|
75
|
+
def process_data(self, fetched_data, target_season):
|
76
|
+
"""
|
77
|
+
處理現金流量表頁面的所有表格
|
78
|
+
金流表本身沒有比例 但是Ifa有算,
|
79
|
+
項目所屬的情況也不一(分別所屬營業,投資,籌資三個活動)
|
80
|
+
所以這裡選擇不用slicing處理
|
81
|
+
"""
|
82
|
+
cash_flows = fetched_data['cash_flows']
|
83
|
+
|
84
|
+
index_names = []
|
85
|
+
column_names = []
|
86
|
+
|
87
|
+
table_dict = dict()
|
88
|
+
CASHO_dict = dict()
|
89
|
+
CASHI_dict = dict()
|
90
|
+
CASHF_dict = dict()
|
91
|
+
|
92
|
+
return_dict = {
|
93
|
+
"ticker": fetched_data['ticker'],
|
94
|
+
"company_name": fetched_data['company_name'],
|
95
|
+
"cash_flow": dict(),
|
96
|
+
"CASHO": dict(),
|
97
|
+
"CASHI": dict(),
|
98
|
+
"CASHF": dict()
|
99
|
+
}
|
100
|
+
|
101
|
+
checkpoints = ["營業活動之現金流量-間接法", "投資活動之現金流量", "籌資活動之現金流量", "匯率變動對現金及約當現金之影響"]
|
102
|
+
main_cash_flows = [
|
103
|
+
"營業活動之淨現金流入(流出)", "投資活動之淨現金流入(流出)", "籌資活動之淨現金流入(流出)", None
|
104
|
+
] # 主要的比例對象
|
105
|
+
partial_cash_flows = [CASHO_dict, CASHI_dict, CASHF_dict, dict()]
|
106
|
+
|
107
|
+
# 作法: dictionary中也有checkpoints,如果出現了就換下一個index去計算
|
108
|
+
|
109
|
+
for data in cash_flows:
|
110
|
+
year = data['year']
|
111
|
+
|
112
|
+
time_index = f"{year}Q{target_season}"
|
113
|
+
|
114
|
+
cash_flow = data['cash_flow']
|
115
|
+
main_cash_flow_name = None
|
116
|
+
partial_cash_flow = None
|
117
|
+
next_checkpoint = 0
|
118
|
+
|
119
|
+
for index_name, value in cash_flow.items():
|
120
|
+
if (next_checkpoint < 3
|
121
|
+
and index_name == checkpoints[next_checkpoint]): # 找到了主要的變動點
|
122
|
+
main_cash_flow_name = main_cash_flows[next_checkpoint]
|
123
|
+
partial_cash_flow = partial_cash_flows[next_checkpoint]
|
124
|
+
next_checkpoint += 1
|
125
|
+
try:
|
126
|
+
table_dict[time_index][index_name]['value'] = value[
|
127
|
+
'value']
|
128
|
+
if (value['value']):
|
129
|
+
table_dict[time_index][index_name][
|
130
|
+
'percentage'] = value['value'] / cash_flow[
|
131
|
+
main_cash_flow_name]['value']
|
132
|
+
else:
|
133
|
+
table_dict[time_index][index_name][
|
134
|
+
'percentage'] = None
|
135
|
+
except:
|
136
|
+
if (time_index not in table_dict.keys()):
|
137
|
+
table_dict[time_index] = dict()
|
138
|
+
table_dict[time_index][index_name] = dict()
|
139
|
+
|
140
|
+
table_dict[time_index][index_name]['value'] = value[
|
141
|
+
'value']
|
142
|
+
if (value['value']):
|
143
|
+
table_dict[time_index][index_name][
|
144
|
+
'percentage'] = value['value'] / cash_flow[
|
145
|
+
main_cash_flow_name]['value']
|
146
|
+
else:
|
147
|
+
table_dict[time_index][index_name][
|
148
|
+
'percentage'] = None
|
149
|
+
|
150
|
+
try:
|
151
|
+
partial_cash_flow[time_index][index_name] = table_dict[
|
152
|
+
time_index][index_name]
|
153
|
+
except:
|
154
|
+
if (time_index not in partial_cash_flow.keys()):
|
155
|
+
partial_cash_flow[time_index] = dict()
|
156
|
+
partial_cash_flow[time_index][index_name] = table_dict[
|
157
|
+
time_index][index_name]
|
158
|
+
|
159
|
+
index_names += list(cash_flow.keys())
|
160
|
+
|
161
|
+
# 轉成dictionary keys
|
162
|
+
index_names = list(dict.fromkeys(index_names))
|
163
|
+
|
164
|
+
cash_flow_table = pd.DataFrame(table_dict)
|
165
|
+
cash_flow_table = StatsProcessor.expand_value_percentage(cash_flow_table)
|
166
|
+
|
167
|
+
CASHO_table = pd.DataFrame(CASHO_dict)
|
168
|
+
CASHO_table = StatsProcessor.expand_value_percentage(CASHO_table)
|
169
|
+
|
170
|
+
CASHI_table = pd.DataFrame(CASHI_dict)
|
171
|
+
CASHI_table = StatsProcessor.expand_value_percentage(CASHI_table)
|
172
|
+
|
173
|
+
CASHF_table = pd.DataFrame(CASHF_dict)
|
174
|
+
CASHF_table = StatsProcessor.expand_value_percentage(CASHF_table)
|
175
|
+
|
176
|
+
return_dict['cash_flow'] = cash_flow_table
|
177
|
+
return_dict['CASHO'] = CASHO_table
|
178
|
+
return_dict['CASHI'] = CASHI_table
|
179
|
+
return_dict['CASHF'] = CASHF_table
|
180
|
+
|
181
|
+
return return_dict
|
@@ -82,7 +82,9 @@ class FinanceOverviewFetcher(StatsFetcher):
|
|
82
82
|
def query_data(self):
|
83
83
|
today = StatsDateTime.get_today()
|
84
84
|
|
85
|
-
|
85
|
+
year = today.year - 1 if (today.season == 1) else today.year
|
86
|
+
season = 4 if (today.season == 1) else today.season - 2
|
87
|
+
fetched_data = self.collect_data(year, season)
|
86
88
|
finance_dict = fetched_data['seasonal_data'][0]
|
87
89
|
FinanceOverviewProcessor.process_all(finance_dict)
|
88
90
|
fetched_data['seasonal_data'] = finance_dict
|
@@ -0,0 +1,92 @@
|
|
1
|
+
from .base import StatsFetcher, StatsDateTime
|
2
|
+
import json
|
3
|
+
import pandas as pd
|
4
|
+
from ..utils import StatsDateTime, StatsProcessor
|
5
|
+
import importlib.resources as pkg_resources
|
6
|
+
import yaml
|
7
|
+
|
8
|
+
|
9
|
+
class MonthRevenueFetcher(StatsFetcher):
|
10
|
+
"""
|
11
|
+
iFa.ai: 財務分析 -> 每月營收
|
12
|
+
"""
|
13
|
+
|
14
|
+
def __init__(self, ticker, db_client):
|
15
|
+
super().__init__(ticker, db_client)
|
16
|
+
|
17
|
+
def prepare_query(self, target_year, target_month):
|
18
|
+
pipeline = super().prepare_query()
|
19
|
+
|
20
|
+
pipeline.append({
|
21
|
+
"$project": {
|
22
|
+
"_id": 0,
|
23
|
+
"ticker": 1,
|
24
|
+
"company_name": 1,
|
25
|
+
"monthly_data": {
|
26
|
+
"$sortArray": {
|
27
|
+
"input": "$monthly_data",
|
28
|
+
"sortBy": {
|
29
|
+
"year": -1,
|
30
|
+
"month": -1
|
31
|
+
}
|
32
|
+
}
|
33
|
+
},
|
34
|
+
}
|
35
|
+
})
|
36
|
+
|
37
|
+
return pipeline
|
38
|
+
|
39
|
+
def collect_data(self, target_year, target_month):
|
40
|
+
pipeline = self.prepare_query(target_year, target_month)
|
41
|
+
|
42
|
+
fetched_data = self.collection.aggregate(pipeline)
|
43
|
+
|
44
|
+
fetched_data = list(fetched_data)
|
45
|
+
|
46
|
+
return fetched_data[-1]
|
47
|
+
|
48
|
+
def query_data(self):
|
49
|
+
today = StatsDateTime.get_today()
|
50
|
+
target_month = today.month
|
51
|
+
target_year = today.year
|
52
|
+
|
53
|
+
# Query data
|
54
|
+
fetched_data = self.collect_data(target_year, target_month)
|
55
|
+
|
56
|
+
return self.process_data(fetched_data)
|
57
|
+
|
58
|
+
def process_data(self, fetched_data):
|
59
|
+
|
60
|
+
monthly_data = fetched_data['monthly_data']
|
61
|
+
target_month = monthly_data[0]['month']
|
62
|
+
monthly_df = pd.DataFrame(monthly_data)
|
63
|
+
target_month_df = monthly_df[monthly_df['month'] == target_month]
|
64
|
+
month_revenue_df = monthly_df.pivot(index='month',
|
65
|
+
columns='year',
|
66
|
+
values='revenue')
|
67
|
+
|
68
|
+
grand_total_df = target_month_df.pivot(index='month',
|
69
|
+
columns='year',
|
70
|
+
values='grand_total')
|
71
|
+
|
72
|
+
grand_total_df.rename(index={target_month: f"grand_total"},
|
73
|
+
inplace=True)
|
74
|
+
month_revenue_df = month_revenue_df.sort_index(ascending = False)
|
75
|
+
month_revenue_df = pd.concat([grand_total_df, month_revenue_df],
|
76
|
+
axis=0)
|
77
|
+
|
78
|
+
fetched_data['month_revenue'] = month_revenue_df[sorted(month_revenue_df.columns, reverse = True)]
|
79
|
+
# 歷年月營收
|
80
|
+
fetched_data[
|
81
|
+
'this_month_revenue_over_years'] = target_month_df.set_index(
|
82
|
+
"year")[["revenue", "revenue_increment_ratio", "YoY_1",
|
83
|
+
"YoY_3", "YoY_5", "YoY_10"]].T
|
84
|
+
# 歷年營收成長量
|
85
|
+
fetched_data['grand_total_over_years'] = target_month_df.set_index(
|
86
|
+
"year")[["grand_total", "grand_total_increment_ratio",
|
87
|
+
"grand_total_YoY_1", "grand_total_YoY_3",
|
88
|
+
"grand_total_YoY_5", "grand_total_YoY_10"]].T
|
89
|
+
|
90
|
+
fetched_data.pop("monthly_data")
|
91
|
+
|
92
|
+
return fetched_data
|
@@ -0,0 +1,131 @@
|
|
1
|
+
from .base import StatsFetcher, StatsDateTime
|
2
|
+
import json
|
3
|
+
import pandas as pd
|
4
|
+
from ..utils import StatsDateTime, StatsProcessor
|
5
|
+
import importlib.resources as pkg_resources
|
6
|
+
import yaml
|
7
|
+
|
8
|
+
|
9
|
+
class ProfitLoseFetcher(StatsFetcher):
|
10
|
+
"""
|
11
|
+
iFa.ai: 財務分析 -> 損益表
|
12
|
+
"""
|
13
|
+
|
14
|
+
def __init__(self, ticker, db_client):
|
15
|
+
super().__init__(ticker, db_client)
|
16
|
+
|
17
|
+
self.table_settings = StatsProcessor.load_yaml("profit_lose.yaml")
|
18
|
+
|
19
|
+
def prepare_query(self, target_season):
|
20
|
+
pipeline = super().prepare_query()
|
21
|
+
|
22
|
+
target_query = {
|
23
|
+
"year": "$$target_season_data.year",
|
24
|
+
"season": "$$target_season_data.season",
|
25
|
+
"balance_sheet": "$$$$target_season_data.balance_sheet"
|
26
|
+
}
|
27
|
+
|
28
|
+
pipeline.append({
|
29
|
+
"$project": {
|
30
|
+
"_id": 0,
|
31
|
+
"ticker": 1,
|
32
|
+
"company_name": 1,
|
33
|
+
"profit_loses": {
|
34
|
+
"$sortArray": {
|
35
|
+
"input": {
|
36
|
+
"$map": {
|
37
|
+
"input": {
|
38
|
+
"$filter": {
|
39
|
+
"input": "$seasonal_data",
|
40
|
+
"as": "season",
|
41
|
+
"cond": {
|
42
|
+
"$eq":
|
43
|
+
["$$season.season", target_season]
|
44
|
+
}
|
45
|
+
}
|
46
|
+
},
|
47
|
+
"as": "target_season_data",
|
48
|
+
"in": {
|
49
|
+
"year":
|
50
|
+
"$$target_season_data.year",
|
51
|
+
"season":
|
52
|
+
"$$target_season_data.season",
|
53
|
+
"profit_lose":
|
54
|
+
"$$target_season_data.profit_lose"
|
55
|
+
}
|
56
|
+
}
|
57
|
+
},
|
58
|
+
"sortBy": {
|
59
|
+
"year": -1
|
60
|
+
} # 按 year 降序排序
|
61
|
+
}
|
62
|
+
}
|
63
|
+
}
|
64
|
+
})
|
65
|
+
|
66
|
+
return pipeline
|
67
|
+
|
68
|
+
def collect_data(self, target_season):
|
69
|
+
pipeline = self.prepare_query(target_season)
|
70
|
+
|
71
|
+
fetched_data = self.collection.aggregate(pipeline)
|
72
|
+
|
73
|
+
return list(fetched_data)[-1]
|
74
|
+
|
75
|
+
def query_data(self):
|
76
|
+
today = StatsDateTime.get_today()
|
77
|
+
|
78
|
+
target_season = today.season
|
79
|
+
target_season = target_season - 1 if target_season > 1 else 4
|
80
|
+
|
81
|
+
fetched_data = self.collect_data(target_season)
|
82
|
+
|
83
|
+
return self.process_data(fetched_data, target_season)
|
84
|
+
|
85
|
+
def process_data(self, fetched_data, target_season):
|
86
|
+
|
87
|
+
profit_loses = fetched_data['profit_loses']
|
88
|
+
|
89
|
+
index_names = []
|
90
|
+
|
91
|
+
table_dict = dict()
|
92
|
+
grand_total_dict = dict()
|
93
|
+
|
94
|
+
return_dict = {
|
95
|
+
"ticker": fetched_data['ticker'],
|
96
|
+
"company_name": fetched_data['company_name'],
|
97
|
+
}
|
98
|
+
|
99
|
+
for data in profit_loses:
|
100
|
+
year = data['year']
|
101
|
+
|
102
|
+
time_index = f"{year}Q{target_season}"
|
103
|
+
|
104
|
+
# 蒐集整體的keys
|
105
|
+
index_names += list(data['profit_lose'].keys())
|
106
|
+
profit_lose = data['profit_lose']
|
107
|
+
|
108
|
+
for index_name, value_dict in profit_lose.items():
|
109
|
+
# (2020Q1, 項目, 金額或%)
|
110
|
+
for item_name, item in value_dict.items():
|
111
|
+
try:
|
112
|
+
table_dict[index_name][(time_index, item_name)] = item
|
113
|
+
|
114
|
+
except KeyError:
|
115
|
+
if (index_name not in table_dict.keys()):
|
116
|
+
table_dict[index_name] = dict()
|
117
|
+
grand_total_dict[index_name] = dict()
|
118
|
+
|
119
|
+
table_dict[index_name][(time_index, item_name)] = item
|
120
|
+
|
121
|
+
total_table = pd.DataFrame.from_dict(table_dict, orient='index')
|
122
|
+
total_table.columns = pd.MultiIndex.from_tuples(total_table.columns)
|
123
|
+
|
124
|
+
for name, setting in self.table_settings.items():
|
125
|
+
return_dict[name] = StatsProcessor.slice_multi_col_table(
|
126
|
+
total_table=total_table,
|
127
|
+
mode=setting['mode'],
|
128
|
+
target_index=setting['target_index']
|
129
|
+
if "target_index" in setting.keys() else None)
|
130
|
+
|
131
|
+
return return_dict
|
@@ -0,0 +1,26 @@
|
|
1
|
+
balance_sheet:
|
2
|
+
mode: value_and_percentage
|
3
|
+
|
4
|
+
total_asset:
|
5
|
+
mode: value_and_percentage
|
6
|
+
target_index: 資產總額 負債總額 權益總額
|
7
|
+
|
8
|
+
current_asset:
|
9
|
+
mode: value_and_percentage
|
10
|
+
target_index: 流動資產合計
|
11
|
+
|
12
|
+
non_current_asset:
|
13
|
+
mode: value_and_percentage
|
14
|
+
target_index: 非流動資產合計
|
15
|
+
|
16
|
+
current_debt:
|
17
|
+
mode: value_and_percentage
|
18
|
+
target_index: 流動負債合計
|
19
|
+
|
20
|
+
non_current_debt:
|
21
|
+
mode: value_and_percentage
|
22
|
+
target_index: 非流動負債合計
|
23
|
+
|
24
|
+
equity:
|
25
|
+
mode: value_and_percentage
|
26
|
+
target_index: 權益總額
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# 注意此並非用於slicing
|
2
|
+
CASHO:
|
3
|
+
main_index: 營業活動之淨現金流入(流出)
|
4
|
+
index:
|
5
|
+
- 繼續營業單位稅前淨利(淨損)
|
6
|
+
- 收益費損項目合計
|
7
|
+
- 折舊費用
|
8
|
+
- 攤銷費用
|
9
|
+
- 與營業活動相關之資產及負債之淨變動合計
|
10
|
+
- 營業活動之淨現金流入(流出)
|
11
|
+
|
12
|
+
CASHI:
|
13
|
+
main_index: 投資活動之淨現金流入(流出)
|
14
|
+
index:
|
15
|
+
- 投資活動之淨現金流入(流出)
|
16
|
+
- 取得不動產、廠房及設備
|
17
|
+
- 處分不動產、廠房及設備
|
18
|
+
- 取得無形資產
|
19
|
+
- 處分無形資產
|
20
|
+
- 取得透過損益按公允價值衡量之金融資產
|
21
|
+
- 處分透過損益按公允價值衡量之金融資產
|
22
|
+
- 取得透過其他綜合損益按公允價值衡量之金融資產
|
23
|
+
- 處分透過其他綜合損益按公允價值衡量之金融資產
|
24
|
+
- 取得按攤銷後成本衡量之金融資產
|
25
|
+
- 處分按攤銷後成本衡量之金融資產
|
26
|
+
- 按攤銷後成本衡量之金融資產到期還本
|
27
|
+
|
28
|
+
CASHO:
|
29
|
+
main_index: 籌資活動之淨現金流入(流出)
|
30
|
+
index:
|
31
|
+
- 籌資活動之淨現金流入(流出)
|
32
|
+
- 短期借款增加
|
33
|
+
- 短期借款減少
|
34
|
+
- 發行公司債
|
35
|
+
- 償還公司債
|
36
|
+
- 舉借長期借款
|
37
|
+
- 償還長期借款
|
38
|
+
- 發放現金股利
|
39
|
+
- 庫藏股票買回成本
|
@@ -1,18 +1,138 @@
|
|
1
1
|
from importlib.resources import files
|
2
2
|
import json
|
3
|
+
import pandas as pd
|
3
4
|
import yaml
|
4
5
|
|
6
|
+
target_metric_dict = {
|
7
|
+
'value': ['value'],
|
8
|
+
'value_and_percentage': ['value', 'percentage'],
|
9
|
+
'percentage': ['percentage'],
|
10
|
+
'grand_total': ['grand_total'],
|
11
|
+
'grand_total_values': ['grand_total', 'grand_total_percentage'],
|
12
|
+
'grand_total_percentage': ['grand_total_percentage'],
|
13
|
+
'growth': [f'YoY_{i}' for i in [1, 3, 5, 10]],
|
14
|
+
'grand_total_growth': [f"grand_total_YoY_{i}" for i in [1, 3, 5, 10]]
|
15
|
+
}
|
16
|
+
|
17
|
+
|
5
18
|
class StatsProcessor:
|
19
|
+
|
6
20
|
@classmethod
|
7
|
-
def load_txt(cls, filename, json_load
|
21
|
+
def load_txt(cls, filename, json_load=True):
|
8
22
|
txt_path = files('neurostats_API.tools').joinpath(filename)
|
9
23
|
with open(txt_path, 'r', encoding='utf-8') as f:
|
10
|
-
data = json.load(f) if (json_load) else f.read()
|
24
|
+
data = json.load(f) if (json_load) else f.read()
|
11
25
|
return data
|
26
|
+
|
12
27
|
@classmethod
|
13
28
|
def load_yaml(cls, filename):
|
14
29
|
yaml_path = files('neurostats_API.tools').joinpath(filename)
|
15
30
|
with open(yaml_path, 'r', encoding='utf-8') as f:
|
16
31
|
data = yaml.safe_load(f)
|
17
32
|
|
18
|
-
return data
|
33
|
+
return data
|
34
|
+
|
35
|
+
@classmethod
|
36
|
+
def expand_value_percentage(cls, dataframe):
|
37
|
+
|
38
|
+
expanded_columns = {}
|
39
|
+
for col in dataframe.columns:
|
40
|
+
# Use json_normalize to split 'value' and 'percentage'
|
41
|
+
expanded_df = pd.json_normalize(
|
42
|
+
dataframe[col]).add_prefix(f"{col}_")
|
43
|
+
expanded_df.index = dataframe.index
|
44
|
+
# Append the expanded columns to the new DataFrame
|
45
|
+
expanded_columns[col] = expanded_df
|
46
|
+
|
47
|
+
expanded_df = pd.concat(expanded_columns.values(), axis=1)
|
48
|
+
|
49
|
+
return expanded_df
|
50
|
+
|
51
|
+
@classmethod
|
52
|
+
def slice_table(
|
53
|
+
cls,
|
54
|
+
total_table,
|
55
|
+
mode='value',
|
56
|
+
target_index=None, # None or Str, 要特別抓哪個index
|
57
|
+
):
|
58
|
+
"""
|
59
|
+
total_table: column應為 <時間>_<單位>
|
60
|
+
對只有單層column的table,切出想要的index
|
61
|
+
"""
|
62
|
+
times = [
|
63
|
+
column.split("_")[0] for column in total_table.columns.unique()
|
64
|
+
] #取出timeIndex
|
65
|
+
try:
|
66
|
+
target_metrics = target_metric_dict[mode]
|
67
|
+
except KeyError as e:
|
68
|
+
return f"mode Error: Get mode should be {list(target_metric_dict.keys())} but get {mode}"
|
69
|
+
|
70
|
+
desired_order = [
|
71
|
+
f"{time}_{value_name}" for time in times
|
72
|
+
for value_name in target_metrics
|
73
|
+
]
|
74
|
+
|
75
|
+
if (target_index):
|
76
|
+
target_index = target_index.split()
|
77
|
+
sliced_table = total_table.loc[target_index, desired_order].T
|
78
|
+
|
79
|
+
return sliced_table.T
|
80
|
+
|
81
|
+
else:
|
82
|
+
return total_table.loc[:, desired_order]
|
83
|
+
|
84
|
+
@classmethod
|
85
|
+
def slice_multi_col_table(
|
86
|
+
cls,
|
87
|
+
total_table,
|
88
|
+
mode='value',
|
89
|
+
target_index=None, # None or Str, 要特別抓哪個index
|
90
|
+
):
|
91
|
+
"""
|
92
|
+
對Multicolumn的dataframe切出目標的index
|
93
|
+
"""
|
94
|
+
times = total_table.columns.get_level_values(0).unique()
|
95
|
+
try:
|
96
|
+
target_metrics = target_metric_dict[mode]
|
97
|
+
except KeyError as e:
|
98
|
+
return f"mode Error: Get mode should be {list(target_metric_dict.keys())} but get {mode}"
|
99
|
+
|
100
|
+
desired_order = [(time, value_name) for time in times
|
101
|
+
for value_name in target_metrics]
|
102
|
+
|
103
|
+
if (target_index):
|
104
|
+
target_index = target_index.split()
|
105
|
+
sliced_table = total_table.loc[
|
106
|
+
target_index, pd.IndexSlice[:,
|
107
|
+
target_metrics]][desired_order].T
|
108
|
+
if (mode == 'value_and_percentage'): # 因應balance_sheet 頁面的格式
|
109
|
+
return_table = sliced_table.T
|
110
|
+
return_table.columns = [
|
111
|
+
"_".join(flatten_indexs)
|
112
|
+
for flatten_indexs in return_table.columns.to_flat_index()
|
113
|
+
]
|
114
|
+
return return_table
|
115
|
+
|
116
|
+
sliced_table = sliced_table.reset_index()
|
117
|
+
sliced_table = sliced_table.pivot(index='level_1',
|
118
|
+
columns='level_0',
|
119
|
+
values=target_index).sort_index(
|
120
|
+
axis=1, level = 1,ascending = False
|
121
|
+
)
|
122
|
+
|
123
|
+
sliced_table.columns = sliced_table.columns.get_level_values(1)
|
124
|
+
sliced_table.columns.name = None
|
125
|
+
sliced_table.index.name = None
|
126
|
+
|
127
|
+
return sliced_table.reindex(target_metrics)
|
128
|
+
|
129
|
+
else:
|
130
|
+
return_table = total_table.loc[:, pd.IndexSlice[:,
|
131
|
+
target_metrics]][
|
132
|
+
desired_order]
|
133
|
+
return_table.columns = [
|
134
|
+
"_".join(flatten_indexs)
|
135
|
+
for flatten_indexs in return_table.columns.to_flat_index()
|
136
|
+
]
|
137
|
+
return return_table
|
138
|
+
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: neurostats-API
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.7
|
4
4
|
Summary: The service of NeuroStats website
|
5
5
|
Home-page: https://github.com/NeurowattStats/NeuroStats_API.git
|
6
6
|
Author: JasonWang@Neurowatt
|
@@ -30,10 +30,15 @@ Description-Content-Type: text/markdown
|
|
30
30
|
│ ├── fetchers
|
31
31
|
│ │ ├── __init__.py
|
32
32
|
│ │ ├── base.py
|
33
|
+
│ │ ├── balance_sheet.py
|
34
|
+
│ │ ├── cash_flow.py
|
33
35
|
│ │ ├── finance_overview.py
|
36
|
+
│ │ ├── profit_lose.py
|
34
37
|
│ │ ├── tech.py
|
35
|
-
│ │
|
38
|
+
│ │ ├── value_invest.py
|
36
39
|
│ ├── tools
|
40
|
+
│ │ ├── balance_sheet.yaml
|
41
|
+
│ │ ├── cash_flow_percentage.yaml
|
37
42
|
│ │ ├── finance_overview_dict.yaml
|
38
43
|
│ │ ├── profit_lose.yaml
|
39
44
|
│ │ └── seasonal_data_field_dict.txt
|
@@ -73,14 +78,15 @@ pip install neurostats-API
|
|
73
78
|
```Python
|
74
79
|
>>> import neurostats_API
|
75
80
|
>>> print(neurostats_API.__version__)
|
76
|
-
0.0.
|
81
|
+
0.0.6
|
77
82
|
```
|
78
83
|
|
79
84
|
### 得到最新一期的評價資料與歷年評價
|
80
85
|
``` Python
|
81
|
-
from neurostats_API.
|
82
|
-
|
83
|
-
|
86
|
+
from neurostats_API.utils import ValueFetcher, DBClient
|
87
|
+
db_client = DBClient("<連接的DB位置>").get_client()
|
88
|
+
ticker = "2330" # 換成tw50內任意ticker
|
89
|
+
fetcher = ValueFetcher(ticker, db_client)
|
84
90
|
data = stats_fetcher.query_data()
|
85
91
|
```
|
86
92
|
|
@@ -118,43 +124,42 @@ data = stats_fetcher.query_data()
|
|
118
124
|
|
119
125
|
### 回傳月營收表
|
120
126
|
``` Python
|
121
|
-
from neurostats_API.
|
127
|
+
from neurostats_API.fetchers import MonthRevenueFetcher, DBClient
|
122
128
|
db_client = DBClient("<連接的DB位置>").get_client()
|
123
|
-
ticker = 2330 # 換成tw50內任意ticker
|
124
|
-
fetcher =
|
125
|
-
data =
|
129
|
+
ticker = "2330" # 換成tw50內任意ticker
|
130
|
+
fetcher = MonthRevenueFetcherFetcher(ticker, db_client)
|
131
|
+
data = fetcher.query_data()
|
126
132
|
```
|
127
133
|
|
128
134
|
#### 回傳
|
129
135
|
```Python
|
130
136
|
{
|
131
|
-
"
|
132
|
-
"
|
133
|
-
"company_name":台積電
|
137
|
+
"ticker": "2330",
|
138
|
+
"company_name": "台積電",
|
134
139
|
"month_revenue":
|
135
|
-
|
136
|
-
|
137
|
-
2
|
138
|
-
...
|
139
|
-
|
140
|
-
|
140
|
+
year 2024 ... 2014
|
141
|
+
month ...
|
142
|
+
grand_total 2.025847e+09 ... NaN
|
143
|
+
12 NaN ... 69510190.0
|
144
|
+
... ... ... ...
|
145
|
+
2 1.816483e+08 ... 46829051.0
|
146
|
+
1 2.157851e+08 ... 51429993.0
|
141
147
|
|
142
148
|
"this_month_revenue_over_years":
|
143
|
-
|
144
|
-
revenue
|
145
|
-
|
146
|
-
...
|
147
|
-
YoY_5
|
148
|
-
YoY_10
|
149
|
-
|
149
|
+
year 2024 ... 2015
|
150
|
+
revenue 2.518727e+08 ... 64514083.0
|
151
|
+
revenue_increment_ratio 3.960000e+01 ... -13.8
|
152
|
+
... ... ... ...
|
153
|
+
YoY_5 1.465200e+02 ... NaN
|
154
|
+
YoY_10 NaN ... NaN
|
150
155
|
|
151
156
|
"grand_total_over_years":
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
...
|
156
|
-
|
157
|
-
|
157
|
+
year 2024 ... 2015
|
158
|
+
grand_total 2.025847e+09 ... 6.399788e+08
|
159
|
+
grand_total_increment_ratio 3.187000e+01 ... 1.845000e+01
|
160
|
+
... ... ... ...
|
161
|
+
grand_total_YoY_5 1.691300e+02 ... NaN
|
162
|
+
grand_total_YoY_10 NaN ... NaN
|
158
163
|
|
159
164
|
|
160
165
|
}
|
@@ -171,7 +176,7 @@ YoY_10 None None ... None None
|
|
171
176
|
### 財務分析: 重要指標
|
172
177
|
對應https://ifa.ai/tw-stock/2330/finance-overview
|
173
178
|
```Python
|
174
|
-
from neurostats_API.
|
179
|
+
from neurostats_API.fetchers import FinanceOverviewFetcher, DBClient
|
175
180
|
db_client = DBClient("<連接的DB位置>").get_client()
|
176
181
|
ticker = "2330"
|
177
182
|
fetcher = FinanceOverviewFetcher(ticker = "2330", db_client = db_client)
|
@@ -194,6 +199,7 @@ markdown
|
|
194
199
|
複製程式碼
|
195
200
|
| 英文 | 中文 |
|
196
201
|
|-----------------------------------|-----------------------------|
|
202
|
+
|**財務概況**|
|
197
203
|
| revenue | 營業收入 |
|
198
204
|
| gross_profit | 營業毛利 |
|
199
205
|
| operating_income | 營業利益 |
|
@@ -201,6 +207,7 @@ markdown
|
|
201
207
|
| operating_cash_flow | 營業活動之現金流 |
|
202
208
|
| invest_cash_flow | 投資活動之淨現金流 |
|
203
209
|
| financing_cash_flow | 籌資活動之淨現金流 |
|
210
|
+
|**每股財務狀況**|
|
204
211
|
| revenue_per_share | 每股營收 |
|
205
212
|
| gross_per_share | 每股營業毛利 |
|
206
213
|
| operating_income_per_share | 每股營業利益 |
|
@@ -209,6 +216,7 @@ markdown
|
|
209
216
|
| fcf_per_share | 每股自由現金流 |
|
210
217
|
| debt_to_operating_cash_flow | 每股有息負債 |
|
211
218
|
| equity | 每股淨值 |
|
219
|
+
|**獲利能力**|
|
212
220
|
| roa | 資產報酬率 |
|
213
221
|
| roe | 股東權益報酬率 |
|
214
222
|
| gross_over_asset | 營業毛利÷總資產 |
|
@@ -217,10 +225,12 @@ markdown
|
|
217
225
|
| operation_profit_rate | 營業利益率 |
|
218
226
|
| net_income_rate | 淨利率 |
|
219
227
|
| operating_cash_flow_profit_rate | 營業現金流利潤率 |
|
228
|
+
|**成長動能**|
|
220
229
|
| revenue_YoY | 營收年成長率 |
|
221
230
|
| gross_prof_YoY | 營業毛利年成長率 |
|
222
231
|
| operating_income_YoY | 營業利益年成長率 |
|
223
232
|
| net_income_YoY | 淨利年成長率 |
|
233
|
+
|**營運指標**|
|
224
234
|
| dso | 應收帳款收現天數 |
|
225
235
|
| account_receive_over_revenue | 應收帳款佔營收比率 |
|
226
236
|
| dio | 平均售貨天數 |
|
@@ -229,6 +239,7 @@ markdown
|
|
229
239
|
| cash_of_conversion_cycle | 現金循環週期 |
|
230
240
|
| asset_turnover | 總資產週轉率 |
|
231
241
|
| applcation_turnover | 不動產、廠房及設備週轉率 |
|
242
|
+
|**財務韌性**|
|
232
243
|
| current_ratio | 流動比率 |
|
233
244
|
| quick_ratio | 速動比率 |
|
234
245
|
| debt_to_equity_ratio | 負債權益比率 |
|
@@ -237,6 +248,7 @@ markdown
|
|
237
248
|
| debt_to_operating_cash_flow | 有息負債÷營業活動現金流 |
|
238
249
|
| debt_to_free_cash_flow | 有息負債÷自由現金流 |
|
239
250
|
| cash_flow_ratio | 現金流量比率 |
|
251
|
+
|**資產負債表**|
|
240
252
|
| current_assets | 流動資產 |
|
241
253
|
| current_liabilities | 流動負債 |
|
242
254
|
| non_current_assets | 非流動資產 |
|
@@ -248,18 +260,19 @@ markdown
|
|
248
260
|
#### 以下數值未在回傳資料中,待資料庫更新
|
249
261
|
|英文|中文|
|
250
262
|
|---|----|
|
251
|
-
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
263
|
+
|**成長動能**|
|
264
|
+
| operating_cash_flow_YoY | 營業現金流年成長率 |
|
265
|
+
| fcf_YoY | 自由現金流年成長率 |
|
266
|
+
| operating_cash_flow_per_share_YoY | 每股營業現金流年成長率 |
|
267
|
+
| fcf_per_share_YoY | 每股自由現金流年成長率 |
|
255
268
|
|
256
269
|
### 損益表
|
257
270
|
```Python
|
258
|
-
from neurostats_API.
|
271
|
+
from neurostats_API.fetchers import ProfitLoseFetcher, DBClient
|
259
272
|
db_client = DBClient("<連接的DB位置>").get_client()
|
260
|
-
fetcher =
|
261
|
-
ticker = 2330 # 換成tw50內任意ticker
|
262
|
-
data = fetcher.
|
273
|
+
fetcher = ProfitLoseFetcher(db_client)
|
274
|
+
ticker = "2330" # 換成tw50內任意ticker
|
275
|
+
data = fetcher.query_data()
|
263
276
|
```
|
264
277
|
|
265
278
|
#### 回傳
|
@@ -272,83 +285,85 @@ data = fetcher.get_profit_lose(ticker)
|
|
272
285
|
"profit_lose": #損益表,
|
273
286
|
"grand_total_profit_lose": #今年度累計損益表,
|
274
287
|
# 營業收入
|
275
|
-
"revenue":
|
276
|
-
"grand_total_revenue":
|
288
|
+
"revenue": # 營收成長率
|
289
|
+
"grand_total_revenue": # 營收累計成場濾
|
277
290
|
# 毛利
|
278
|
-
"gross_profit":
|
279
|
-
"grand_total_gross_profit":
|
280
|
-
"gross_profit_percentage":
|
281
|
-
"grand_total_gross_profit_percentage"
|
291
|
+
"gross_profit": # 毛利成長率
|
292
|
+
"grand_total_gross_profit": # 累計毛利成長率
|
293
|
+
"gross_profit_percentage": # 毛利率
|
294
|
+
"grand_total_gross_profit_percentage" # 累計毛利率
|
282
295
|
# 營利
|
283
|
-
"operating_income":
|
284
|
-
"grand_total_operating_income":
|
285
|
-
"operating_income_percentage":
|
286
|
-
"grand_total_operating_income_percentage":
|
296
|
+
"operating_income": # 營利成長率
|
297
|
+
"grand_total_operating_income": # 累計營利成長率
|
298
|
+
"operating_income_percentage": # 營利率
|
299
|
+
"grand_total_operating_income_percentage": # 累計營利率
|
287
300
|
# 稅前淨利
|
288
|
-
"net_income_before_tax":
|
289
|
-
"grand_total_net_income_before_tax":
|
290
|
-
"net_income_before_tax_percentage":
|
291
|
-
"grand_total_net_income_before_tax_percentage":
|
301
|
+
"net_income_before_tax": # 稅前淨利成長率
|
302
|
+
"grand_total_net_income_before_tax": # 累計稅前淨利成長率
|
303
|
+
"net_income_before_tax_percentage": # 稅前淨利率
|
304
|
+
"grand_total_net_income_before_tax_percentage": # 累計稅前淨利率
|
292
305
|
# 本期淨利
|
293
|
-
"net_income":
|
294
|
-
"grand_total_net_income":
|
295
|
-
"net_income_percentage":
|
296
|
-
"grand_total_income_percentage":
|
306
|
+
"net_income": # 本期淨利成長率
|
307
|
+
"grand_total_net_income": # 累計本期淨利成長率
|
308
|
+
"net_income_percentage": # 本期淨利率
|
309
|
+
"grand_total_income_percentage": # 累計本期淨利率
|
297
310
|
# EPS
|
298
|
-
"EPS":
|
299
|
-
"EPS_growth":
|
300
|
-
"grand_total_EPS":
|
301
|
-
"grand_total_EPS_growth":
|
311
|
+
"EPS": # EPS
|
312
|
+
"EPS_growth": # EPS成長率
|
313
|
+
"grand_total_EPS": # 累計EPS
|
314
|
+
"grand_total_EPS_growth": # 累計EPS成長率
|
302
315
|
}
|
303
316
|
```
|
304
317
|
|
305
318
|
### 資產負債表
|
306
319
|
``` Python
|
307
|
-
from neurostats_API.
|
320
|
+
from neurostats_API.fetchers import BalanceSheetFetcher, DBClient
|
308
321
|
db_client = DBClient("<連接的DB位置>").get_client()
|
309
|
-
|
310
|
-
|
311
|
-
|
322
|
+
ticker = "2330" # 換成tw50內任意ticker
|
323
|
+
fetcher = BalanceSheetFetcher(ticker, db_client)
|
324
|
+
|
325
|
+
stats_fetcher.query_data()
|
312
326
|
```
|
313
327
|
|
314
328
|
#### 回傳
|
315
329
|
```Python
|
316
330
|
{
|
317
331
|
"ticker": "2330"
|
318
|
-
"company_name":
|
332
|
+
"company_name":"台積電"
|
319
333
|
"balance_sheet":
|
320
|
-
|
321
|
-
流動資產
|
322
|
-
現金及約當現金
|
323
|
-
...
|
324
|
-
|
325
|
-
|
334
|
+
2024Q2_value ... 2018Q2_percentage
|
335
|
+
流動資產 NaN ... NaN
|
336
|
+
現金及約當現金 1.799127e+09 ... 30.79
|
337
|
+
... ... ... ...
|
338
|
+
避險之衍生金融負債-流動 NaN ... 0.00
|
339
|
+
負債準備-流動 NaN ... 0.00
|
326
340
|
|
327
341
|
"total_asset":
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
342
|
+
2024Q2_value ... 2018Q2_percentage
|
343
|
+
資產總額 5.982364e+09 ... 100.00
|
344
|
+
負債總額 2.162216e+09 ... 27.41
|
345
|
+
權益總額 3.820148e+09 ... 72.59
|
346
|
+
|
332
347
|
|
333
348
|
"current_asset":
|
334
|
-
|
335
|
-
|
349
|
+
2024Q2_value ... 2018Q2_percentage
|
350
|
+
流動資產合計 2.591658e+09 ... 46.7
|
336
351
|
|
337
352
|
"non_current_asset":
|
338
|
-
|
339
|
-
|
353
|
+
2024Q2_value ... 2018Q2_percentage
|
354
|
+
非流動資產合計 3.390706e+09 ... 53.3
|
340
355
|
|
341
356
|
"current_debt":
|
342
|
-
|
343
|
-
|
357
|
+
2024Q2_value ... 2018Q2_percentage
|
358
|
+
流動負債合計 1.048916e+09 ... 22.55
|
344
359
|
|
345
360
|
"non_current_debt":
|
346
|
-
|
347
|
-
|
361
|
+
2024Q2_value ... 2018Q2_percentage
|
362
|
+
非流動負債合計 1.113300e+09 ... 4.86
|
348
363
|
|
349
364
|
"equity":
|
350
|
-
|
351
|
-
|
365
|
+
2024Q2_value ... 2018Q2_percentage
|
366
|
+
權益總額 3.820148e+09 ... 72.59
|
352
367
|
|
353
368
|
}
|
354
369
|
```
|
@@ -373,68 +388,39 @@ stats_fetcher.get_cash_flow(ticker)
|
|
373
388
|
#### 回傳
|
374
389
|
```Python
|
375
390
|
{
|
376
|
-
"ticker":2330
|
377
|
-
"company_name"
|
391
|
+
"ticker": "2330"
|
392
|
+
"company_name": "台積電"
|
378
393
|
"cash_flow":
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
2018Q2_value 2018Q2_percentage ... 2024Q2_value \
|
410
|
-
投資活動之現金流量 NaN NaN ... NaN
|
411
|
-
取得透過其他綜合損益按公允價值衡量之金融資產 -47523622.0 0.355889 ... -43780180.0
|
412
|
-
... ... ... ... ...
|
413
|
-
其他投資活動 -149104.0 0.001117 ... 7956680.0
|
414
|
-
投資活動之淨現金流入(流出) -133534789.0 1.000000 ... -357414321.0
|
415
|
-
|
416
|
-
2024Q2_percentage
|
417
|
-
投資活動之現金流量 NaN
|
418
|
-
取得透過其他綜合損益按公允價值衡量之金融資產 0.122491
|
419
|
-
... ...
|
420
|
-
其他投資活動 -0.022262
|
421
|
-
投資活動之淨現金流入(流出) 1.000000
|
422
|
-
|
423
|
-
"CASHF":
|
424
|
-
2018Q2_value 2018Q2_percentage ... 2024Q2_value \
|
425
|
-
籌資活動之現金流量 NaN NaN ... NaN
|
426
|
-
短期借款減少 -33743725.0 0.387977 ... NaN
|
427
|
-
... ... ... ... ...
|
428
|
-
存出保證金增加 NaN NaN ... -122271.0
|
429
|
-
收取之股利 NaN NaN ... 895503.0
|
430
|
-
|
431
|
-
2024Q2_percentage
|
432
|
-
籌資活動之現金流量 NaN
|
433
|
-
短期借款減少 NaN
|
434
|
-
... ...
|
435
|
-
存出保證金增加 0.000755
|
436
|
-
收取之股利 -0.005530
|
437
|
-
|
394
|
+
2023Q3_value ... 2018Q3_percentage
|
395
|
+
營業活動之現金流量-間接法 NaN ... NaN
|
396
|
+
繼續營業單位稅前淨利(淨損) 700890335.0 ... 0.744778
|
397
|
+
... ... ... ...
|
398
|
+
以成本衡量之金融資產減資退回股款 NaN ... NaN
|
399
|
+
除列避險之金融負債∕避險 之衍生金融負債 NaN ... -0.000770
|
400
|
+
|
401
|
+
"CASHO":
|
402
|
+
2023Q3_value ... 2018Q3_percentage
|
403
|
+
營業活動之現金流量-間接法 NaN ... NaN
|
404
|
+
繼續營業單位稅前淨利(淨損) 700890335.0 ... 0.744778
|
405
|
+
... ... ... ...
|
406
|
+
持有供交易之金融資產(增加)減少 NaN ... 0.001664
|
407
|
+
負債準備增加(減少) NaN ... NaN
|
408
|
+
|
409
|
+
"CASHI":
|
410
|
+
2023Q3_value ... 2018Q3_percentage
|
411
|
+
投資活動之現金流量 NaN ... NaN
|
412
|
+
取得透過其他綜合損益按公允價值衡量之金融資產 -54832622.0 ... 0.367413
|
413
|
+
... ... ... ...
|
414
|
+
持有至到期日金融資產到期還本 NaN ... NaN
|
415
|
+
取得以成本衡量之金融資產 NaN ... NaN
|
416
|
+
|
417
|
+
"CASHF":
|
418
|
+
2023Q3_value ... 2018Q3_percentage
|
419
|
+
籌資活動之現金流量 NaN ... NaN
|
420
|
+
短期借款減少 0.0 ... NaN
|
421
|
+
... ... ... ...
|
422
|
+
以成本衡量之金融資產減資退回股款 NaN ... NaN
|
423
|
+
除列避險之金融負債∕避險 之衍生金融負債 NaN ... -0.00077
|
438
424
|
}
|
439
425
|
```
|
440
426
|
- `'ticker'`: 股票代碼
|
@@ -447,44 +433,6 @@ stats_fetcher.get_cash_flow(ticker)
|
|
447
433
|
> 大部分資料缺失是因為尚未計算,僅先填上已經有的資料
|
448
434
|
|
449
435
|
|
450
|
-
|
451
|
-
|
452
|
-
## cli 範例輸入
|
453
|
-
```
|
454
|
-
python ./cli.py --ticker 1101
|
455
|
-
```
|
456
|
-
|
457
|
-
### cli 輸出
|
458
|
-
```Python
|
459
|
-
_id
|
460
|
-
ObjectId('67219e046104872ef7490cd3')
|
461
|
-
ticker
|
462
|
-
'1101'
|
463
|
-
company_name
|
464
|
-
'台泥'
|
465
|
-
yearly_data
|
466
|
-
year P_E P_FCF P_B P_S EV_OPI EV_EBIT EV_EBITDA EV_S
|
467
|
-
0 107 9.78 66.346637 1.07 1.963814 16.061211 14.807325 32.858903 3.685025
|
468
|
-
1 108 10.76 23.532308 1.35 3.296749 21.995948 19.877169 44.395187 5.338454
|
469
|
-
2 109 10.29 -26.134468 1.32 4.282560 26.353871 23.417022 46.269197 6.908155
|
470
|
-
3 110 14.08 22.345215 1.50 5.558267 42.034804 32.290621 77.139298 7.973839
|
471
|
-
4 111 28.04 -63.248928 1.16 4.595606 -136332.740038 149.169022 -756.781156 7.324556
|
472
|
-
5 112 29.53 -18.313377 1.15 5.301801 98.225453 65.598234 321.991608 8.582981
|
473
|
-
6 過去4季 NaN -24.929987 NaN 4.300817 83.102921 55.788996 -1073.037084 7.436656
|
474
|
-
daily_data
|
475
|
-
{ 'EV_EBIT': 55.78899626851681,
|
476
|
-
'EV_EBITDA': -1073.037084015388,
|
477
|
-
'EV_OPI': 83.10292101832388,
|
478
|
-
'EV_S': 7.436656083243897,
|
479
|
-
'P_B': None,
|
480
|
-
'P_E': None,
|
481
|
-
'P_FCF': -24.92998660989962,
|
482
|
-
'P_S': 4.300817175744059,
|
483
|
-
'close': 32.150001525878906,
|
484
|
-
}
|
485
|
-
```
|
486
|
-
> 這裡有Nan是因為本益比與P/B等資料沒有爬到最新的時間
|
487
|
-
|
488
436
|
## TODO
|
489
437
|
- 將utils/fetcher.py中的功能切分到fetchers資料夾中
|
490
438
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
neurostats_API/__init__.py,sha256=3Kn8sHWnxjlagph2LmftcF8JLlcMlmQIbU5t_jzgK3w,19
|
2
|
+
neurostats_API/cli.py,sha256=UJSWLIw03P24p-gkBb6JSEI5dW5U12UvLf1L8HjQD-o,873
|
3
|
+
neurostats_API/main.py,sha256=QcsfmWivg2Dnqw3MTJWiI0QvEiRs0VuH-BjwQHFCv00,677
|
4
|
+
neurostats_API/fetchers/__init__.py,sha256=U_OMG-mLpsVKYnCBrW2OjFuCzvPeVQ__7A676vGzztY,313
|
5
|
+
neurostats_API/fetchers/balance_sheet.py,sha256=FPsVobaNzFOxpXCo1Ui_rPmlG1crTRj9ukqQ4J8aiJg,4268
|
6
|
+
neurostats_API/fetchers/base.py,sha256=NW2SFzrimyAIrdJx1LVmTazelyZOAtcj54kJKHc4Vaw,1662
|
7
|
+
neurostats_API/fetchers/cash_flow.py,sha256=muVnteEEyeFPapGqgBOoHa8PAieEI796rHPNi5otOMY,7009
|
8
|
+
neurostats_API/fetchers/finance_overview.py,sha256=t5QlTM0bL3fkrqlyMn8-8GB6YgMLsofH9NRI8PfPRxE,18447
|
9
|
+
neurostats_API/fetchers/month_revenue.py,sha256=RNA7ROl2vm8Xbib3k50p_1shsHDVSKIbHkyNiRa8yMw,3182
|
10
|
+
neurostats_API/fetchers/profit_lose.py,sha256=ffEVNo7-fvtnAq2_gj-Ga55TGW7pPd6WhruOZH8NGYM,4463
|
11
|
+
neurostats_API/fetchers/tech.py,sha256=wH1kkqiETQhF0HAhk-UIiucnZ3EiL85Q-yMWCcVOiFM,11395
|
12
|
+
neurostats_API/fetchers/value_invest.py,sha256=tg8yELbVnTFTEclrwgXnCRW377KkcoLiP-Gk2pyM-9Y,2886
|
13
|
+
neurostats_API/tools/balance_sheet.yaml,sha256=dKTMbsYR9EFp48WAzmm_ISHMiJQLyE0V-XWS_gkxmr0,541
|
14
|
+
neurostats_API/tools/cash_flow_percentage.yaml,sha256=fk2Z4eb1JjGFvP134eJatHacB7BgTkBenhDJr83w8RE,1345
|
15
|
+
neurostats_API/tools/finance_overview_dict.yaml,sha256=Vvf8bv23NwJP8Yyw8DPS8c0_jjT_Wctnnz51SHS4AeI,2335
|
16
|
+
neurostats_API/tools/profit_lose.yaml,sha256=qHBnqG7fR4Pxc_c3n4raL-3l7o5RnABLz9YGOXoaGiA,2086
|
17
|
+
neurostats_API/tools/seasonal_data_field_dict.txt,sha256=KlIIdTTdbvUd9TSDE9-gpzk2jt2ck_LdisX8cnrWMD4,7869
|
18
|
+
neurostats_API/utils/__init__.py,sha256=FTYKRFzW2XVXdnSHXnS3mQQaHlKF9xGqrMsgZZ2kroc,142
|
19
|
+
neurostats_API/utils/data_process.py,sha256=rRKf2H0X2J-tDXDreErcz3Y3TGb8_0Q6GKe0izRjnmA,4942
|
20
|
+
neurostats_API/utils/datetime.py,sha256=I9CIgZdE5OMzUciOS5wvapOVEIrXG_0Qb6iDKfIod6c,574
|
21
|
+
neurostats_API/utils/db_client.py,sha256=OYe6yazcR4Aa6jYmy47JrryUeh2NnKGqY2K_lSZe6i8,455
|
22
|
+
neurostats_API/utils/fetcher.py,sha256=VbrUhjA-GG5AyjPX2SHtFIbZM4dm3jo0RgZzuCbb_Io,40927
|
23
|
+
neurostats_API-0.0.7.dist-info/METADATA,sha256=Ajugxp0vnHoxdzriFfmLhTt32ckq2VnIiIO_eRU2QDo,18241
|
24
|
+
neurostats_API-0.0.7.dist-info/WHEEL,sha256=bFJAMchF8aTQGUgMZzHJyDDMPTO3ToJ7x23SLJa1SVo,92
|
25
|
+
neurostats_API-0.0.7.dist-info/top_level.txt,sha256=nSlQPMG0VtXivJyedp4Bkf86EOy2TpW10VGxolXrqnU,15
|
26
|
+
neurostats_API-0.0.7.dist-info/RECORD,,
|
@@ -1,23 +0,0 @@
|
|
1
|
-
neurostats_API/__init__.py,sha256=zzMTjJY407ibU6_ATOPO8KibRVB3tyW9yO0tRYzjTqw,19
|
2
|
-
neurostats_API/cli.py,sha256=UJSWLIw03P24p-gkBb6JSEI5dW5U12UvLf1L8HjQD-o,873
|
3
|
-
neurostats_API/main.py,sha256=QcsfmWivg2Dnqw3MTJWiI0QvEiRs0VuH-BjwQHFCv00,677
|
4
|
-
neurostats_API/fetchers/__init__.py,sha256=k-pPWen3gQNr6d6DPFEXP1Q6SPC6TGIuIFf8w6r75YQ,137
|
5
|
-
neurostats_API/fetchers/balance_sheet.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
-
neurostats_API/fetchers/base.py,sha256=NW2SFzrimyAIrdJx1LVmTazelyZOAtcj54kJKHc4Vaw,1662
|
7
|
-
neurostats_API/fetchers/finance_overview.py,sha256=_w5OzFQpzC7eL_S5iMtgrIhx5gcov9gKzc6XzFutEFU,18309
|
8
|
-
neurostats_API/fetchers/month_revenue.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
-
neurostats_API/fetchers/profit_lose.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
-
neurostats_API/fetchers/tech.py,sha256=wH1kkqiETQhF0HAhk-UIiucnZ3EiL85Q-yMWCcVOiFM,11395
|
11
|
-
neurostats_API/fetchers/value_invest.py,sha256=tg8yELbVnTFTEclrwgXnCRW377KkcoLiP-Gk2pyM-9Y,2886
|
12
|
-
neurostats_API/tools/finance_overview_dict.yaml,sha256=Vvf8bv23NwJP8Yyw8DPS8c0_jjT_Wctnnz51SHS4AeI,2335
|
13
|
-
neurostats_API/tools/profit_lose.yaml,sha256=f8balMTcK9elMW0k1PtbZ_6kEobTQ51fcBzqpbMObkk,2086
|
14
|
-
neurostats_API/tools/seasonal_data_field_dict.txt,sha256=KlIIdTTdbvUd9TSDE9-gpzk2jt2ck_LdisX8cnrWMD4,7869
|
15
|
-
neurostats_API/utils/__init__.py,sha256=FTYKRFzW2XVXdnSHXnS3mQQaHlKF9xGqrMsgZZ2kroc,142
|
16
|
-
neurostats_API/utils/data_process.py,sha256=AeDWS9eHlHRqzfDKph-14GXERtHLSMKijsIrzZ0pPPo,595
|
17
|
-
neurostats_API/utils/datetime.py,sha256=I9CIgZdE5OMzUciOS5wvapOVEIrXG_0Qb6iDKfIod6c,574
|
18
|
-
neurostats_API/utils/db_client.py,sha256=OYe6yazcR4Aa6jYmy47JrryUeh2NnKGqY2K_lSZe6i8,455
|
19
|
-
neurostats_API/utils/fetcher.py,sha256=VbrUhjA-GG5AyjPX2SHtFIbZM4dm3jo0RgZzuCbb_Io,40927
|
20
|
-
neurostats_API-0.0.6.dist-info/METADATA,sha256=cOomXaKrsCOt1V9ZRTuPgfLCzdWnw0fzWd7PkGKlo8U,20666
|
21
|
-
neurostats_API-0.0.6.dist-info/WHEEL,sha256=bFJAMchF8aTQGUgMZzHJyDDMPTO3ToJ7x23SLJa1SVo,92
|
22
|
-
neurostats_API-0.0.6.dist-info/top_level.txt,sha256=nSlQPMG0VtXivJyedp4Bkf86EOy2TpW10VGxolXrqnU,15
|
23
|
-
neurostats_API-0.0.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|