🚀 AI 一键生成 joinquant 策略代码
立即体验
👉 AI 生成代码 👈

为什么 JoinQuant 因子看板与 analyze_factor 的累计收益图坐标轴不一致?

joinquant | 发布时间: 2026/5/29
以下内容由 EasyQuant 生成。

问题描述

因子看板 和analyze_factor里坐标轴

同样的因子、同样的时间段、同样的因子处理方式
同样都是对数轴,
为什么用analyze_factor,plot_cumulative_returns_by_quantile()
得到的累计收益图和因子看板里的累计收益图
坐标轴不一致,是不是有一个不对?

解决方案

在 JoinQuant(聚宽)平台上进行因子分析时,发现 analyze_factor 生成的累计收益图与因子看板(Factor Kanban)的图表在坐标轴或曲线上存在不一致,是一个非常常见的现象。这并不一定意味着其中一个是错误的,而是因为两者在底层计算逻辑、默认参数设置以及展示细节上存在差异。

以下是导致这种不一致的几个核心原因:

1. 交易成本(手续费与滑点)的差异

这是最常见的原因。

  • 因子看板:在获取因子看板数据(如 get_factor_kanban_values)或查看看板 UI 时,通常会包含 commision_slippage(手续费及滑点)参数。看板默认可能会扣除一定的交易成本(例如 3‱佣金 + 1‰印花税 + 滑点)。
  • analyze_factor:这是一个纯粹的统计学单因子分析工具,它计算的是纯因子收益(理论收益),默认不包含任何手续费和滑点。因此,analyze_factor 的累计收益通常会比因子看板看起来更高,坐标轴的绝对数值也会更大。

2. 超额收益(Demeaned)与绝对收益的默认设置

  • plot_cumulative_returns_by_quantile():该函数有 demeanedgroup_adjust 参数。如果 demeaned=False(默认值),它绘制的是各分位数的绝对累计收益。如果设置为 True,则是减去基准后的超额收益
  • 因子看板:看板中的多头组合(long_only)或多空组合(long_short)在展示时,其基准对齐逻辑可能与你调用 analyze_factor 时的参数不完全一致。请检查你在调用绘图函数时是否对齐了 demeaned 参数。

3. 对数轴(Log Scale)的基准点与计算方式

虽然两者都使用了对数轴,但底层的绘图库处理方式可能微有不同:

  • 起点差异:累计收益的起点是 0 还是 1?在对数坐标下,如果起点是 1(即 $10^0$),累计收益 $R$ 绘制的是 $\log(1+R)$;如果直接绘制累计收益率 $R$,对数轴的处理会有所不同。
  • Alphalens 底层analyze_factor 底层基于开源的 Alphalens 库,其对数轴通常是直接对累计净值(Cumulative Returns + 1)取对数。因子看板的前端展示组件可能在刻度标签(Tick Labels)的格式化上做了不同的映射(例如显示百分比 vs 显示净值)。

4. 调仓价格与延迟假设(T+1 vs T+2)

  • analyze_factor:默认假设在获取因子值的当天(T日)收盘后计算,并在下一期(通常是 T+1 日的收盘价或开盘价,取决于内部实现,通常为 close-to-close)进行调仓计算收益。
  • 因子看板:根据官方文档说明,因子看板的收益计算有严格的延迟设定:“T日收盘后的因子收益需要T+1的收盘价才可得出,数据需要在T+2日凌晨3点计算之后才可得到”。这种严格的撮合时间差可能导致两者的收益序列在极端行情下出现微小错位,从而在长期累计后产生视觉上的差异。

5. 缺失值与极端值处理(Max Loss & 填充)

  • analyze_factor 中,有 max_loss 参数(默认 0.25),并且在预处理时会剔除 NaN 值。
  • 因子看板在后台计算时,对于停牌股、涨跌停股的过滤(skip_paused 参数)以及行业缺失值的填充逻辑可能有一套固定的标准化流程。如果你在 analyze_factor 中没有完全复刻这套清洗流程(例如行业中性化时的权重处理),分位数的成分股就会有微小差异。

总结与建议

两者都没有“错”,只是评估维度不同analyze_factor 偏向于理论因子的预测能力(IC/IR)和纯收益,而因子看板更偏向于贴近实盘约束(扣除费率、严格延迟)的策略表现

如何验证?

  1. 在因子看板中,将 commision_slippage 设置为 0(无手续费无滑点)。
  2. analyze_factor 的绘图函数中,明确设置 demeaned=Falsegroup_adjust=False
  3. 确保两者的 skip_paused 参数完全一致。
  4. 提取两者的底层 DataFrame(far.mean_return_by_quantile),直接对比第一天的收益率数值,看差异是出在第一天的计算上,还是长期复利累积上。