小果条件多因子量化回测系统专家
一、系统概述
使用教程:https://gitcode.com/qq_50882340/xg_condi_factor_backtrader
1.1 系统定位
小果条件因子量化回测系统(xg_condi_factor_backtrader)是一套专为条件因子轮动型策略设计的专业级量化回测框架。它把「用一组买入条件筛出候选 → 用若干排序因子排出强弱 → 持有排名前 N 只 → 触发卖出条件或止盈就离场」这套经典条件选股轮动逻辑,抽象成可配置的流水线:研究者只需配置「因子公式 + 条件字典」,即可完成策略回测、轮动调仓与绩效归因。
1.2 核心策略逻辑(每个调仓日)
系统按 interval 个自然日为一个调仓周期,只在调仓日做决策(非调仓日仅记录净值):
① 买入条件过滤:buy_condi_factor 中 and 组全部满足 / or 组至少满足其一 / not 组必须不满足
→ 得到候选股票池
② 排序因子排名:rank_factor = {因子名: 降序/升序},按字典顺序多列排序(NaN 排最后)
③ 目标持仓:hold_stock_limit;先处理卖出腾出空位
④ 卖出条件离场:sell_condi_factor 中任一条件命中 → 该持仓【整仓卖出】(因子卖出)
⑤ 止盈离场:持仓收益率(现价/买入价-1) ≥ sell_zdf → 按 sell_type/sell_value 部分卖出(止盈卖出)
⑥ 空位补入:按排序结果从前到后买入新标的,直到持仓数 = hold_stock_limit
⑦ 约束:min_hold_days 保护(持有不足 N 天不卖)、涨跌停过滤、单票仓位上限、先卖后买
> 条件因子系统区别于"纯因子打分"的地方在于:买入/卖出由离散条件触发(Bool/数值比较),排序只决定"买谁"的优先级,两者解耦、配置直观。
1.3 因子值语义约定
- Bool 类型因子(金叉/死叉、价格在均线上等):自定义因子公式返回
True/False(也可写'T'/'t'/'TRUE'/'F'/'f'/'FALSE'字符串)。 - 数值类型因子(连续上涨/下跌天数、涨跌幅、价格、评分等):正常数值,用
大于/小于/等于比较。 - ⚠️
1和0不作为 Bool 值,一律按数值处理。布尔判断请用True/False,条件配置的"值"也填True/False。
1.4 设计哲学
- 条件驱动、解耦透明:买入/卖出/排序各自独立配置,策略意图一目了然,且每次调仓的排序结果、触发条件都可追溯。
- 灵活可扩展:自定义因子公式(内置通达信
xg_tdx_func全函数)、自定义数据注入(标的+指数)、子类可重写任一环节。 - 稳健风控:最小持有天数、涨跌停过滤、单票仓位上限、止盈/条件卖出双通道。
1.5 数据文件字段要求
| 字段名 | 说明 | 是否必须 |
|---|---|---|
date |
日期 | ✅ 必须 |
close |
收盘价 | ✅ 必须 |
open |
开盘价 | ✅ 必须 |
high |
最高价 | ✅ 必须 |
low |
最低价 | ✅ 必须 |
volume |
成交量 | ✅ 必须 |
preClose |
前收盘价(用于涨跌停过滤与复权) | ⚠️ 推荐 |
zdf |
涨跌幅(缺失时自动用 close.pct_change()) | ⚠️ 推荐 |
amount |
成交金额 | ⚠️ 可选 |
证券代码 / 证券名称 |
代码/名称(缺失时自动补) | ⚠️ 可选 |
二、安装与运行环境
2.1 安装包
教程 https://gitcode.com/qq_50882340/xg_condi_factor_backtrader
2.2 依赖
pandas、numpypyarrow(读取本地 parquet 数据用)openpyxl(导出 Excel 用)- 可选:
xg_tdx_func(自定义因子公式函数库,提供 MA/EMA/MACD/RSI/KDJ/IF/REF 等全函数)
2.3 包结构
xg_condi_factor_backtrader/
├── xg_condi_factor_backtrader.py # 主回测类(含模块级配置解析辅助函数)
├── data/ # (可选)本地行情数据
│ ├── 全部因子数据/ # {代码}.parquet(优先,内含因子列)
│ ├── 历史数据/ # {代码}.parquet(回退目录)
│ └── 指数数据/ # {指数代码}.parquet
└── xg_tdx_func/ # (可选)通达信指标函数库
2.4 导入方式
from xg_condi_factor_backtrader.xg_condi_factor_backtrader import xg_condi_factor_backtrader
2.5 数据目录与读取规则
get_stock_data 读取标的时按需取列(base 列 + 买入/排序/卖出因子名 + 自定义因子基础列):
- 自定义数据源:
use_custom_data=True且已注入 → 直接使用; - 本地 parquet:优先
data/全部因子数据/{代码}.parquet(该目录数据本身含大量因子列),不存在则回退data/历史数据/{代码}.parquet。
指数数据 get_index_data():优先使用注入的自定义指数,否则读取 data/指数数据/{指数代码}.parquet(带缓存)。
三、初始化参数完整参考
> 实例化签名见源码 __init__;下面按功能分组说明全部参数。
3.1 时间与标的 (Time & Universe)
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
start_date |
str |
'20250101' |
回测起始日期,格式 YYYYMMDD 或 YYYY-MM-DD |
end_date |
str |
'20261201' |
回测结束日期 |
stock_list |
list |
['159915.SZ','513100.SH','518880.SH'] |
候选标的池,需带交易所后缀 |
index_stock |
str |
'000300.SH' |
对比指数代码(注入自定义指数后以注入数据为准) |
3.2 资金与费用 (Capital & Cost)
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
cash |
float |
100000 |
初始资金(元) |
comm |
float |
0.0001 |
手续费率(万1=0.0001) |
min_commission |
float |
0 |
单笔最低佣金(元),0=不设下限 |
slippage |
float |
0 |
滑点比例(0.001=千1),买入加价、卖出折价 |
risk_free_rate |
float |
0.02 |
无风险利率(年化),用于夏普计算 |
3.3 交易与仓位 (Trading & Position)
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
trader_type |
str |
'百分比' |
买入资金分配:'百分比' / '金额' / '数量' |
trader_value |
float |
0.5 |
交易值,含义随 trader_type 变化 |
hold_stock_limit |
int |
2 |
最大持仓数(也是排序取前N的N) |
max_single_position_ratio |
float |
1.0 |
单只标的占总资产比例上限(百分比模式生效),范围 (0,1] |
interval |
int |
1 |
调仓间隔(自然日),非调仓日只记账不交易 |
min_hold_days |
int |
1 |
最小持有天数(自然日),不足 N 天不卖出 |
max_workers |
int |
4 |
数据多线程加载并发数 |
trader_type 与 trader_value 匹配表:
trader_type |
trader_value 含义 |
每只买入金额逻辑 |
|---|---|---|
'百分比' |
总资产比例 | 总资产 × trader_value ÷ 待买数量,并受单票上限约束 |
'金额' |
固定总金额 | trader_value ÷ 待买数量 |
'数量' |
固定总股数 | trader_value 股(金额 = 股数 × 价格) |
3.4 自定义因子与条件 (Factor & Condition)
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
is_open_user_factor |
bool |
True |
是否启用自定义因子公式计算 |
user_factor_list |
list |
['close','high','low','open','amount','volume','zdf'] |
自定义公式所需的基础列 |
user_factor_cacal |
dict |
内置示例 | 自定义因子公式字典 {因子名: 公式字符串} |
buy_condi_factor |
dict |
内置示例 | 买入条件配置字典 |
rank_factor |
dict |
内置示例 | 排序因子配置字典 {因子名: '降序'/'升序'} |
sell_condi_factor |
dict |
内置示例 | 卖出条件配置字典 |
sell_type |
str |
'百分比' |
止盈类型:'none' / '金额' / '数量' / '百分比' |
sell_zdf |
float |
0.03 |
止盈阈值:持仓收益率(现价/买入价−1) ≥ 此值触发 |
sell_value |
float |
0.5 |
止盈卖出值,含义随 sell_type 变化 |
enable_limit_up_down_filter |
bool |
True |
是否按涨跌幅(前收口径)过滤涨跌停,|pct_change|≥9.5% 剔除 |
use_custom_data |
bool |
False |
是否使用内存注入的标的数据(注入成功自动置 True) |
use_custom_index_data |
bool |
False |
是否使用内存注入的指数数据(注入成功自动置 True) |
sell_type 与 sell_value 匹配表:
sell_type |
sell_value 含义 |
触发后行为 |
|---|---|---|
'none' |
忽略 | 不启用涨幅止盈,仅靠卖出条件离场 |
'金额' |
卖出市值金额 | 卖出约指定金额(不足则全卖) |
'数量' |
卖出股数 | 卖出指定股数(不足则全卖) |
'百分比' |
卖出仓位比例 | 卖出 持仓 × sell_value(须在 (0,1]),如 0.5=卖一半 |
3.5 内部固定参数(一般不修改)
| 参数 | 值 | 说明 |
|---|---|---|
adj_type |
'none' |
复权方式(当前固定不做前/后复权) |
min_shares / share_multiple |
100 |
ETF 一手 100 份;买卖按 100 份整数倍 |
verbose |
True |
是否打印详细运行日志 |
四、自定义因子公式引擎详解
4.1 公式编写规范
- 数据对象:公式中固定用
df代表当前股票全量日线 DataFrame; - 函数库:自动导入
xg_tdx_func全部函数(MA/EMA/MACD/KDJ/RSI/CCI/WR/BOLL/SAR/OBV/MFI/VR/REF/HHV/LLV/COUNT/EVERY/CROSS/CROSS_DOWN/IF/STD 等),无需 import; - 返回类型:
pd.Series(长度与 df 一致)、bool/int/float标量(广播为常量列)、或可转换对象; - Bool 因子写法:条件成立返回
True,否则False,例如"IF(df['close']>MA(df['close'],5),True,False)"; - 命名约束:自定义因子名不可与已有列名重复,含空格/下划线/横杠的名称会被自动归一化匹配。
4.2 典型自定义因子示例
user_factor_cacal = {
# Bool 因子:收盘价是否在 5 日均线上(True=是)
"收盘价大于5日均线": "IF(df['close']>MA(df['close'],5),True,False)",
# Bool 因子:5 日均线是否在 10 日均线上
"5日均线在10日均线上": "IF(MA(df['close'],5)>MA(df['close'],10),True,False)",
# 数值因子:均线多头评分(0-100)
"均线评分": (
"IF(MA(df['close'],3)>MA(df['close'],5),25,0)+"
"IF(MA(df['close'],5)>MA(df['close'],10),25,0)+"
"IF(MA(df['close'],10)>MA(df['close'],20),25,0)+"
"IF(MA(df['close'],20)>MA(df['close'],30),25,0)"
),
# 数值因子:20 日动量
"20日动量": "df['close']/REF(df['close'],20)-1",
# Bool 因子:MACD 金叉
"MACD金叉": "IF(CROSS(MACD(df['close'],12,26,9)[0], MACD(df['close'],12,26,9)[1]),True,False)",
}
> 注:若本地 data/全部因子数据 已含某个因子列(如"连续上涨天数"、"5日涨跌幅"等),可直接在条件/排序中引用,无需再写公式。
五、策略配置详解与最佳实践
5.1 买入条件配置 (buy_condi_factor)
配置格式:
buy_condi_factor = {
"因子名A": {"选择类型": "and|or|not", "选择方向": "等于|大于|小于", "值": 目标值},
...
}
逻辑组合规则: 系统把条件分成三组——
and组:所有 and 条件必须同时满足;or组:or 条件至少满足其一(与 and 组结果取交集);not组:条件必须不满足(取反后与 and 组结果取交集)。
Bool 判定示例:
buy_condi_factor = {
"收盘价大于5日均线": {"选择类型": "and", "选择方向": "等于", "值": True},
"20日动量": {"选择类型": "and", "选择方向": "大于", "值": 0},
"5日均线在10日均线上": {"选择类型": "not", "选择方向": "等于", "值": False}, # 5均线不能跌破10均线
}
最佳实践: 布尔因子用 等于 True 表达"必须满足"、等于 False 表达"必须不满足";数值因子用 大于/小于 过滤;条件数量建议 3-5 个,避免候选池过小。
5.2 卖出条件配置 (sell_condi_factor)
配置格式同买入条件。判定规则: 对当前持仓的每只标的,任一卖出因子条件命中即整仓卖出(因子卖出,全部卖出)。
sell_condi_factor = {
"收盘价大于5日均线": {"选择类型": "or", "选择方向": "等于", "值": False}, # 跌破5日线就卖
"连续下跌天数": {"选择类型": "or", "选择方向": "大于", "值": 2}, # 或连跌超2天
}
> 注意:卖出条件是"整仓离场"信号;想要"分批止盈/减仓",请用涨幅止盈 sell_type(部分卖出)。
5.3 排序因子配置 (rank_factor)
rank_factor = {
"均线评分": "降序", # 分数高者排前、优先买入
"20日动量": "降序",
}
- 值只支持
'降序'/'升序'; - 按字典定义顺序做多列排序(先按第一个因子排,再按第二个…),先定义的因子优先级更高;
- 因子值缺失(NaN)统一排最后;因子名不存在会记入
missing_factor_log,并在报告尾部打印"因子缺失警告"。
5.4 涨幅止盈配置 (sell_type / sell_zdf / sell_value)
- 触发条件用持仓收益率:
(当日收盘价 ÷ 买入价 − 1) ≥ sell_zdf(区别于"当日涨跌幅"); - 触发后按
sell_type部分卖出sell_value对应市值/股数/仓位比例; sell_type='none'表示不启用涨幅止盈;- 卖出条件与止盈可同时生效:同一标的若同时命中卖出条件与止盈,止盈卖出优先按止盈规则处理(有 min_hold_days 保护时两者都会被拦下)。
5.5 交易参数调优建议
| 参数 | 建议 | 说明 |
|---|---|---|
interval |
1-5(短线) / 5-20(波段) | 间隔越大调仓越少、换手越低;注意按自然日计 |
hold_stock_limit |
3-10 | 持仓数越多越分散、换手越低 |
min_hold_days |
1-10 | 防止刚买入就因短期噪音被卖出条件洗出 |
sell_zdf |
0.05-0.30 | 涨幅止盈阈值,与持仓周期匹配 |
max_single_position_ratio |
0.1-0.3 | 控制单票集中度 |
六、完整函数清单(含内部方法)
> 框架类 xg_condi_factor_backtrader 共 69 个类方法;另提供 2 个模块级辅助函数(parse_factor_config / parse_config_with_bool,安全解析含 True/False/T/F 等布尔形态的因子配置字符串)。以下按功能模块列出,便于二次开发和深度集成。
6.1 初始化和配置管理
| 方法签名 | 说明 |
|---|---|
__init__(...) |
构造回测实例,配置全部参数 |
_validate_factor_config() |
验证买入/卖出条件与排序因子配置格式合法性 |
_validate_params() |
验证参数边界(持仓数、日期范围等) |
_print_config() |
打印完整初始化配置 |
get_config() -> dict |
获取当前全部策略参数(含数据源状态) |
6.2 数据加载与处理
| 方法签名 | 说明 |
|---|---|
get_stock_data(stock_code) |
读取单只标的(自定义源 → 全部因子数据 → 历史数据),按需取列并算因子 |
_process_stock_data(df, stock_code) |
处理单只标的数据:日期解析/过滤、涨跌停过滤、复权、自动 zdf、自定义因子计算 |
_load_single_stock(stock) |
多线程任务包装:加载单只,返回 (code, df, success, msg) |
get_all_stock_data() -> dict |
多线程并发加载全部标的 |
get_index_data() -> pd.DataFrame |
获取指数数据(自定义指数优先,其次本地 parquet,带缓存) |
adjust_price(df) |
价格复权处理(adj_type='none' 时原样返回) |
_normalize_dataframe(df, stock_code) |
标准化注入数据(日期、缺列、代码列、zdf、复权因子) |
_normalize_index_dataframe(df) |
标准化注入的指数数据(日期识别/排序、close>0 过滤) |
_calculate_buy_hold_net_value(stock_data) |
计算等权持有不动基准净值 |
_calculate_index_net_value(index_data) |
计算指数净值 |
6.3 自定义数据注入接口
| 方法签名 | 说明 |
|---|---|
add_stock_data_from_dataframe(stock_code, df) -> bool |
标的:从 DataFrame 注入 |
add_stock_data_from_excel(stock_code, file_path, sheet_name=0, **kwargs) |
标的:从 Excel 注入 |
add_stock_data_from_csv(stock_code, file_path, **kwargs) |
标的:从 CSV 注入(自动尝试多编码) |
add_stock_data_from_json(stock_code, file_path, orient='records', **kwargs) |
标的:从 JSON 注入 |
add_stock_data_from_dict(stock_code, data) |
标的:从字典注入 |
add_stock_data_from_parquet(stock_code, file_path, columns=None, **kwargs) |
标的:从 Parquet 注入 |
add_stock_data_from_bytes(stock_code, file_bytes, file_type='csv', **kwargs) |
标的:从字节流注入(适合上传) |
add_index_data_from_dataframe(df) -> bool |
指数:从 DataFrame 加载 |
add_index_data_from_excel(file_path, sheet_name=0, **kwargs) |
指数:从 Excel 加载 |
add_index_data_from_csv(file_path, **kwargs) |
指数:从 CSV 加载 |
add_index_data_from_json(file_path, orient='records', **kwargs) |
指数:从 JSON 加载 |
add_index_data_from_dict(data) |
指数:从字典加载 |
add_index_data_from_parquet(file_path, columns=None, **kwargs) |
指数:从 Parquet 加载 |
add_index_data_from_bytes(file_bytes, file_type='csv', **kwargs) |
指数:从字节流加载 |
6.4 自定义数据管理
| 方法签名 | 说明 |
|---|---|
clear_custom_data() |
清空所有注入的标的自定义数据 |
remove_stock_data(stock_code) -> bool |
移除指定标的自定义数据 |
get_custom_data_keys() -> list |
已注入数据的标的代码列表 |
has_custom_data(stock_code) -> bool |
检查某标的是否已注入 |
clear_index_data() |
清空自定义指数数据 |
has_index_data() -> bool |
是否已有自定义指数数据 |
get_index_data_info() -> dict |
指数数据信息(has_data/rows/start_date/end_date/columns) |
6.5 因子条件判定与排序
| 方法签名 | 说明 |
|---|---|
_parse_date(date_str) |
灵活日期解析 |
_normalize_factor_name(factor_name, df_columns) |
规范化因子名(去空格/下划线/横杠模糊匹配) |
_is_bool_string(value) |
判断是否为布尔字符串('T','true','F','false' 等) |
_is_bool_like(value) |
判断值是否布尔类型(含字符串布尔) |
_convert_to_bool(value) |
各种布尔形态转 Python bool |
_check_factor_condition_single(value, target, direction) |
检查单个因子条件(等于/大于/小于,含布尔比较) |
check_factor_condition(df, factor_name, config) |
检查某因子条件,返回布尔掩码 Series |
filter_by_buy_conditions(df_date) |
应用买入条件过滤(and/or/not 分组),返回候选代码列表 |
apply_rank_factors(df_date, stock_list) |
按 rank_factor 多列排序,返回排序后的代码列表 |
filter_by_sell_conditions(df_date, holdings) |
判断当前持仓是否命中卖出条件,返回需卖出列表 |
6.6 交易执行与计算
| 方法签名 | 说明 |
|---|---|
calculate_commission(amount) |
计算佣金(支持最低佣金) |
calculate_buy_shares(amount, price) |
计算买入股数(含滑点、先扣佣金、100 份整手)与实际成本 |
calculate_sell_shares(shares, price, sell_value, sell_type) |
按止盈类型(金额/数量/百分比)计算卖出股数与实收金额 |
_record_daily_position(date, cash, positions, factor_maps, merged_records) |
记录每日持仓与账户状态 |
6.7 回测执行
| 方法签名 | 说明 |
|---|---|
run_backtest() -> dict |
执行完整条件因子回测,返回绩效指标(有缓存) |
_print_results() |
打印回测结果摘要 |
6.8 数据生成与转换
| 方法签名 | 说明 |
|---|---|
_convert_to_serializable(obj) |
递归转 JSON 可序列化类型 |
generate_equity_curve_data() -> dict |
生成净值曲线数据 |
generate_trade_data() -> dict |
生成成交数据 |
generate_position_data() -> dict |
生成持仓数据 |
generate_account_data() -> dict |
生成账户数据 |
6.9 绩效评估与报告
| 方法签名 | 说明 |
|---|---|
calculate_performance_metrics(index_data=None) -> dict |
计算核心绩效指标 |
calculate_annual_performance() -> dict |
计算年度收益统计 |
get_performance_metrics() -> dict |
获取绩效指标(自动触发回测) |
get_equity_curve_data() -> dict |
获取净值曲线 |
get_trade_data() -> dict |
获取成交数据 |
get_position_data() -> dict |
获取持仓数据 |
get_account_data() -> dict |
获取账户数据 |
get_annual_performance() -> dict |
获取年度收益统计 |
get_backtrader_data() -> dict |
获取回测全量数据(JSON 可序列化) |
generate_report() -> str |
生成格式化文本报告 |
save_to_json(filename=None) -> str |
保存回测结果为 JSON 文件 |
save_backtrader_data(user='test') -> str |
分类保存为 Excel + JSON |
七、输出数据结构详解
> 回测结束后生成 8 类输出数据,所有 get_* 首次调用会自动触发回测缓存。
7.1 绩效指标字段 (performance_metrics)
| 字段 | 类型 | 说明 |
|---|---|---|
start_date / end_date |
str |
回测起止日期 |
total_days |
int |
记录总天数 |
final_value |
float |
最终总资产 |
total_pnl |
float |
总盈亏额 |
total_return |
float |
总收益率(小数,0.25=25%) |
annual_return |
float |
年化收益率 |
max_drawdown |
float |
最大回撤(负数) |
sharpe_ratio |
float |
夏普比率 |
volatility |
float |
年化波动率 |
total_trades |
int |
总交易笔数 |
win_rate |
float |
胜率 |
profit_loss_ratio |
float |
盈亏比 |
final_holdings |
float |
期末持仓总股数 |
remaining_cash |
float |
期末剩余现金 |
benchmark_final_value |
float |
基准(等权持有不动)期末净值 |
benchmark_total_return |
float |
基准总收益率 |
index_final_value |
float |
指数期末净值 |
index_total_return |
float |
指数总收益率 |
excess_return |
float |
相对基准超额收益 |
7.2 净值曲线字段 (equity_curve_data)
dates / total_value / cumulative_pnl / cash / holdings / net_value(总资产÷初始资金)/ drawdown(%) / total_return / daily_return / benchmark_net_value / index_net_value,可直接画图。
7.3 成交记录字段 (trade_data / trade_log)
| 字段 | 说明 |
|---|---|
date |
交易日期 |
stock |
标的代码 |
type |
买入 / 因子卖出 / 止盈卖出 |
price / shares / amount / commission |
价格 / 股数 / 金额(买入为含手续费成本,卖出为实收) / 手续费 |
reason |
原因,如 排名买入 (排名: 2)、触发卖出因子条件、金额止盈 盈利520.00元 |
trade_data['statistics']:total_trades / buy_trades / sell_trades / total_commission。
7.4 持仓/账户/年度统计
position_data:{'positions': [{date, holdings, cash, total_value, cumulative_pnl, total_return(百分数), details(代码:股数;…)}], 'statistics': {'total_days': N}};account_data:{'account_history': [{date, total_value, cash, holdings, cumulative_pnl, net_value, total_return(百分数), daily_return(百分数), benchmark_net_value, index_net_value}], 'statistics': {...}};annual_performance:按年统计year/start_date/end_date/total_days/total_return/annual_return/max_drawdown/sharpe_ratio/sortino_ratio/calmar_ratio/win_rate/volatility/benchmark_total_return/index_total_return/excess_return/excess_return_pct。
7.5 导出目录结构
> save_backtrader_data(user='xxx') 保存到类脚本目录下:
策略模型/条件多因子策略/<user>/
├── 指数数据/ 指数数据.xlsx + .json
├── 成交数据/ 成交数据.xlsx + .json
├── 持股数据/ 持股数据.xlsx + .json
├── 账户数据/ 账户数据.xlsx + .json
├── 绩效指标/ 绩效指标.xlsx + .json
├── 净值曲线/ 净值曲线.xlsx + .json
├── 策略参数/ 策略参数.xlsx + .json
└── 年度收益统计/ 年度收益统计.xlsx + .json
八、完整实战示例合集
8.1 示例1:经典条件因子轮动(本地数据)
from xg_condi_factor_backtrader.xg_condi_factor_backtrader import xg_condi_factor_backtrader
bt = xg_condi_factor_backtrader(
start_date='20240101',
end_date='20261201',
stock_list=['159915.SZ', '513100.SH', '518880.SH'],
index_stock='000300.SH',
cash=100000,
comm=0.0001,
trader_type='百分比',
trader_value=0.5,
hold_stock_limit=2,
user_factor_cacal={
"收盘价大于5日均线": "IF(df['close']>MA(df['close'],5),True,False)",
"均线评分": "IF(MA(df['close'],3)>MA(df['close'],5),25,0)+IF(MA(df['close'],5)>MA(df['close'],10),25,0)+IF(MA(df['close'],10)>MA(df['close'],20),25,0)+IF(MA(df['close'],20)>MA(df['close'],30),25,0)",
},
buy_condi_factor={
"收盘价大于5日均线": {"选择类型": "and", "选择方向": "等于", "值": True},
"20日动量": {"选择类型": "and", "选择方向": "大于", "值": 0},
},
rank_factor={
"均线评分": "降序",
},
sell_condi_factor={
"收盘价大于5日均线": {"选择类型": "or", "选择方向": "等于", "值": False},
"连续下跌天数": {"选择类型": "or", "选择方向": "大于", "值": 2},
},
sell_type='金额', # 涨幅止盈:按金额卖出
sell_zdf=0.03, # 持仓收益 ≥3% 触发
sell_value=1000, # 每次止盈卖 1000 元
interval=1,
min_hold_days=1,
)
metrics = bt.run_backtest()
print(bt.generate_report())
bt.save_backtrader_data(user="条件多因子示例")
8.2 示例2:关闭涨幅止盈,纯卖出条件轮动
bt = xg_condi_factor_backtrader(
start_date='20240101', end_date='20261201',
stock_list=['159915.SZ', '513100.SH', '518880.SH', '510300.SH'],
cash=200000, hold_stock_limit=3, interval=2,
buy_condi_factor={"20日动量": {"选择类型": "and", "选择方向": "大于", "值": 0}},
rank_factor={"20日动量": "降序"},
sell_condi_factor={"20日动量": {"选择类型": "or", "选择方向": "小于", "值": 0}},
sell_type='none', # 不启用涨幅止盈
)
bt.run_backtest()
print(bt.generate_report())
8.3 示例3:自定义数据注入(标的 + 指数)回测
import pandas as pd
bt = xg_condi_factor_backtrader(
start_date='20240101', end_date='20261201',
stock_list=['159915.SZ', '513100.SH', '518880.SH'],
index_stock='CUSTOM_INDEX', # 占位,指数以注入为准
cash=100000, hold_stock_limit=2,
buy_condi_factor={"收盘价大于5日均线": {"选择类型": "and", "选择方向": "等于", "值": True}},
rank_factor={"均线评分": "降序"},
sell_condi_factor={"收盘价大于5日均线": {"选择类型": "or", "选择方向": "等于", "值": False}},
use_custom_data=True,
use_custom_index_data=True,
)
bt.add_stock_data_from_csv('159915.SZ', 'data/159915.csv')
bt.add_stock_data_from_excel('513100.SH', 'data/513100.xlsx', sheet_name='Sheet1')
bt.add_index_data_from_dataframe(pd.read_csv('data/index_000300.csv'))
print("已注入标的:", bt.get_custom_data_keys())
print("指数信息:", bt.get_index_data_info())
bt.run_backtest()
print(bt.generate_report())
bt.save_to_json('result/condi_backtest.json')
8.4 示例4:提取结果做二次分析 / 画净值曲线
bt.run_backtest()
metrics = bt.get_performance_metrics()
curve = bt.get_equity_curve_data()
trades = bt.get_trade_data()
print(f"总收益率: {metrics['total_return']*100:.2f}%")
print(f"年化收益率: {metrics['annual_return']*100:.2f}%")
print(f"最大回撤: {metrics['max_drawdown']*100:.2f}%")
print(f"夏普比率: {metrics['sharpe_ratio']:.3f}")
print(f"胜率: {metrics['win_rate']*100:.2f}%")
print(f"交易笔数: {metrics['total_trades']}")
import matplotlib.pyplot as plt
dates = curve['dates']
plt.figure(figsize=(12, 6))
plt.plot(dates, curve['net_value'], label='策略净值', linewidth=2)
if curve.get('benchmark_net_value'):
plt.plot(dates, curve['benchmark_net_value'], label='基准(持有不动)', linestyle='--')
if curve.get('index_net_value'):
plt.plot(dates, curve['index_net_value'], label='指数', linestyle=':')
plt.legend(); plt.xticks(rotation=45); plt.tight_layout(); plt.show()
plt.figure(figsize=(12, 4))
plt.fill_between(dates, 0, curve['drawdown'], color='red', alpha=0.3)
plt.plot(dates, curve['drawdown'], color='red')
plt.xlabel('日期'); plt.ylabel('回撤(%)'); plt.tight_layout(); plt.show()
8.5 示例5:查看年度绩效与调仓明细
bt.run_backtest()
annual = bt.get_annual_performance()
for year, stats in sorted(annual.items()):
print(year, f"收益{stats['total_return']*100:.2f}%", f"回撤{stats['max_drawdown']*100:.2f}%")
# 交易日志(含每笔 reason)
log = bt.get_trade_log()
for t in log[:10]:
print(t['date'], t['stock'], t['type'], t['shares'], t['reason'])
九、高级技巧与二次开发
9.1 因子与条件优化
- 条件因子(离散)与排序因子(连续)各司其职:条件管"能不能买/要不要卖",排序管"先买谁";
- Bool 因子尽量用数值评分代替(如"均线多头评分 0-100")参与排序,区分度更高;
- 避免排序因子间高度相关(如 5 日与 10 日动量同时降序),可用不同类型因子互补。
9.2 换手与风控
- 调大
interval、min_hold_days可显著降换手与手续费; min_hold_days会同时拦截卖出条件与止盈,防止短期噪音洗出;- 涨跌停过滤(
enable_limit_up_down_filter)按前收preClose计算当日涨跌幅并剔除 ≥9.5% 的标的(数据无preClose列时该过滤不生效)。
9.3 继承重写(二次开发)
from xg_condi_factor_backtrader.xg_condi_factor_backtrader import xg_condi_factor_backtrader
class MyCondi(xg_condi_factor_backtrader):
def apply_rank_factors(self, df_date, stock_list):
# 例:自定义加权打分排序(替代多列排序)
df = df_date.loc[stock_list].copy()
df['_score'] = df.get('均线评分', 0) * 2 + df.get('20日动量', 0) * 3
return df.sort_values('_score', ascending=False).index.tolist()
def filter_by_sell_conditions(self, df_date, holdings):
# 例:自定义止损逻辑(可访问 self.hold_buy_prices / self.hold_buy_dates)
result = []
for stock in holdings:
if stock in df_date.index:
pass # 按需实现
return result
其它可重写点:filter_by_buy_conditions(改买入逻辑)、calculate_buy_shares / calculate_sell_shares(改撮合与成本规则)、calculate_performance_metrics(自定义绩效指标)。
十、常见问题与解决方案 (FAQ)
| 问题 | 解决方案 |
|---|---|
| Q1: 提示"数据文件不存在" | 检查 data/全部因子数据 或 data/历史数据 下是否有 {代码}.parquet;或使用 add_stock_data_from_* 注入数据 |
| Q2: 买入候选池为空 | 检查买入条件是否过严;Bool 因子确认用 等于 True/False;在 generate_report() 尾部查看"因子缺失警告",确认条件因子已在数据列或 user_factor_cacal 中定义 |
| Q3: 排序结果不对 / 因子没参与排序 | rank_factor 的值只能是 '降序' 或 '升序';排序优先级按字典书写顺序;含 NaN 的标的排在最后 |
| Q4: 持仓迟迟不卖 / 超过 N 只 | 需命中卖出条件或涨幅止盈才会卖;min_hold_days 未到期会被拦截;interval 非调仓日不交易 |
| Q5: 自定义因子一直算不出来 | 公式返回 Series 长度必须与 df 一致(或返回标量);公式中函数需来自 xg_tdx_func;开启 verbose 观察因子列是否生成 |
| Q6: 布尔判断不生效 | 请用 True/False(系统也识别 'T','t','TRUE','F','f' 等写法);不要用 1/0 表达布尔——它们按数值处理 |
| Q7: 止盈一直不触发 | 止盈按"持仓收益率(现价÷买入价−1) ≥ sell_zdf"触发;sell_type='none' 时不启用;min_hold_days 未到期也会拦截 |
| Q8: 收益率为 0 或异常 | 检查价格列是否全为正;cash 初始值;买入是否因金额不足一手/现金不足失败 |
| Q9: 自定义数据注入后未生效 | 确认注入返回 True;用 get_custom_data_keys() / has_index_data() 验证;代码须与 stock_list 完全一致 |
| Q10: 如何查看某日排序/因子缺失 | 默认 verbose=True 打印因子列清单;回测结束统计因子缺失与 NaN 排序记录;generate_report() 含"因子缺失警告"段 |
十一、技术说明与提示
performance_metrics中的比率多为小数(total_return=0.25表示 25%);报告打印已 ×100;position_data/account_data里的total_return、daily_return为百分数口径。- 年化按 365.25 天折算;夏普/波动按 250 个交易日年化(不足 252 日时按实际交易日开方);无风险利率固定
risk_free_rate。 interval与min_hold_days均按自然日计算。- 基准为"等权持有不动"(全部标的共同交易日归一后等权平均);指数仅作展示对比。
- 源码当前
adj_type='none',如需前/后复权可在adjust_price中扩展。 - 模块级辅助函数
parse_factor_config/parse_config_with_bool可用于安全解析含各种 Bool 写法的因子配置字符串(Python 字典文本),供 Web/配置文件场景复用。
小果条件因子量化回测系统,让条件选股 / 因子轮动策略开发更高效、更透明。如需源码、更多因子库或技术支持,欢迎联系小果(微信:xg_quant)。