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

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


# [已修改] 函式名稱: audit_weekly_high_consistency

def audit_weekly_high_consistency(market_key: str, week_path: str):

    """

    [🌟 修正路徑] QA 稽核路徑:改為讀取本地快取。

    """

    qa_dir = Path(f"{DRIVE_BASE}/{market_key}/_qa"); qa_dir.mkdir(parents=True, exist_ok=True)

    if not Path(week_path).exists():

        (qa_dir / "weekly_high_mismatch.csv").write_text("", encoding="utf-8-sig")

        print("❌ QA: 找不到週K Parquet,跳過週高一致性。")

        return



    dfw = pd.read_parquet(week_path).copy()

    dfw['日期'] = pd.to_datetime(dfw['日期'], errors='coerce').dt.tz_localize(None)

    dfw = dfw[(dfw['日期'] >= DATE_START) & (dfw['日期'] <= DATE_END)]



    # 🌟 關鍵修正:從 Drive 路徑改為本地快取路徑

    local_dayk_dir = Path(f"{LOCAL_DAYK_CACHE}/{market_key}")

   

    day_cache = {}

    for f in local_dayk_dir.glob("*.csv"): # 從本地快取讀取

        sid = f.stem.split("_")[0]

        try:

            d = _read_csv_fast(str(f))

            dc = _pick(d, DATE_COLS); hc = _pick(d, HIGH_COLS)

            d = d.rename(columns={dc:'日期', hc:'最高'})

            d['日期'] = pd.to_datetime(d['日期'], errors='coerce').dt.tz_localize(None)

            d = d.dropna(subset=['日期','最高']).sort_values('日期')

            d = d[(d['日期'] >= DATE_PAD_START) & (d['日期'] <= DATE_END)]

            day_cache[sid] = d[['日期','最高']]

        except:

            pass



    bad = []

    for sid, g in dfw.groupby('StockID'):

        d = day_cache.get(sid)

        if d is None:

            continue

        for _, r in g.iterrows():

            iso = d['日期'].dt.isocalendar()

            wk = r.get('ISO_Week')

            mask = (iso.year.astype(str) + '-' + iso.week.astype(str).str.zfill(2)) == wk

            if not mask.any():

                continue

            week_max = float(d.loc[mask, '最高'].max())

            if np.isfinite(week_max) and np.isfinite(r['最高']) and abs(week_max - r['最高']) > 1e-6:

                bad.append({'StockID': sid, 'ISO_Week': wk, '週末日': r['日期'], '週高(weekK)': float(r['最高']), '日高max(week)': week_max, '差額': float(r['最高'] - week_max)})



    pd.DataFrame(bad).to_csv(qa_dir / "weekly_high_mismatch.csv", index=False, encoding='utf-8-sig')

    print(f"🧪 QA 週高一致性完成 → {qa_dir/'weekly_high_mismatch.csv'}")



def audit_weekly_high_tail(market_key: str, week_path: str):

    # ... (函式內容保持不變) ...

    qa_dir = Path(f"{DRIVE_BASE}/{market_key}/_qa"); qa_dir.mkdir(parents=True, exist_ok=True)

    if not Path(week_path).exists():

        (qa_dir / "weekly_distribution_summary_high.csv").write_text("", encoding='utf-8-sig')

        (qa_dir / "weekly_top_outliers_high.csv").write_text("", encoding='utf-8-sig')

        print("❌ QA: 找不到週K Parquet,跳過週高分布/厚尾。")

        return



    dfw = pd.read_parquet(week_path).copy()

    dfw['日期'] = pd.to_datetime(dfw['日期'], errors='coerce').dt.tz_localize(None)

    dfw = dfw[(dfw['日期'] >= DATE_START) & (dfw['日期'] <= DATE_END)]



    edges, labels = _week_bins()

    wk = dfw[['ISO_Week','StockID','日期','PrevC_W','最高','Ret_Max_H_W']].dropna(subset=['Ret_Max_H_W']).copy()

    wk['ret_pct'] = (wk['Ret_Max_H_W'] * 100).astype('float64')

    wk['bin'] = pd.cut(wk['ret_pct'], bins=edges, labels=labels, right=False)



    dist = wk.pivot_table(index='ISO_Week', columns='bin', values='StockID', aggfunc='count', fill_value=0)

    dist = dist.div(dist.sum(axis=1).replace(0, np.nan), axis=0)

    dist.sort_index().to_csv(qa_dir / "weekly_distribution_summary_high.csv", encoding='utf-8-sig')



    tail_bins = ['100~200%', '200~1000%', '1000~10000%', '-1000~-200%', '-200~-100%']

    outliers = wk[wk['bin'].isin(tail_bins)].copy()

    outliers['abs_z'] = outliers.groupby('ISO_Week')['ret_pct'].transform(lambda s: (s - s.mean()) / (s.std(ddof=1) if s.std(ddof=1) else np.nan)).abs()

    outliers['abs_z_rank'] = outliers.groupby('ISO_Week')['abs_z'].rank(ascending=False, method='first')

    outliers.sort_values(['ISO_Week','abs_z_rank']).groupby('ISO_Week').head(200)[['ISO_Week','日期','StockID','ret_pct','abs_z','bin']].to_csv(qa_dir / "weekly_top_outliers_high.csv", index=False, encoding='utf-8-sig')



    print(f"🧪 QA 週高分布/厚尾完成 → {qa_dir/'weekly_distribution_summary_high.csv'}, {qa_dir/'weekly_top_outliers_high.csv'}")



