neurostats-API 0.0.5__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.
@@ -1 +1 @@
1
- __version__='0.0.5'
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
- fetched_data = self.collect_data(2024, 2)
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
+ - 庫藏股票買回成本
@@ -9,7 +9,7 @@ revenue:
9
9
  target_index: 營業收入合計
10
10
 
11
11
  grand_total_revenue:
12
- mode: grand_total_values
12
+ mode: grand_total_growth
13
13
  target_index: 營業收入合計
14
14
 
15
15
  gross_profit: