險警示數(shù)據(jù)清洗:ST/*ST/PT標(biāo)簽的日度面板構(gòu)建與Stata實現(xiàn))
1. 項目概述為什么我們需要一份干凈的A股風(fēng)險警示標(biāo)簽數(shù)據(jù)如果你在A股市場做量化研究、因子挖掘或者公司財務(wù)分析有一個問題幾乎無法回避如何處理那些被貼上“ST”、“*ST”甚至更早的“PT”標(biāo)簽的股票這些標(biāo)簽是交易所對存在特定風(fēng)險比如連續(xù)虧損、財務(wù)造假、經(jīng)營異常的上市公司施加的特殊處理標(biāo)識。簡單來說它們就像是股票代碼上的“黃牌”或“紅牌”。在構(gòu)建投資組合、計算市場指數(shù)或者進行學(xué)術(shù)研究時如果不把這些“問題股”剔除你的回測結(jié)果可能會被嚴(yán)重扭曲。想象一下你設(shè)計了一個基于盈利能力的選股策略結(jié)果里面混入了一堆因為連續(xù)虧損而被ST的公司策略的有效性從源頭上就打了折扣。然而獲取一份準(zhǔn)確、完整且易于使用的A股風(fēng)險警示歷史數(shù)據(jù)并不是一件簡單的事。交易所的公告是分散的數(shù)據(jù)格式不一手動整理費時費力且容易出錯。更麻煩的是ST狀態(tài)是動態(tài)變化的一家公司今年被ST明年可能因為情況好轉(zhuǎn)而“摘帽”也可能情況惡化變成*ST甚至最終退市。這就需要一份按時間序列記錄每家上市公司風(fēng)險警示狀態(tài)的數(shù)據(jù)面板。這正是本項目要解決的問題整理一份從A股市場誕生至今所有上市公司是否被ST、*ST或PT的標(biāo)識數(shù)據(jù)并附上清晰、可復(fù)現(xiàn)的Stata整理代碼數(shù)據(jù)更新至2023年。有了這套“工具包”研究者可以一鍵篩選出任意時間點上的“正常股”確保分析基礎(chǔ)的潔凈。2. 核心需求解析與數(shù)據(jù)來源設(shè)計2.1 明確“ST/*ST/PT”的定義與影響首先我們必須厘清這幾個標(biāo)簽的具體含義和演變這是數(shù)據(jù)準(zhǔn)確性的基石。PT (Particular Transfer特別轉(zhuǎn)讓)這是歷史產(chǎn)物主要在2000年代初用于暫停上市的公司。這類股票只能在每周五進行集合競價轉(zhuǎn)讓流動性極差。隨著退市制度的完善PT制度已基本退出舞臺但在整理早期歷史數(shù)據(jù)時必須包含。ST (Special Treatment特別處理)這是當(dāng)前最常見的風(fēng)險警示。公司被ST通常因為財務(wù)狀況異常例如最近兩個會計年度凈利潤為負(fù)或最近一年凈資產(chǎn)為負(fù)。被ST后股票日漲跌幅限制從10%縮小為5%意在提示風(fēng)險。*ST (退市風(fēng)險警示)在ST基礎(chǔ)上風(fēng)險進一步升級。通常因為情況比ST更嚴(yán)重例如連續(xù)三年虧損或存在重大違法強制退市情形。*ST是退市前的最后一道警示漲跌幅同樣為5%。對于研究而言這些股票通常需要被排除原因有三1)財務(wù)異常其財務(wù)數(shù)據(jù)本身已失真或處于非正常狀態(tài)不具可比性2)交易規(guī)則異化漲跌幅限制不同影響收益率計算的公平性3)樣本污染它們往往伴隨極端的價格波動連續(xù)跌停或漲停會嚴(yán)重干擾因子有效性檢驗和市場收益率計算。2.2 數(shù)據(jù)來源的選取與挑戰(zhàn)整理這類數(shù)據(jù)可靠的數(shù)據(jù)源是關(guān)鍵。通常有三個主要渠道交易所官方公告最權(quán)威的來源。上海證券交易所和深圳證券交易所會發(fā)布關(guān)于對上市公司實施ST、*ST及撤銷處理的公告。但問題在于公告是文本格式且歷史公告的獲取和解析爬蟲工作量巨大需要處理PDF、HTML等多種格式并準(zhǔn)確提取公司代碼、全稱、實施日期等關(guān)鍵信息。專業(yè)金融數(shù)據(jù)終端如Wind、Choice、iFinD等。它們有結(jié)構(gòu)化的風(fēng)險警示歷史數(shù)據(jù)但屬于付費商業(yè)數(shù)據(jù)且直接導(dǎo)出可能無法滿足個性化的時間頻率如日度或字段需求。開源數(shù)據(jù)社區(qū)與學(xué)術(shù)數(shù)據(jù)庫如CNRDS、CSMAR等學(xué)術(shù)數(shù)據(jù)庫或GitHub上的一些開源項目。這些數(shù)據(jù)質(zhì)量參差不齊需要仔細核對但通常是研究者尤其是學(xué)生和獨立研究者性價比最高的起點。本項目的設(shè)計思路是以開源/學(xué)術(shù)數(shù)據(jù)為基礎(chǔ)通過交叉驗證和邏輯規(guī)則清洗構(gòu)建高質(zhì)量面板數(shù)據(jù)。具體而言我們可以從CSMAR或CNRDS獲取上市公司基本資料表中的“是否ST/PT”字段作為基礎(chǔ)但這通常是季度或年度數(shù)據(jù)且可能存在缺失或錯誤。然后我們需要結(jié)合交易所公告日期可從網(wǎng)絡(luò)爬取或利用現(xiàn)有整理好的公告日期列表進行精細化處理將狀態(tài)變更精確到“日”級別并編寫邏輯規(guī)則如“摘帽”后狀態(tài)應(yīng)歸為正常來修正數(shù)據(jù)矛盾。注意數(shù)據(jù)源的任何微小錯誤都可能導(dǎo)致最終結(jié)果出現(xiàn)系統(tǒng)性偏差。例如如果漏掉了一家公司在某段時間的ST狀態(tài)那么在回測中就會錯誤地將其納入樣本。因此在代碼中必須設(shè)置多重校驗和人工抽查環(huán)節(jié)。3. 數(shù)據(jù)整理的核心步驟與Stata實現(xiàn)3.1 數(shù)據(jù)準(zhǔn)備與初步清洗假設(shè)我們已經(jīng)從CSMAR數(shù)據(jù)庫下載了“中國上市公司基本信息表”表名通常如Company_Background其中包含字段Stkcd股票代碼、Listdt上市日期、Enddt退市日期、State上市狀態(tài)以及我們最關(guān)心的RiskWarning風(fēng)險警示類型可能用代碼表示如‘ST’ ‘*ST’ ‘S’等不同數(shù)據(jù)庫編碼方式不同。第一步是在Stata中導(dǎo)入并理解數(shù)據(jù)結(jié)構(gòu)。* 假設(shè)數(shù)據(jù)已保存為company_info.dta use path/to/company_info.dta, clear * 查看變量標(biāo)簽和內(nèi)容 describe codebook RiskWarning, compact tab RiskWarning這一步的目的是確認(rèn)RiskWarning字段的編碼規(guī)則。例如可能發(fā)現(xiàn)“1”代表正?!?”代表ST“3”代表*ST“4”代表PT或者直接用字符串‘ST’、‘*ST’表示。我們需要將其統(tǒng)一轉(zhuǎn)換為便于理解的分類變量。* 示例根據(jù)編碼創(chuàng)建新的分類變量 gen risk_type replace risk_type 正常 if RiskWarning 1 replace risk_type ST if RiskWarning 2 | RiskWarning ST replace risk_type *ST if RiskWarning 3 | RiskWarning *ST replace risk_type PT if RiskWarning 4 | RiskWarning PT replace risk_type 其他 if missing(risk_type) !missing(RiskWarning) label var risk_type 風(fēng)險警示類型 * 檢查轉(zhuǎn)換結(jié)果 tab risk_type但這里有一個核心問題數(shù)據(jù)庫中的這個字段通常是報告期如年報、季報的截面狀態(tài)。我們需要的是日度面板數(shù)據(jù)即對于每一天每只股票都有一個風(fēng)險警示狀態(tài)。這就需要進行時間序列的擴展和插值。3.2 構(gòu)建日度風(fēng)險警示狀態(tài)面板構(gòu)建日度面板的核心邏輯是“狀態(tài)持續(xù)假設(shè)”即一家公司的風(fēng)險警示狀態(tài)從生效日開始持續(xù)到下一次狀態(tài)變更日或退市日的前一天。首先我們需要一份A股全市場所有股票、所有交易日的日期面板作為骨架。* 生成一個包含所有交易日期的文件trading_dates.dta可從行情數(shù)據(jù)中提取唯一日期獲得 use path/to/all_stock_daily_prices.dta, clear keep date duplicates drop date, force sort date save trading_dates.dta, replace然后我們需要一份記錄了每家股票風(fēng)險警示狀態(tài)變更事件的數(shù)據(jù)。理想情況下這份數(shù)據(jù)應(yīng)包含Stkcd股票代碼、change_date狀態(tài)變更生效日、new_risk_type變更后的新狀態(tài)。這份數(shù)據(jù)可以通過解析交易所公告獲得是整理工作的核心難點和價值所在。假設(shè)我們已經(jīng)通過爬蟲和人工核對得到了一份相對干凈的變更事件表risk_change_events.dta。* 步驟1為每只股票創(chuàng)建其生命周期的日期序列 use company_info.dta, clear keep Stkcd Listdt Enddt expand (Enddt - Listdt 1) // 假設(shè)Enddt存在若未退市則為最新日期 bysort Stkcd: gen date Listdt _n - 1 format date %td keep if dow(date)!0 dow(date)!6 // 粗略剔除周末更精確應(yīng)用交易日歷 save stock_daily_skeleton.dta, replace * 步驟2與狀態(tài)變更事件表合并 merge m:1 Stkcd date using risk_change_events.dta, keepusing(new_risk_type) rename new_risk_type risk_type_event接下來是關(guān)鍵的一步使用Stata的carryforward命令或fillin 排序邏輯來填充狀態(tài)。我們假設(shè)在第一次狀態(tài)變更前股票狀態(tài)為“正?!?。* 步驟3填充狀態(tài) bysort Stkcd (date): gen risk_type_daily risk_type_event * 對于每個股票將非缺失值向前填充locf, last observation carried forward bysort Stkcd (date): replace risk_type_daily risk_type_daily[_n-1] if missing(risk_type_daily) _n1 * 處理初始狀態(tài)上市首日到第一次事件日之間 bysort Stkcd (date): replace risk_type_daily 正常 if missing(risk_type_daily) _n1然而現(xiàn)實情況往往更復(fù)雜。變更事件表可能有缺失或者數(shù)據(jù)庫中的季度狀態(tài)數(shù)據(jù)與事件日期不完全匹配。這時就需要引入第二數(shù)據(jù)源進行交叉驗證與沖突解決。3.3 多源數(shù)據(jù)交叉驗證與沖突解決我們可以引入另一個數(shù)據(jù)源比如從Wind導(dǎo)出的月度ST狀態(tài)列表。將其處理成與我們的日度面板相同格式Stkcd,date,risk_type_wind然后進行合并比對。merge 1:1 Stkcd date using wind_risk_monthly.dta, keepusing(risk_type_wind)合并后risk_type_daily來自事件填充和risk_type_wind來自Wind月度之間可能出現(xiàn)不一致。我們需要制定一套沖突解決規(guī)則兩者一致直接采納。*Wind顯示為ST/ST而事件填充為正常這可能是我們的事件表漏記了。需要以Wind數(shù)據(jù)為線索回溯查找該時間段內(nèi)的交易所公告進行核實。在代碼中我們可以先將其標(biāo)記為“待核實”并輸出到日志文件。*事件填充為ST/ST而Wind顯示為正常同樣需要核實。有時Wind的數(shù)據(jù)更新存在滯后事件數(shù)據(jù)可能更及時。兩者均為缺失通常意味著該日股票狀態(tài)為正常。在Stata中我們可以這樣實現(xiàn)一個簡單的沖突標(biāo)記邏輯gen conflict_flag 0 gen risk_type_final risk_type_daily * 規(guī)則當(dāng)Wind數(shù)據(jù)非缺失且與當(dāng)前填充數(shù)據(jù)不同時標(biāo)記沖突并優(yōu)先采用更嚴(yán)格的警示狀態(tài)即*ST ST 正常 replace conflict_flag 1 if !missing(risk_type_wind) risk_type_wind ! risk_type_daily * 定義一個狀態(tài)嚴(yán)重程度的數(shù)值 gen severity 0 replace severity 1 if risk_type_daily ST replace severity 2 if risk_type_daily *ST replace severity 3 if risk_type_daily PT gen severity_wind 0 replace severity_wind 1 if risk_type_wind ST replace severity_wind 2 if risk_type_wind *ST replace severity_wind 3 if risk_type_wind PT * 沖突時取嚴(yán)重程度更高的狀態(tài) replace risk_type_final risk_type_wind if conflict_flag 1 severity_wind severity處理完沖突后我們得到的就是一份初步的、經(jīng)過校驗的日度風(fēng)險警示狀態(tài)面板risk_panel_preliminary.dta。4. 數(shù)據(jù)質(zhì)量檢查與常見問題處理4.1 邏輯一致性檢查數(shù)據(jù)整理出來后必須進行嚴(yán)格的邏輯檢查以下是一些必做的檢查項及其Stata實現(xiàn)狀態(tài)連續(xù)性檢查同一只股票其風(fēng)險警示狀態(tài)的變化應(yīng)符合“正常-ST-ST-退市”或“正常-PT”的基本路徑不應(yīng)出現(xiàn)跳躍如直接從正常變?yōu)镾T雖然罕見但需核查或反復(fù)橫跳。bysort Stkcd (date): gen prev_type risk_type_final[_n-1] gen illogical_change 0 * 例如檢查是否出現(xiàn)“正常”直接變“*ST”跳過ST通常需要結(jié)合具體規(guī)則這里僅示例 replace illogical_change 1 if risk_type_final*ST prev_type正常 list Stkcd date prev_type risk_type_final if illogical_change 1與退市狀態(tài)的兼容性檢查已經(jīng)退市的股票在退市日期之后不應(yīng)再有任何狀態(tài)記錄。同時在退市前其狀態(tài)很可能就是*ST。merge m:1 Stkcd using “company_info.dta” keepusing(Enddt) gen after_delist date Enddt !missing(Enddt) count if after_delist 1 !missing(risk_type_final) * 如果計數(shù)0說明退市后還有數(shù)據(jù)需要清理關(guān)鍵日期的驗證隨機抽取若干次知名的ST/*ST事件例如某些財務(wù)造假大案的公司核對我們的數(shù)據(jù)中狀態(tài)變更的日期是否與公開報道的生效日一致。4.2 缺失值與邊界情況處理上市初期新股上市初期不可能被ST。我們的數(shù)據(jù)在上市日期到首次財報披露日之間應(yīng)強制設(shè)為“正?!薄iL時間停牌期間股票停牌時其風(fēng)險警示狀態(tài)是凍結(jié)的。我們的日度面板在停牌日依然保留其狀態(tài)是沒問題的因為我們需要的是日歷日的狀態(tài)標(biāo)識。但需注意在計算收益率等需要交易數(shù)據(jù)的指標(biāo)時這些日期自然會被剔除。B股、科創(chuàng)板、創(chuàng)業(yè)板等特殊板塊ST規(guī)則基本一致但科創(chuàng)板、創(chuàng)業(yè)板試點注冊制后退市流程有所變化。數(shù)據(jù)源需要覆蓋全市場代碼處理邏輯上通常無需特別區(qū)分但需知曉規(guī)則背景。4.3 生成最終易用的指標(biāo)對于大多數(shù)研究而言我們最終需要的往往不是一個字符串分類變量而是一個或多個簡潔的二進制0/1指標(biāo)便于在回歸或篩選時直接使用。* 生成最常用的“是否被風(fēng)險警示”指標(biāo) gen is_risk_warning (inlist(risk_type_final, ST, *ST, PT)) label var is_risk_warning 是否被ST/*ST/PT (1是) * 有時需要區(qū)分ST和*ST gen is_ST (risk_type_final ST) gen is_STstar (risk_type_final *ST) label var is_ST 是否為ST (1是) label var is_STstar 是否為*ST (1是) * 保存最終數(shù)據(jù)集 keep Stkcd date is_risk_warning is_ST is_STstar risk_type_final order Stkcd date sort Stkcd date save A_share_ST_data_2023.dta, replace最終的數(shù)據(jù)集應(yīng)包含股票代碼、日期、以及幾個核心的指示變量。文件不宜過大可以通過compress命令優(yōu)化存儲。5. Stata高級技巧效率優(yōu)化與批量處理當(dāng)處理全市場、長達三十多年的日度數(shù)據(jù)時數(shù)據(jù)量可能達到千萬行級別。原始的循環(huán)操作會極其緩慢。必須運用Stata的向量化操作和高效命令。避免循環(huán)如上文所示使用bysort配合_n,_N以及replace ... if ...的向量化操作效率遠高于forvalues循環(huán)。使用joinby替代多層嵌套循環(huán)在創(chuàng)建股票-日期骨架時如果股票數(shù)量多expand日期差的方法可能內(nèi)存消耗大。另一種更高效的方法是使用joinby。* 方法二使用joinby (更高效處理大量股票) use “company_info.dta” clear keep Stkcd Listdt Enddt cross using “trading_dates.dta” keep if date Listdt (date Enddt | missing(Enddt))合理使用preserve/restore和臨時文件在復(fù)雜的多步驟清洗中將中間結(jié)果保存為臨時文件tempfile可以釋放內(nèi)存并使流程更清晰。tempfile skeleton events merged preliminary final save skeleton ... merge 1:1 Stkcd date using events save merged利用collapse快速匯總統(tǒng)計如果你想快速了解每年被ST的公司數(shù)量collapse命令是神器。use “A_share_ST_data_2023.dta” clear gen year year(date) collapse (sum) count_ST is_ST count_STstar is_STstar, by(year) line count_ST count_STstar year, title(“A股歷年ST與*ST公司數(shù)量”)這正是熱詞中“stata的collapse怎么畫圖”的一個典型應(yīng)用場景先collapse匯總再使用twoway line等繪圖命令進行可視化。6. 常見問題排查與實戰(zhàn)心得在實際操作中你一定會遇到各種報錯和數(shù)據(jù)異常。以下是一些典型問題的排查思路問題merge命令后觀測值數(shù)量異常增多遠多于預(yù)期。原因最可能是合并鍵Stkcd和date不唯一。使用duplicates report Stkcd date分別檢查主表和using表。解決在合并前務(wù)必確保兩個數(shù)據(jù)集中Stkcd和date的組合是唯一的。如果有重復(fù)需要根據(jù)業(yè)務(wù)邏輯決定是去重duplicates drop還是匯總。問題使用bysort和_n-1向前填充時狀態(tài)沒有正確延續(xù)。原因數(shù)據(jù)沒有嚴(yán)格按照Stkcd和date排序。bysort默認(rèn)升序排列如果日期順序混亂填充就會出錯。解決在執(zhí)行bysort Stkcd (date): ...之前先用sort Stkcd date或isid Stkcd date確保排序正確。isid命令可以同時檢查是否唯一且已排序。問題從數(shù)據(jù)庫導(dǎo)出的RiskWarning字段是字符串但包含空格、換行符等不可見字符導(dǎo)致判斷失效。原因原始數(shù)據(jù)清洗不徹底。解決使用replace RiskWarning strtrim(stritrim(RiskWarning))去除首尾和中間多余空格。對于更復(fù)雜的字符可以用subinstr()函數(shù)替換。問題最終數(shù)據(jù)集中某些股票在已知的被ST期間is_risk_warning指標(biāo)仍為0。排查檢查原始事件表中該股票的ST生效日期是否正確錄入。檢查該股票在ST生效日是否處于上市狀態(tài)是否已退市或尚未上市。檢查沖突解決規(guī)則是否錯誤地覆蓋了正確的事件數(shù)據(jù)。心得建立一個“已知案例測試集”。手動整理10-20家歷史上典型ST公司的關(guān)鍵日期和狀態(tài)在每一步數(shù)據(jù)處理后都篩選出這些公司進行比對這是驗證數(shù)據(jù)流水線是否可靠的最直接方法。關(guān)于Stata版本與命令熱詞中提到了Stata 18。新版本通常有性能提升和新功能。例如frame命令可以更方便地管理多個數(shù)據(jù)集。但核心的數(shù)據(jù)處理邏輯merge,bysort,replace是通用的。對于“亞組分析”、“莫蘭指數(shù)”、“核密度估計”等那是數(shù)據(jù)準(zhǔn)備好之后的分析階段與本項目的數(shù)據(jù)整理階段是上下游關(guān)系。本項目產(chǎn)出的潔凈數(shù)據(jù)正是為這些高級分析奠定可靠的基礎(chǔ)。最后數(shù)據(jù)整理工作沒有絕對的“完成”只有不斷的“迭代”。交易所的規(guī)則在微調(diào)數(shù)據(jù)源也可能變化。因此將整個清洗流程封裝成清晰的、模塊化的Do文件至關(guān)重要。當(dāng)需要更新到2024年數(shù)據(jù)時你只需要替換最新的原始數(shù)據(jù)文件重新運行整個Do文件就能高效地獲得更新后的數(shù)據(jù)集。這套方法的價值不僅在于一份現(xiàn)成的數(shù)據(jù)更在于一套可復(fù)現(xiàn)、可審計、可持續(xù)更新的數(shù)據(jù)生產(chǎn)流程。