运行回测中第三步:资金回测以及它的报告解读

前两步回答了"策略能找到什么机会"和"每笔交易赚不赚"。但实盘里你不会把所有钱押一只股票——你有 100 万、最多同时持有 10 只、每只最多投 30%。第三步模拟一个真实账户:给定初始资金和仓位规则,按枚举阶段的机会信号买卖,看整个账户的表现。

案例:用 RSI v1 跑一次资金回测

跑资金回测

python cli.py so --strategy rsi_v1

so 是 strategy_portfolio 的缩写。跑完后的报告在:

userspace/extensions/strategies/rsi_v1/results/simulations/{version_id}/portfolio/
├── overall_report.json       # 总体报告
├── entity_list.json          # 逐股摘要
├── performance.json          # 运行性能
├── trades.json               # 每笔成交记录(可选)
└── equity_curve.json         # 净值曲线(可选)

 

读总体报告

打开 overall_report.json,summary 里关注以下几组数:

 

账户总览

initial_capital: 1000000        # 初始资金 100 万
final_cash: 320000              # 期末现金 32 万
final_total_equity: 1156000    # 期末总权益 115.6 万
total_return: 0.156             # 总收益 15.6%
total_trades: 286                # 总成交 286 笔
buy_trades: 143                  # 买入 143 笔
sell_trades: 143                 # 卖出 143 笔
completed_investments: 130       # 已完成投资 130 笔
open_positions: 8               # 期末还有 8 个持仓

total_return 是最直观的数字——100 万变成了 115.6 万。但要结合持仓周期看:如果这是 3 年的回测,年化只有 5% 左右,不如买理财。

 

胜率和盈亏

win_rate: 55.4                   # 胜率 55.4%
win_trades: 72                   # 盈利卖出 72 次
loss_trades: 58                  # 亏损卖出 58 次
total_profit: 186000             # 已实现盈亏 18.6 万
avg_pnl_per_trade: 1300.7        # 平均每笔卖出盈亏 1300 元
calmar_ratio: 1.23               # Calmar 比率 1.23

calmar_ratio 是总收益除以最大回撤,衡量"每承担 1 单位回撤风险获得多少收益"。大于 1 算不错,大于 2 算优秀。

 

回撤

max_drawdown: 0.127              # 最大回撤 12.7%
max_drawdown_duration_days: 85   # 回撤持续 85 天
average_open_positions: 6.2     # 平均持仓 6 只
peak_open_positions: 10         # 峰值持仓 10 只(满仓)
capital_utilization_ratio_pct: 62.0   # 资金利用率 62%
average_cash_ratio_pct: 38.0           # 平均现金占比 38%

最大回撤 12.7% 意味着账户从最高点一度跌了 12.7%。持续 85 天意味着你要在 3 个月里看着账户缩水而不动摇——实盘中能不能拿住是个问题。

资金利用率 62% 说明有 38% 的时间钱在账上闲着。如果策略信号稀少,这个比例会更高,收益率也会受影响。

 

净值曲线

equity_curve_labels: ["2022-01-04", "2022-01-07", ...]
equity_curve_values: [1000000, 1002000, 1005000, 998000, ...]
drawdown_curve_values: [0, 0, 0, -0.002, ...]

净值曲线是账户总资产随时间的变化。回撤曲线是从每个时间点的最高点回落的幅度。Web UI 会把这两条线画出来,直观看策略走势。

读逐股报告

entity_list.json 每只股票一行:

entity_id: "000001"
stock_name: "平安银行"
trade_count: 3              # 卖出 3 次
total_profit: 8200.0        # 这只股票贡献了 8200 元利润
win_trades: 2               # 2 次盈利卖出
loss_trades: 1              # 1 次亏损卖出
win_rate: 66.7              # 胜率 66.7%

用这个表可以看"哪些股票最赚钱"和"哪些股票最亏钱"。如果利润高度集中在前 5 只,说明策略的利润来源很集中——一旦这几只股票表现不好,整体收益就没了。

