> 技术文档 > (✅)改进_开源证券_VCF_多尺度量价背离检测因子!

(✅)改进_开源证券_VCF_多尺度量价背离检测因子!


(✅)改进:开源证券_VCF_多尺度量价背离检测因子

  • 原因子( 2025.7.14 2025.7.14 2025.7.14
    • 代码实现
    • 因子表现
    • 问题分析
      • 深入分析因子构建逻辑的不足之处
        • 1. **背离检测逻辑存在根本性缺陷**
        • 2. **特征工程结构性问题**
        • 3. **标准化方法导致前瞻性偏差**
        • 4. **最终因子合成逻辑冲突**
        • 5. **市场机制未充分考量**
        • 6. **计算效率与实现风险**
        • 7. **理论依据薄弱环节**
      • 改进方向建议(暂不实施)
    • 具体分析:因子头尾部收益率表现反常
      • 1. **背离信号的方向性冲突(核心问题)**
      • 2. **标准化过程的特征混淆**
      • 3. **尾部权重的自我冲突**
      • 4. **资金流信号的未被充分利用**
      • 5. **极端值区域的逻辑矛盾**
      • 根本原因总结
    • 头尾分析
      • 因子值大小的分析
        • **1. 因子值较小时的情况(低因子值)**
        • **2. 因子值较大时的情况(高因子值)**
      • **可能遗漏的反常情况(头尾部失效风险)**
        • **1. 尾部失效场景**
        • **2. 标准化滞后导致的分位数扭曲**
        • **3. 成交量与波动率的极端耦合**
      • 建议改进方向
  • 改不出来了。操!( 2025.7.16 2025.7.16 2025.7.16
    • 代码实现(硬上弓)
    • 因子表现
    • 评价

原因子( 2025.7.14 2025.7.14 2025.7.14

代码实现

def factor(df): # 计算主动买入量占比 df[\'buy_ratio\'] = df[\'taker_buy_volume\'] / (df[\'volume\'] + 1e-7) # 计算价格波动率 price_change = df[\'close\'].pct_change() volatility = price_change.ewm(span=96, min_periods=24).std() # 计算量价比率 vp_ratio = df[\'volume\'] / (volatility + 1e-7) # 关键周期定义 periods = [96, 288, 672, 1440, 2880] # 计算各周期量价比率 vp_matrix = np.column_stack([ vp_ratio.ewm(span=p, min_periods=int(p/4)).mean() for p in periods ]) # === 核心改进3: 多尺度量价背离检测 === divergence_matrix = np.zeros((len(df), len(periods))) for i, p in enumerate(periods): # 价格动量 price_momentum = df[\'close\'].pct_change(p) # 成交量动量 volume_momentum = df[\'volume\'].pct_change(p) # 资金流动量 flow_momentum = df[\'buy_ratio\'].diff(p) # 量价背离检测 price_volume_div = np.sign(price_momentum) * np.sign(volume_momentum) < 0 # 价资背离检测 price_flow_div = np.sign(price_momentum) * np.sign(flow_momentum) < 0 # 综合背离强度 div_strength = (price_volume_div.astype(int) + price_flow_div.astype(int)) * np.abs(flow_momentum) # 在尾部区域放大背离信号 is_tail = (df[\'buy_ratio\'] < 0.25) div_strength = np.where(is_tail, div_strength * 1.5, div_strength) divergence_matrix[:, i] = div_strength # 组合特征矩阵 combined_matrix = np.concatenate((vp_matrix, divergence_matrix), axis=1) # 使用背离矩阵替代协同矩阵 # 滚动分位数标准化 scaled_std = np.zeros_like(combined_matrix) for i in range(combined_matrix.shape[1]): col = pd.Series(combined_matrix[:, i]) rolling_q20 = col.rolling(window=2880, min_periods=720).quantile(0.20) rolling_q80 = col.rolling(window=2880, min_periods=720).quantile(0.80) scaled_std[:, i] = (col - rolling_q20) / (rolling_q80 - rolling_q20 + 1e-7) # 最终因子计算 (尾部敏感加权) # 在尾部区域增加背离信号的权重 tail_weight = np.where(df[\'buy_ratio\'] < 0.25, 1.5, 1.0) factor = np.mean(scaled_std, axis=1) * np.log1p(df[\'volume\']) * df[\'buy_ratio\'] * tail_weight return factor

因子表现

📊 单币种 (single) 详细评估结果:--------------------------------------------------📈 平稳性检验 (ADF): p_value: 0.000000 是否平稳: 是🔗 相关性分析: IC (Pearson): 0.015414 Rank_IC (Spearman): 0.016373📊 信息比率: IR: 0.244797

(✅)改进_开源证券_VCF_多尺度量价背离检测因子!
(✅)改进_开源证券_VCF_多尺度量价背离检测因子!
(✅)改进_开源证券_VCF_多尺度量价背离检测因子!
(✅)改进_开源证券_VCF_多尺度量价背离检测因子!

问题分析

深入分析因子构建逻辑的不足之处

1. 背离检测逻辑存在根本性缺陷
  • 问题核心:背离检测基于np.sign(price_momentum) * np.sign(other_momentum) < 0,但未考虑动量强度
    • 仅依赖符号会导致弱动量与强动量被同等对待(例如:价格变动0.01%与成交量变动50%被视为有效背离)
    • 未设置动量阈值过滤,大量噪音信号被纳入(如微小价格波动+随机成交量波动)
  • 量纲不一致
    • price_momentum使用百分比变化(无量纲)
    • volume_momentum使用百分比变化(无量纲)
    • flow_momentum却使用原始差分(有量纲),导致三者不可直接比较
  • 尾部放大机制失真
    • div_strength = ... * np.abs(flow_momentum)buy_ratio<0.25时放大,但:
      • 低buy_ratio时段本身flow_momentum绝对值较小(分母效应)
      • 放大操作反而可能强化无效信号
2. 特征工程结构性问题
  • 量价比率(vp_ratio)构建不合理
    vp_ratio = df[\'volume\'] / (volatility + 1e-7)
    • 分子volume具有时间累积性,分母volatility是瞬时波动率,两者时间尺度不匹配
    • 未考虑成交量自身的波动特性(应使用成交量波动率而非原始volume)
  • 多周期处理方式粗糙
    vp_matrix = np.column_stack([vp_ratio.ewm(span=p...]) divergence_matrix = np.zeros((len(df), len(periods)))
    • 所有周期采用相同权重,未考虑市场周期特性(如:短周期应更高频更新)
    • 未处理周期间的共线性问题(如96与288周期高度相关)
3. 标准化方法导致前瞻性偏差
  • 滚动分位数标准化缺陷
    rolling_q20 = col.rolling(window=2880).quantile(0.20)scaled_std = (col - rolling_q20) / (rolling_q80 - rolling_q20)
    • 窗口长度2880(≈30天)在加密货币市场过长,无法适应快速波动
    • 未进行边界处理:当rolling_q80 ≈ rolling_q20时产生数值爆炸(尽管有1e-7但治标不治本)
    • 滚动窗口在时间序列末端使用未来信息(需改为expanding或固定滞后)
4. 最终因子合成逻辑冲突
  • 相互矛盾的权重设计
    factor = mean(scaled_std) * log1p(volume) * buy_ratio * tail_weight
    • log1p(volume)强化大成交量时段
    • buy_ratio强化主动买入时段
    • 但两者常呈负相关(大成交量往往伴随buy_ratio回归均值)
    • tail_weight在低buy_ratio区二次放大,形成逻辑闭环但缺乏实证支持
5. 市场机制未充分考量
  • 加密货币特性缺失
    • 未处理“鲸鱼地址”的影响(大单导致的瞬时buy_ratio跳变)
    • 忽略交易所API的taker数据可靠性问题(部分交易所包含自成交)
    • 未考虑山寨币联动效应(BTC主导行情时的传导关系)
  • 波动率计算缺陷
    volatility = price_change.ewm(span=96).std()
    • 使用固定span=96(24小时)未适应市场波动率聚类特征
    • 应引入GARCH类模型或波动率回归调整
6. 计算效率与实现风险
  • 循环矩阵操作低效
    for i, p in enumerate(periods): # 5周期循环 divergence_matrix[:, i] = ... # 向量化操作可提速10倍+
    • 对每个周期独立计算未利用矩阵并行运算
    • 滚动分位数循环for i in range(combined_matrix.shape[1])更应避免
  • 内存消耗隐患
    • 创建vp_matrix(5列), divergence_matrix(5列), combined_matrix(10列)等中间矩阵
    • 在长时序(如ETH 15m 4年数据≈140,160条)消耗超2GB内存
7. 理论依据薄弱环节
  • 量价背离假设待验证
    • 传统市场“价涨量缩→反转”规律在加密货币市场有效性存疑
    • 未区分牛市/熊市中的背离信号差异(如牛市中的无量上涨可持续)
  • 尾部强化机制缺乏实证
    • buy_ratio<0.25作为尾部区域的阈值设定随意
    • 未验证低buy_ratio区间的信号预测能力是否确实更强

改进方向建议(暂不实施)

  1. 重构背离检测

    • 改用动态阈值:|price_momentum| > σ(noise) 且 sign(price) != sign(volume)
    • 引入自适应动量强度权重:div_strength = tanh(|Δprice|) * tanh(|Δvolume|) * sign_conflict
  2. 优化特征工程

    # 建议替代方案vp_ratio = volume_std / (volatility * typical_price) # 引入典型价格归一化divergence_score = MACD(price) * MACD(volume) < 0 # 基于指标协同性
  3. 改进标准化方法

    # 动态滚动窗口 + 稳健标准化window = min(1440, int(22.5 / volatility)) # 波动率自适应窗口scaled = (x - median) / IQR # 使用四分位距抗异常值
  4. 因子合成逻辑调整

    # 分层加权方案alpha = entropy_weights(vp_matrix) # 信息熵赋权beta = decay_weights(divergence_matrix) # 时间衰减赋权factor = dot(alpha, vp_scaled) + dot(beta, div_scaled)

该因子核心问题在于试图用复杂结构弥补基础逻辑缺陷。建议回归市场微观结构本质,重点攻关:订单流不平衡度 + 波动率异常检测 + 多尺度市场情绪合成三个核心维度。当前方案中约60%的代码(尤其是矩阵操作部分)可被更简洁的物理意义明确的方法替代。

尝试对以上问题进行了改进。然而无果、效果都不怎么样。


具体分析:因子头尾部收益率表现反常

1. 背离信号的方向性冲突(核心问题)

  • 背离检测逻辑

    price_volume_div = np.sign(price_momentum) * np.sign(volume_momentum) < 0 # 量价背离price_flow_div = np.sign(price_momentum) * np.sign(flow_momentum) < 0 # 价资背离
    • 此逻辑将所有类型的背离都标记为正向信号(背离强度值 > 0)
    • 但实际包含两种相反的市场信号:
      • 看涨背离(价格↓ + 资金流↑):预示底部反转
      • 看跌背离(价格↑ + 资金流↓):预示顶部反转
  • 尾部放大机制的副作用

    is_tail = (df[\'buy_ratio\'] < 0.25)div_strength = np.where(is_tail, div_strength * 1.5, div_strength) # 尾部区域放大
    • 在尾部区域(buy_ratio < 0.25),同时放大了看涨和看跌背离信号
    • 导致因子头部同时包含:
      • 真实的顶部看跌背离(应做空)
      • 被放大的底部看涨背离(应做多)

2. 标准化过程的特征混淆

  • 混合标准化问题
    combined_matrix = np.concatenate((vp_matrix, divergence_matrix), axis=1)scaled_std = ... # 对组合矩阵统一标准化factor = np.mean(scaled_std, axis=1) # 取均值
    • 量价趋势特征(vp_matrix)和背离特征(divergence_matrix)合并后标准化
    • 两类特征的经济含义相反:
      • vp_matrix 代表动量强度(值越大越看涨)
      • divergence_matrix 代表反转强度(值越大反转风险越高)
    • 在头部区域,高动量值和高背离值相互抵消,导致因子无法明确表达市场方向

3. 尾部权重的自我冲突

  • 重复放大逻辑
    tail_weight = np.where(df[\'buy_ratio\'] < 0.25, 1.5, 1.0) # 二次放大factor = ... * tail_weight
    • 在背离检测阶段已对尾部区域放大1.5倍(div_strength * 1.5
    • 最终因子又乘以相同的尾部权重(tail_weight
    • 导致尾部区域的噪声信号被双重放大,而核心信号(资金流方向)被弱化

4. 资金流信号的未被充分利用

  • buy_ratio的线性使用
    factor = ... * df[\'buy_ratio\'] # 线性乘以资金流
    • 在头部区域(因子值最大处),高因子值可能由以下组成:
      • 高buy_ratio(看涨) + 高看跌背离(看跌)
    • 在尾部区域(因子值最小处):
      • 低buy_ratio(看跌) + 高看涨背离(看涨)
    • 线性相乘无法解决这种内在矛盾

5. 极端值区域的逻辑矛盾

  • 因子头部(最大值区域)

    成分 期望信号 实际信号 量价趋势(vp_matrix) 看涨 ✅ 看涨 背离信号 - ❌ 同时含多空 buy_ratio 看涨 ✅ 看涨 综合效果 看涨 方向模糊
  • 因子尾部(最小值区域)

    成分 期望信号 实际信号 量价趋势(vp_matrix) 看跌 ✅ 看跌 背离信号 - ❌ 同时含多空 buy_ratio 看跌 ✅ 看跌 综合效果 看跌 方向模糊

根本原因总结

  1. 信号方向冲突:背离检测未区分看涨/看跌类型,导致头部混入看跌信号,尾部混入看涨信号
  2. 特征标准化混淆:动量特征(vp_matrix)与反转特征(divergence_matrix)被合并处理,模糊经济含义
  3. 尾部过度放大:双重放大机制强化了噪声而非有效信号
  4. 资金流未主导:在极端区域,buy_ratio未能主导因子方向,被冲突信号抵消

这些设计缺陷导致因子在极端值区域失去方向性预测能力:头部可能包含真实看跌信号,尾部可能包含真实看涨信号,与预期收益方向相反。

头尾分析

因子值大小的分析

1. 因子值较小时的情况(低因子值)
  • 低成交量环境

    • np.log1p(volume) 在成交量极低时趋近于 0,直接压制因子值。
    • 例如:市场流动性枯竭时(如深夜交易时段或低波动横盘期),因子值显著减小。
  • 买方力量弱(低 buy_ratio)

    • buy_ratio 直接作为乘数,当 taker_buy_volume 占比低时(如持续卖方主导),因子值减小。
    • 例外:若同时满足尾部条件(见下文),tail_weight 可能部分抵消,但整体仍偏小。
  • 标准化特征值低迷

    • scaled_std 均值低表明:
      • VP 比率(volume/volatility)处于历史低位(滚动分位数标准化后接近 0)。
      • 背离信号弱(divergence_matrix 值小),例如:
        • 价格动量与成交量/资金流同向(无背离)。
        • 资金流动量变化(flow_momentum)绝对值小。
  • 非尾部且波动率适中

    • buy_ratio 未触发尾部阈值(即 buy_ratio > dynamic_tail_threshold),且 vol_rank 处于中间区间(0.3-0.7),此时 tail_weight=1,无放大效应。

2. 因子值较大时的情况(高因子值)
  • 高成交量 + 买方主导

    • np.log1p(volume)buy_ratio 双高时,因子值被显著推升。
    • 例如:突破行情中放量上涨(高 buy_ratio + 高 volume)。
  • 标准化特征值强劲

    • scaled_std 均值高表明:
      • VP 比率处于历史高位(滚动分位数标准化后接近 3)。
      • 强背离信号(divergence_matrix 值大),例如:
        • 价格上涨但成交量萎缩(price_volume_div 为真)。
        • 价格上涨但资金流入减少(price_flow_div 为真)。
        • 资金流动量变化(flow_momentum)绝对值大。
  • 尾部条件触发且波动率适配

    • buy_ratio < dynamic_tail_threshold(尾部)时:
      • 低波动市场vol_rank < 0.3):阈值收紧至 0.20,更容易触发尾部,且 tail_weight 最大(1.8 - 0.6 * 低 vol_rank),因子值显著放大。
      • 高波动市场vol_rank > 0.7):阈值放宽至 0.30,触发尾部后 tail_weight 仍较高(约 1.32),但放大效应弱于低波动环境。
    • 例如:恐慌性抛售(buy_ratio 极低)但波动率处于历史低位时,因子值可能异常高。

可能遗漏的反常情况(头尾部失效风险)

1. 尾部失效场景
  • 高波动市场中的假尾部

    • 当市场突然转向高波动(如黑天鹅事件),vol_rank 需 168 根 K 线才更新,导致:
      • 实际波动率高,但 vol_rank 仍显示低位 → 错误使用低波动阈值(0.20)。
      • 结果:本应宽松的尾部阈值被收紧,正常 buy_ratio 被误判为尾部,过度放大因子值。
    • 反常表现:因子值虚高,但实际市场处于无序状态(无有效背离)。
  • 低波动市场中的尾部漏判

    • buy_ratio 略高于动态阈值(如 0.21),但处于极低波动环境:
      • 尾部条件未触发(tail_weight=1),但此时轻微买方力量减弱可能预示反转。
      • 反常表现:因子值未充分放大,错过底部信号。
2. 标准化滞后导致的分位数扭曲
  • 滚动窗口(2800 根 K 线)的滞后性

    • 市场结构突变时(如牛市转熊市),历史分位数(20%/80%)无法及时调整:
      • 旧牛市的高 VP 比率被用作标准化基准 → 新熊市中 VP 比率被错误压缩(scaled_std 偏低)。
      • 反常表现:因子值在熊市初期系统性偏低,即使出现有效背离。
  • 自适应阈值失效

    • rolling_q80 - rolling_q20 < 1e-5 时,使用 0.01 * 滚动平均绝对值 替代分母:
      • 在超窄区间震荡市中,分母可能被压至极低 → 标准化值 scaled_col 异常波动。
      • 反常表现:因子值在无趋势市场中随机跳变(假信号)。
3. 成交量与波动率的极端耦合
  • 超高波动 + 超高成交量(如闪崩):

    • vp_ratio = volume / volatility 可能因分子分母同增而保持常态。
    • log1p(volume) 极大,掩盖背离信号不足的问题 → 因子值虚高。
    • 风险:在流动性危机中,因子误判为强势。
  • 超低波动 + 超低成交量(如假期行情):

    • vp_ratio 计算的分母趋近于 0 → 数值不稳定。
    • 标准化时滚动分位数可能失效(因 min_periods=168 未满足)。
    • 风险:因子值随机波动,失去预测性。

建议改进方向

  1. 动态阈值平滑

    • dynamic_tail_threshold 增加波动率变化率的条件(如 volatility.pct_change()),避免市场突变时的误判。
  2. 标准化窗口自适应

    • 缩短滚动窗口(如 2800 → 1000)或引入指数加权分位数,提高对市场结构变化的敏感度。
  3. 尾部条件优化

    • tail_weight 计算中加入成交量过滤(如仅当 volume > 滚动中位数 时生效),避免低流动性环境下的噪声放大。
  4. 极端值熔断

    • volatility < 1e-5volume < 1e-5 时,暂停因子计算,防止数值不稳定。

通过上述分析,建议在回测中重点关注 高波动环境下的尾部信号市场结构突变期的标准化过程,这两者是头尾部失效的高发区。可借助 evaluator.run_full_evaluation() 输出的分位数转换图(Quintile Transition)和 IC 衰减曲线进一步验证。

改不出来了。操!( 2025.7.16 2025.7.16 2025.7.16

代码实现(硬上弓)

def factor(df): # 原始因子计算(保持不变) df[\'buy_ratio\'] = df[\'taker_buy_volume\'] / (df[\'volume\'] + 1e-7) price_change = df[\'close\'].pct_change() volatility = price_change.ewm(span=96, min_periods=24).std() vp_ratio = df[\'volume\'] / (volatility + 1e-7) periods = [96, 288, 672, 1440, 2880] vp_matrix = np.column_stack([ vp_ratio.ewm(span=p, min_periods=int(p/4)).mean() for p in periods ]) # === 改进点:波动率动态尾部阈值 === # 计算波动率分位数 vol_rank = volatility.rolling(window=672, min_periods=168).rank(pct=True) # 动态调整尾部阈值 dynamic_tail_threshold = np.where( vol_rank < 0.3, 0.20, # 低波动市场收紧阈值 np.where(vol_rank > 0.7, 0.30, 0.25) # 高波动市场放宽阈值 ) divergence_matrix = np.zeros((len(df), len(periods))) for i, p in enumerate(periods): price_momentum = df[\'close\'].pct_change(p) volume_momentum = df[\'volume\'].pct_change(p) flow_momentum = df[\'buy_ratio\'].diff(p) price_volume_div = np.sign(price_momentum) * np.sign(volume_momentum) < 0 price_flow_div = np.sign(price_momentum) * np.sign(flow_momentum) < 0 div_strength = (price_volume_div.astype(int) + price_flow_div.astype(int)) * np.abs(flow_momentum) # 使用动态尾部阈值 is_tail = (df[\'buy_ratio\'] < dynamic_tail_threshold) div_strength = np.where(is_tail, div_strength * (1.8 - vol_rank * 0.6), div_strength) divergence_matrix[:, i] = div_strength combined_matrix = np.concatenate((vp_matrix, divergence_matrix), axis=1) # 标准化(保持不变) scaled_std = np.zeros_like(combined_matrix) window = 2800 min_periods = 168 for i in range(combined_matrix.shape[1]): col = pd.Series(combined_matrix[:, i]) rolling_q20 = col.shift(1).rolling(window=window, min_periods=min_periods).quantile(0.20) rolling_q80 = col.shift(1).rolling(window=window, min_periods=min_periods).quantile(0.80) denominator = rolling_q80 - rolling_q20 adaptive_threshold = 0.01 * col.abs().rolling(288, min_periods=72).mean() denominator = np.where(denominator < 1e-5, adaptive_threshold, denominator) scaled_col = (col - rolling_q20) / (denominator + 1e-7) scaled_col = np.clip(scaled_col, -3, 3) scaled_std[:, i] = scaled_col # 最终因子计算(使用动态尾部权重) tail_weight = np.where(df[\'buy_ratio\'] < dynamic_tail_threshold, (2 - vol_rank * 0.5), 1.0) factor = (np.mean(scaled_std, axis=1) * np.log1p(df[\'volume\']) * df[\'buy_ratio\'] * tail_weight).values factor = np.where(factor < 0.76, np.nan, factor) return factor

因子表现

📊 单币种 (single) 详细评估结果:--------------------------------------------------📈 平稳性检验 (ADF): p_value: 0.000000 是否平稳: 是🔗 相关性分析: IC (Pearson): 0.020159 Rank_IC (Spearman): 0.023357📊 信息比率: IR: 0.450116 有效分组数: 10📊 因子分布:📋 数据概况: 数据长度: 112865 因子列: factor 收益率列: future_return 未来收益周期: 26--------------------------------------------------🖼️ 单币种 (single) 图片展示:----------------------------------------

(✅)改进_开源证券_VCF_多尺度量价背离检测因子!
(✅)改进_开源证券_VCF_多尺度量价背离检测因子!
(✅)改进_开源证券_VCF_多尺度量价背离检测因子!
(✅)改进_开源证券_VCF_多尺度量价背离检测因子!

评价

其实是把尾部硬截掉的。因为实在没办法了。
(然后其实是把预测时间周期从10提到26的。没什么道理,纯粹硬凑。
唉,还是菜啊

救命😭😭