點選右邊主選單查看更多資訊

Python 回測怎麼寫?用 pandas 算均線、向量化回測加權指數十年,附完整程式碼

一分鐘看懂

用 Python 回測,最常用的工具是 pandas。把歷史資料讀成表格後,均線用 rolling 一行算完,部位、損益、交易成本、回撤都是整欄一起算,這叫向量化回測,十年日線一秒內跑完。最重要的一行是 shift(1):今天收盤才知道的訊號,部位要從明天才開始算,少了這一步就是在偷看未來,回測會好得不真實。本文附的程式碼實際跑過加權指數 2016~2026 年的資料,20/60 日均線策略總損益 31,569 點、最大回撤 −7,808 點。

很多人學 Python 回測,第一步是去找回測框架,結果花了一個禮拜在裝套件、看文件。一個簡單的均線策略,用 pandas 二十幾行就寫得完,而且每一步你都看得懂在算什麼。

這篇的程式碼可以直接複製使用,跑的是加權指數 2016 年 10 月 31 日到 2026 年 10 月 30 日的日線,共 2,430 個交易日。

準備資料

需要一個 CSV 檔,欄位是日期、開盤、最高、最低、收盤:

dateopenhighlowclose
2026-10-2947014.8747015.4546055.0746368.33
2026-10-3046139.7946139.7944726.2645211.83

加權指數的歷史資料可以從證交所網站逐月下載;台指期的歷史資料,期交所也有提供下載。

完整程式碼

import pandas as pd

# 讀取加權指數日線(欄位:date, open, high, low, close)
df = pd.read_csv("twii10.csv", parse_dates=["date"], index_col="date")

# 1. 計算指標:20 日與 60 日均線
df["ma_fast"] = df["close"].rolling(20).mean()
df["ma_slow"] = df["close"].rolling(60).mean()

# 2. 產生部位:快線在慢線上方就做多(1),否則空手(0)
#    今天收盤才知道訊號,所以部位從明天才生效(shift 1 天,避免偷看未來)
df["position"] = (df["ma_fast"] > df["ma_slow"]).astype(int).shift(1).fillna(0)

# 3. 計算每天損益(點數):持有部位 × 當天漲跌
df["change"] = df["close"].diff().fillna(0)
df["pnl"] = df["position"] * df["change"]

# 4. 扣交易成本:部位每變動一次,假設來回各 2 點(稅+滑價)
df["trade"] = df["position"].diff().abs().fillna(0)
df["pnl"] -= df["trade"] * 2

# 5. 累積損益與回撤
df["equity"] = df["pnl"].cumsum()
df["drawdown"] = df["equity"] - df["equity"].cummax()

print("總損益(點):", round(df["equity"].iloc[-1]))
print("最大回撤(點):", round(df["drawdown"].min()))
print("進出次數:", int(df["trade"].sum() / 2))
print("持有天數比例:", round(df["position"].mean() * 100, 1), "%")

執行結果:

總損益(點): 31569
最大回撤(點): -7808
進出次數: 21
持有天數比例: 68.3 %

進出次數顯示 21,是因為最後一筆在 2026 年 10 月 30 日還持有中,還沒出場。

每一步在做什麼

步驟程式說明
均線rolling(20).mean()往前 20 筆收盤價的平均,前 19 天算不出來是空值
部位(快線 > 慢線)結果是 True/False,轉成 1/0
避免偷看未來shift(1)把部位往後挪一天
損益部位 × 當天漲跌有部位才算漲跌,空手是 0
成本部位變動 × 2 點進場一次、出場一次各扣一次
回撤累積損益 − 歷史最高從最高點掉下來多少

這種寫法叫向量化回測:整欄資料一次算完,不用一天一天跑迴圈,所以十年的資料一秒內就跑完。

最容易犯的錯:偷看未來

把 shift(1) 拿掉,等於假設你在今天收盤前就知道今天的收盤價,用它決定今天的部位。回測結果會明顯變好,但實單根本做不到。

常見的偷看未來怎麼避免
用今天收盤算的訊號,賺今天的漲跌部位一律 shift(1)
用全部資料算平均或標準差,再拿來設門檻只用當天以前的資料算
用今天的最高、最低價決定今天的進場改成用昨天的,或用 stop 單模擬
用還沒公布的財報或數據用公布日當天之後的資料

把 2 點成本換成台指期的金額

這個回測用的是加權指數的點數。換算成台指期,每點價值不同,金額差很多:

商品每點價值31,569 點換算最大回撤 −7,808 點換算
微台10 元約 31.6 萬元約 −7.8 萬元
小台50 元約 157.8 萬元約 −39 萬元
大台200 元約 631 萬元約 −156 萬元

實際交易台指期,還要考慮每月換倉的價差、夜盤,以及成交價和收盤價的差距,結果會跟用指數回測不一樣。台指期的稅和成本,可以看台指期交易成本試算。

寫完回測之後先做這三件事

  1. 檢查 shift:把部位那一欄印出來,確認訊號出現的隔天才有部位。
  2. 算更多指標:只看總損益不夠,還要看勝率、獲利因子、最大回撤和夏普值,可以看策略回測關鍵指標解析。
  3. 留一段資料不碰:參數只用前段資料找,後段資料留著最後驗證,做法可以看樣本外測試與滾動前進分析。

平台怎麼選可以看程式交易平台怎麼選,整體流程在期貨程式交易怎麼開始。

分享你的喜愛
我愛期貨
我愛期貨
文章: 66