Table of Contents
在股市風起雲湧的今天,企業股價波動背後的謎團值得深入挖掘。內部人持股轉讓反映公司內部運營、管理層對未來發展的信心,以及對當前經濟環境的評估。透過分析內部人鉅額交易背後的動因,探討股價與內部人持股轉讓之關係。本文將運用TEJ API聚焦於內部人持股轉讓對台灣上市櫃公司股價進行量化分析。
依據交易數量與買賣金額,證券交易若符合以下標準,就要申報鉅額交易:
鉅額交易分為逐筆交易與配對交易兩種,其申報價格範圍與一般交易的漲跌停限制相同。逐筆交易的成交價格會以買賣雙方申報價格的高低與申報時間的先後順序進行搓合;配對交易則是委託人洽特定人以指定的價格與數量對敲。另外,鉅額交易的委託與搓合時間最晚到 17 : 00 為止,為了不讓盤中價格產生大幅波動,因此鉅額交易通常會在盤後執行。
本文使用 Windows 11 作業系統以及 VS code 作為編輯器。
取得內部人申報轉讓資料,資料由董監申報轉讓資料庫導入,其中轉讓的方式包含很多種,如:一般交易、洽特定人、信託、贈與、鉅額交易、盤後定價,而我們從中抓取鉅額交易的資料。
import tejapi
import pandas as pd
import matplotlib.pyplot as plt
import plotly.graph_objects as go
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
tejapi.ApiConfig.api_key = "Your Key"
tejapi.ApiConfig.api_base = "https://api.tej.com.tw"
tejapi.ApiConfig.ignoretz = True
# 取得內部人申報轉讓資料,並篩選出鉅額交易的資料
data_trans = tejapi.get('TWN/ATRANS', # 上市(櫃)董監申報轉讓資料庫
# 證券碼、申報日、股東身份、轉讓方式、申報轉讓股數
opts = {'columns':['coid', 'mdate', 'clnm_c', 'tpnm_c', 'tns2']},
paginate = True,
chinese_column_name = True)
data_trans = data_trans[data_trans['轉讓方式'].str.contains('鉅額')] # 篩選鉅額交易資料
設置總發行股數函數,資料由上市(櫃)董監全體持股狀況資料庫導入。
# 取得總發行股數函數,將證券碼、日期設為變數
def data_total_stock(coid, date):
# 由於此資料庫將日期設為次月15號更新,本次實作2024/01的資料尚未發布,所以若有2023/12/01之後的事件都以2023/12/01取得總發行股數
reference_date = datetime.strptime('2023-12-01', '%Y-%m-%d')
date_object = datetime.strptime(date, '%Y-%m-%d') # 將日期變數轉為datetime格式
# 判斷日期是否在2023/12/01之後,若在之後以2023/12/01取得總發行股數
if date_object > reference_date:
gte = reference_date
lte = reference_date
# 若在2023/12/01之前,此資料是月頻率的資料,因此若日期不是1號,則取這個月到下個月之間的那個1號,反之則不用改變日期
else:
gte = date # 設置開始日期
if date_object.day != 1:
next_month_date = date_object + relativedelta(months=1) # 加一個月
else:
next_month_date = date_object
lte = next_month_date.strftime('%Y-%m-%d') # 設置結束日期
data_total = tejapi.get('TWN/ABSTN1', # 上市(櫃)董監全體持股狀況資料庫
coid = coid,
mdate = {'gte':gte, 'lte':lte},
opts = {'columns':['coid','fld003']}, # 總發行股數
paginate = True,
chinese_column_name = True)
return data_total
設置股價函數,資料由上市(櫃)未調整股價(日)資料庫導入。
# 取得股價函數,將證券碼、起迄日期設為變數
def data_stock_price(coid, start, end):
data_coid = tejapi.get('TWN/APRCD', # 上市(櫃)未調整股價(日)資料庫
coid = coid,
mdate = {'gte':start, 'lte': end},
paginate = True,
chinese_column_name = True,
opts = {'columns':['coid','mdate','close_d']},
)
return data_coid
考量到不同時間點,企業的總發行股數會有波動,因此設置公司碼、申報日為變數代入總發行股數函數,以得到對應時間點的總發行股數,並計算出申報股數(%)。
# 下面兩個欄位轉成list資料,以便後續帶入函數
coid = list(data_trans['公司碼'])
date = list(data_trans['申報日'])
# 設置總發行股數的dataframe
data_total = pd.DataFrame()
for x, y in zip(coid, date):
data_stock = data_total_stock(x, str(y)[0:10]) # 將證券碼、日期轉成字串取年月日帶入函數
if data_stock.size == 0: # 若沒有資料不處理
pass
else:
data_total = pd.concat([data_total, data_stock], ignore_index=True)\
data_trans = data_trans[data_trans['公司碼'] != '5261'] # 申報轉讓資料刪除上方沒有資料的證券碼(經查詢已下櫃)
data_trans.reset_index(inplace=True)
data_trans = data_trans.drop(columns='None')
# 將總發行股數併入申報轉讓資料,並新增欄位申報股數(%)
data_outstanding = [outstanding for outstanding in data_total['總股數']]
data_trans['總股數'] = data_outstanding
stock_pct = [(j/i)*100 for i, j in zip(data_trans['總股數'], data_trans['申報轉讓股數'])]
data_trans['申報股數(%)'] = stock_pct
# 取出每支股票申報股數(%)最高的資料
max_percentage_coid = data_trans.groupby('公司碼')['申報股數(%)'].idxmax()
final_data = data_trans.loc[max_percentage_coid]
綜合以上數據與相關新聞,本文歸納出以下四種結果,並取 3036、6452、3017、2311、2597、2723 為例:
我們想知道各股申報轉讓後股價與報酬率的變化,算出申報日的下一個工作日,來觀察是正向變動還是負向變動的多,又或者是股價不受影響。
# 找出申報日的下一個工作日,以利進行申報轉讓前後之對比
next_workdays = []
for c in range (len(all_data)):
next_work_day = all_data['申報日'][c] + timedelta(days=1)
next_working = str(next_work_day)[0:10]
ROC_data = data_stock_price(all_data['公司碼'][c], str(all_data['申報日'][c])[0:10], next_working)
while True:
if len(ROC_data) < 2: # 如果資料少於兩筆代表目前只抓到申報日這天的股價資料,所以日期再繼續往後抓
next_work_day += timedelta(days=1)
next_working = str(next_work_day)[0:10]
ROC_data = data_stock_price(all_data['公司碼'][c], str(all_data['申報日'][c])[0:10], next_working)
else:
break
next_workdays.append(next_working)
all_data['下一個工作日'] = next_workdays
找到下一個工作日後,設置日報酬率函數,資料由上市(櫃)股價報酬(日)-報酬率資料庫導入。
# 取得日報酬率函數,將證券碼、起迄日期設為變數
def data_rate_of_return(coid, start, end):
data_return = tejapi.get('TWN/APRCD2', # 上市(櫃)股價報酬(日)-報酬率資料庫
coid = coid,
mdate = {'gte':start, 'lte':end},
opts = {'columns':['coid','mdate','zclose','roia']},
# 證券碼、日期、收盤價、日報酬率%
paginate = True,
chinese_column_name = True)
return data_return
將申報日、下一個工作日與證券代碼代入日報酬率函數
# 將申報日、下一個工作日與證券代碼代入日報酬率函數
ROC_df = pd.DataFrame(columns=['證券代碼', '年月日', '未調整收盤價(元)', '日報酬率 %'])
for count in range (len(all_data)):
ROC_data = data_rate_of_return(all_data['公司碼'][count],
str(all_data['申報日'][count])[0:10],
str(all_data['下一個工作日'][count])[0:10])
ROC_df = pd.concat([ROC_df, ROC_data], ignore_index=True)
計算日報酬率的數量,大於 0 歸納在positive
、小於 0 歸納在negative
、等於 0 歸納在zero
# 計算正向、反向及無變動的數量
positive = 0
negative = 0
zero = 0
for i in range(1, len(ROC_df), 2):
rate = ROC_df['日報酬率 %'][i]
if rate == 0:
zero += 1
elif rate > 0:
positive += 1
elif rate < 0:
negative += 1
total = zero+positive+negative # 計算資料筆數
# 利用plotly套件畫出圓餅圖
labels = ['Zero', 'Positive', 'Negative']
values = [zero, positive, negative]
colors = ['gold', 'mediumturquoise', 'darkorange']
fig = go.Figure(data=[go.Pie(labels=labels, values=values)])
fig.update_traces(hoverinfo='label+percent+value',
textinfo='percent+value+label',
textfont_size=15,
marker=dict(colors=colors, line=dict(color='#000000', width=1.5)))
title_text = ("內部人持股轉讓後,下一個工作日股市變動情形圓餅圖,總資料筆數為{}筆" .format(total))
fig.update_layout(title_text=title_text)
fig.show()
上圖呈現的是申報日後的下一個工作日之收盤價的變動率比例圖,可以看到還是以負向變動居多,佔比將近 50%,可以直覺看出投資人在面對不確定性時第一時間較傾向將資金撤出。不過若一間公司的基本面體質優良,或是鉅額交易並沒有稀釋到股份,正向變動或者無變動也都有發生的可能。
從數據上來看,鉅額交易對股價產生的影響確實是一個值得關注的議題。我們發現內部人持股轉讓後,股價短期變動主要呈現負向趨勢,顯示市場對於這類交易存在著一定程度的擔憂與不確定性。
然而,鉅額交易並不一定對股價造成負面的影響。透過上述的分析,我們可以看到股東轉讓股票可能是因為個人的理財或信託規劃,其並不會造成企業股份的變動。另外,若公司的財報亮眼,或鉅額交易僅是企業內部的股權重新分配,股價就不會因此而下跌。儘管如此,投資人也應密切關注投資的企業對於鉅額交易的相關說明,以了解其轉讓股票的目的,若企業無法明確說明轉讓原因,加上公司營運績效不佳,或市場普遍瀰漫利空的氛圍,使股價接連下跌,即需要考慮降低持股比列或出脫股票。
本次實作透過 TEJ API 實現內部人持股轉讓分析,僅用 TEJ API 所提供的有效資料和視覺化的套件,就可以做出這樣的結果並實作自身所想像的各類型之視覺化分析,省時又省力的情況下,也能提供投資人更多元的評估指標。選購 TEJ E-Shop 的相關方案,用高品質的資料庫,建構出適合自己的分析應用或交易策略。
溫馨提醒,本次策略僅供參考,不代表任何商品或投資上的建議。
電子報訂閱