资讯动态

决策树特征重要性分析:量化交易因子筛选的实战指南

发布时间:2026/9/14 4:47:37 来源:尧图企业网站定制
先跑个题聊点背景。我接触量化交易的几年里最早啃的模型就是决策树。原因很简单它不像神经网络那样是个黑箱也不像线性回归那样对市场数据的前提假设苛刻。决策树天然能输出“特征重要性”这个属性几乎成了当时我筛选因子的一把尺子。但用久了才发现这把尺子本身也有刻度偏向。今天这篇就是把“决策树在交易里的特征重要性分析”这件事从头到尾拆开讲清楚哪些特征真的在起作用哪些只是看起来在起作用。这个内容适合谁一种是刚开始用机器学习做交易、满脑子都是“我该把哪些指标喂给模型”的人另一种是已经跑通流程但面对模型给出一堆重要性分数时不知道怎么解读的人。看完这篇你会明白特征重要性背后的计算逻辑、常见的坑、以及一套可以直接照搬的实操流程。咱们从最核心的问题切入决策树凭什么告诉你哪个特征重要1. 整体设计思路拆解为什么选决策树来做特征重要性分析1.1 这个世界不是线性的决策树更贴合交易场景做交易的人每天盯着的指标很多收盘价、成交量、MACD、RSI、布林带、资金流向……如果用一个线性模型去拟合成交量与未来收益之间的关系你得到的可能是一条直线成交量越大未来收益越高或越低。但真实市场里这个关系往往是阶段性的也是非线性的——成交量放大的时候有时候是上涨的起点有时候是出货的信号取决于当时的价格位置、市场情绪、大盘环境。决策树的优势在于它不需要你事先假设这种非线性关系的具体形式。它会把特征空间切分成一块一块的区域每一块对应一个预测结果。比如它可能自己学会了“当成交量放大且价格站上20日均线时未来5天上涨的概率较高当成交量放大但价格跌破60日均线时未来5天下跌的概率较高”。这种自动分段能力让它特别适合处理交易数据里那些“分情况讨论”的逻辑。特征重要性分析的逻辑也因此变得更直观决策树每一次分裂都在选一个特征选中的特征越多、越靠近根节点它对预测结果的贡献就越大。你看一棵树怎么分裂的就等于在看模型认为哪些特征值得关注。1.2 特征重要性到底是什么一套可以量化的规则决策树算出特征重要性的数学逻辑刚开始接触的人会觉得绕我尽量用买菜的例子讲清楚。假设你要判断一颗西瓜甜不甜特征有“敲击声音”“外观颜色”“瓜蒂状态”“重量”。决策树每次分裂时都会尝试不同的特征和不同的切分点目标是让分裂后的两个子节点尽可能“纯”——也就是每个子节点里的西瓜尽可能都是甜的或都是不甜的。“纯度”的度量指标最常用的是基尼不纯度。基尼不纯度的公式是Gini 1 - Σ(p_i^2)。其中p_i是节点中第i类样本的占比。如果节点里十个西瓜八个甜基尼就是 1 - (0.8² 0.2²) 0.32。如果五个甜五个不甜基尼就是 0.5。基尼越小纯度越高。每次选择一个特征做分裂时模型会计算分裂前后的基尼不纯度差值这个差值就是该特征这次分裂带来的“信息增益”。一棵树里同一个特征可能在多个节点被选中把所有节点上的不纯度下降量按样本量加权累加再做归一化就得到了每个特征的“重要性分数”。分数越高说明该特征在整棵树的分裂过程中贡献的“纯度提升”越大。放到交易场景里就是某指标在模型中带头区分“上涨样本”和“下跌样本”的能力越强它的重要性分数就越高。1.3 为什么用决策树而不是更复杂的模型你可能会问XGBoost、LightGBM、神经网络这些模型不都有重要性分析吗为什么单独拿决策树出来讲我的观点是决策树是所有树模型的地基先把地基的逻辑吃透后面理解随机森林、梯度提升树的特征重要性就事半功倍。而且单棵决策树的可解释性是最强的——你可以可视化出完整的树结构看着它一步步怎么切分特征这种透明性是其他模型给不了的。另一个现实原因很多人在交易里遇到的不是模型精度不够而是“特征太多、不知道保留哪些”。用决策树做一次初筛把排名靠后的特征直接剔除再用降维后的特征集去训练更复杂的模型这个工作流我已经用了很多年稳定可靠。决策树在这里的角色更像一个“特征筛选器”而不是最终的预测器。不过单棵决策树有个明显的毛病它对数据的扰动非常敏感。数据稍微变一下树结构就完全变了特征重要性的排序也跟着变。这也是为什么我在前面的截图里用单棵决策树做演示但在真正筛选因子时会优先跑随机森林——随机森林的稳定性好很多这一点后面细说。2. 核心细节解析与实操要点三类重要性指标怎么选、怎么用2.1 默认重要性基于不纯度下降的真实偏向大部分Python库sklearn、XGBoost、LightGBM默认输出的feature_importances_用的都是基于不纯度下降的算法。这个指标的优点是计算速度快不需要额外验证数据缺点是存在三个系统性偏向在交易场景里特别要命。第一个偏向是数值型特征天生占便宜。连续型特征比如成交量、收盘价可选的切分点多模型能更精细地找到让基尼下降最大的切分位置而离散型特征的可选切分点少在竞争中有天然的劣势。这意味着一个连续型的“噪音指标”很容易比一个更有逻辑的“离散型事件特征”拿到更高的分数但后者其实更符合交易常识。第二个偏向是高频出现的特征更占优。一个日内交易指标哪怕它对未来收益的解释力度一般因为样本量大、分裂次数多累计下来的不纯度下降总量也会很大。反过来一个只在极端行情才出现的特征比如“跳空缺口幅度”出现次数少但每次出现都含金量高在默认重要性指标里很容易被低估。第三个偏向是对共线性特征会“分摊”重要性。两个特征高度相关比如“5日均线”和“10日均线”模型会随机地今天选这个、明天选那个最后两者的重要性分数都被稀释。如果你只看排序很可能得出“这两个都不重要”的错误结论。2.2 置换重要性一个更贴近真实的基准置换重要性的思路和默认重要性完全不同。它不关心模型内部是怎么分裂的而是直接看效果如果一个特征真的重要那么把这一列的值随机打乱模型的效果应该明显变差。打乱后模型误差上升的幅度就是该特征的重要性分数。这个指标在交易场景里非常实用因为它能发现“默认重要性”的盲区。我在自己的因子池里跑过几次对比经常出现某特征在默认重要性里排倒数在置换重要性里却排前三的情况。原因在于置换重要性不依赖分裂次数只要特征被打乱后预测效果显著变差它就会给出高分。而且它对共线性问题没那么敏感——如果两个特征完全相关打乱其中一个模型还可以借助另一个来弥补重要性会偏低但你至少能识别出“冗余”这个事实。不过置换重要性的实现细节里有个坑如果你打乱测试集特征时用了随机数不固定的方式结果会不稳定另外针对时间序列数据直接打乱行序会破坏时间依赖关系所以我再用它时基本只用它来做因子筛选的辅助排序不做唯一标准。推荐做法把默认重要性和置换重要性两个结果放在一张表里对比。如果某个特征只有默认重要性高置换重要性却很低那说明它大概率是“虚胖”——模型内的贡献靠分裂次数堆出来的但去掉它之后模型的预测能力并不受显著影响。2.3 SHAP想深挖特征影响方向的直接上这一套默认重要性和置换重要性解决“哪个特征重要”的问题但解决不了“这个特征是怎么影响预测结果”的问题。在交易场景里后者同样关键。假设“RSI”的重要性排名靠前你总得知道RSI高的时候模型是把它当作看涨信号还是看跌信号吧。单看重要性分数是不知道方向的。这时候就需要SHAPSHapley Additive exPlanations。SHAP是从博弈论里的Shapley值演化而来的。从概念上讲它计算的是在所有特征组合的上下文中某个特征对预测结果的边际贡献的平均值。输出结果是每条样本上一个值正数推高模型输出负数压低模型输出。对所有样本的SHAP值画一个summary plot就能从“重要性排序”和“影响方向”两个维度同时对特征做体检。我自己的习惯是先用默认重要性粗筛掉明显没用的特征只留下前10-15个再对这些特征跑SHAP看方向和逻辑是否符合交易常识。比如我之前做过一个回测模型给“当日成交量/过去5日平均成交量”打了很高的重要性SHAP图显示比值越大模型越倾向于预测上涨。这个逻辑在牛市的初期是成立的但在熊市反弹阶段会出现背离后来我把数据按牛市/熊市分段时间段分别建模方向性的结论就更清晰了。2.4 构建特征池哪些特征值得放进模型测试特征重要性分析再厉害前提是你得有个质量不错的特征池。如果喂进去的全是噪音分析出来的重要性排序也没什么意义。我在实际项目中把交易特征分为五类供你参考价格类特征收盘价、开盘价、最高价、最低价、各种均线值、布林带位置、ATR波动幅度等。这类特征主要用于捕捉价格趋势和波动状态。成交量类特征成交量、成交额、量比、换手率、资金流向指标等。量价配合是交易分析永恒的主题这类特征在决策树里通常能拿到不错的重要性。动量/反转类特征过去N日收益率、RSI、MACD、KDJ、动量因子等。用于捕捉短期价格动能和超买超卖状态。时间类特征星期几、月份、日内时段、距离上次极端波动的天数等。我在A股和期货市场都验证过这类特征对捕捉周期性效应有实效。跨市场/环境类特征大盘指数的涨跌幅、板块联动指标、波动率指数VIX等。单个品种的行情往往受大环境影响加入这类特征后模型往往能学到更结构化的逻辑。构造特征时有一个禁忌穿越未来。比如用当天的收盘价数据构造出“当天是否突破过去20日最高价”时这个特征是可以在收盘时点知道的没问题但如果你把“未来5天收益”这个标签的统计数据意外混进特征里模型就会学到一条“作弊捷径”特征重要性分析会显得特别“好看”实盘却完全失效。这是在特征工程阶段就要严防死守的红线。3. 实操过程与核心环节实现用Python跑完一整套特征重要性分析3.1 实验环境和数据集准备这部分我直接给出可复现的代码。我习惯用akshare或tushare拉取行情数据以下代码以tushare为例读者需要自行替换成自己手上的行情数据源字段名保持一致即可。import pandas as pd import numpy as np from sklearn.tree import DecisionTreeClassifier, plot_tree from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split, TimeSeriesSplit from sklearn.inspection import permutation_importance from sklearn.metrics import accuracy_score import matplotlib.pyplot as plt # 假设已有日线行情DataFramedf包含 date, open, high, low, close, volume 等字段 # df 需要按日期升序排列3.2 特征构造与标签生成我首先基于原始行情数据构造特征。以沪深300指数日线数据为例时间区间可以选近8年分类标签是“未来第5个交易日收盘价是否高于当前收盘价”即5日后的涨跌方向。def build_features(df): df df.sort_values(date).reset_index(dropTrue) c df[close] v df[volume] features pd.DataFrame(indexdf.index) features[ret_1d] c.pct_change(1) # 近1日收益率 features[ret_5d] c.pct_change(5) # 近5日收益率 features[ret_10d] c.pct_change(10) # 近10日收益率 features[ma5] c.rolling(5).mean() / c - 1 # 5日均线乖离率 features[ma10] c.rolling(10).mean() / c - 1 # 10日均线乖离率 features[ma20] c.rolling(20).mean() / c - 1 # 20日均线乖离率 features[vol_ratio] v / v.rolling(20).mean() # 量比 features[vol_std_20] c.pct_change(1).rolling(20).std() # 20日波动率 features[rsi_14] calc_rsi(c, 14) # RSI指标 features[high_low_pos] (df[close] - df[low]) / (df[high] - df[low]) # 在当日区间中的位置 features[st_weekday] pd.to_datetime(df[date]).dt.weekday # 星期几做one-hot编码 features pd.get_dummies(features, columns[st_weekday], drop_firstTrue) # 未来5日涨跌方向作为标签 df[label] (c.shift(-5) c).astype(int) return features, df[label]注意一点特征里有缺失值很正常rolling和pct_change开头若干行都为NaN训练前要dropna。此外weekday这类时间特征在做成one-hot后决策树在重要性分析里往往能体现出星期效应这是我多次验证过的事情。3.3 时间序列切分别用随机切分交易数据最大的特点是有时间顺序用随机切分训练集/测试集是新手最常犯的错误会导致模型“偷看未来”。我用的是TimeSeriesSplit或者自定义的expanding window切分。# 删除缺失值 feature_df, label build_features(load_data()) feature_df feature_df.iloc[20:].reset_index(dropTrue) label label.iloc[20:].reset_index(dropTrue) # 时间序列切分按 7:3 前训练后测试 split_idx int(len(feature_df) * 0.7) X_train, X_test feature_df.iloc[:split_idx], feature_df.iloc[split_idx:] y_train, y_test label.iloc[:split_idx], label.iloc[split_idx:] print(f训练集样本数: {len(X_train)}, 测试集样本数: {len(X_test)}) # 输出: 训练集样本数: 1271, 测试集样本数: 545为什么要强调这点因为如果你的训练集时间在测试集时间之后模型学到的未来规律反推历史测出来的特征重要性毫无参考价值。这种错误在特征重要性分析里尤其常见因为模型给出漂亮的重要性数字最关键的影响因素却被掩盖了。3.4 训练决策树直接看特征怎么分裂训练一棵基线决策树模型参数先别调太复杂限制一下树的深度避免过拟合。dt DecisionTreeClassifier( max_depth5, min_samples_leaf5, random_state42 ) dt.fit(X_train, y_train) train_acc accuracy_score(y_train, dt.predict(X_train)) test_acc accuracy_score(y_test, dt.predict(X_test)) print(f决策树训练集准确率: {train_acc:.3f}) print(f决策树测试集准确率: {test_acc:.3f}) # 输出示例: 训练集准确率: 0.578, 测试集准确率: 0.541测试集准确率在0.54左右对沪深300未来5日涨跌的方向预测来说是一个很现实的水平本身并不高因为市场有很强的随机性。但这个模型的目的不是追求极致准确率而是找出特征与标签之间相对稳定的关系。接下来直接查看特征重要性排序feature_importance pd.Series(dt.feature_importances_, indexX_train.columns) feature_importance.sort_values(ascendingFalse, inplaceTrue) print(feature_importance)输出可能长这样vol_ratio 0.213 ma20 0.186 ret_5d 0.148 rsi_14 0.117 vol_std_20 0.092 ret_10d 0.081 ma10 0.064 ret_1d 0.052 high_low_pos 0.029 ma5 0.018 st_weekday_1 0.000 ...这里“量比”排第一、“20日均线乖离率”排第二、“近5日收益率”排第三看起来这个组合很有逻辑。问题在于这只是一次运行的结果换个随机种子排序就可能不同。所以我们需要用随机森林来稳定结论。3.5 用随机森林加固结论稳定性优先随机森林通过多棵树上不同子样本的学习把单棵树的方差降下来特征重要性排序会更稳定。我在全流程里直接把随机森林当主力模型来用。rf RandomForestClassifier( n_estimators300, max_depth6, min_samples_leaf10, random_state42, n_jobs-1 ) rf.fit(X_train, y_train) feature_importance_rf pd.Series(rf.feature_importances_, indexX_train.columns) feature_importance_rf.sort_values(ascendingFalse, inplaceTrue) print(feature_importance_rf)同时计算置换重要性作为对比参考perm_res permutation_importance( rf, X_test, y_test, n_repeats30, random_state42, n_jobs-1 ) perm_order np.argsort(perm_res.importances_mean)[::-1] print(pd.DataFrame({ feature: X_test.columns[perm_order], importance_mean: perm_res.importances_mean[perm_order], importance_std: perm_res.importances_std[perm_order] }))如果默认重要性和置换重要性都指向同一批特征那么这批特征值得重点关注。如果两者排序严重不一致就要警惕共线性或者数据噪音了。3.6 可视化两张图看清全貌先画默认重要性柱状图再画SHAP summary plot。plt.figure(figsize(8, 6)) plt.barh(feature_importance_rf.head(10).index[::-1], feature_importance_rf.head(10).values[::-1]) plt.xlabel(Feature Importance) plt.title(Top 10 Features by Random Forest) plt.tight_layout() plt.show()SHAP部分需要安装shap库计算全部的样本SHAP值再画summary plotimport shap explainer shap.TreeExplainer(rf) shap_values explainer.shap_values(X_test[:500])[1] shap.summary_plot(shap_values, X_test.iloc[:500], max_display10)图上纵轴是特征名按重要度从高到低排列横轴是SHAP值正负表示推动预测结果的方向。颜色代表原始特征取值的大小红色高、蓝色低。这张图能直接告诉你量比越大时模型是更倾向于预测上涨还是下跌。我在做因子分析时这张图提供的信息量超过前面任何一种单一指标。4. 常见问题与排查技巧实录这些坑我全都踩过4.1 特征重要性排名每次都不一样怎么办这个问题刚接触时非常容易慌张。今天跑出来“量比”排第一明天换了一批数据就变成“MA20”排第一。其实这背后有两个原因一是模型的随机性二是特征之间的相关性。对策也很直接第一把随机种子固定住第二用随机森林或梯度提升树替代单棵决策树它们自带集成降噪功能第三用多折交叉验证跑多次把每次的重要性分数存下来取均值和标准差。如果某个特征的均值排名很高且标准差很小这才是真的重要如果均值一般但标准差很大那它的重要性就是不稳定不值得依赖。4.2 某个特征重要性非常高但实际回测一塌糊涂我踩过一次最深的坑有一版模型跑出来某资金流向类特征的重要性稳居前三看起来逻辑也顺但放到模拟盘上就是亏损。后来排查发现问题出在“引用未复权数据”上。除权除息产生的价格跳变被模型学成了某种规律重要性自然虚高实盘里不除权的数据一进来就失效了。这个教训给了一个排查思路验证某个高重要性特征是否真的有效可以把该特征做随机延迟处理lag 1天、lag 2天重新跑模型看重要性是否剧烈变化。如果延迟一天重要性就崩了说明模型依赖的是一种极度短期的瞬时关系这种关系在真实交易里往往很难兑现。这是我在做特征审查时必跑的一组对照实验推荐你也养这个习惯。4.3 默认重要性和置换重要性排序差异太大以哪个为准按我的经验默认重要性更适合“初步粗筛”置换重要性更适合“最终确认”。如果两者都排名靠前这个特征大概率是核心因子如果默认高、置换低说明特征在模型内部贡献了大量分裂但去掉它之后模型预测精度并没有显著下降这类特征可以考虑剔除。为什么会出现这种差异因为默认重要性受到“分裂次数多”的假象影响置换重要性则直接测试对预测精度的影响。置换重要性相对更可靠但计算代价大——交易数据动不动几十万行跑一次置换需要很多时间。所以我的流程是先用默认重要性粗筛到前20个特征再对这批特征跑置换重要性效率和可靠性就都有了平衡。4.4 共线性会让模型决定分歧如何妥当处理决策树和随机森林本身并不怕特征共线性——去掉任何一个都会影响预测精度模型还能继续跑。但它会让“哪一个特征最重要”这个问题变得模糊。最典型的例子我现在同时放入MA10、MA20、MA60三者高度相关模型今天选A明天选B重要性分数各自被“摊薄”了结果看起来哪个都不重要但实际去掉它们全部模型效果马上掉。处理方案先算相关性矩阵把相关系数高于0.8的一组特征只保留一个可解释性最强的成员或者用PCA做特征压缩但PCA会牺牲可解释性交易场景里我不太推荐。我自己一般保留对交易有直接逻辑的那个特征比如保留“均线乖离率”而不是“均线值本身”既解决共线性又保留业务含义。4.5 分类和回归哪个更适合做特征重要性分析我把“预测未来5日涨跌方向”做成二分类是因为标签更稳健没那么容易受极端值影响。但很多时候收益率本身比方向更有信息量这时候可以考虑回归任务——预测未来5日的实际收益率。回归和分类的决策树用的是不同的分裂指标分类用基尼不纯度或信息熵回归用均方误差MSE或绝对误差MAE。回归树的重要性计算方式逻辑相同都是看特征分裂时带来的误差下降量。我建议你两个都试一下然后把两套重要性排序对比分析。如果一个特征在分类和回归模型里都排名靠前它大概率是真有效因子如果只在某一个任务里突出要留个心眼可能它对极值样本敏感。4.6 特征重要性分析的结果能直接用于交易吗这是我最想强调的一件事特征重要性分析是因子筛选的手段不是交易信号的直接来源。我见过有人拿着特征重要性Top的因子直接做信号回测结果还不错但这种做法风险很大。原因在于重要性反映的是特征在“整个样本分布”里的平均贡献而交易赚钱靠的是某些市场环境下的“局部高胜率”。一个特征整体重要性中等但在特定市场状态下比如高波动期贡献极高这种特征可能比一个整体重要性很高但分布平均的特征更有交易价值。所以分析的时候我还会额外做分市场状态的细分或者对特征做条件重要性分析。5. 实操经验总结关于特征重要性分析最后想说的几句话做了这么多轮特征重要性分析之后我的个人体会是特征重要性分析最适合的场景是“减少你的决策空间”而不是替你做交易决策。交易者通常面临几十个候选因子真正值得投入精力的其实只有三五个。决策树帮你在这个缩减过程中给出一个相对客观的依据但最后判断“哪个特征值得留”的还是你对市场的理解。另外一个实用技巧百试百灵每次训练完模型顺手把前几个重要特征的边际效果单独看一遍。如果某个重要特征的逻辑在业务上完全讲不通——比如“星期几”成了最重要的预测因子而它解释不了市场的实际波动——那你需要回头检查数据的完整性或特征构造是否有隐性问题手工检查通常能暴露数据里深埋的坑。模型里的重要性是相关性不等同于市场的因果关系。它告诉你的只是“在数据统计意义上这个特征和标签关系紧密”但数据里的关系需要人的判断来赋予意义。最后一点是我做特征筛选时反复用的一句话一个特征只有在跨时间、跨样本上保持排名稳定才值得被认真对待。特征重要性分析不是跑一次就完事的工作滚动窗口重复验证才是它的正确打开方式。市场数据每一段都不同因子在某些窗口重要、其他窗口失效这种现象本身也是值得你持续观察的信息因为它往往映照着市场状态的变化。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价