读逐笔成交

trades.json 每笔成交一条记录:

entity_id: "000001"
side: "buy"                 # 买入
date: "20220315"
price: 12.45
shares: 8000
amount: 99600
fee: 29.88
cash_after: 900400

和

entity_id: "000001"
side: "sell"                # 卖出
date: "20220328"
price: 13.10
shares: 8000
amount: 104800
fee: 104.80
profit: 5100.32
cash_after: 994500.32

profit 只在卖出时有值——这是这笔卖出的已实现盈亏(扣了手续费)。

其他可用配置

初始资金和仓位

"portfolio": {
    "initial_capital": 1000000,       # 初始资金 100 万
    "allocation": {
        "mode": "equal_capital",      # 等额分配
        "max_portfolio_size": 10,     # 最多同时持有 10 只
        "max_weight_per_stock": 0.3,  # 单只最多占 30%
        "lots_per_trade": 1,          # 每次交易 1 手
    },
}

 

分配模式可选:

模式

逻辑

equal_capital

等额分配:每次买入用总资金 / 最大持仓数

equal_shares

等股数:每次买入固定股数

kelly

凯利公式:按历史胜率动态调整仓位

custom

自定义:在策略钩子里写分配逻辑

止盈止损

止盈止损在 goal 里配。这是策略的核心——决定什么时候卖。

"goal": {
    "stop_loss": {
        "stages": [
            {"ratio": -0.10, "close_invest": True},   # 亏 10% 全部止损
        ],
    },
    "take_profit": {
        "stages": [
            {"ratio": 0.20, "close_invest": False, "exit_ratio": 0.5},  # 涨 20% 卖一半
            {"ratio": 0.50, "close_invest": True},                      # 涨 50% 全部止盈
        ],
    },
}

关键字段:

  • ratio:触发比例。正数是止盈,负数是止损
  • close_invest:True = 整笔退出,False = 部分退出
  • exit_ratio:退出比例。0.5 = 退出初始仓位的 50%

 

过期平仓

"goal": {
    "expiration": {
        "fixed_window_in_days": 60,      # 持仓 60 天后强制平仓
        "mode": "trading_day",           # 按交易日算
    },
}

mode 可选 trading_day(交易日)或 open_day(日历日)。过期平仓是不触发止盈止损时的兜底——避免一笔交易一直挂着。

 

手续费

"fees": {
    "commission_rate": 0.0003,      # 佣金万三
    "min_commission": 5.0,           # 最低 5 元
    "stamp_duty_rate": 0.001,        # 印花税千一(卖出时收)
    "transfer_fee_rate": 0.00001,    # 过户费万零点一
}

手续费会吃掉一部分利润,特别是频繁交易的策略。RSI 策略平均持仓 12 天,换手不算快,手续费影响不大。但如果策略每天交易,手续费可能吃掉 30% 以上的利润。

 

风险控制

"simulation": {
    "risk_control": {
        "skip_enter_when": ["st", "star_st"],     # ST 股不买
        "force_exit_when": [                       # 持仓中触发条件强平
            "st",
            {"status": "star_st", "close_invest": True, "exit_ratio": 1.0},
        ],
    },
}

force_exit_when 和 skip_enter_when 的区别:前者是已经持有的股票变 ST 了要强制卖出,后者是新发现的 ST 股不买入。

 

净值曲线和成交记录开关

"portfolio": {
    "output": {
        "save_trades": True,            # 保存逐笔成交
        "save_equity_curve": True,      # 保存净值曲线
    },
}

两个都默认开启。如果回测很快不需要看明细,可以关掉省硬盘。

 

关于 Sharpe 和 Sortino

目前报告中不包含 Sharpe 和 Sortino 指标。这两个指标需要每日盯市净值序列(不是每个成交日的净值),属于独立的后处理步骤。后续版本会以独立计算模块的形式加入。