neurostats-API 0.0.11__py3-none-any.whl → 0.0.13__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.11'
1
+ __version__='0.0.13'
@@ -3,6 +3,7 @@ from .balance_sheet import BalanceSheetFetcher
3
3
  from .cash_flow import CashFlowFetcher
4
4
  from .finance_overview import FinanceOverviewFetcher
5
5
  from .institution import InstitutionFetcher
6
+ from .margin_trading import MarginTradingFetcher
6
7
  from .month_revenue import MonthRevenueFetcher
7
8
  from .profit_lose import ProfitLoseFetcher
8
9
  from .value_invest import ValueFetcher
@@ -51,6 +51,27 @@ class InstitutionFetcher(StatsFetcher):
51
51
  "as": "target_daily_data",
52
52
  "in": "$$target_daily_data"
53
53
  }
54
+ },
55
+ "institution_trading": {
56
+ "$map": {
57
+ "input": {
58
+ "$filter": {
59
+ "input": "$institution_trading",
60
+ "as": "institution",
61
+ "cond": {
62
+ "$and": [{
63
+ "$gte":
64
+ ["$$institution.date", start_date]
65
+ }, {
66
+ "$lte":
67
+ ["$$institution.date", end_date]
68
+ }]
69
+ }
70
+ }
71
+ },
72
+ "as": "target_institution_data",
73
+ "in": "$$target_institution_data"
74
+ },
54
75
  }
55
76
  }
56
77
  })
@@ -68,81 +89,110 @@ class InstitutionFetcher(StatsFetcher):
68
89
  try:
69
90
  latest_time = StatsDateTime.get_latest_time(
70
91
  self.ticker, self.collection)['last_update_time']
71
- latest_date = latest_time['institution_trading'][
72
- 'latest_date']
73
- date = latest_date.replace(hour=0,
74
- minute=0,
75
- second=0,
76
- microsecond=0)
92
+ latest_date = latest_time['institution_trading']['latest_date']
93
+ end_date = latest_date.replace(hour=0,
94
+ minute=0,
95
+ second=0,
96
+ microsecond=0)
77
97
  except Exception as e:
78
98
  print(
79
99
  f"No updated time for institution_trading in {self.ticker}, use current time instead"
80
100
  )
81
- date = datetime.now(self.timezone)
82
- date = date.replace(hour=0, minute=0, second=0, microsecond=0)
101
+ end_date = datetime.now(self.timezone)
102
+ end_date = date.replace(hour=0, minute=0, second=0, microsecond=0)
83
103
 
84
104
  if (date.hour < 17): # 拿不到今天的資料
85
- date = date - timedelta(days=1)
105
+ end_date = end_date - timedelta(days=1)
86
106
 
87
- start_date = date - timedelta(days=365)
107
+ start_date = end_date - timedelta(days=365)
88
108
 
89
- daily_data = self.collect_data(start_date, end_date=date)
109
+ fetched_data = self.collect_data(start_date, end_date)
90
110
 
91
- daily_data = sorted(daily_data['daily_data'],
92
- key=lambda x: x['date'],
93
- reverse=True)
111
+ fetched_data['daily_data'] = sorted(fetched_data['daily_data'],
112
+ key=lambda x: x['date'],
113
+ reverse=True)
114
+ fetched_data['institution_trading'] = sorted(
115
+ fetched_data['institution_trading'],
116
+ key=lambda x: x['date'],
117
+ reverse=True)
94
118
 
95
- table_dict = self.process_data(daily_data)
119
+ table_dict = self.process_data(fetched_data)
96
120
 
97
121
  return table_dict
98
122
 
99
- def process_data(self, daily_data):
123
+ def process_data(self, fetched_data):
100
124
  table_dict = dict()
101
125
 
102
- latest_data = daily_data[0]
103
- yesterday_data = daily_data[1]
126
+ daily_datas = fetched_data['daily_data']
127
+ institution_tradings = fetched_data['institution_trading']
128
+
129
+ latest_daily_data = daily_datas[0]
130
+ yesterday_daily_data = daily_datas[1]
104
131
 
105
132
  # 交易價格與昨天交易
106
133
  price_dict = {
107
- "open": latest_data['open'],
108
- 'close': latest_data['close'],
109
- 'range': f"{latest_data['low']}-{latest_data['high']}",
110
- 'volumn': latest_data['volume'] / 1000,
111
- 'last_open': yesterday_data['open'],
112
- 'last_close': yesterday_data['close'],
113
- 'last_range': f"{yesterday_data['low']}-{yesterday_data['high']}",
114
- 'last_volumn': yesterday_data['volume'] / 1000
134
+ "open": latest_daily_data['open'],
135
+ 'close': latest_daily_data['close'],
136
+ 'range':
137
+ f"{latest_daily_data['low']} - {latest_daily_data['high']}",
138
+ 'volume': latest_daily_data['volume'] / 1000,
139
+ 'last_open': yesterday_daily_data['open'],
140
+ 'last_close': yesterday_daily_data['close'],
141
+ 'last_range':
142
+ f"{yesterday_daily_data['low']} - {yesterday_daily_data['high']}",
143
+ 'last_volume': yesterday_daily_data['volume'] / 1000
115
144
  }
116
145
  # 一年範圍
117
- annual_lows = [data['low'] for data in daily_data]
118
- annual_highs = [data['high'] for data in daily_data]
146
+ annual_lows = [data['low'] for data in daily_datas]
147
+ annual_highs = [data['high'] for data in daily_datas]
119
148
  lowest = np.min(annual_lows).item()
120
149
  highest = np.max(annual_highs).item()
121
150
 
122
- price_dict['52weeks_range'] = f"{lowest}-{highest}"
151
+ price_dict['52weeks_range'] = f"{lowest} - {highest}"
123
152
  table_dict['price'] = price_dict
