xalpha 基金回测实战:用开源引擎对比定投、买入持有、网格三种策略

2026-06-14

买基金的人大概率想过这个问题:定投真的比一次性买入好吗?网格交易是不是稳赚?

想归想,大部分人靠直觉做选择。定投党觉得”摊低成本肯定稳”,梭哈党信奉”看好就全押”,网格党则迷恋”低买高卖自动化”。

但直觉靠不住。同一时间段,同一只基金,三种策略的年化收益可能差出 10 个百分点。

有没有办法用真实数据,把这三种策略在自己的基金上跑一遍?

有。xalpha 就是干这个的。

三种投资策略的路径对比

xalpha 是什么

一个 Python 写的场外基金全流程管理引擎,GitHub 3k+ Star,MIT 协议。核心理念:记最少的账,算最精确的结果

它能干什么?举几个例子:

  • 申购费折扣、赎回费阶梯、基金分红、份额折算——全部自动处理,误差精确到一分钱
  • 每次交易只记一个数(买入记金额,卖出记份额)
  • 内置 7 种策略引擎,包括买入持有、定期定投、网格交易、技术指标交叉等
  • 一行代码获取全球市场数据(A 股、港股、美股、外汇、大宗商品)

安装就一行:

pip install xalpha

xalpha 依赖 pandas 1.5.3。如果你的环境是 pandas 2.x,装完后会有 FutureWarning 刷屏。不影响结果,但看着烦。解决办法:pip install pandas==1.5.3 或者跑脚本时加 python -W ignore your_script.py

三种策略,三套逻辑

买入持有(Buy and Hold)

最简单的策略:某天一次性投入全部资金,然后什么都不做。

import xalpha as xa

f = xa.fundinfo("000968")  # 广发养老指数A
st = xa.policy.buyandhold(f, '2020-01-01', totmoney=100000)
t = xa.trade(f, st.status)
print(t.xirrrate())  # 输出年化内部收益率

适合什么人?看好某只基金的长期表现,不想花时间盯盘,手头刚好有一笔闲钱。

优点是操作成本为零。缺点是买在高点会很痛——想想 2021 年初满仓消费基金的人。

定期定投(DCA)

固定时间间隔,投入固定金额,不管市场涨跌。

import pandas as pd

# 每周四定投 1000 元
times = pd.date_range('2020-01-01', '2024-12-31', freq='W-THU')
st = xa.policy.scheduled(f, totmoney=1000, times=times)
t = xa.trade(f, st.status)
print(t.xirrrate())

换月度定投也简单,改 freq='MS'(Month Start),金额改成 5000 就行。

定投的好处是纪律性强,下跌时自动买入更多份额,摊低成本。坏处是如果市场一路上涨,收益反而不如一次性买入——因为你后买的钱都买在更高价位了。

xalpha 还支持 scheduled_tune(变额定投),根据指数估值自动调整定投金额。低估多投、高估少投,比傻瓜定投多一层择时逻辑。

策略 3:网格交易(Grid Trading)

网格交易的节点与信号可视化

设定价格网格:每跌一定百分比加仓,每涨一定百分比减仓。

# 跌5%加仓1份,涨8%卖出1份,总共4档
st = xa.policy.grid(
    f,
    buypercent=[5, 5, 5, 5],
    sellpercent=[8, 8, 8, 8],
    start='2020-01-01',
    totmoney=50000
)
t = xa.trade(f, st.status)
print(t.xirrrate())           # 年化收益率
print(len(st.status))          # 总交易次数

buypercentsellpercent 都是列表。[5,5,5,5] 表示分 4 档,每档下跌 5% 触发一次买入。列表长度决定最大仓位层数。

网格在震荡市里表现出色——涨涨跌跌之间反复低买高卖。但单边下跌会一路加仓直到子弹打完,单边上涨则会过早清仓踏空。

8 只基金实测对比

光说原理没用,跑一遍才知道。下面是回测脚本的核心部分,对 8 只不同类型的基金分别跑三种策略:

import xalpha as xa
import pandas as pd
import warnings
warnings.filterwarnings("ignore")

funds = {
    "004407": "招商消费80C",
    "000307": "易方达黄金A",
    "021654": "南方计算机C",
    "012895": "天弘科创50C",
    "013402": "华夏恒生科技A",
    "006555": "浦银全球智能科技A",
    "009689": "易方达瑞锦混合A",
    "012737": "广发创新药A",
}

results = []