# [***新增 QA 函式 5***] 統計週 KNaN 過濾數量 (補充週 K 統計報告)

def audit_weekly_nan_filter(market_key: str, week_path: str):

    print("\n📊 週 K 過濾統計摘要:")

    if not Path(week_path).exists():

        print("   ❌ 週 K: Parquet 檔案不存在,無法統計過濾數量。")

        return



    try:

        # 只讀取 StockID 和 Ret_Trad_W 欄位

        dfw = pd.read_parquet(week_path, columns=['StockID', 'Ret_Trad_W'])



        # 統計 Ret_Trad_W 為 NaN 的行 (即被過濾的行)

        filtered_mask = dfw['Ret_Trad_W'].isna()

        df_filtered = dfw[filtered_mask].copy()



        total_filtered_rows = len(df_filtered)

        total_filtered_stocks = df_filtered['StockID'].nunique()



        if total_filtered_rows > 0:

            # 輸出詳細清單到 QA 資料夾

            qa_dir = Path(f"{DRIVE_BASE}/{market_key}/_qa"); qa_dir.mkdir(parents=True, exist_ok=True)

            summary_df = df_filtered.groupby('StockID').size().reset_index(name='Filtered_Count')

            summary_df.sort_values('Filtered_Count', ascending=False, inplace=True)

            summary_path = qa_dir / f"weekly_nan_filtered_summary.csv"

            summary_df.to_csv(summary_path, index=False, encoding='utf-8-sig')



            print(f"   ✅ 週 K: 共 {total_filtered_rows} 筆報酬被過濾為 NaN (涉及 {total_filtered_stocks} 檔股票)。")

            print(f"   📜 過濾清單儲存至: {summary_path}")

        else:

            print(f"   ✅ 週 K: 沒有報酬被過濾為 NaN。")

    except Exception as e:

        print(f"   ⚠️ 讀取/統計 週 K 檔案失敗: {e}")



# [***修正後的 QA 函式 4***] 讀取 M/Y Parquet 並統計過濾數量

def audit_monthly_yearly_filter(market_key: str, month_path: str, year_path: str):

    print("\n📊 月/年 K 過濾統計摘要:")



    def _report(path, freq_name_cn, freq_name_en):

        if not Path(path).exists():

            print(f"   ❌ {freq_name_cn} K: Parquet 檔案不存在,無法統計過濾數量。")

            return



        try:

            # 只讀取 StockID 和 IsFiltered_QA 欄位以節省記憶體

            df = pd.read_parquet(path, columns=['StockID', 'IsFiltered_QA'])

            df = df[df['IsFiltered_QA'] == 1]



            total_filtered_rows = len(df)

            total_filtered_stocks = df['StockID'].nunique()



            if total_filtered_rows > 0:

                # 輸出詳細清單到 QA 資料夾

                qa_dir = Path(f"{DRIVE_BASE}/{market_key}/_qa"); qa_dir.mkdir(parents=True, exist_ok=True)

                summary_df = df.groupby('StockID').size().reset_index(name='Filtered_Count')

                summary_df.sort_values('Filtered_Count', ascending=False, inplace=True)

                summary_path = qa_dir / f"{freq_name_en}_filtered_summary.csv"

                summary_df.to_csv(summary_path, index=False, encoding='utf-8-sig')



                print(f"   ✅ {freq_name_cn} K: 共 {total_filtered_rows} 筆報酬被過濾 (涉及 {total_filtered_stocks} 檔股票)。")

                print(f"   📜 過濾清單儲存至: {summary_path}")

            else:

                print(f"   ✅ {freq_name_cn} K: 沒有報酬被過濾。")

        except Exception as e:

            print(f"   ⚠️ 讀取/統計 {freq_name_cn} K 檔案失敗: {e}")



    _report(month_path, '月', 'monthly')

    _report(year_path, '年', 'yearly')

# --------------------------------------------------------------------------------

# Colab Cell 5: 主流程執行 (Part 5)

# --------------------------------------------------------------------------------