124
153
 
125
154
  # 發行股數 & 市值
155
+ # 沒有實作
126
156
 
127
157
  # 今日法人買賣
158
+ latest_trading = institution_tradings[0]
128
159
  table_dict['latest_trading'] = {
129
160
  "date":
130
- daily_data[0]['date'],
161
+ latest_trading['date'],
131
162
  "table":
132
- self.process_latest_trading(daily_data[0]['institution_trading'], daily_data[0]['volume'])
163
+ self.process_latest_trading(latest_trading,
164
+ latest_daily_data['volume'])
133
165
  }
134
166
  # 一年內法人
135
- annual_trading = [
136
- {
137
- **data['institution_trading'],
138
- "收盤價": int(data['close'])
167
+ annual_dates = [data['date'].strftime("%Y-%m-%d") for data in daily_datas]
168
+ annual_closes = {
169
+ data['date'].strftime("%Y-%m-%d") : data['close']
170
+ for data in daily_datas
171
+ if (data['date'].strftime("%Y-%m-%d") in annual_dates)
172
+ }
173
+ annual_volumes = {
174
+ data['date'].strftime("%Y-%m-%d") : data['volume']
175
+ for data in daily_datas
176
+ if (data['date'].strftime("%Y-%m-%d") in annual_dates)
177
+ }
178
+ annual_trading = {
179
+ data['date'].strftime("%Y-%m-%d") : data
180
+ for data in institution_tradings
181
+ }
182
+
183
+ annual_trading = {
184
+ date: {
185
+
186
+ "close": annual_closes[date],
187
+ "volume": annual_volumes[date],
188
+ **annual_trading[date]
139
189
  }
140
- for data in daily_data
141
- ] # 將close也併入這個表格
142
- annual_dates = [data['date'] for data in daily_data]
190
+ for date in annual_dates
191
+ }
192
+
143
193
  table_dict['annual_trading'] = self.process_annual_trading(
144
194
  annual_dates, annual_trading)
145
-
195
+
146
196
  return table_dict
147
197
 
148
198
  def process_latest_trading(self, latest_trading, volume):
@@ -150,34 +200,61 @@ class InstitutionFetcher(StatsFetcher):
150
200
  "foreign": self.default_institution_chart(),
151
201
  "mutual": self.default_institution_chart(),
152
202
  "prop": self.default_institution_chart(),
153
- "institutional_investor":self.default_institution_chart(),
203
+ "institutional_investor": self.default_institution_chart(),
154
204
  }
155
205
 
156
206
  for key in latest_trading.keys():
157
207
  if (key.find("外陸資") >= 0 or key.find("外資") >= 0):
158
- self.target_institution(latest_trading, latest_table['foreign'], key, volume)
208
+ self.target_institution(latest_trading,
209
+ latest_table['foreign'], key, volume)
159
210
  elif (key.find("自營商") >= 0):
160
- self.target_institution(latest_trading,latest_table['prop'], key, volume)
211
+ self.target_institution(latest_trading, latest_table['prop'],
212
+ key, volume)
161
213
  elif (key.find("投信") >= 0):
162
- self.target_institution(latest_trading,latest_table['mutual'], key, volume)
214
+ self.target_institution(latest_trading, latest_table['mutual'],
215
+ key, volume)
163
216
  elif (key.find("三大法人") >= 0):
164
- self.target_institution(latest_trading,latest_table['institutional_investor'], key, volume)
217
+ self.target_institution(latest_trading,
218
+ latest_table['institutional_investor'],
219
+ key, volume)
220
+
221
+ for trade_type in ['buy', 'sell']:
222
+ for unit in ['stock', 'percentage']:
223
+ latest_table['institutional_investor'][trade_type][
224
+ unit] = (latest_table['foreign'][trade_type][unit] +
225
+ latest_table['prop'][trade_type][unit] +
226
+ latest_table['mutual'][trade_type][unit])
165
227
 
166
228
  frames = []
167
229
  for category, trades in latest_table.items():
168
230
  temp_df = pd.DataFrame(trades).T
169
231
  temp_df['category'] = category
170
232
  frames.append(temp_df)
171
-
233
+
172
234
  latest_df = pd.concat(frames)
173
235
  latest_df = latest_df.reset_index().rename(columns={'index': 'type'})
174
- latest_df = latest_df[['type', 'category', 'stock', 'price', 'average_price', 'percentage']]
236
+ latest_df = latest_df[[
237
+ 'type', 'category', 'stock', 'price', 'average_price', 'percentage'
238
+ ]]
239
+
240
+ latest_df = pd.melt(latest_df,
241
+ id_vars=['type', 'category'],
242
+ var_name='variable',
243
+ value_name='value')
244
+
245
+ latest_df = latest_df.pivot_table(index=['category', 'variable'],
246
+ columns='type',
247
+ values='value',
248
+ aggfunc='first')
249
+
250
+ # 重設列名,去除多層索引
251
+ latest_df.columns.name = None # 去除列名稱
252
+ latest_df = latest_df.reset_index()
175
253
 
176
254
  return latest_df
177
255
 
178
256
  def process_annual_trading(self, dates, annual_tradings):
179
- dates = [date.strftime("%m/%d") for date in dates]
180
- return pd.DataFrame(annual_tradings, index=dates)
257
+ return pd.DataFrame.from_dict(annual_tradings, orient='index')
181
258
 
182
259
  def target_institution(self, old_table, new_table, key, volume):
183
260
  if (key.find("買進") >= 0):
@@ -185,12 +262,14 @@ class InstitutionFetcher(StatsFetcher):
185
262
  elif (key.find("賣出") >= 0):