for code, name in funds.items():
    try:
        f = xa.fundinfo(code)
        start = f.price['date'].iloc[0].strftime('%Y-%m-%d')
        if start < '2020-01-01':
            start = '2020-01-01'

        # 策略1: 买入持有
        st_buy = xa.policy.buyandhold(f, start, totmoney=100000)
        t_buy = xa.trade(f, st_buy.status)

        # 策略2: 每周定投
        times_w = pd.date_range(start, '2024-12-31', freq='W-THU')
        st_weekly = xa.policy.scheduled(f, totmoney=1000, times=times_w)
        t_weekly = xa.trade(f, st_weekly.status)

        # 策略3: 网格
        st_grid = xa.policy.grid(
            f, buypercent=[5,5,5,5], sellpercent=[8,8,8,8],
            start=start, totmoney=50000
        )
        t_grid = xa.trade(f, st_grid.status)

        results.append({
            'name': name,
            'buy_hold': t_buy.xirrrate() * 100,
            'weekly_dca': t_weekly.xirrrate() * 100,
            'grid': t_grid.xirrrate() * 100,
            'grid_trades': len(st_grid.status),
        })
    except Exception as e:
        print(f"❌ {name}({code}): {e}")

# 输出对比表
print(f"\n{'基金':<16} {'买入持有':>8} {'周定投':>7} {'网格':>7} {'网格交易次数':>10}")
for r in results:
    print(f"{r['name']:<14} {r['buy_hold']:>7.2f}% {r['weekly_dca']:>6.2f}% "
          f"{r['grid']:>6.2f}% {r['grid_trades']:>8}")

跑完之后你会看到一个有意思的结论:不同类型基金的最优策略完全不同

  • 波动大的行业主题基金(如计算机、创新药):网格策略往往跑赢其他两种。因为波动大,网格能频繁触发交易,反复低买高卖。
  • 长期趋势向上的宽基指数(如养老指数、科创 50):定投和买入持有差异不大,但定投的回撤更小,心理压力更低。
  • 震荡方向不明的基金(如恒生科技):三种策略都不太好看,但网格至少能赚到波动的差价。

踩坑记录

网格参数不能抄作业

最容易踩的坑:buypercentsellpercent 直接照搬别人的参数。

基金的波动率差异很大。一只年化波动率 30% 的行业基金和一只波动率 8% 的债券基金,用同一套网格参数,效果天差地别。

建议先用 f.price 看一下基金的历史波动情况,再决定网格间距。波动小的基金干脆别用网格——触发不了几笔交易。

XIRR 计算偶尔返回 None

买入持有的 XIRR 需要多笔现金流才能算。如果只有一笔买入和最终市值,现金流太少,算法可能不收敛。

解决办法:用 t.xirrrate('2020-01-01') 明确指定起始日期,或者改用定期定投策略——天然是多笔现金流,XIRR 稳定收敛。

批量跑基金时注意数据缓存

连续获取多只基金数据时,天天基金的 API 可能返回空数据(频率限制)。

# 设置本地缓存,避免重复请求
xa.set_backend(backend="csv", path="./data")

第一次跑还是会全量拉,之后就从本地读了。如果你的网络环境需要代理:

xa.set_proxy("http://127.0.0.1:7890")

xalpha 还能做什么

三种策略只是起点。xalpha 的进阶能力包括:

  • 技术指标策略indicator_cross 基于均线交叉,indicator_points 基于指标点位触发交易
  • 持仓穿透:查看基金底层持有哪些股票,季度级别的持仓数据
  • QDII 净值预测:对于跨境基金,可以预测 T-1 和 T-0 的净值
  • 通用数据获取:人民币中间价、布伦特原油、港股实时行情,一行代码搞定

xalpha 从 v0.9 开始内置了 AGENTS.md,原生适配 Claude Code 和 Codex 等 AI agent。你可以用自然语言让 AI 帮你写回测脚本、分析收益数据、调优网格参数。

总结

三种策略没有绝对的优劣,只有适不适合。选策略之前,先搞清楚你的基金是什么类型、你的资金是什么性质、你能承受多大的回撤。

xalpha 的价值在于:不用自己写回测框架,不用处理费率和分红的细节,把精力集中在策略本身。几行代码跑一遍,数据自己会说话。

项目地址:https://github.com/refraction-ray/xalpha

文档:https://xalpha.readthedocs.io/

更多示例 Notebook:https://github.com/refraction-ray/xalpha/tree/master/doc/samples

标签: AI 工具 教程