if __name__ == "__main__":

    print(f"⏳ 僅處理期間:{DATE_START_STR} ~ {DATE_END_STR}(含起始緩衝 {PAD_DAYS} 天)")

    print(f"⚠️ 月K/年K 報酬過濾已啟用:小於 {MIN_DAYS_MONTH} / {MIN_DAYS_YEAR} 交易日,或遇停牌/極端跳空報酬,該期報酬將被設為 NaN。")

    print(f"🛠️ [時區修正已啟用] 修正月份邊界錯誤(如 9/1 誤算進 8 月)。")



    for MK in MARKET_LIST:

        print(f"\n{'='*20} 處理市場:{MK}(W-FRI + 玩股口徑 + 本地快取 + 7 QA) {'='*20}")



        # 1. 生成新的 Parquet 檔案

        w, m, y = build_wmy_parquets(MK)



        if w:

            # 2. 運行週 K QA

            audit_weekly_parquet(MK, w)           # 收盤對收盤:分箱/漂移/厚尾

            audit_weekly_vs_daily(MK)             # 週 vs 日 幾何連乘

            audit_weekly_high_consistency(MK, w)  # 週高一致性(weekK 高 vs 週內日高 max)

            audit_weekly_high_tail(MK, w)         # 週高相對上週收的分布/厚尾



            # 3. 新增 週 K 過濾統計 (基於 Ret_Trad_W = NaN)

            audit_weekly_nan_filter(MK, w)



            # 4. 新增 月/K 過濾統計 (基於 IsFiltered_QA)

            audit_monthly_yearly_filter(MK, m, y)



        qa_dir = Path(f"{DRIVE_BASE}/{MK}/_qa")

        if qa_dir.exists():

            print("\n🌟 QA 報表輸出確認:")

            for filename in [

                "skip_pingpong.csv",

                "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",

                "monthly_filtered_summary.csv",

                "yearly_filtered_summary.csv",

                "weekly_nan_filtered_summary.csv", # <-- 新增週 K 清單

            ]:

                f = qa_dir / filename

                status = "✅" if f.exists() and f.stat().st_size > 0 else ("✅ (空表)" if f.exists() else "❌")

                print(f"   {status} {filename}")

        else:

            print("\n⚠️ 找不到 QA 資料夾,請檢查 DRIVE_BASE 設定或寫入權限。")



        print(f"\n🗃️ 本地日K快取保留:/content/_wmy_tmp/dayK_cache")

