kyle_作业
存档_1:学习老韵作业
1️⃣ 这段代码在做什么
用横截面“分位数因子”训练一个轻量 XGBoost 回归器,每 60 个交易日重训,毎 5 日调仓,按预测分数等权买入 Top10。
同时在每次训练后记录 MSE/MAE/R²/IC 与特征重要性,回测结束把训练历史与绩效打包保存。
2️⃣ 数据与因子(initialize / SQL)
数据源:cn_stock_prefactors(已过滤 is_risk_warning=0、list_days>100)。
因子全部做横截面分位数:pct_rank_by(date, …),把不同量纲压到 0–1,适合线/树混用。
fac_rank_total_market_cap:ln(total_market_cap) 当日分位数(规模)。
fac_rank_turn:换手率分位数(流动性/情绪)。
fac_ret_5:close/m_lag(close,5) 当日分位数(近 5 日动量)。
若干候选因子被注释掉(close 分位、net_profit_mrq_yoy、量能比、20 日波动、momentum5),注释缘由写在文件头注释里:净利同比多次为 0、为提速先精简。
标签(label):pct_rank_by(date, m_lead(close,2)/m_lead(close,1))
解释:看 明天到后天这一段“相对收益”的横截面分位数(0–1)。这是短期相对强弱的目标,不是绝对收益。
3️⃣ 训练与预测时序(before_trading_start)
关键参数:
训练窗 train_days=252(≈1 年交易日)
重训间隔 retrain=60(为了提速)
调仓周期 holding_days=5
lookback_days=10(保证 m_lag/m_lead 有足够的数据边界)
什么时候训练:当 trading_day_index % 60 == 0。
查询 [train_days + lookback_days] 的原始窗口;
再截出真正的训练期 [train_days];
dropna 清洗,样本 < 50 或 y.std() 太小就跳过;
特征做 StandardScaler(树模型其实不需要,但保留也不伤);
训练 XGBRegressor(极速参数:20 棵树、深度 2、lr=0.1、subsample=0.7,n_jobs=4);
计算训练内 MSE/MAE/R² 与 IC(y 与 ŷ 相关系数);记录 特征重要性;把这些塞进 context.training_history。
什么时候预测/下单:当 trading_day_index % 5 == 0。
取 lookback_days 内数据,但只保留 当天 current_date 的一行截面;
用训练时的 scaler 做 transform,喂给 XGB,得到 score;
把 instrument, score 放进 context.pred 交给盘中执行。
4️⃣ 交易执行(handle_data)
从 context.pred 里挑 Top 10 得分股票等权买入;
卖出当前持有但不在目标集合的股票;
成交按 开盘价(order_price_field_*='open'),与“盘前计算信号”逻辑一致。
5️⃣ 结果保存与回顾
训练历史:在 after_trading 的最后一天(硬编码 '2025-08-08')调用 save_training_history_only,把每次训练的评估指标与特征重要性序列化到 pkl。
整体绩效:回测后调用 save_strategy_simple,把 performance.stats、净值序列、收益率序列和训练历史一起保存。
6️⃣ 优缺点
👍 优点
时序安全:标签用 m_lead,训练只到“今天”,靠 dropna 避免用到“未来不可得”的行。
轻量稳妥:分位数因子 + 浅树 + 降采样,计算快、抗异常值。
可解释:记录了 MSE/MAE/R²/IC 与 XGB 的 feature_importances_。
⚠️ 风险点 / 兼容性
目标-持有期不一致(上面已提醒)。
StandardScaler 对树无必要:可以直接喂分位数特征(0–1),节省少量时间。
只用 3 个因子:容量足够但 α 限;可酌情把注释的低波动或量能比加回。
global_training_history 的使用:在 initialize 里把全局变量指向了 context.training_history,之后每次 append 都会反映到全局引用,因此能在末尾 save_strategy_simple(..., global_training_history) 正常保存——这点是 OK 的。
如果要更稳,建议把 after_trading 的 end_date='2025-08-08' 改成读取 context.end_date(避免换日期就忘了改常量)。
BigQuant CodeShare