pitfalls · 回测两大坑
内容:
坑一:未来函数(用了当时根本不可能知道的信息)
坑二:交易成本(手续费 / 印花税 / 滑点)
用法:在终端运行 python pitfalls.py
本脚本为教学演示,不构成投资建议
导入
from datetime import datetime
from quant_data import get_kline
from metrics import make_signals
参数区 · 三个"假设值"
三个假设值都可以改,改完重新运行看差别。
滑点率
0.001
你想买,实际成交价比预期贵一点;假设千分之一(0.1%)
手续费率
0.0003
佣金:券商收的,买卖各收一次;假设万分之三(0.03%)
印花税率
0.0005
印花税:卖出时单边收;假设万分之五(0.05%)
滑点率 = 0.001
手续费率 = 0.0003
印花税率 = 0.0005
① 拉数据
df = get_kline(
symbol="600519",
start_date="20230101",
end_date=datetime.now().strftime("%Y%m%d"),
)
df["收益率"] = df["close"].pct_change()
df = make_signals(df)
② 坑一:未来函数
两种"成交假设"对比:
作弊版:今天的信号 × 今天的收益
正确版:昨天的信号 × 今天的收益(shift(1))
df["持仓_作弊"] = df["signal"]
df["持仓_正确"] = df["signal"].shift(1).fillna(0)
df["收益_作弊"] = df["持仓_作弊"] * df["收益率"]
df["收益_正确"] = df["持仓_正确"] * df["收益率"]
净值_作弊 = (1 + df["收益_作弊"].fillna(0)).cumprod()
净值_正确 = (1 + df["收益_正确"].fillna(0)).cumprod()
print("\n===== 坑一:未来函数(同一份数据,两种成交假设)=====")
print(f"作弊版(今天的信号赚今天的收益)累计收益:{(净值_作弊.iloc[-1] - 1) * 100:.2f}%")
print(f"正确版(昨天的信号赚今天的收益)累计收益:{(净值_正确.iloc[-1] - 1) * 100:.2f}%")
③ 坑二:交易成本
买一次:滑点(买贵了)+ 手续费(佣金)
卖一次:滑点(卖便宜了)+ 手续费 + 印花税
买入成本比例 = (1 + 滑点率) * (1 + 手续费率) - 1
卖出成本比例 = 1 - (1 - 滑点率) * (1 - 手续费率 - 印花税率)
df["成本"] = 0.0
df.loc[df["signal_change"] == 1, "成本"] = 买入成本比例
df.loc[df["signal_change"] == -1, "成本"] = 卖出成本比例
if df["signal_change"].iloc[-1] == 1:
df.loc[df.index[-1], "成本"] = 0.0
df["收益_含成本"] = df["收益_正确"] - df["成本"]
净值_无成本 = 净值_正确
净值_有成本 = (1 + df["收益_含成本"].fillna(0)).cumprod()
print("\n===== 坑二:交易成本(滑点 0.1% / 佣金 0.03% / 印花税 0.05%)=====")
print(f"不算成本:累计收益 {(净值_无成本.iloc[-1] - 1) * 100:.2f}%")
print(f"算了成本:累计收益 {(净值_有成本.iloc[-1] - 1) * 100:.2f}%")
差价 = (净值_无成本.iloc[-1] - 净值_有成本.iloc[-1]) * 100
print(f"被成本吃掉:{差价:.2f} 个百分点")
交易次数 = len(df[df["signal_change"] != 0])
print(f"期间一共动了 {交易次数} 次(买一次卖一次算两次)")
print("\n⚠️ 以上费率都是教学用的假设值,实盘以你的券商费率为准。")
print("⚠️ 教学演示,不构成投资建议。")
核心要点速览
未来函数:用了当时不知道的信息
交易成本:滑点 + 佣金 + 印花税
防未来函数:signal.shift(1)
买入成本:(1+滑点)×(1+佣金)-1
卖出成本:1-(1-滑点)×(1-佣金-印花税)
净值曲线:(1+收益).cumprod()
对比:作弊版 vs 正确版
结论:成本会吃掉真金白银
金句:
shift(1) 防未来函数,
cumprod() 算净值曲线 ——
回测不排坑,实盘两行泪。
运行输出 · python .\1.py
PS> python .\1.py
[数据源] akshare 不可用(ProxyError),尝试 baostock...
login success!
logout success!
[数据源] baostock 获取成功,共 179 条
===== 坑一:未来函数(同一份数据,两种成交假设)=====
作弊版(今天的信号赚今天的收益)累计收益:-2.59%
正确版(昨天的信号赚今天的收益)累计收益:-5.82%
===== 坑二:交易成本(滑点 0.1% / 佣金 0.03% / 印花税 0.05%)=====
不算成本:累计收益 -5.82%
算了成本:累计收益 -7.27%
被成本吃掉:1.45 个百分点
期间一共动了 10 次(买一次卖一次算两次)
⚠️ 以上费率都是教学用的假设值,实盘以你的券商费率为准。
⚠️ 教学演示,不构成投资建议。
关键结论:作弊版 -2.59% vs 正确版 -5.82% —— 未来函数虚增了 3.23 个百分点;
加成本后从 -5.82% 变成 -7.27%,被成本吃掉 1.45 个百分点;
期间共交易 10 次(买 5 次 + 卖 5 次)。