作者:許夢潔 (中山大學)
Stata 連享會:
知乎 | 簡書 | 碼雲 Note: 助教招聘資訊請進入「課程首頁」檢視。 因果推斷-内生性 專題 ⌚ 2020.11.12-15 主講:王存同 (中央财經大學);司繼春(上海對外經貿大學) 課程首頁 : https://gitee.com/arlionn/YG | 微信版
http://qr32.cn/BlTL43 (二維碼自動識别)
空間計量 專題 ⌚ 2020.12.10-13 主講:楊海生 (中山大學);範巧 (蘭州大學) 課程首頁 : https://gitee.com/arlionn/SP | 微信版
https://gitee.com/arlionn/DSGE (二維碼自動識别)
一、任務描述
對2010年後49083條上市公司股權變更資料(Firm-Event 觀測)分别統計每個事件發生前後15天公司:
- 釋出的臨時公告數
- 累計超額收益(CAR)
二、資料描述
資料集總樣本數2010年後的樣本數上市公司股權變更記錄5758449083上市公司公告記錄27870262758934上市公司日超額收益97494645534947
三、解決思路
在
Python
構造一個類似于
Excel
中的
countif函數即可。具體見我上一篇博文
[百萬級大樣本中的 countif 實作]。
四、潛在問題
雖然按照上一篇文章的思路也能基本完成任務,但是程式運作非常慢,光跑一次統計視窗期公告資料的程式就要27個小時,是以必須優化程式以提高運作速度。
五、優化思路
由于全樣本非常大,上篇博文的程式相當于是對每一個公司股東股權變動事件在全部的公告池(2758934條記錄)中進行搜尋,顯然這樣做是無效率的。
是以,此次程式優化的主要思路是分别拆分 49083 條公司股東股權變動事件和 2758934 條上市公司公告記錄,并将兩類拆分的檔案對應起來。舉個例子:
- 第一步: 将股票代碼在 000001 到 000049 的公司股權變動事件拆出來
- 第二步: 将股票代碼在000001 到 000049 的公司公告拆出來形成一個小的公告搜尋池,然後對這部分公司的股權事件在這個小搜尋池裡統計公告記錄。
通過拆分檔案得到的精确比對大大減小了每一個事件的搜尋範圍,可以大幅提高程式運作效率。優化後跑一次同樣的統計視窗期公告資料程式僅需 12 分鐘,是原來運作速度的 135 倍。
六、核心代碼(以統計視窗期CAR為例)
1. 初步拆分
為了保證運作效率最高,首先平均分拆 CAR 序列,周遊股票日超額收益序列資料,設定門檻值為 50000,每 50000 條資料拆出一個檔案,以
"CAR+編号"為檔案名 (eg:
CAR109.txt
)。最後共拆出了 110 個檔案,Python 代碼如下:
LIMIT = 50000
file_count = 0
url_list = []
with open("股票日超額收益序列.txt") as f:
for line in f:
url_list.append(line)
if len(url_list)<LIMIT:
continue
file_name = str(file_count)+".txt"
with open(file_name,'w') as file:
for url in url_list[:-1]:
file.write(url)
file.write(url_list[-1].strip())
url_list = []
file_count += 1
2. 根據分拆檔案記錄拆分股票節點
周遊拆出來的 110 個 CAR 檔案,分别記錄每個檔案最後一個觀測的股票代碼,并逐條寫入 “拆分節點.txt” 中。Python 代碼如下:
import pandas as pd
for i in range(111):
file = "CAR"+repr(i)+".txt"
data = pd.read_table(file, header=None, encoding='utf-8', delim_whitespace=True)
data.columns = ['stkcd', '日期序列', '日超額收益']
stkcdlist = data.loc[:,'stkcd']
end = str(stkcdlist[len(stkcdlist)-1])
with open("拆分節點.txt",'a') as f:
f.write(end+"n")
print(end)
f.close()
連享會計量方法專題……
3. 根據拆分節點拆分事件清單并再拆分 CAR 清單
由于初步拆分是根據樣本數拆分,是以出現了同一隻股票不同日期的 CAR 會被拆到兩個不同檔案的情況,十分不利于後面股權變更檔案與 CAR 檔案拆分後實作完美比對。
是以,需要根據第二步得到拆分節點處的股票代碼對 CAR 清單再拆分,并同時拆分股權變更清單。經過這一步後拆分後的兩種檔案就可以實作精确比對。
此外,由于事先已經對股權變更檔案以及 CAR 檔案根據股票代碼以及日期進行排過序,是以接下來的拆分隻需逐行周遊,判斷周遊到的觀測股票代碼與拆分節點處的股票代碼的關系,如果周遊處股票代碼大于目前拆分節點,則儲存一個分拆檔案,清空相關變量并開啟下一個分拆檔案。Python 代碼如下:
import pandas as pd
dataf = pd.read_table("拆分節點.txt",header=None,encoding='utf-8',delim_whitespace=True)
dataf.columns = ['節點']
LIMIT = dataf.loc[:,'節點']
file_count = 0
url_list = []
line_count = 0
#這裡的分拆檔案也可以是公司股權變更事件
with open("股票日超額收益序列.txt") as f:
for line in f:
url_list.append(line)
line_count += 1
currentstkcd = int(line.split("t")[0])
print(currentstkcd)
try:
print(LIMIT[file_count])
if (currentstkcd < LIMIT[file_count]):
continue
except:
print("已經是最後一個觀測")
file_name = "CAR"+str(file_count)+".txt"
print(file_name)
with open(file_name,'w') as file:
try:
file.write(left)
except:
pass
for url in url_list[:-1]:
file.write(url)
left = url_list[-1]
url_list = []
file_count += 1
4. 基于拆分後的事件清單和日期序列統計資料
一一對應地拆完大檔案之後就可以在縮小的搜尋範圍裡countif啦,這部分思路見上一篇博文
百萬級大樣本中的countif實作。跑完49083條資料的結果隻需要12分鐘,簡直是飛一般的感覺(`・ω・´)。
import pandas as pd
from datetime import *
timespan = timedelta(days=1)
def getlist(add):
data = pd.read_table(add,encoding='utf-8',delim_whitespace=True)
data.columns=['stkcd','日期序列','日超額收益']
return(data)
def 區間計數(股票代碼,減持日期,前置視窗長度,後置視窗長度):
減持時間戳 = datetime.strptime(減持日期,"%Y-%m-%d")
開始日期 = (減持時間戳-timespan*前置視窗長度).strftime("%Y-%m-%d")
print(開始日期)
結束日期 = (減持時間戳+timespan*後置視窗長度).strftime("%Y-%m-%d")
print(結束日期)
clist = data.loc[(data['stkcd'] == 股票代碼) & (data['日期序列'] <= 結束日期) & (data['日期序列'] >= 開始日期), '日超額收益']
區間CAR = sum(clist)
return(區間CAR)
for i in range(111):
with open("15天CAR統計結果.txt",'a') as g:
try:
f = open("事件" + repr(i)+".txt",'r')
data = getlist("CAR" +repr(i)+".txt")
lines = f.readlines()
for line in lines:
stkcd = int(line.split(',')[0].split("n")[0])
print(stkcd)
eventdate = line.split(',')[1].split("n")[0]
事件前15天CAR = 區間計數(stkcd, eventdate, 15, 0)
事件後15天CAR = 區間計數(stkcd, eventdate, 0, 15)
print([stkcd, eventdate, 事件前15天CAR, 事件後15天CAR])
g.write(','.join([repr(stkcd), eventdate, repr(事件前15天CAR), repr(事件後15天CAR)])+'n')
f.close()
except:
print("skip "+repr(i))
連享會計量方法專題……
七、統計結果樣例
股票代碼事件日期事件前15天CAR事件後15天CAR事件前15天公告數事件後15天公告數22014-03-210.1928260.0639818622014-08-29-0.057021-0.03309719622014-09-16-0.031721-0.03163561622015-01-24-0.010155-0.10772231322015-01-28-0.069575-0.04520161022015-07-110.356788-0.126676151622015-07-25-0.1925250.0095171222015-08-040.019329-0.12050872922015-08-270.142061-0.048584221122015-12-070.2429670.221147111622015-12-090.3533910.276527121522015-12-160.2687260.124451172622016-07-07-0.27522-0.133624461822016-08-050.2940570.1905699822016-08-090.2950280.076121715
連享會計量方法專題…… 關于我們
- Stata連享會 由中山大學連玉君老師團隊創辦,定期分享實證分析經驗。
- 歡迎賜稿: 歡迎賜稿至[email protected]。錄用稿件達 三篇 以上,即可 免費 獲得一期 Stata 現場教育訓練資格。
- 往期精彩推文: Stata繪圖 | 時間序列+面闆資料 | Stata資源 | 資料處理+程式 | 回歸分析-交乘項-内生性