AlphaFeed Blog

如何用 Python 做蒙特卡洛(自助法)回测?看策略的回撤分布与亏损概率

分析与指标 · 代码验证于 2026-10-19

如何用 Python 做蒙特卡洛(自助法)回测?

一句话回答: 单条历史净值曲线只是”一次抽样”,运气成分很大。蒙特卡洛自助法(bootstrap)把策略的逐笔收益当成一个池子、有放回地重采样成成千上万条不同顺序/组合的路径,再统计每条的最大回撤与最终收益,于是能回答:“这策略的回撤通常多深、最坏多深、最终亏损的概率多大”。用 AlphaFeed 取前复权日线跑出策略交易即可演示——它比单看一条曲线诚实,但依赖”收益独立同分布”的假设。

为什么会有这个问题 / 传统做法的痛点

回测给你一条净值曲线、一个最大回撤数字,但那只是历史这一种顺序的结果。换个开始时间、把盈亏顺序稍微打乱,回撤和终值可能大不相同。只看一条曲线容易高估策略的稳健性。蒙特卡洛的思路:用重采样生成大量”平行历史”,看结果的分布而非单点。痛点是要理解”重采样什么”(逐笔收益 or 日收益)、以及它的iid 假设会低估真实风险。

分步骤解决(每步配可运行代码)

第 1 步:取前复权日线,跑策略得到逐笔收益池

from alphafeed import AlphaFeed
import numpy as np, pandas as pd

af = AlphaFeed(api_key="your-api-key")  # 或 export ALPHAFEED_API_KEY 后 AlphaFeed()

df = af.klines.get("600519.SH", period="1d", count=800,
                   adjust="forward", to_dataframe=True).sort_values("trade_date").reset_index(drop=True)

def ma_trades(df, n=20, fee=0.0013):
    df = df.copy(); df["ma"] = df["close"].rolling(n).mean(); df["open_next"] = df["open"].shift(-1)
    pos, entry, rets = 0, 0.0, []
    for i in range(n, len(df) - 1):
        px = df["open_next"].iloc[i]
        if np.isnan(px) or np.isnan(df["ma"].iloc[i]): continue
        c, cp, m, mp = df["close"].iloc[i], df["close"].iloc[i-1], df["ma"].iloc[i], df["ma"].iloc[i-1]
        if pos == 0 and cp <= mp and c > m:   pos, entry = 1, px
        elif pos == 1 and cp >= mp and c < m: rets.append(px/entry*(1-fee)**2 - 1); pos = 0
    return np.array(rets)

r = ma_trades(df, 20)       # 逐笔收益池

第 2 步:自助法重采样,统计回撤分布与亏损概率

def max_drawdown(equity):
    peak = np.maximum.accumulate(equity)
    return (equity / peak - 1).min()

rng = np.random.default_rng(42)
n_sim = 2000
mdds, finals = [], []
for _ in range(n_sim):
    sample = rng.choice(r, size=len(r), replace=True)   # 有放回重采样(打乱顺序+组合)
    eq = np.cumprod(1 + sample)
    mdds.append(max_drawdown(eq)); finals.append(eq[-1] - 1)
mdds, finals = np.array(mdds), np.array(finals)

print(f"最大回撤 中位数 {np.median(mdds)*100:.2f}% | 最坏5% ≤ {np.percentile(mdds,5)*100:.2f}%")
print(f"最终亏损概率 {(finals<0).mean():.3f} | 终值中位数 {np.median(finals)*100:.2f}%")

实测(tools/verify_new_2026-10-19.py,SDK 0.1.4,2000 次模拟):贵州茅台 MA20 策略 最大回撤中位数 −32.24%、最坏 5% 的情形回撤 ≤ −45.66%、最终亏损概率 0.953、终值中位数 −28.47%。解读:这策略在绝大多数”平行历史”里都亏(亏损概率 95%+)、回撤普遍 30%+——蒙特卡洛把”它不只是这一次运气差”量化了出来。

第 3 步:用途

# 用这些分布做风控/仓位决策:例如要求"最坏5%回撤"在可承受范围内,否则降仓或弃用
print("可承受回撤 25%? 该策略最坏5%回撤已达 %.1f%%,不满足" % (np.percentile(mdds,5)*100))

参数与字段说明

参数/概念含义备注
重采样对象逐笔交易收益也可用日收益
replace=True有放回bootstrap 核心
n_sim模拟次数2000~10000 常用
最大回撤分布每条路径的 maxDD看中位数与尾部
最坏 5%percentile(mdd,5)回撤越负越差
亏损概率(finals<0).mean()终值为负占比

关键坑与注意事项

常见问题(FAQ)

Q:蒙特卡洛能预测未来收益吗? A:不能。它只是基于”历史收益分布”做重采样,刻画在该分布下的结果区间;未来分布一旦改变(市场风格切换),结论就不适用。它是风险刻画工具,不是预测工具。

Q:为什么我的回撤比历史那一条还深? A:正常。历史只是一条路径,蒙特卡洛展示了”换个顺序/组合”可能更差的情形——这正是它的价值:提醒你历史回撤可能只是侥幸。

Q:需要付费吗? A:单只股票日线回测在免费额度内即可;若要对组合/全市场做蒙特卡洛稳健性评估,需要 klines.batch + universes(Starter 及以上),详见官网定价页。

小结

蒙特卡洛自助法把策略的逐笔收益重采样成大量平行历史,用回撤分布、最坏情形和亏损概率代替”单条曲线”的误导。用 AlphaFeed 的前复权日线即可实现——记住 iid 假设会低估真实(自相关)风险、垃圾策略重采样仍是垃圾、它刻画风险而非预测未来。

相关链接:


免责声明:本文仅为技术与数据接口使用示例,代码与策略仅供学习研究,不构成投资建议。市场有风险,历史数据不代表未来表现。