小果资产配置回测系统助手
教程 https://gitcode.com/qq_50882340/xg_pz_backtrader
基于 xg_pz_backtrader 的 ETF 资产配置回测系统。核心思想:按用户设定的百分比 / 金额 / 数量给多个 ETF 一次性配置建仓并买入持有,随后逐日检查各标的涨跌幅,涨幅超过 sell_zdf 触发止盈卖出、跌幅低于 buy_zdf 触发止损买入(做T 高抛低吸),从而在持有底仓的同时滚动增厚收益。
本助手覆盖:数据治理(本地多标的 + 指数 + 自定义注入)→ 配置方案(三种 dt_type)→ 期初建仓 → 逐日做T → 绩效/年度/基准对比 → Excel+JSON 落盘全流程,并提供 60 个类方法的完整参考。
一、系统架构与核心逻辑
输入数据(多标的 OHLCV + 基准指数)
│
▼
┌───────────────────────────────┐
│ 1. 共同交易日对齐 │ 取所有标的日期交集
└───────────────────────────────┘
│
▼
┌───────────────────────────────┐
│ 2. 期初一次性建仓 │ 按 dt_type(百分比/金额/数量)×weight_list
│ (Initial Build) │ 计算每标的目标市值→100股整数倍买入
└───────────────────────────────┘
│
▼
┌───────────────────────────────┐
│ 3. 逐日做T (高抛低吸) │ 每日检查持仓标的当日 zdf:
│ 涨 ≥ sell_zdf → 止盈卖出 │ trade_value 金额卖出
│ 跌 ≤ buy_zdf → 止损买入 │ trade_value 金额买回
└───────────────────────────────┘
│
▼
┌───────────────────────────────┐
│ 4. 绩效归因 / 年度 / 基准对比 │ 净值曲线、回撤、夏普、超额…
└───────────────────────────────┘
│
▼
落盘 Excel + JSON(策略模型/资产配置策略/{user}/)
要点:
- 整体资金池:全部标的共享一份总现金 cash,靠配置权重决定各标的初始仓位;期初建仓后剩余现金作为做T子弹。
- 持仓永不主动清仓:除做T 卖出一部分外,底仓长期持有至回测结束,属于典型"底仓 + 机动做T"结构。
- 基准:按配置权重持有不动(不做T)的净值曲线。
二、安装与环境
# 依赖仅需
pip install pandas numpy pyarrow openpyxl
> 本系统依赖 pyarrow(读本地 parquet)与 openpyxl(导出 xlsx),无需 scipy。
三、导入方式
from xg_pz_backtrader.xg_pz_backtrader import xg_pz_backtrader
> 若你的脚本与 xg_pz_backtrader.py 位于同一目录,也可直接模块名导入:
> from xg_pz_backtrader import xg_pz_backtrader
四、快速上手
from xg_pz_backtrader.xg_pz_backtrader import xg_pz_backtrader
bt = xg_pz_backtrader(
start_date='20250101',
end_date='20500101',
stock_list=['159915.SZ', '513100.SH', '518880.SH'],
dt_type='百分比',
weight_list=[0.35, 0.35, 0.3], # 35%/35%/30%
index_stock='000300.SH',
cash=100000,
sell_zdf=0.03, # 当日涨幅 ≥3% → 止盈卖出做T
buy_zdf=-0.03, # 当日跌幅 ≤-3% → 止损买入做T
trade_value=1000, # 每次做T金额
comm=0.0001,
)
bt.run_backtest()
print(bt.generate_report())
bt.save_backtrader_data(user='test')
五、初始化参数详解
5.1 基础参数(Base)
| 参数 | 默认 | 说明 |
|---|---|---|
start_date |
'20260101' |
回测开始日期 %Y%m%d |
end_date |
'20500101' |
回测结束日期 |
stock_list |
['159915.SZ','513100.SH','518880.SH'] |
标的(ETF/股票)代码列表 |
index_stock |
'000300.SH' |
基准指数代码(仅用于对比/落盘) |
cash |
100000 |
初始总资金 |
comm |
0.0001 |
手续费率(默认万 1) |
max_workers |
4 |
多线程加载数据的线程数 |
use_custom_data |
False |
是否使用自定义标的数据源 |
use_custom_index_data |
False |
是否使用自定义指数数据源 |
5.2 配置参数(核心:dt_type + weight_list)
| 参数 | 默认 | 说明 |
|---|---|---|
dt_type |
'百分比' |
配置方式:'百分比'/'金额'/'数量' |
weight_list |
[0.4,0.4,0.2] |
与 stock_list 一一对应。按 dt_type 解释:百分比→权重(自动归一化);金额→每标的金额(元);数量→每标的目标份额(份) |
dt_type 三种口径:
- '百分比':weight_list=[0.4,0.4,0.2] 表示 40%/40%/20%,构造时若总和≠1 自动归一化。
- '金额':weight_list=[5000,5000,5000] 表示每标的各配置 5000 元买入。
- '数量':weight_list=[100,100,100] 表示每标的目标持有 100 份(按首日价折算金额买入)。
> weight_list 长度必须等于 stock_list,否则构造抛 ValueError。
5.3 做T参数(高抛低吸)
| 参数 | 默认 | 说明 |
|---|---|---|
sell_zdf |
0.03 |
止盈阈值:某标当日涨跌幅 ≥ 此值 → 卖出 trade_value 金额份额 |
buy_zdf |
-0.03 |
止损阈值:某标当日涨跌幅 ≤ 此值 且 现金够 → 买入 trade_value 金额份额 |
trade_value |
1000 |
每次做T 的交易金额 |
> 做T方向约定(本系统反直觉点):sell_zdf 是卖出触发阈值(正数,涨幅),buy_zdf 是买入触发阈值(负数,跌幅),trade_value 是每次机动金额,与海龟/网格等一致用 zdf 判断当日相对昨收的涨跌。
六、数据字段与数据目录要求
6.1 必需字段(OHLCV)
date / close / open / high / low / volume 六列必须存在。
6.2 推荐字段
| 字段 | 说明 |
|---|---|
preClose |
前收盘价(用于复权计算,本系统默认 adj_type='none' 不强制) |
zdf |
涨跌幅(不存在会自动 close.pct_change() 计算) |
amount |
成交金额(可选) |
6.3 本地数据目录结构
工作目录/
├── xg_pz_backtrader.py
└── data/
├── 历史数据/
│ ├── 159915.SZ.parquet # 每标的一份,股票代码命名
│ ├── 513100.SH.parquet
│ └── ...
└── 指数数据/
└── 000300.SH.parquet # 基准指数,指数代码命名
- 标的本地读取仅走
data/历史数据/{code}.parquet(单一路径,无多路径回退)。 - 指数本地读取仅走
data/指数数据/{index_stock}.parquet(仅 parquet)。 - 未用自定义数据时,读取会过滤
close>0、open>0的无效行并自动计算zdf。
七、方法清单(60 个类方法速查)
> 详细签名见第 8 章示例与文末;save_to_json 在本系统真实存在(区别于综合动量)。
| 分类 | 方法 |
|---|---|
| 构造/工具 | __init__ _convert_to_serializable |
| 指数注入(7) | add_index_data_from_dataframe add_index_data_from_excel add_index_data_from_csv add_index_data_from_json add_index_data_from_dict add_index_data_from_parquet add_index_data_from_bytes |
| 指数管理 | clear_index_data has_index_data get_index_data_info _normalize_index_dataframe |
| 标的注入(7) | add_stock_data_from_dataframe add_stock_data_from_excel add_stock_data_from_csv add_stock_data_from_json add_stock_data_from_dict add_stock_data_from_parquet add_stock_data_from_bytes |
| 标的管理 | clear_custom_data remove_stock_data get_custom_data_keys has_custom_data |
| 数据加载/处理 | _normalize_dataframe get_stock_data get_index_data _load_single_stock get_all_stock_data adjust_price |
| 基准/撮合 | calculate_benchmark_curve calculate_buy_shares calculate_sell_shares calculate_sell_shares_all |
| 绩效/年度 | calculate_annual_performance get_annual_performance get_annual_performance_df calculate_performance_metrics |
| 回测/生成 | vectorized_backtest generate_equity_curve_data generate_trade_data generate_position_data generate_account_data |
| getter(懒触发) | get_trade_data get_position_data get_account_data get_daily_positions get_trade_log get_portfolio_history get_performance_metrics get_equity_curve_data get_stock_results get_backtest_summary get_all_data get_backtrader_data |
| 配置/报告/导出 | get_config run_backtest generate_report save_backtrader_data save_to_json |
八、完整实战示例
8.1 百分比配置 + 做T(本地数据)
from xg_pz_backtrader.xg_pz_backtrader import xg_pz_backtrader
bt = xg_pz_backtrader(
start_date='20240101', end_date='20261231',
stock_list=['159915.SZ', '513100.SH', '518880.SH'],
dt_type='百分比', weight_list=[0.4, 0.4, 0.2],
index_stock='000300.SH', cash=100000,
sell_zdf=0.05, buy_zdf=-0.05, trade_value=2000,
)
bt.run_backtest()
perf = bt.get_performance_metrics()
print(f"年化:{perf['annual_return']:.2%} 回撤:{perf['max_drawdown']:.2%} 夏普:{perf['sharpe_ratio']:.2f}")
8.2 金额配置(固定金额买入持有)
bt = xg_pz_backtrader(
stock_list=['510300.SH', '510500.SH'],
dt_type='金额', weight_list=[50000, 50000], # 每只 5 万
cash=120000,
)
8.3 数量配置(固定份额)
bt = xg_pz_backtrader(
stock_list=['159915.SZ', '510300.SH'],
dt_type='数量', weight_list=[1000, 500], # 目标各 1000/500 份
)
8.4 自定义标的数据注入(DataFrame)+ 结果 JSON 落盘
import pandas as pd
from xg_pz_backtrader.xg_pz_backtrader import xg_pz_backtrader
bt = xg_pz_backtrader(
stock_list=['A', 'B'],
dt_type='百分比', weight_list=[0.5, 0.5],
use_custom_data=True,
)
bt.add_stock_data_from_dataframe('A', pd.read_parquet('data/a.parquet'))
bt.add_stock_data_from_dataframe('B', pd.read_parquet('data/b.parquet'))
bt.run_backtest()
bt.save_backtrader_data(user='test') # 分类落盘 Excel+JSON
bt.save_to_json('result/pz.json') # 本系统真实存在的 JSON 便捷落盘
8.5 自定义指数注入 + 关闭做T
bt = xg_pz_backtrader(..., use_custom_index_data=True)
bt.add_index_data_from_csv('000300.SH', 'data/index.csv') # 需 date+close 列
# 做T可用极大阈值近似关闭:
bt.sell_zdf, bt.buy_zdf = 1.0, -1.0 # 正常涨跌几乎不触发 → 纯持有
九、输出与落盘
9.1 导出目录结构
策略模型/资产配置策略/{user}/
├── 指数数据/指数数据.{xlsx,json}
├── 成交数据/成交数据.{xlsx,json}
├── 持股数据/持股数据.{xlsx,json}
├── 账户数据/账户数据.{xlsx,json}
├── 绩效指标/绩效指标.{xlsx,json}
├── 净值曲线/净值曲线.json
├── 策略参数/策略参数.{xlsx,json}
└── 年度收益统计/年度收益统计.{xlsx,json}
9.2 绩效指标字段
start_date end_date total_days total_cash final_value total_return total_pnl total_commission remaining_cash final_holdings annual_return max_drawdown max_drawdown_date sharpe_ratio sortino_ratio calmar_ratio win_rate volatility 及基准对比 benchmark_final_value benchmark_total_return benchmark_start_value benchmark_max_drawdown excess_return excess_return_pct,另含配置留档 dt_type weight_list sell_zdf buy_zdf trade_value use_custom_data use_custom_index_data。
9.3 成交/持股/账户数据结构
- 成交:
date stock type price shares amount commission reason - 持股:每标期末
持有份额 / 市值 / 成本 / 盈亏 - 账户:
date total_value cash holdings daily_return benchmark_net_value ...
9.4 交易类型(trade_log.type)
初始建仓 / 止盈卖出 / 止损买入(做T 机动不改变"持有"属性)。
十、常见问题 FAQ
Q1 导入时报 ModuleNotFoundError?
确认文件名 xg_pz_backtrader.py 与导入写法匹配;脚本与 .py 同目录用模块名导入,否则用包路径导入(见第三章)。
Q2 某标的没建仓?
首日 close<=0 会被跳过;weight_list 对应配置值需 >0;总现金需覆盖该标的目标金额。
Q3 为什么几乎不做T?
做T 需要标的当日涨跌幅超过阈值,且需持有份额 >0(卖)或现金 ≥trade_value(买)。阈值设过大或标的不活跃就不触发。
Q4 weight_list 长度不一致?
构造会抛 ValueError;务必与 stock_list 等长。
Q5 涨跌停怎么办?
本系统按当日涨跌幅 zdf 判断做T,未内置涨跌停过滤;如需可自行在注入数据时剔除异常。
十一、二次开发入口
- 修改
calculate_buy_shares/calculate_sell_shares:定制撮合(份额、四舍五入、滑点)。 - 修改
vectorized_backtest主循环:插入自定义调仓/风控规则。 - 修改
calculate_benchmark_curve:更换基准口径。 - 结果取出:所有
get_*getter 懒触发自动run_backtest()。
更多细节见配套《小果资产配置回测系统助手.md》完整使用教程(含全部源码)。