股票數據清洗與日K轉換周月年K工具 - 完整功能解析-1

更新 發佈閱讀 29 分鐘
投資理財內容聲明


📋 程式概述

這是一個企業級股票數據清洗與時間週期轉換系統,專門處理日 K 線數據並轉換為週/月/年 K 線,同時進行多層次的數據品質控管與異常偵測。程式採用玩股網口徑標準,確保數據品質符合量化交易需求。


🎯 核心功能架構

1. 數據來源與處理範圍

  • 時間範圍:2000-01-01 ~ 2025-12-31(可調整)
  • 緩衝機制:起始日前保留 14 天數據,確保第一期有前收盤價可計算
  • 市場支援:台股、美股、港股、日股、韓股、陸股
  • 處理模式:3 種寫入模式(本地 → Drive 移動/複製/直接寫入)

🧹 數據清洗機制(12 大類別)

一、基礎價量檢查 (_basic_price_checks)

| 檢查項目     | 條件說明                                       | 目的與備註                     |
|--------------|------------------------------------------------|-------------------------------|
| 價格正值 |///> 0 | 排除負值或零價格 |
| OHLC 邏輯 | 最高 ≥ 開 //<br>最低 ≤ 開 //| 確保價格關係合理 |
| 成交量(可選)| 成交量 > 0(預設允許零量,可配置) | 避免停牌或無效資料 |
| 日期有效性 | 年份 ≥ 1990 | 排除異常年份 |
| 時區處理 | 統一移除時區(tz_localize(None)) | 避免跨時區計算錯誤 |


# 範例:排除不合理價格
df = df[(df['最高'] >= df[['開盤','收盤','最低']].max(axis=1))]

二、極端報酬過濾 (_extreme_return_filter_day)

🚨 日 K 極端值門檻

| 報酬類型         | 門檻值 | 計算公式             | 說明                       |
|------------------|--------|----------------------|----------------------------|
| Ret_Gap | ±100% | 開盤 / 前收 - 1 | 跳空報酬(Gap) |
| Ret_Candle | ±100% | 收盤 / 開盤 - 1 | 當日漲跌(Candle) |
| Ret_Max_FromP | ±100% | 最高 / 前收 - 1 | 最高價相對前收報酬 |
| Ret_Min_FromP | ±100% | 最低 / 前收 - 1 | 最低價相對前收報酬 |

過濾邏輯:

# 任一報酬 > 100% 即視為異常(可能是除權、拆股未調整)
mask_ok = (
(ret_gap.abs() < 100) &
(ret_candle.abs() < 100) &
(ret_max_p.abs() < 100) &
(ret_min_p.abs() < 100)
)

三、Ghost 數據偵測與刪除

什麼是 Ghost 數據?

  • 定義:四價相等(開=高=低=收)且成交量為 0 的占位數據
  • 來源:交易所在停牌期間填充的假數據
  • 處理:自動識別並刪除

Ghost 段落偵測規則