留言
avatar-img
留言分享你的想法!
avatar-img
《炒股不看周月年K漲幅機率就是耍流氓》
3會員
227內容數
普通上班族,用 AI 與 Python 將炒股量化。我的數據宣言是:《炒股不做量化,都是在耍流氓》。
2025/11/08
# -------------------------------------------------------------------------------- # Colab Cell 4: QA 稽核函式 (Part 4) # ----------------------------
2025/11/08
# -------------------------------------------------------------------------------- # Colab Cell 4: QA 稽核函式 (Part 4) # ----------------------------
2025/11/08
因為程式碼太常超過篇幅字數限制所以分段
2025/11/08
因為程式碼太常超過篇幅字數限制所以分段
2025/11/08
📋 程式概述 這是一個企業級股票數據清洗與時間週期轉換系統,專門處理日 K 線數據並轉換為週/月/年 K 線,同時進行多層次的數據品質控管與異常偵測。程式採用玩股網口徑標準,確保數據品質符合量化交易需求。 🎯 核心功能架構 1. 數據來源與處理範圍 時間範圍:2000-01-01
2025/11/08
📋 程式概述 這是一個企業級股票數據清洗與時間週期轉換系統,專門處理日 K 線數據並轉換為週/月/年 K 線,同時進行多層次的數據品質控管與異常偵測。程式採用玩股網口徑標準,確保數據品質符合量化交易需求。 🎯 核心功能架構 1. 數據來源與處理範圍 時間範圍:2000-01-01
看更多
你可能也想看
Thumbnail
臺灣獨立調香師品牌 Sunkronizo,Friday : Sexy Vibe 淡香精,揉合威士忌、菸草、皮革、蜂蜜與花香的多層次魅力,讓知性自信與內斂的從容態度,從視覺、嗅覺都充分表現。
Thumbnail
臺灣獨立調香師品牌 Sunkronizo,Friday : Sexy Vibe 淡香精,揉合威士忌、菸草、皮革、蜂蜜與花香的多層次魅力,讓知性自信與內斂的從容態度,從視覺、嗅覺都充分表現。
Thumbnail
使台劇得以突破過往印象中偶像劇、鄉土劇等範疇,產製更多類型,甚至紅到國外、帶動台灣觀光的最重要原因,便是「隨選串流平台」服務在近十年的蓬勃發展,台灣人愛看串流的程度或許比你我想像中都高,高到連美國電影協會(MPA),都委託Frontier Economics進行研究
Thumbnail
使台劇得以突破過往印象中偶像劇、鄉土劇等範疇,產製更多類型,甚至紅到國外、帶動台灣觀光的最重要原因,便是「隨選串流平台」服務在近十年的蓬勃發展,台灣人愛看串流的程度或許比你我想像中都高,高到連美國電影協會(MPA),都委託Frontier Economics進行研究
Thumbnail
本文探討串流平臺(VOD)如何徹底改變好萊塢和臺灣影視產業的生態。從美國電影協會(MPA)的數據報告,揭示串流服務在臺灣的驚人普及率與在地內容的消費趨勢。文章分析國際作品如何透過在地化元素開拓新市場。同時,作者也擔憂政府過度監管可能扼殺臺灣影視創新自由,以越南為鑑,呼籲以開放態度擁抱串流時代的新機遇
Thumbnail
本文探討串流平臺(VOD)如何徹底改變好萊塢和臺灣影視產業的生態。從美國電影協會(MPA)的數據報告,揭示串流服務在臺灣的驚人普及率與在地內容的消費趨勢。文章分析國際作品如何透過在地化元素開拓新市場。同時,作者也擔憂政府過度監管可能扼殺臺灣影視創新自由,以越南為鑑,呼籲以開放態度擁抱串流時代的新機遇
Thumbnail
歡迎來到Scikit-learn教學系列的第二篇文章!在上篇中,我們介紹了Scikit-learn與機器學習基礎,並探索了Iris資料集。這一篇將聚焦於資料預處理,我們將學習如何使用Scikit-learn清理資料、處理缺失值、進行特徵縮放與類別編碼,並以真實資料集進行實作。
Thumbnail
歡迎來到Scikit-learn教學系列的第二篇文章!在上篇中,我們介紹了Scikit-learn與機器學習基礎,並探索了Iris資料集。這一篇將聚焦於資料預處理,我們將學習如何使用Scikit-learn清理資料、處理缺失值、進行特徵縮放與類別編碼,並以真實資料集進行實作。
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 整理目前手上有的素材: AI說書 - 從0開始 - 180 | RoBERTa 預訓練前言:RoBERTa 預訓練前言 AI說書 - 從0開始 - 181 | 預訓
Thumbnail
我想要一天分享一點「LLM從底層堆疊的技術」,並且每篇文章長度控制在三分鐘以內,讓大家不會壓力太大,但是又能夠每天成長一點。 整理目前手上有的素材: AI說書 - 從0開始 - 180 | RoBERTa 預訓練前言:RoBERTa 預訓練前言 AI說書 - 從0開始 - 181 | 預訓
Thumbnail
為了讓資料更適合進行後續的分析、建立模型,模型的決策準確性,資料探索與清理是資料分析過程中非常重要的步驟,主要目的在於確保資料的品質和可靠性。 因為前幾篇的例子中的資料,並沒有缺失值與重複值的部分,我另外找了一份有包含的資料來做案例分析,由於找到的資料沒有重複值的部分,故本文主要解釋處理缺失值的部
Thumbnail
為了讓資料更適合進行後續的分析、建立模型,模型的決策準確性,資料探索與清理是資料分析過程中非常重要的步驟,主要目的在於確保資料的品質和可靠性。 因為前幾篇的例子中的資料,並沒有缺失值與重複值的部分,我另外找了一份有包含的資料來做案例分析,由於找到的資料沒有重複值的部分,故本文主要解釋處理缺失值的部
Thumbnail
從基本概念開始,然後逐步深入學習 pandas 的各種功能。這是一個非常強大的 Python 資料分析工具,常用於處理結構化數據。 基本概念 pandas 主要有兩個核心資料結構: Series: 一維的資料結構,類似於 Python 中的列表,但它可以帶有標籤(index)。 DataFr
Thumbnail
從基本概念開始,然後逐步深入學習 pandas 的各種功能。這是一個非常強大的 Python 資料分析工具,常用於處理結構化數據。 基本概念 pandas 主要有兩個核心資料結構: Series: 一維的資料結構,類似於 Python 中的列表,但它可以帶有標籤(index)。 DataFr
Thumbnail
繼「【🔒 Python實戰營 - Data Science 必修班】Pandas 資料清洗技 - 填補式」之後,我們已經學會怎麼填補空缺資料了,那這個章節我們來教您如何對某些欄位有條件的整形,有時候我們的資料來源某些欄位資料格式不一,甚至型態都不是正規統一的值,此時我們就需要針對這些值進行一些處理
Thumbnail
繼「【🔒 Python實戰營 - Data Science 必修班】Pandas 資料清洗技 - 填補式」之後,我們已經學會怎麼填補空缺資料了,那這個章節我們來教您如何對某些欄位有條件的整形,有時候我們的資料來源某些欄位資料格式不一,甚至型態都不是正規統一的值,此時我們就需要針對這些值進行一些處理
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News