186
263
  self.cal_institution(old_table, new_table['sell'], key, volume)
187
264
  elif (key.find("買賣超") >= 0):
188
- self.cal_institution(old_table, new_table['over_buy_sell'], key, volume)
189
-
265
+ self.cal_institution(old_table, new_table['over_buy_sell'], key,
266
+ volume)
267
+
190
268
  def cal_institution(self, old_table, new_table, key, volume):
191
269
  new_table['stock'] = np.round(old_table[key] / 1000, 2).item()
192
- new_table['percentage'] = np.round((old_table[key] / volume) * 100, 2).item()
193
-
270
+ new_table['percentage'] = np.round((old_table[key] / volume) * 100,
271
+ 2).item()
272
+
194
273
  def default_institution_chart(self):
195
274
  return {
196
275
  "buy": {
@@ -211,4 +290,4 @@ class InstitutionFetcher(StatsFetcher):
211
290
  "average_price": 0,
212
291
  "percentage": 0
213
292
  },
214
- }
293
+ }
@@ -0,0 +1,279 @@
1
+ from .base import StatsFetcher
2
+ from datetime import datetime, timedelta
3
+ import json
4
+ import numpy as np
5
+ import pandas as pd
6
+ from ..utils import StatsDateTime, StatsProcessor
7
+ import importlib.resources as pkg_resources
8
+ import yaml
9
+
10
+
11
+ class MarginTradingFetcher(StatsFetcher):
12
+
13
+ def __init__(self, ticker, db_client):
14
+ """
15
+ iFa -> 交易資訊 -> 資券變化
16
+
17
+ 包括:
18
+ 1. 當日交易
19
+ 2. 一年內交易
20
+ """
21
+ super().__init__(ticker, db_client)
22
+
23
+ def prepare_query(self, start_date, end_date):
24
+ pipeline = super().prepare_query()
25
+
26
+ pipeline.append({
27
+ "$project": {
28
+ "_id": 0,
29
+ "ticker": 1,
30
+ "company_name": 1,
31
+ "daily_data": {
32
+ "$map": {
33
+ "input": {
34
+ "$filter": {
35
+ "input": "$daily_data",
36
+ "as": "daliy",
37
+ "cond": {
38
+ "$and": [{
39
+ "$gte": ["$$daliy.date", start_date]
40
+ }, {
41
+ "$lte": ["$$daliy.date", end_date]
42
+ }]
43
+ }
44
+ }
45
+ },
46
+ "as": "target_daliy_data",
47
+ "in": "$$target_daliy_data"
48
+ }
49
+ },
50
+ "margin_trading": {
51
+ "$map": {
52
+ "input": {
53
+ "$filter": {
54
+ "input": "$margin_trading",
55
+ "as": "margin",
56
+ "cond": {
57
+ "$and": [{
58
+ "$gte": ["$$margin.date", start_date]
59
+ }, {
60
+ "$lte": ["$$margin.date", end_date]
61
+ }]
62
+ }
63
+ }
64
+ },
65
+ "as": "target_margin_data",
66
+ "in": "$$target_margin_data"
67
+ }
68
+ },
69
+ "security_lending": {
70
+ "$map": {
71
+ "input": {
72
+ "$filter": {
73
+ "input": "$security_lending",
74
+ "as": "lending",
75
+ "cond": {
76
+ "$and": [{
77
+ "$gte": ["$$lending.date", start_date]
78
+ }, {
79
+ "$lte": ["$$lending.date", end_date]
80
+ }]
81
+ }
82
+ }
83
+ },
84
+ "as": "target_lending_data",
85
+ "in": "$$target_lending_data"
86
+ }
87
+ }
88
+ }
89
+ })
90
+
91
+ return pipeline
92
+
93
+ def collect_data(self, start_date, end_date):
94
+ pipeline = self.prepare_query(start_date, end_date)
95
+
96
+ fetched_data = self.collection.aggregate(pipeline).to_list()
97
+
98
+ return fetched_data[-1]
99
+
100
+ def query_data(self):
101
+ try:
102
+ latest_time = StatsDateTime.get_latest_time(
103
+ self.ticker, self.collection)['last_update_time']
104
+ latest_date = latest_time['margin_trading']['latest_date']
105
+ end_date = latest_date.replace(hour=0,
106
+ minute=0,
107
+ second=0,
108
+ microsecond=0)
109
+ except Exception as e:
110
+ print(
111
+ f"No updated time for institution_trading in {self.ticker}, use current time instead"
112
+ )
113
+ end_date = datetime.now(self.timezone)
114
+ end_date = end_date.replace(hour=0,
115
+ minute=0,
116
+ second=0,
117
+ microsecond=0)
118
+
119
+ if (end_date.hour < 22): # 拿不到今天的資料
120
+ end_date = end_date - timedelta(days=1)
121
+
122
+ start_date = end_date - timedelta(days=365)
123
+
124
+ fetched_data = self.collect_data(start_date, end_date)
125
+
126
+ fetched_data['daily_data'] = sorted(fetched_data['daily_data'],
127
+ key=lambda x: x['date'],
128
+ reverse=True)
129
+ fetched_data['margin_trading'] = sorted(fetched_data['margin_trading'],
130
+ key=lambda x: x['date'],
131
+ reverse=True)
132
+ fetched_data['security_lending'] = sorted(
133
+ fetched_data['security_lending'],
134
+ key=lambda x: x['date'],
135
+ reverse=True)
136
+
137
+ table_dict = self.process_data(fetched_data)
138
+
139
+ return table_dict
140
+
141
+ def process_data(self, fetched_data):
142
+ return_dict = dict()
143
+
144
+ daily_datas = fetched_data['daily_data']
145
+ latest_data = fetched_data['daily_data'][0]
146
+ yesterday_data = fetched_data['daily_data'][1]
147
+
148
+ # 交易價格與昨天交易
149
+ price_dict = {
150
+ "open": latest_data['open'],
151
+ 'close': latest_data['close'],
152
+ 'range': f"{latest_data['low']} - {latest_data['high']}",
153
+ 'volume': latest_data['volume'] / 1000,
154
+ 'last_open': yesterday_data['open'],
155
+ 'last_close': yesterday_data['close'],
156
+ 'last_range':
157
+ f"{yesterday_data['low']} - {yesterday_data['high']}",
158
+ 'last_volume': yesterday_data['volume'] / 1000
159
+ }
160
+ annual_lows = [data['low'] for data in daily_datas]
161
+ annual_highs = [data['high'] for data in daily_datas]
162
+ lowest = np.min(annual_lows).item()
163
+ highest = np.max(annual_highs).item()
164
+ price_dict['52weeks_range'] = f"{lowest} - {highest}"
165
+
166
+ return_dict['price'] = price_dict
167
+
168
+ # 當日交易
169
+ margin_trading = fetched_data['margin_trading']
170
+ security_lending = fetched_data['security_lending']
171
+
172
+ latest_margin_date = margin_trading[0]['date']
173
+ latest_lending_date = security_lending[0]['date']
174
+ ## 融資融券
175
+ ### 先將所有現金償還與現券償還改成現償
176
+ for trading in margin_trading:
177
+ trading['financing']['現償'] = trading['financing'].pop('現金償還')
178
+ trading['short_selling']['現償'] = trading['short_selling'].pop(
179
+ '現券償還')
180
+ ### 轉換
181
+ latest_margin_trading = margin_trading[0]
182
+ latest_margin_trading_df = {
183
+ category: sub_dict
184
+ for category, sub_dict in latest_margin_trading.items()
185
+ if (isinstance(sub_dict, dict))
186
+ }
187
+ latest_margin_trading_df = pd.DataFrame.from_dict(
188
+ latest_margin_trading_df)
189
+
190
+ ## 借券表格
191
+ latest_stock_lending = security_lending[0]['stock_lending']
192
+
193
+ latest_stock_lending = {
194
+ type_name: StatsProcessor.cal_round_int(value / 1000)
195
+ for type_name, value in latest_stock_lending.items()
196
+ }
197
+ latest_stock_lending.pop("前日餘額")
198
+ latest_stock_lending_df = pd.DataFrame.from_dict(latest_stock_lending,
199
+ orient="index",
200
+ columns=['stock_lending'])
201
+
202
+ latest_dict = {
203
+ "date": latest_margin_date,
204
+ "margin_trading": latest_margin_trading_df,
205
+ "stock_lending": latest_stock_lending_df,
206
+ "security_offset": latest_margin_trading['資券互抵']
207
+ }
208
+
209
+ return_dict['latest_trading'] = latest_dict
210
+
211
+ # 一年內
212
+ annual_dates = [
213
+ data['date'].strftime('%Y-%m-%d')
214
+ for data in fetched_data['margin_trading']
215
+ ]
216
+ close_prices = {
217
+ data['date'].strftime('%Y-%m-%d'): data['close']
218
+ for data in fetched_data['daily_data']
219
+ if data['date'].strftime('%Y-%m-%d') in annual_dates
220
+ }
221
+ volumes = {
222
+ data['date'].strftime('%Y-%m-%d'):
223
+ StatsProcessor.cal_round_int(data['volume'] / 1000)
224
+ for data in fetched_data['daily_data']
225
+ if data['date'].strftime('%Y-%m-%d') in annual_dates
226
+ }
227
+
228
+ ## 融資融券
229
+ financings = {
230
+ data['date'].strftime("%Y-%m-%d"): {
231
+ f"融資_{type_name}": num_of_stocks
232
+ for type_name, num_of_stocks in data['financing'].items()
233
+ }
234
+ for data in fetched_data['margin_trading']
235
+ }
236
+
237
+ short_sellings = {
238
+ data['date'].strftime("%Y-%m-%d"): {
239
+ f"融券_{type_name}": num_of_stocks
240
+ for type_name, num_of_stocks in data['short_selling'].items()
241
+ }
242
+ for data in fetched_data['margin_trading']
243
+ }
244
+
245
+ ### 資券互抵
246
+ security_offsets = {
247
+ data['date'].strftime("%Y-%m-%d"): data['資券互抵']
248
+ for data in fetched_data['margin_trading']
249
+ }
250
+
251
+ ## 借券(stock lendings)
252
+ stock_lendings = {
253
+ data['date'].strftime('%Y-%m-%d'): {
254
+ f"借券_{type_name}":
255
+ (num_of_stocks /
256
+ 1000) if isinstance(num_of_stocks,
257
+ (int, float)) else num_of_stocks
258
+ for type_name, num_of_stocks in data['stock_lending'].items()
259
+ }
260
+ for data in fetched_data['security_lending']
261
+ }
262
+
263
+ annual_dict = {
264
+ date: {
265
+ "close": close_prices[date],
266
+ "volume": volumes[date],
267
+ **financings[date],
268
+ **short_sellings[date],
269
+ **stock_lendings[date],
270
+ "資券互抵":security_offsets[date]
271
+ }
272
+ for date in annual_dates
273
+ }
274
+
275
+ annual_table = pd.DataFrame.from_dict(annual_dict)
276
+
277
+ return_dict['annual_margin'] = annual_table.T
278
+
279
+ return return_dict
@@ -79,6 +79,78 @@ class ValueFetcher(StatsFetcher):
79
79
  )
80
80
 
81
81
  return fetched_data
82
+
83
+ def query_value_serie(self):
84
+ """
85
+ 回傳指定公司的歷來評價
86
+ return : Dict[pd.DataFrame]
87
+ Dict中包含以下八個key, 每個key對應DataFrame
88
+ {
89
+ P_E,
90
+ P_FCF,
91
+ P_S,
92
+ P_B,
93
+ EV_OPI,
94
+ EV_EBIT,
95
+ EV_EBITDA,
96
+ EV_S
97
+ }
98
+ """
99
+
100
+ pipeline = [
101
+ {
102
+ "$match": {
103
+ "ticker": self.ticker,
104
+ }
105
+ },
106
+ {
107
+ "$project": {
108
+ "_id": 0,
109
+ "ticker": 1,
110
+ "company_name": 1,
111
+ "daily_data": {
112
+ "$map": {
113
+ "input": "$daily_data", # 正確地指定要處理的陣列
114
+ "as": "daily", # 每個元素的名稱
115
+ "in": {
116
+ "date": "$$daily.date",
117
+ "P_E": "$$daily.P_E",
118
+ "P_FCF": "$$daily.P_FCF",
119
+ "P_B": "$$daily.P_B",
120
+ "P_S": "$$daily.P_S",
121
+ "EV_OPI": "$$daily.EV_OPI",
122
+ "EV_EBIT": "$$daily.EV_EBIT",
123
+ "EV_EBITDA": "$$daily.EV_EBITDA",
124
+ "EV_S": "$$daily.EV_S"
125
+ }
126
+ }
127
+ }
128
+ }
129
+ }
130
+ ]
131
+
132
+ fetched_data = self.collection.aggregate(pipeline).to_list()
133
+ fetched_data = fetched_data[0]
134
+
135
+ value_keys = ["P_E", "P_FCF", "P_B", "P_S", "EV_OPI", "EV_EBIT", "EV_EVITDA", "EV_S"]
136
+ return_dict = {
137
+ value_key: dict() for value_key in value_keys
138
+ }
139
+
140
+ for value_key in value_keys:
141
+ for data in fetched_data['daily_data']:
142
+ if (value_key not in data.keys()):
143
+ continue
144
+ else:
145
+ return_dict[value_key].update({
146
+ data['date']: data[value_key]
147
+ })
148
+
149
+ return_dict = {
150
+ value_key: pd.DataFrame.from_dict(value_dict, orient = 'index', columns = [value_key])
151
+ for value_key, value_dict in return_dict.items()
152
+ }
153
+ return return_dict
82
154
 
