← 返回博客列表

【量化系统从零构建 #17】回测(下):绩效指标·参数遍历

2026年09月09日 08:50 · 智兔数服 · 量化系统从零构建

摘要:【量化系统从零构建 #17】回测(下):绩效指标·参数遍历 系列:《量化系统从零构建》|连载项目 · 纯 GET 取数 · 仅依赖 requests 适用:想在 #16 回测输出上算绩效、并做参数遍

系列:《量化系统从零构建》|连载项目 · 纯 GET 取数 · 仅依赖 requests
适用:想在 #16 回测输出上算绩效、并做参数遍历的读者;数据由智兔数服提供,本篇给出年化收益、最大回撤、夏普,以及参数网格遍历与过拟合警示,不依赖任何行情终端,也不发起网络请求。所有数值均为算法演示,不构成收益承诺。

1. 你将得到什么

  • daily_rets / max_drawdown / sharpe / annual_return:四类核心绩效指标。
  • param_sweep:对参数网格跑回测,挑总收益最优的一组(演示用,生产需样本外验证)。
  • 过拟合警示:参数越多越易过拟合,附「样本外验证」提醒。
  • 本篇交付:回测绩效闭环,#18 风控与 #19 策略装配在其上做评价。

2. 本篇取数约定

本篇是回测层,不发起网络请求,吃 #16 的 backtest 输出(权益曲线)。数据来自 智兔数服(www.zhituapi.com)。纯 Python,不引 numpy/pandas。

3. 绩效指标

指标 含义
年化收益 日收益复利年化(×√252)
最大回撤 权益曲线峰后最大跌幅
夏普 (均值-无风险)/标准差 ×√周期

4. 核心模板函数

import sys, math


def backtest(prices, signals, fee=0.0003, slippage=0.001):
    """(同 #16)日频回测,返回 (权益曲线, 总收益)。"""
    eq = [1.0]; pos = 0
    for i in range(1, len(prices)):
        ret = prices[i] / prices[i - 1] - 1
        day_ret = pos * ret
        if signals[i] != pos:
            cost = (fee + slippage) * 2 if pos != 0 else (fee + slippage)
            pos = signals[i]
        else:
            cost = 0
        eq.append(eq[-1] * (1 + day_ret) * (1 - cost))
    return eq, eq[-1] - 1


def daily_rets(eq):
    return [eq[i] / eq[i - 1] - 1 for i in range(1, len(eq))]


def max_drawdown(eq):
    peak, mdd = eq[0], 0.0
    for v in eq:
        peak = max(peak, v)
        mdd = min(mdd, v / peak - 1)
    return mdd


def annual_return(eq, periods=252):
    if len(eq) < 2:
        return 0.0
    total = eq[-1] / eq[0] - 1
    years = (len(eq) - 1) / periods
    return (1 + total) ** (1 / years) - 1 if years > 0 else 0.0


def sharpe(rets, rf=0.0, periods=252):
    if not rets:
        return 0.0
    m = sum(rets) / len(rets)
    var = sum((x - m) ** 2 for x in rets) / len(rets)
    sd = math.sqrt(var)
    return 0.0 if sd == 0 else (m - rf) / sd * math.sqrt(periods)


def param_sweep(prices, make_signal, param_grid):
    """遍历参数网格,挑总收益最优;返回 (最优参数, 总收益, 夏普)。"""
    best = None
    for p in param_grid:
        sig = make_signal(prices, p)
        eq, tot = backtest(prices, sig)
        sh = sharpe(daily_rets(eq))
        if best is None or tot > best[1]:
            best = (p, tot, sh)
    return best


def run_check():
    # 合成权益曲线,非真实行情
    eq = [1.0, 1.01, 1.02, 1.015, 1.03, 1.028]
    r = daily_rets(eq)
    assert len(r) == 5
    assert abs(max_drawdown(eq) - (1.015 / 1.02 - 1)) < 1e-9
    assert annual_return(eq) > 0
    assert sharpe([0.01] * 20) == 0          # 无波动 -> 分母保护返回 0
    assert sharpe([0.01, 0.02, 0.0, 0.015]) > 0  # 正收益且带波动 -> 正夏普
    print("校验通过")


if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1] == "--check":
        run_check()
    else:
        print("本篇基于权益曲线算绩效,不发起网络;param_sweep 需样本外验证以防过拟合。")

5. 跑通示例

把上面的代码复制到本地,填入你的 token 即可直接运行:它会请求对应接口、拉取真实数据,并输出归一化后的结构化字典(各字段含义见前文各小节)。

6. 坑与注意事项

  1. 最大回撤是峰值后最大跌幅:用 v/peak-1 取最小,别用收盘价直接减。
  2. 夏普分母保护:收益无波动时标准差为 0,直接除会崩;sd==0 返回 0。
  3. 参数遍历易过拟合param_sweep 挑的是样本内最优,必须用样本外数据复验。
  4. 不承诺收益:绩效数字是历史算法演示,不代表未来,更不构成收益保证。

7. 小结与下篇预告

本篇把回测补齐绩效指标与参数遍历,并强调样本外验证防过拟合。回测层完整。

下一篇计划写 #18《组合与风控:仓位·止损·可转债双低·回撤》:在回测之上加仓位管理(等权/风险平价/凯利)、止损止盈、可转债双低选债与回撤控制。

8. 免责声明

本文仅演示绩效指标与参数遍历的算法,所有代码示例均以合成数据校验,未含任何真实数据;文中示例数据仅作演示用途,不构成投资建议,亦不承诺收益。


免费领取证书
数据来自 智兔数服(www.zhituapi.com):零 SDK、纯 GET、免费版即可起步。

领取路径:进入 www.zhituapi.com → 点击「请求证书」→「证书获取」→「免费版」(邮箱验证 3 步即可拿到 token)。

把代码里的 你的智兔token 换成你拿到的真实 token,上面的脚本就能直接打印回测绩效指标数据。

想亲自试一下?免费获取证书