neurostats-API 0.0.5__py3-none-any.whl → 0.0.7__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.
@@ -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: