neurostats-API 0.0.11__py3-none-any.whl → 0.0.13__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.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,,