| 參數名稱       | 設定值  | 說明                                       |
|----------------|---------|--------------------------------------------|
| GHOST_MIN_LEN | 2| 連續至少 2 天 ghost 才算占位段 |
| EPS_EQ | 1e-8 | 四價相等容許誤差(開高低收差距 ≤ 1e-8|


# Ghost 判定邏輯
ghost_mask = (成交量 == 0) & (===)

四、停牌/復牌偵測 (_mark_resume_flags_with_ghost)

🎯 復牌標記條件(3 層偵測)

條件 A:長期停牌後復牌

停牌間隔 ≥ 5 天 且 開盤跳空 ≥ 30%

條件 B:Ghost 段落前極端跳空

Ghost 連續 ≥ 2 天 且 Ghost 前 5 天內有跳空 ≥ 30%

條件 C:極端日報酬(新增強化)

日報酬絕對值 > 100%(股價翻倍或腰斬)

參數配置:

| 參數名稱              | 設定值   | 說明                                 |
|-----------------------|----------|--------------------------------------|
| LONG_GAP_DAYS | 5| 停牌判定天數(與前一筆相隔 ≥ 5 天) |
| RESUME_JUMP_THRESHOLD | 30% | 復牌跳空門檻(開盤/前收 ±30%|
| PRE_RESUME_DAYS | 5| Ghost 段前回溯天數 |
| DAILY_JUMP_THRESHOLD | 100% | 極端日報酬門檻(收盤/前收 ±100%|


五、週 K 過濾機制(3 種規則)

規則 1:玩股網口徑(上週無交易 + 大跳空)


條件:上週交易天數 = 0 且 本週跳空 > 80%
結果:本週報酬設為 NaN

應用場景: 長假後復牌、IPO 首週

規則 2:復牌事件極端值過濾

| 條件類型         | 判斷邏輯說明                                               | 過濾目的                     |
|------------------|------------------------------------------------------------|------------------------------|
| 極端事件當週 | HasResume = 1`Ret_Trad` 絕對值 > 100% | 排除復牌週的極端報酬干擾 |
| 極端事件次週 | 上週為極端事件 → 本週一併排除 | 避免連續干擾與統計偏誤 |
| 薄樣本極端值 | HasResume = 1 且(週內交易日數 < 3`Ret_Trad` > 50%| 排除復牌後樣本不足的異常週期 |

規則 3:報酬異常值處理


# 週報酬過濾後設為 NaN
cols_to_nan = ['Ret_Trad_W', 'Ret_Max_H_W', 'Ret_Min_L_W']

六、月/年 K 穩健過濾(3 大條件)

條件 A:覆蓋率不足(新掛牌/殘年)

| 週期類型 | 最低交易日數 | 說明                                       |
|----------|---------------|--------------------------------------------|
|K | 5| 若低於 5 天,視為樣本不足,不納入統計 |
|K | 120| 約半年交易日,避免新股首年干擾 |

條件 B:停牌/復牌極端值

過濾邏輯:
HasResume = 1 (
週期交易日 < 3
|Ret_Trad| > 50%
|Ret_Gap| > 50%
)

條件 C:上期無交易 + 本期大跳空

上期交易日 = 0|Ret_Gap| > 80%

QA 追蹤機制:

  • 月/年 K 新增 IsFiltered_QA 欄位
  • IsFiltered_QA = 1 表示該行報酬被過濾為 NaN
  • 自動輸出過濾清單到 _qa/monthly_filtered_summary.csv

七、Ping-Pong 除權錯位偵測

什麼是 Ping-Pong 模式?

連續兩天出現大幅反向波動,可能是除權日期標記錯誤。

偵測規則


條件:
前一日漲跌幅 > 40%
次一日漲跌幅 > 40%
兩日方向相反(一漲一跌)

處理方式:

  • 偵測到 Ping-Pong 模式 → 跳過該股票
  • 輸出疑似清單到 _qa/skip_pingpong.csv


# 範例:2330 在某日出現 +45%-43%
異常日期: 2024-03-15
前日收盤: 100 → 當日收盤: 145 → 次日收盤: 82.65

八、時區邊界修正(月份歸屬錯誤)

🐛 問題案例

修正前:

日期: 2024-09-01 08:00 (UTC+8)
轉換後被歸入: 8

修正後:


# 統一處理時區
df['日期'] = pd.to_datetime(df['日期'])
if df['日期'].dt.tz is not None:
df['日期'] = df['日期'].dt.tz_convert('Asia/Taipei').dt.tz_localize(None)

影響範圍: 月/年 K 聚合、週末日判定


📊 數據轉換功能

1. OHLC 聚合規則

週 K(W-FRI)


規則:以「週五」為週末日(與玩股網一致)
開盤:週內第一個交易日的開盤價
最高:週內最高價
最低:週內最低價
收盤:週內最後一個交易日的收盤價
成交量:週內成交量加總

月 K(ME)/ 年 K(YE)


規則:以月末/年末為結算日
聚合邏輯:同週 K
額外欄位:
- CurPeriod_Days: 本期交易日數
- PrevPeriod_Days: 上期交易日數
- HasResume: 本期是否發生復牌事件

2. 報酬率計算(12 種指標)


| 指標名稱         | 計算公式                     | 說明                         |
|------------------|------------------------------|------------------------------|
| Ret_Gap | (開盤 / 前收) - 1 | 跳空報酬 |
| Ret_Trad | (收盤 - 前收) / 前收 | 交易報酬(完整週期) |
| Ret_C | (收盤 - 開盤) / 開盤 | 盤中報酬 |
| Ret_Max_H | (最高 - 前收) / 前收 | 最大報酬 |
| Ret_Min_L | (最低 - 前收) / 前收 | 最小報酬 |
| Range | (最高 - 最低) / 開盤 | 振幅 |
| Ret_Max_H_Pos | Ret_Max_H.clip(lower=0) | 正向最大報酬(負值設為 0|
| Ret_H_from_O | (最高 - 開盤) / 開盤 | 最高相對開盤 |
| Ret_L_from_O | (最低 - 開盤) / 開盤 | 最低相對開盤 |
| Ret_End_RelH | (收盤 - 最高) / 最高 | 收盤相對最高 |
| Ret_End_RelL | (收盤 - 最低) / 最低 | 收盤相對最低 |
| PrevC | 前一期收盤價 | 基準價格(報酬計算依據) |

🔍 QA 品質稽核(7 大報表)

QA 1:週收盤報酬分布分析

檔案: weekly_distribution_summary.csv


功能:
- 將週報酬分 25 個區間(-1000% ~ +10000%
- 統計每週各區間股票數量占比
- 滾動 26 週計算 Z-Score

異常警報觸發條件:

  • 任一區間 Z-Score > 5
  • 極端尾部(±100%)Z-Score > 3

QA 2:週報酬漂移警報

檔案: weekly_drift_alerts.csv


偵測邏輯:
- 當週分布相對過去 26 週基準線偏離過大
- 極端尾部占比異常增加

輸出範例:


ISO_Week,reason,max_abs_z
2024-12,distribution_drift,7.3
2024-25,distribution_drift,5.8

QA 3:週 vs 日幾何連乘驗證

檔案: weekly_vs_daily_diff.csv


驗證邏輯:
週報酬 (Ret_Trad_W) vs. 週內日報酬幾何連乘
diff = geom_chain - Ret_Trad_W

品質指標:

  • 中位數差異 < 0.001(0.1%)
  • |diff| > 0.05 的比例 < 1%

QA 4:週高一致性檢查

檔案: weekly_high_mismatch.csv


檢查項目:
K最高價 vs. 週內日K最高價的最大值

異常條件:
|K最高 - 週內日高max| > 1e-6

QA 5:週高報酬厚尾分析

檔案: weekly_top_outliers_high.csv


功能:
- 分析 Ret_Max_H_W(最高相對前收)的極端值
- 每週輸出前 200 大離群值
- 標記可能的數據錯誤

QA 6:週 K 過濾統計

檔案: weekly_nan_filtered_summary.csv


統計項目:
- 有多少週報酬被過濾為 NaN
- 每檔股票被過濾的週數

輸出範例:
StockID,Filtered_Count
2330,3
2454,5

QA 7:月/年 K 過濾統計

檔案:

  • monthly_filtered_summary.csv
  • yearly_filtered_summary.csv


統計項目:
- 基於 IsFiltered_QA = 1 標記
- 每檔股票被過濾的期數

過濾原因:
1. 覆蓋率不足(交易日 < 門檻)
2. 停牌/復牌極端值
3. 上期無交易 + 本期大跳空

⚙️ 性能優化設計

1. 多執行緒並行處理


參數:MAX_WORKERS = 16
策略:每個 CSV 檔案獨立處理
加速效果:處理 2000 檔股票約 3-5 分鐘

2. 本地快取機制


流程:
1. Drive dayK → 本地 /content/_wmy_tmp/dayK_cache
2. 增量複製(只複製新增/修改檔案)
3. 處理完成後可保留快取

優勢:

  • 避免重複下載(節省 70% 時間)
  • 網路不穩時有備份
  • 支援離線重新處理

3. 批次合併寫入


SHARD_SIZE = 10,000  # 每批處理 10,000
ROW_GROUP_SIZE = 512,000 # Parquet 行組大小

效果:

  • 減少磁碟 I/O 次數
  • 降低記憶體峰值
  • 提升 Parquet 壓縮率

4. 串流寫入 Parquet


class ParquetStreamer:
# 邊處理邊寫入,不需累積全部數據到記憶體
def append_df(self, df):
self.writer.write_table(table, row_group_size=...)

📁 輸出檔案結構

各國股票檔案/
├── tw-share/
│ ├── dayK/ # 原始日K CSV
│ ├── weekK_TW.parquet # 週K (帶 ISO_Week)
│ ├── monthK_TW.parquet # 月K (帶 IsFiltered_QA)
│ ├── yearK_TW.parquet # 年K (帶 IsFiltered_QA)
│ └── _qa/ # QA 報表資料夾
│ ├── skip_pingpong.csv # Ping-Pong 略過清單
│ ├── weekly_distribution_summary.csv
│ ├── weekly_drift_alerts.csv
│ ├── weekly_top_outliers.csv
│ ├── weekly_vs_daily_diff.csv
│ ├── weekly_high_mismatch.csv
│ ├── weekly_distribution_summary_high.csv
│ ├── weekly_top_outliers_high.csv
│ ├── weekly_nan_filtered_summary.csv
│ ├── monthly_filtered_summary.csv
│ └── yearly_filtered_summary.csv

🎓 適用場景

1. 量化交易回測


# 使用清洗後的週K進行策略回測
weekK = pd.read_parquet('weekK_TW.parquet')
# Ret_Trad_W 已過濾極端值,可直接用於計算策略報酬

2. 風險控管


# 識別高風險標的
high_risk = weekK[weekK['HasResume'] > 0]
# 避開停牌復牌標的

3. 數據品質監控


# 定期檢查 QA 報表
alerts = pd.read_csv('_qa/weekly_drift_alerts.csv')
if len(alerts) > 0:
send_email_alert(alerts)

4. 學術研究


# 排除異常數據的實證研究
clean_data = monthK[monthK['IsFiltered_QA'] == 0]

🔧 關鍵參數配置表


| 類別         | 參數名稱               | 預設值        | 調整建議或說明                              |
|--------------|------------------------|---------------|---------------------------------------------|
| 時間範圍 | DATE_START | 2000-01-01 | 可依需求調整起始日期 |
| | DATE_END | 2025-12-31 | 建議設為最新資料日期 |
| | PAD_DAYS | 14 | 起始緩衝,建議 730|
| 極端報酬 | EXTREME_RET_GAP | 100% | 台股:50100%;美股:100200% |
| | DAILY_JUMP_THRESHOLD | 100% | 復牌極端報酬門檻 |
| 停牌偵測 | LONG_GAP_DAYS | 5 | 停牌判定天數(與前一筆相隔 ≥ 5 天) |
| | RESUME_JUMP_THRESHOLD | 30% | 復牌跳空門檻(開盤/前收 ±30%|
|K過濾 | STOPWEEK_JUMP_THRESHOLD| 80% | 玩股口徑門檻(跳空 ±80%|
| | BIG_RET_THRESHOLD | 50% | 復牌薄樣本門檻(報酬 ±50%|
|/K過濾 | MIN_DAYS_MONTH | 5 |K最低交易日數 |
| | MIN_DAYS_YEAR | 120 |K最低交易日數(約半年) |
| | BIG_RET_MONTH_YEAR | 50% |/K極端報酬門檻 |
| Ping-Pong偵測| PINGPONG_THRESHOLD | 40% | 除權錯位偵測門檻 |
| 性能設定 | MAX_WORKERS | 16 | 建議依 CPU 核心數調整 |
| | SHARD_SIZE | 10,000 | 記憶體 16GB:5–10k;32GB:10–15k |
| | ROW_GROUP_SIZE | 512,000 | Parquet 優化建議值 |

✨ 技術亮點總結


| 特色項目             | 說明內容                                               |
|----------------------|--------------------------------------------------------|
|12 層數據清洗 | 從基礎價量到復牌事件,涵蓋 ghost、極端報酬、樣本過濾等 |
|3 級復牌偵測 | 長停牌 + ghost 段落 + 極端跳空,多層邏輯交叉判定 |
| ✅ 週期適應過濾 | W/M/Y 各自設計過濾邏輯,避免統一門檻造成誤殺 |
| ✅ 時區邊界修正 | 解決跨時區造成的月份歸屬錯誤,統一為本地時間 |
|7QA 報表 | 分布 / 漂移 / 一致性等指標,支援回測品質監控 |
| ✅ 增量快取機制 | 快取已下載檔案,節省約 70% 重複下載時間 |
| ✅ 批次串流寫入 | Parquet 串流寫入,低記憶體峰值處理大數據 |
| ✅ 玩股網口徑 |K結算日與跳空邏輯符合業界標準 |
| ✅ IsFiltered_QA 標記 | 可追溯每筆被過濾的原因,支援 QA 統計與回測排除 |
| ✅ Ping-Pong 偵測 | 自動識別除權錯位,排除疑似異常報酬週期 |

🚨 注意事項

數據品質提醒

  1. 過濾後報酬為 NaN
    • 週 K: Ret_Trad_W = NaN 表示該週報酬不可信
    • 月/年 K: IsFiltered_QA = 1 表示該期被過濾
  2. 復牌事件處理
    • HasResume = 1 的週/月/年建議額外注意
    • 可能伴隨極端報酬(即使未被過濾為 NaN)
  3. 新掛牌股票
    • 首月/首年可能因交易日不足被過濾
    • 建議至少上市滿 1 年後再納入回測
  4. 時區敏感場景
    • 跨國市場比較需注意時區統一
    • 月末日判定已處理台灣時區(UTC+8)

性能調優建議

  1. 記憶體不足時
    • 降低 SHARD_SIZE 至 3,000-5,000
    • 降低 MAX_WORKERS 至 8
  2. 速度優化
    • SSD 硬碟可提升 SHARD_SIZE 至 15,000
    • 增加 MAX_WORKERS 至 CPU 核心數的 1.5 倍
  3. Colab 環境
    • 使用 WRITE_MODE = 'B' 避免 Drive 直寫慢
    • 定期清理 /content/_wmy_tmp 避免空間不足
留言
avatar-img
留言分享你的想法!
avatar-img
《炒股不看周月年K漲幅機率就是耍流氓》
3會員
230內容數
普通上班族,用 AI 與 Python 將炒股量化。我的數據宣言是:《炒股不做量化,都是在耍流氓》。
2025/11/05
我不是什麼量化高手,也不是什麼金融工程師。 我只是那種會自己寫下載器、跑回測的人。 但說真的,如果沒有 AI 幫我,我根本不知道怎麼處理這些資料。 🧠 問題一:ETF 拆股後,Yahoo Finance 的 Adj Close 竟然沒調整? 理論上: Adj Close = Close
2025/11/05
我不是什麼量化高手,也不是什麼金融工程師。 我只是那種會自己寫下載器、跑回測的人。 但說真的,如果沒有 AI 幫我,我根本不知道怎麼處理這些資料。 🧠 問題一:ETF 拆股後,Yahoo Finance 的 Adj Close 竟然沒調整? 理論上: Adj Close = Close
2025/11/04
我會回答:有沒有可能付費資料也有問題,只是沒被發現? 市面上絕大多數教學都遵循一條「快速上手」的路線: 使用某個 T 開頭的回測平台(你知道是哪個) 安裝 Python 套件如 backtrader、bt、yfinance 丟入策略,跑出回測結果 然後就開始分析報酬率、夏普值、最大回撤
2025/11/04
我會回答:有沒有可能付費資料也有問題,只是沒被發現? 市面上絕大多數教學都遵循一條「快速上手」的路線: 使用某個 T 開頭的回測平台(你知道是哪個) 安裝 Python 套件如 backtrader、bt、yfinance 丟入策略,跑出回測結果 然後就開始分析報酬率、夏普值、最大回撤
2025/11/04
── 一次真實的資料偵錯實戰紀錄 一、故事的起點:一筆「詭異的 +113%」 某天,我在檢查台股週K報酬分布時,發現一筆極度誇張的紀錄: 8476.TW(台境)在 2023 年第一週的週K漲幅竟高達 +113%。 我原以為是興櫃或除牌後復牌的特殊行情,但越看越不對勁。 台境並不是那種會一週翻倍
2025/11/04
── 一次真實的資料偵錯實戰紀錄 一、故事的起點:一筆「詭異的 +113%」 某天,我在檢查台股週K報酬分布時,發現一筆極度誇張的紀錄: 8476.TW(台境)在 2023 年第一週的週K漲幅竟高達 +113%。 我原以為是興櫃或除牌後復牌的特殊行情,但越看越不對勁。 台境並不是那種會一週翻倍
看更多