83
155
 
84
156
  class ValueProcessor(StatsProcessor):
@@ -17,6 +17,11 @@ target_metric_dict = {
17
17
 
18
18
 
19
19
  class StatsProcessor:
20
+ """
21
+ 1. 讀檔: txt / yaml
22
+ 2. 將巢狀dictionary / DataFrame扁平化
23
+
24
+ """
20
25
 
21
26
  @classmethod
22
27
  def load_txt(cls, filename, json_load=True):
@@ -173,3 +178,13 @@ class StatsProcessor:
173
178
 
174
179
  else:
175
180
  return value
181
+
182
+ @classmethod
183
+ def cal_round_int(cls, value):
184
+ """
185
+ 將數值取到個位數後轉為int
186
+ """
187
+ if (isinstance(value, (int, float))):
188
+ return int(np.round(value).item())
189
+ else:
190
+ return value
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: neurostats-API
3
- Version: 0.0.11
3
+ Version: 0.0.13
4
4
  Summary: The service of NeuroStats website
5
5
  Home-page: https://github.com/NeurowattStats/NeuroStats_API.git
6
6
  Author: JasonWang@Neurowatt
@@ -14,11 +14,14 @@ Description-Content-Type: text/markdown
14
14
  - [使用方法](#使用方法)
15
15
  - [下載](#下載)
16
16
  - [價值投資](#得到最新一期的評價資料與歷年評價)
17
+ - [歷史評價](#得到指定公司的歷來評價)
17
18
  - [財務分析-重要指標](#財務分析-重要指標)
18
19
  - [月營收表](#回傳月營收表)
19
20
  - [損益表](#損益表)
20
21
  - [資產負債表](#資產負債表)
21
22
  - [現金流量表](#現金流量表)
23
+ - [法人交易](#法人交易)
24
+ - [資券餘額](#資券餘額)
22
25
  - [版本紀錄](#版本紀錄)
23
26
 
24
27
 
@@ -80,7 +83,7 @@ pip install neurostats-API
80
83
  ```Python
81
84
  >>> import neurostats_API
82
85
  >>> print(neurostats_API.__version__)
83
- 0.0.10
86
+ 0.0.13
84
87
  ```
85
88
 
86
89
  ### 得到最新一期的評價資料與歷年評價
@@ -89,7 +92,8 @@ from neurostats_API.utils import ValueFetcher, DBClient
89
92
  db_client = DBClient("<連接的DB位置>").get_client()
90
93
  ticker = "2330" # 換成tw50內任意ticker
91
94
  fetcher = ValueFetcher(ticker, db_client)
92
- data = stats_fetcher.query_data()
95
+
96
+ fetcher.query_data()
93
97
  ```
94
98
 
95
99
  #### 回傳(2330為例)
@@ -124,6 +128,69 @@ data = stats_fetcher.query_data()
124
128
  ```
125
129
  > 這裡有Nan是因為本益比與P/B等資料沒有爬到最新的時間
126
130
 
131
+ ### 得到指定公司的歷來評價
132
+ ``` Python
133
+ from neurostats_API.utils import ValueFetcher, DBClient
134
+ db_client = DBClient("<連接的DB位置>").get_client()
135
+ ticker = "2330" # 換成tw50內任意ticker
136
+ fetcher = ValueFetcher(ticker, db_client)
137
+
138
+ fetcher.query_value_serie()
139
+ ```
140
+ #### 回傳(2330為例)
141
+ ```Python
142
+ {
143
+ 'EV_EBIT': EV_EBIT
144
+ 2014-01-02 NaN
145
+ 2014-01-03 NaN
146
+ ... ...
147
+ 2024-12-12 15.021431
148
+ 2024-12-13 15.088321
149
+ ,
150
+ 'EV_OPI': EV_OPI
151
+ 2014-01-03 NaN
152
+ ... ...
153
+ 2024-12-12 15.999880
154
+ 2024-12-13 16.071128
155
+ ,
156
+ 'EV_S': EV_S
157
+ 2014-01-02 NaN
158
+ 2014-01-03 NaN
159
+ ... ...
160
+ 2024-12-12 6.945457
161
+ 2024-12-13 6.976385
162
+ ,
163
+ 'P_B': P_B
164
+ 2014-01-02 NaN
165
+ 2014-01-03 NaN
166
+ ... ...
167
+ 2024-12-12 6.79
168
+ 2024-12-13 6.89
169
+ ,
170
+ 'P_E': P_E
171
+ 2014-01-02 NaN
172
+ 2014-01-03 NaN
173
+ ... ...
174
+ 2024-12-12 26.13
175
+ 2024-12-13 26.50
176
+ ,
177
+ 'P_FCF': P_FCF
178
+ 2014-01-02 NaN
179
+ 2014-01-03 NaN
180
+ ... ...
181
+ 2024-12-12 45.302108
182
+ 2024-12-13 45.515797
183
+ ,
184
+ 'P_S': P_S
185
+ 2014-01-02 NaN
186
+ 2014-01-03 NaN
187
+ ... ...
188
+ 2024-12-12 6.556760
189
+ 2024-12-13 6.587688
190
+ }
191
+ ```
192
+
193
+
127
194
  ### 回傳月營收表
128
195
  ``` Python
129
196
  from neurostats_API.fetchers import MonthRevenueFetcher, DBClient
@@ -324,7 +391,7 @@ db_client = DBClient("<連接的DB位置>").get_client()
324
391
  ticker = "2330" # 換成tw50內任意ticker
325
392
  fetcher = BalanceSheetFetcher(ticker, db_client)
326
393
 
327
- stats_fetcher.query_data()
394
+ fetcher.query_data()
328
395
  ```
329
396
 
330
397
  #### 回傳
@@ -386,7 +453,7 @@ db_client = DBClient("<連接的DB位置>").get_client()
386
453
  ticker = 2330 # 換成tw50內任意ticker
387
454
  fetcher = StatsFetcher(ticker, db_client)
388
455
 
389
- stats_fetcher.query()
456
+ fetcher.query()
390
457
  ```
391
458
  #### 回傳
392
459
  ```Python
@@ -435,8 +502,186 @@ stats_fetcher.query()
435
502
 
436
503
  > 大部分資料缺失是因為尚未計算,僅先填上已經有的資料
437
504
 
505
+ ## 籌碼面
506
+ ### 法人交易
507
+ ``` Python
508
+ from neurostats_API.fetchers import InstitutionFetcher
509
+ db_client = DBClient("<連接的DB位置>").get_client()
510
+ ticker = 2330 # 換成tw50內任意ticker
511
+ fetcher = StatsFetcher(ticker, db_client)
512
+
513
+ fetcher.query()
514
+ ```
515
+ ### 回傳
516
+ ```Python
517
+ { 'annual_trading':
518
+ close volume ... 自營商買賣超股數(避險) 三大法人買賣超股數
519
+ 2024-12-02 1035.000000 31168404.0 ... -133215.0 11176252.0
520
+ 2024-11-29 996.000000 40094983.0 ... 401044.0 -7880519.0
521
+ ... ... ... ... ... ...
522
+ 2023-12-05 559.731873 22229723.0 ... 33,400 -5,988,621
523
+ 2023-12-04 563.659790 26847171.0 ... -135,991 -5,236,743
524
+
525
+ ,
526
+ 'latest_trading':
527
+ { 'date': datetime.datetime(2024, 12, 2, 0, 0),
528
+ 'table':
529
+ category variable ... over_buy_sell sell
530
+ 0 foreign average_price ... 0.00 0.0
531
+ 1 foreign percentage ... 0.00 0.0
532
+ .. ... ... ... ... ...
533
+ 14 prop price ... 0.00 0.0
534
+ 15 prop stock ... -133.22 217.2
535
+ }
536
+ ,
537
+ 'price':
538
+ {
539
+ '52weeks_range': '555.8038940429688-1100.0', # str
540
+ 'close': 1035.0, # float
541
+ 'last_close': 996.0, # float
542
+ 'last_open': 995.0, # float
543
+ 'last_range': '994.0-1010.0', # str
544
+ 'last_volume': 40094.983, # float
545
+ 'open': 1020.0, # float
546
+ 'range': '1015.0-1040.0', # str
547
+ 'volume': 32238.019 # float
548
+ }
549
+ }
550
+ ```
551
+
552
+ - `annual_trading`: 對應一年內每日的交易量
553
+ - `latest_trading`: 對應當日交易
554
+ ##### 欄位項目名稱
555
+ |英文|中文對應|
556
+ |----|-------|
557
+ |buy|買進|
558
+ |sell|賣出|
559
+ |over_buy_sell|買賣超|
560
+
561
+ ##### category項目名稱
562
+ |英文|中文對應|
563
+ |----|-------|
564
+ |foreign|外資|
565
+ |prop|自營商|
566
+ |mutual|投信|
567
+ |institutional_investor|三大法人|
568
+
569
+ ##### variable項目名稱
570
+ |英文|中文對應|
571
+ |----|-------|
572
+ |stock|股票張數|
573
+ |price|成交金額|
574
+ |average_price|均價|
575
+ |percetage|佔成交比重|
576
+
577
+ **成交金額以及均價因為資料沒有爬到而無法計算**
578
+ **仍然先將這項目加入,只是數值都會是0**
579
+
580
+ - `price`: 對應法人買賣頁面的今日與昨日交易價
581
+ 請注意`range`, `last_range`, `52week_range`這三個項目型態為字串,其餘為float
582
+
583
+ ##### 項目名稱
584
+ |英文|中文對應|
585
+ |----|-------|
586
+ |open|開盤價|
587
+ |close|收盤價|
588
+ |range|當日範圍|
589
+ |volume|成交張數|
590
+ |last_open|開盤價(昨)|
591
+ |last_close|收盤價(昨)|
592
+ |last_range|昨日範圍|
593
+ |last_volume|成交張數(昨)|
594
+ |52weeks_range|52週範圍|
595
+
596
+ ## 資券餘額
597
+ 對應iFa.ai -> 交易資訊 -> 資券變化
598
+ ```Python
599
+ from neurostats_API.fetchers import MarginTradingFetcher
600
+ db_client = DBClient("<連接的DB位置>").get_client()
601
+ ticker = 2330 # 換成tw50內任意ticker
602
+ fetcher = MarginTradingFetcher(ticker, db_client)
603
+
604
+ fetcher.query()
605
+ ```
606
+
607
+ ### 回傳
608
+ ```Python
609
+ { 'annual_margin':
610
+ close volume ... 借券_次一營業日可限額 資券互抵
611
+ 2024-12-03 1060.000000 29637.0 ... 12222.252 0.0
612
+ 2024-12-02 1035.000000 31168.0 ... 12156.872 1.0
613
+ ... ... ... ... ... ...
614
+ 2023-12-05 559.731873 22230.0 ... 7838.665 1.0
615
+ 2023-12-04 563.659790 26847.0 ... 7722.725 2.0
616
+
617
+ 'latest_trading': {
618
+ 'date': datetime.datetime(2024, 12, 3, 0, 0),
619
+ 'margin_trading':
620
+ financing short_selling
621
+ 買進 761.0 34.0
622
+ 賣出 1979.0 44.0
623
+ ... ... ...
624
+ 次一營業日限額 6483183.0 6483183.0
625
+ 現償 3.0 12.0
626
+
627
+
628
+ 'security_offset': 0.0,
629
+ 'stock_lending': stock_lending
630
+ 當日賣出 10
631
+ 當日還券 0
632
+ 當日調整 0
633
+ 當日餘額 14688
634
+ 次一營業日可限額 12222
635
+ },
636
+ 'price': { '52weeks_range': '555.8038940429688 - 1100.0',
637
+ 'close': 1060.0,
638
+ 'last_close': 1035.0,
639
+ 'last_open': 1020.0,
640
+ 'last_range': '1015.0 - 1040.0',
641
+ 'last_volume': 31168.404,
642
+ 'open': 1060.0,
643
+ 'range': '1055.0 - 1065.0',
644
+ 'volume': 29636.523}}
645
+ ```
646
+ - `annual_trading`: 對應一年內每日的資券變化量
647
+ - `latest_trading`: 對應當日交易
648
+ ##### 欄位項目名稱
649
+ |英文|中文對應|
650
+ |----|-------|
651
+ |financing|融資|
652
+ |short_selling|融券|
653
+
654
+ - `price`: 對應法人買賣頁面的今日與昨日交易價
655
+ ##### 項目名稱
656
+ |英文|中文對應|
657
+ |----|-------|
658
+ |open|開盤價|
659
+ |close|收盤價|
660
+ |range|當日範圍|
661
+ |volume|成交張數|
662
+ |last_open|開盤價(昨)|
663
+ |last_close|收盤價(昨)|
664
+ |last_range|昨日範圍|
665
+ |last_volume|成交張數(昨)|
666
+ |52weeks_range|52週範圍|
667
+
668
+ 請注意`range`, `last_range`, `52week_range`這三個項目型態為字串,其餘為float
438
669
 
439
670
  ## 版本紀錄
671
+ ### 0.0.13
672
+ - value_fetcher 新增獲得一序列評價的功能
673
+
674
+ ### 0.0.12
675
+ - 新增資券變化(margin trading)
676
+
677
+ - 修改法人買賣(institution_trading)的query方式
678
+
679
+ ### 0.0.11
680
+ - 修復財務分析的千元計算問題
681
+
682
+ - 籌碼面新增法人買賣(institution_trading)
683
+
684
+ - 將財報三表與月營收的資料型態與數值做轉換(%轉字串, 千元乘以1000)
440
685
  ### 0.0.10
441
686
  - 更新指標的資料型態: 單位為千元乘以1000之後回傳整數
442
687
 
@@ -1,27 +1,28 @@
1
- neurostats_API/__init__.py,sha256=oR5iCRZvbIRoODxS1VocreTo19N5L8Omvx_AgflzOO0,20
1
+ neurostats_API/__init__.py,sha256=UDEAcv3wrFvy1-DjCv7qON8202kc3zUGHpfClyTJWhs,20
2
2
  neurostats_API/cli.py,sha256=UJSWLIw03P24p-gkBb6JSEI5dW5U12UvLf1L8HjQD-o,873
3
3
  neurostats_API/main.py,sha256=QcsfmWivg2Dnqw3MTJWiI0QvEiRs0VuH-BjwQHFCv00,677
4
- neurostats_API/fetchers/__init__.py,sha256=27kdeBuM7dNBRcIyQ1u863CYw0P_DQz-I1G6iSFDq-c,357
4
+ neurostats_API/fetchers/__init__.py,sha256=ylYEySHQxcAhUUWEOCGZfmaAg7Mir5MfuEhOjk3POJg,406
5
5
  neurostats_API/fetchers/balance_sheet.py,sha256=sQv4Gk5uoKURLEdh57YknOQWiyVwaXJ2Mw75jxNqUS0,5804
6
6
  neurostats_API/fetchers/base.py,sha256=NW2SFzrimyAIrdJx1LVmTazelyZOAtcj54kJKHc4Vaw,1662
7
7
  neurostats_API/fetchers/cash_flow.py,sha256=TY7VAWVXkj5-mzH5Iu0sIE-oV8MvGmmDy0URNotNV1E,7614
8
8
  neurostats_API/fetchers/finance_overview.py,sha256=PxUdWY0x030olYMLcCHDBn068JLmCE2RTOce1dxs5vM,27753
9
- neurostats_API/fetchers/institution.py,sha256=aODtsFyQcnD9PnMeaehMAN9wZdZ2a0EqSSZO57dY9RE,7691
9
+ neurostats_API/fetchers/institution.py,sha256=rEcs5-JKbWjg8lSjG1V3YdHJQuPSN1GntnxvedESCeo,10984
10
+ neurostats_API/fetchers/margin_trading.py,sha256=lQImtNdvaBoSlKhJvQ3DkH3HjSSgKRJz4ZZpyR5-Z4I,10433
10
11
  neurostats_API/fetchers/month_revenue.py,sha256=nixX2llzjCFr2m2YVjxrSfkBusnZPrPb2dRDq1XLGhw,4251
11
12
  neurostats_API/fetchers/profit_lose.py,sha256=xlLNsGSy4Azf4HyZyYaX3dFad-ACO-vuQToBooZi1_w,5698
12
13
  neurostats_API/fetchers/tech.py,sha256=wH1kkqiETQhF0HAhk-UIiucnZ3EiL85Q-yMWCcVOiFM,11395
13
- neurostats_API/fetchers/value_invest.py,sha256=O5IKC8Nl7p5-E-1zoyAyWtiDznaxNemeabanmaHDdJs,3327
14
+ neurostats_API/fetchers/value_invest.py,sha256=_eQxuEnIYvksb06QHixGK29Gnwr_3xmI6Tu7dv4J__E,5769
14
15
  neurostats_API/tools/balance_sheet.yaml,sha256=yTxrWh7m4K3LnaNunETidfNzl6S4Bf58VIg9U38XShQ,648
15
16
  neurostats_API/tools/cash_flow_percentage.yaml,sha256=fk2Z4eb1JjGFvP134eJatHacB7BgTkBenhDJr83w8RE,1345
16
17
  neurostats_API/tools/finance_overview_dict.yaml,sha256=B9nV75StXkrF3yv2-eezzitlJ38eEK86RD_VY6588gQ,2884
17
18
  neurostats_API/tools/profit_lose.yaml,sha256=dcO-0J0BC4p06XBNuowu8ux0NTbyZiOkGfy6szHF6fw,2402
18
19
  neurostats_API/tools/seasonal_data_field_dict.txt,sha256=X8yc_el6p8BH_3FikTqBVFGsvWdXT6MHXLfKfi44334,8491
19
20
  neurostats_API/utils/__init__.py,sha256=FTYKRFzW2XVXdnSHXnS3mQQaHlKF9xGqrMsgZZ2kroc,142
20
- neurostats_API/utils/data_process.py,sha256=mDznLqAAZ7gFX3LlJkJvtrMPt38Lh5-NONqgnqT5tSY,5990
21
+ neurostats_API/utils/data_process.py,sha256=2yrO0iP1LHhF0uhXZ442PHQBI-Zd2xIqNItkNf5hKIc,6339
21
22
  neurostats_API/utils/datetime.py,sha256=XJya4G8b_-ZOaBbMXgQjWh2MC4wc-o6goQ7EQJQMWrQ,773
22
23
  neurostats_API/utils/db_client.py,sha256=OYe6yazcR4Aa6jYmy47JrryUeh2NnKGqY2K_lSZe6i8,455
23
24
  neurostats_API/utils/fetcher.py,sha256=VbrUhjA-GG5AyjPX2SHtFIbZM4dm3jo0RgZzuCbb_Io,40927
24
- neurostats_API-0.0.11.dist-info/METADATA,sha256=Tddw5SxRekTkTtemDXgYPoiJf9sxICyRkdlFAbvniSM,18529
25
- neurostats_API-0.0.11.dist-info/WHEEL,sha256=bFJAMchF8aTQGUgMZzHJyDDMPTO3ToJ7x23SLJa1SVo,92
26
- neurostats_API-0.0.11.dist-info/top_level.txt,sha256=nSlQPMG0VtXivJyedp4Bkf86EOy2TpW10VGxolXrqnU,15
27
- neurostats_API-0.0.11.dist-info/RECORD,,
25
+ neurostats_API-0.0.13.dist-info/METADATA,sha256=tj8M4sSgXPprPV472mYHdEY_QOBpcObMXXo5lLGSOic,25744
26
+ neurostats_API-0.0.13.dist-info/WHEEL,sha256=bFJAMchF8aTQGUgMZzHJyDDMPTO3ToJ7x23SLJa1SVo,92
27
+ neurostats_API-0.0.13.dist-info/top_level.txt,sha256=nSlQPMG0VtXivJyedp4Bkf86EOy2TpW10VGxolXrqnU,15
28
+ neurostats_API-0.0.13.dist-info/RECORD,,