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