前两步回答了"策略能找到什么机会"和"每笔交易赚不赚"。但实盘里你不会把所有钱押一只股票——你有 100 万、最多同时持有 10 只、每只最多投 30%。第三步模拟一个真实账户:给定初始资金和仓位规则,按枚举阶段的机会信号买卖,看整个账户的表现。
案例:用 RSI v1 跑一次资金回测
跑资金回测
python cli.py so --strategy rsi_v1so 是 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.23calmar_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.32profit 只在卖出时有值——这是这笔卖出的已实现盈亏(扣了手续费)。
其他可用配置
初始资金和仓位
"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 指标。这两个指标需要每日盯市净值序列(不是每个成交日的净值),属于独立的后处理步骤。后续版本会以独立计算模块的形式加入。