neurostats-API 0.0.11__py3-none-any.whl → 0.0.12__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.12'
@@ -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
@@ -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
- Name: neurostats-API
3
- Version: 0.0.11
2
+ Name: neurostats_API
3
+ Version: 0.0.12
4
4
  Summary: The service of NeuroStats website
5
5
  Home-page: https://github.com/NeurowattStats/NeuroStats_API.git
6
6
  Author: JasonWang@Neurowatt
@@ -19,6 +19,8 @@ Description-Content-Type: text/markdown
19
19
  - [損益表](#損益表)
20
20
  - [資產負債表](#資產負債表)
21
21
  - [現金流量表](#現金流量表)
22
+ - [法人交易](#法人交易)
23
+ - [資券餘額](#資券餘額)
22
24
  - [版本紀錄](#版本紀錄)
23
25
 
24
26
 
@@ -89,7 +91,8 @@ from neurostats_API.utils import ValueFetcher, DBClient
89
91
  db_client = DBClient("<連接的DB位置>").get_client()
90
92
  ticker = "2330" # 換成tw50內任意ticker
91
93
  fetcher = ValueFetcher(ticker, db_client)
92
- data = stats_fetcher.query_data()
94
+
95
+ fetcher.query_data()
93
96
  ```
94
97
 
95
98
  #### 回傳(2330為例)
@@ -324,7 +327,7 @@ db_client = DBClient("<連接的DB位置>").get_client()
324
327
  ticker = "2330" # 換成tw50內任意ticker
325
328
  fetcher = BalanceSheetFetcher(ticker, db_client)
326
329
 
327
- stats_fetcher.query_data()
330
+ fetcher.query_data()
328
331
  ```
329
332
 
330
333
  #### 回傳
@@ -386,7 +389,7 @@ db_client = DBClient("<連接的DB位置>").get_client()
386
389
  ticker = 2330 # 換成tw50內任意ticker
387
390
  fetcher = StatsFetcher(ticker, db_client)
388
391
 
389
- stats_fetcher.query()
392
+ fetcher.query()
390
393
  ```
391
394
  #### 回傳
392
395
  ```Python
@@ -435,8 +438,178 @@ stats_fetcher.query()
435
438
 
436
439
  > 大部分資料缺失是因為尚未計算,僅先填上已經有的資料
437
440
 
441
+ ## 籌碼面
442
+ ### 法人交易
443
+ ``` Python
444
+ from neurostats_API.fetchers import InstitutionFetcher
445
+ db_client = DBClient("<連接的DB位置>").get_client()
446
+ ticker = 2330 # 換成tw50內任意ticker
447
+ fetcher = StatsFetcher(ticker, db_client)
448
+
449
+ fetcher.query()
450
+ ```
451
+ ### 回傳
452
+ ```Python
453
+ { 'annual_trading':
454
+ close volume ... 自營商買賣超股數(避險) 三大法人買賣超股數
455
+ 2024-12-02 1035.000000 31168404.0 ... -133215.0 11176252.0
456
+ 2024-11-29 996.000000 40094983.0 ... 401044.0 -7880519.0
457
+ ... ... ... ... ... ...
458
+ 2023-12-05 559.731873 22229723.0 ... 33,400 -5,988,621
459
+ 2023-12-04 563.659790 26847171.0 ... -135,991 -5,236,743
460
+
461
+ ,
462
+ 'latest_trading':
463
+ { 'date': datetime.datetime(2024, 12, 2, 0, 0),
464
+ 'table':
465
+ category variable ... over_buy_sell sell
466
+ 0 foreign average_price ... 0.00 0.0
467
+ 1 foreign percentage ... 0.00 0.0
468
+ .. ... ... ... ... ...
469
+ 14 prop price ... 0.00 0.0
470
+ 15 prop stock ... -133.22 217.2
471
+ }
472
+ ,
473
+ 'price':
474
+ {
475
+ '52weeks_range': '555.8038940429688-1100.0', # str
476
+ 'close': 1035.0, # float
477
+ 'last_close': 996.0, # float
478
+ 'last_open': 995.0, # float
479
+ 'last_range': '994.0-1010.0', # str
480
+ 'last_volume': 40094.983, # float
481
+ 'open': 1020.0, # float
482
+ 'range': '1015.0-1040.0', # str
483
+ 'volume': 32238.019 # float
484
+ }
485
+ }
486
+ ```
487
+
488
+ - `annual_trading`: 對應一年內每日的交易量
489
+ - `latest_trading`: 對應當日交易
490
+ ##### 欄位項目名稱
491
+ |英文|中文對應|
492
+ |----|-------|
493
+ |buy|買進|
494
+ |sell|賣出|
495
+ |over_buy_sell|買賣超|
496
+
497
+ ##### category項目名稱
498
+ |英文|中文對應|
499
+ |----|-------|
500
+ |foreign|外資|
501
+ |prop|自營商|
502
+ |mutual|投信|
503
+ |institutional_investor|三大法人|
504
+
505
+ ##### variable項目名稱
506
+ |英文|中文對應|
507
+ |----|-------|
508
+ |stock|股票張數|
509
+ |price|成交金額|
510
+ |average_price|均價|
511
+ |percetage|佔成交比重|
512
+
513
+ **成交金額以及均價因為資料沒有爬到而無法計算**
514
+ **仍然先將這項目加入,只是數值都會是0**
515
+
516
+ - `price`: 對應法人買賣頁面的今日與昨日交易價
517
+ 請注意`range`, `last_range`, `52week_range`這三個項目型態為字串,其餘為float
518
+
519
+ ##### 項目名稱
520
+ |英文|中文對應|
521
+ |----|-------|
522
+ |open|開盤價|
523
+ |close|收盤價|
524
+ |range|當日範圍|
525
+ |volume|成交張數|
526
+ |last_open|開盤價(昨)|
527
+ |last_close|收盤價(昨)|
528
+ |last_range|昨日範圍|
529
+ |last_volume|成交張數(昨)|
530
+ |52weeks_range|52週範圍|
531
+
532
+ ## 資券餘額
533
+ 對應iFa.ai -> 交易資訊 -> 資券變化
534
+ ```Python
535
+ from neurostats_API.fetchers import MarginTradingFetcher
536
+ db_client = DBClient("<連接的DB位置>").get_client()
537
+ ticker = 2330 # 換成tw50內任意ticker
538
+ fetcher = MarginTradingFetcher(ticker, db_client)
539
+
540
+ fetcher.query()
541
+ ```
542
+
543
+ ### 回傳
544
+ ```Python
545
+ { 'annual_margin':
546
+ close volume ... 借券_次一營業日可限額 資券互抵
547
+ 2024-12-03 1060.000000 29637.0 ... 12222.252 0.0
548
+ 2024-12-02 1035.000000 31168.0 ... 12156.872 1.0
549
+ ... ... ... ... ... ...
550
+ 2023-12-05 559.731873 22230.0 ... 7838.665 1.0
551
+ 2023-12-04 563.659790 26847.0 ... 7722.725 2.0
552
+
553
+ 'latest_trading': {
554
+ 'date': datetime.datetime(2024, 12, 3, 0, 0),
555
+ 'margin_trading':
556
+ financing short_selling
557
+ 買進 761.0 34.0
558
+ 賣出 1979.0 44.0
559
+ ... ... ...
560
+ 次一營業日限額 6483183.0 6483183.0
561
+ 現償 3.0 12.0
562
+
563
+
564
+ 'security_offset': 0.0,
565
+ 'stock_lending': stock_lending
566
+ 當日賣出 10
567
+ 當日還券 0
568
+ 當日調整 0
569
+ 當日餘額 14688
570
+ 次一營業日可限額 12222
571
+ },
572
+ 'price': { '52weeks_range': '555.8038940429688 - 1100.0',
573
+ 'close': 1060.0,
574
+ 'last_close': 1035.0,
575
+ 'last_open': 1020.0,
576
+ 'last_range': '1015.0 - 1040.0',
577
+ 'last_volume': 31168.404,
578
+ 'open': 1060.0,
579
+ 'range': '1055.0 - 1065.0',
580
+ 'volume': 29636.523}}
581
+ ```
582
+ - `annual_trading`: 對應一年內每日的資券變化量
583
+ - `latest_trading`: 對應當日交易
584
+ ##### 欄位項目名稱
585
+ |英文|中文對應|
586
+ |----|-------|
587
+ |financing|融資|
588
+ |short_selling|融券|
589
+
590
+ - `price`: 對應法人買賣頁面的今日與昨日交易價
591
+ ##### 項目名稱
592
+ |英文|中文對應|
593
+ |----|-------|
594
+ |open|開盤價|
595
+ |close|收盤價|
596
+ |range|當日範圍|
597
+ |volume|成交張數|
598
+ |last_open|開盤價(昨)|
599
+ |last_close|收盤價(昨)|
600
+ |last_range|昨日範圍|
601
+ |last_volume|成交張數(昨)|
602
+ |52weeks_range|52週範圍|
603
+
604
+ 請注意`range`, `last_range`, `52week_range`這三個項目型態為字串,其餘為float
438
605
 
439
606
  ## 版本紀錄
607
+ ### 0.0.11
608
+ - 修復財務分析的千元計算問題
609
+
610
+ - 籌碼面新增法人買賣(institution_trading)
611
+
612
+ - 將財報三表與月營收的資料型態與數值做轉換(%轉字串, 千元乘以1000)
440
613
  ### 0.0.10
441
614
  - 更新指標的資料型態: 單位為千元乘以1000之後回傳整數
442
615
 
@@ -1,12 +1,13 @@
1
- neurostats_API/__init__.py,sha256=oR5iCRZvbIRoODxS1VocreTo19N5L8Omvx_AgflzOO0,20
1
+ neurostats_API/__init__.py,sha256=9UMAkivMiZfppwpwVdBxJI652ZiB-ir2iX3yh0Gxu9M,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
@@ -17,11 +18,11 @@ neurostats_API/tools/finance_overview_dict.yaml,sha256=B9nV75StXkrF3yv2-eezzitlJ
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.12.dist-info/METADATA,sha256=-1MqxrAVp-6u25PFIMmrMv5ad6RrWwguHiONp0worpc,24008
26
+ neurostats_API-0.0.12.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
27
+ neurostats_API-0.0.12.dist-info/top_level.txt,sha256=nSlQPMG0VtXivJyedp4Bkf86EOy2TpW10VGxolXrqnU,15
28
+ neurostats_API-0.0.12.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.45.0)
2
+ Generator: setuptools (75.5.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5