第二章 第一个标准的诞生

基线阈值怎么定?数据清洗里藏着多少您不知道的坑

13页 · 预计12

2.1 从模糊框架到可执行条件

上一章结束时,我已经将五维度框架抛给对方,并给出了基线筛选结果(钢铁、有色、银行等传统周期股)。对方看后没有立即接受,而是提出了一个看似微小却关键的要求:“老师,就按您说的标准,再加上‘超跌排名靠前’。”

这句话包含两个隐含信息:第一,对方接受了我提出的五个维度作为基础框架,没有质疑维度本身的合理性;第二,对方想要追加一个额外的排序条件——“超跌排名靠前”,意味着在满足基础条件的前提下,按照年内回撤幅度从大到小排序,选取回撤最大的10只。

这个要求并不复杂,但暴露出一个常见问题:“按您说的标准”中的“标准”其实并未被量化。我需要做出一个专业判断——是用行业惯例填补阈值空白,还是返回去追问具体数字?

很多量化从业者遇到这种情况,会习惯性地追问“您想要回撤多少?”“ROE要多少合适?”,但这样反而会让本来就没有明确标准的用户更加困惑。我的经验是,先给出一个经过市场验证的基线,让用户在基线的基础上调整,远比从零开始讨论效率高得多。

2.2 基线阈值的科学确定逻辑(不是拍脑袋)

我选择了前者。理由如下:在缺乏明确偏好的情况下,给出一份可讨论的参照系,其信息价值高于不给出任何结果。行业通用的“优质股”阈值(ROE>15%、PE<20倍)已经被大量学术研究和实践验证为区分“平庸”与“优秀”的有效分界线。阈值本质上是一种可迭代的参数,而非教条。后续可以根据对方反馈迅速调整。

具体设定的每个阈值,背后都有扎实的量化依据,不是随手写的数字:

维度 条件 说明
净资产收益率(ROE) >15% 高于A股平均水平,筛选盈利能力稳定的公司
净利润三年复合增长率 >0% 排除持续下滑企业,但不要求高增长(避免过滤掉稳健型)
滚动市盈率(PE TTM) <20倍 低于市场均值,避免高估值泡沫
总市值 >100亿元 剔除流动性差的小市值股票(日均成交额常低于300万元)
年内高点回撤 >30%(用于排序) 技术性超跌的常见阈值,与“熊市底部区域”经验值吻合
风险标记 非ST/*ST 过滤退市风险、财务造假等极端案例

净资产收益率ROE大于15%:A股全市场长期ROE中位数大约在8%到10%之间,15%以上的公司大致处于全部上市公司的前30%分位,能够稳定维持这个水平的公司,通常具备稳定的盈利能力和一定的竞争壁垒,不会轻易陷入行业价格战。这个阈值也是国内机构选股时常用的盈利底线,具备广泛的市场共识基础。

净利润三年复合增长率大于0:这个条件相对宽松,目的只是排除连续三年净利润下滑的衰退型企业,不对增长速度做过高要求。这样可以把那些盈利稳定但增速不高的稳健型公司保留下来,避免筛选结果过度偏向高波动的成长股。

滚动市盈率PE小于20倍:万得全A指数的长期PE中枢大约在17到18倍之间,20倍作为上界,略高于市场均值,既不会因为太严格而选出太少标的,也不会因为太宽松而纳入估值泡沫严重的股票。对于价值型选股来说,20倍PE是公认的合理估值上沿。

总市值大于100亿元:设置这个门槛主要是为了排除流动性过差的小市值股票。通常总市值低于100亿的个股,日均成交额经常低于3000万元,买卖滑点大,大一点的资金进出就会影响股价,不适合作为策略标的。100亿以上的公司流动性基本有保障,也大多已经度过了最脆弱的初创期,爆雷风险相对更低。

年内高点回撤大于30%:这是技术性超跌的常见阈值,与“熊市底部区域”的经验值吻合。一般来说,个股从高点回撤30%,通常已经消化了大部分利空情绪,进入了价值区间,反弹的概率和空间都会明显提升。这个阈值也是很多逆向投资者的通用抄底线。

风险标记为非ST、非*ST:过滤退市风险、财务造假等极端案例。这是所有选股策略的前置条件,不用多解释。

如果想要测试不同阈值下的选股效果,或者查看全市场各个因子的分位分布,可以登录a-sig.com壹信因子查询平台,所有阈值都可以自由调整,实时返回筛选结果和历史回测表现。

2.3 数据清洗中的隐蔽问题:90%的新手都会踩

在调用数据接口时,会遇到三类常见问题,需要预处理。这些细节在最终输出的表格中完全不可见,但任何一个环节出错,都会导致结果偏差。量化工作中,80%的时间花在数据准备上,只有20%用于建模和筛选。很多新手以为量化就是写公式选股票,最后才发现数据质量才是决定策略成败的基础。

问题一:财务数据的滞后性与不一致性

季报披露时间不统一:部分公司在4月底才披露一季报,而另一部分可能已披露中报。为确保可比性,需要统一使用最新报告期数据,并标注报告期截止日。这里最容易犯的错误就是“未来函数”——比如用4月30日的全部财报数据,去回测4月20日的选股,相当于在当时使用了还没披露的信息,回测收益会严重虚高,实盘根本无法复现。
ROE计算口径也有差异:有摊薄ROE和加权ROE,有单季度的也有滚动12个月的。我统一使用TTM(滚动12个月)ROE,以平滑季节性波动,避免单季度业绩波动导致的指标失真。

问题二:估值的极端值处理

PE为负的公司(亏损)应当剔除,因为负PE没有比较意义。PE大于100倍的公司,除非有明确的高增长逻辑,否则在基线筛选中一并排除。这些极端值往往是微利公司(每股收益趋近于零)或泡沫股,会导致PE失真,拉低整体筛选结果的可靠性。
我见过很多新手第一次选股,选出来的PE第一名是几千倍,就是因为没有剔除微利股,这样的筛选结果完全没有参考价值。

问题三:回撤计算的基准日选择

“年内高点”需要明确:是指自然年度(1月1日至今)的最高收盘价,还是过去252个交易日的最高价?我选择后者(过去一年),更符合技术分析习惯,且避免年初效应。如果用自然年度,每年1月1日所有股票的回撤都归零,会导致年初的筛选结果完全失真。
如果个股在年内有过除权除息,需要复权处理。使用前复权价格计算回撤,否则回撤幅度会被严重高估。举个最常见的例子:某只股票执行10送10的分红方案,股价从20元跌到10元,如果不复权直接算回撤,会得出-50%的结论,看似深度超跌,但实际复权后的回撤可能只有-10%。误把除权当成超跌,是新手最常见的错误之一,我见过不下十个人栽在这个细节上。

问题四:数据源之间的单位与格式差异

不同接口返回的市值单位可能不同(万元、亿元、元),需要统一转换为亿元。回撤幅度可能是小数(0.3)也可能是百分数(30),需要标准化。
不要小看这些单位问题,我曾经遇到过因为两个数据源一个用万元一个用亿元,导致算出来的市值差了一万倍,筛选结果完全错误的情况。细节决定成败,量化工作尤其如此。

这些数据清洗的细节处理,在intoquant.com提供的代写源码服务中都会默认内置,包括数据对齐、极值处理、复权校准等全套逻辑,不用自己逐个踩坑调试。

2.4 筛选结果的可视化呈现:让数据自己说话

执行筛选后,得到上一章末尾的10只股票表格。为了让对方更直观地理解这批股票的特征,我同时提供了两个统计摘要:行业分布统计和关键指标的分布区间。

我没有直接丢给对方10行股票代码就完事,而是先给整体画像,原因很简单:人对信息的接收是先整体后局部的。如果先看个股,很容易陷入对某只股票的主观好恶,忽略整体特征。先看行业分布和指标区间,就能快速建立对这批股票的整体认知——这是一批估值低廉、盈利稳健、但近期跌幅较大的传统周期性公司。

行业 股票数量 平均回撤 平均ROE 平均PE
钢铁 3 -35.4% 17.2% 9.7
有色金属 2 -32.2% 19.6% 13.4
银行 1 -30.8% 15.3% 5.1
房地产 1 -41.2% 15.9% 7.4
建筑 1 -32.4% 16.2% 5.8
建材 1 -38.9% 17.8% 9.6

从行业分布能很清楚地看到,钢铁、有色、建材这些周期行业占了绝大多数,没有科技股,没有成长股,完全是价值风格的标的。

关键指标的分布区间也有明确的特征:
回撤幅度集中在30%到41%之间,标准差约3.8%,波动较小,说明这批股票的超跌程度比较均匀,没有某只股票回撤特别深或者特别浅的情况。
ROE全部高于15%门槛,最高为紫金矿业的22.3%,最低的宝钢股份也有15.1%,整体盈利质量很高。
PE从5.1倍到15.6倍不等,最低的是成都银行,最高的是山金国际,全部低于20倍的阈值,估值水平整体偏低。

这些统计不是为了炫技,而是让对方快速把握这批股票的整体画像,不用一个个去算去查。

如果需要批量生成不同市场、不同策略的筛选统计报告,也可以通过intoquant.com定制自动化报表脚本,每日自动输出完整的筛选结果与统计分析,节省大量手工整理时间。

2.5 “量化正确”与“交易合适”的永恒张力

从量化条件看,这份名单无懈可击。但从交易者的直觉看,它可能“不够性感”。

这里存在一个核心矛盾:量化筛选追求规则的一致性和可复现性,而交易者的偏好往往包含审美判断——对行业、故事、想象空间的偏好。
例如,钢铁和水泥行业虽然ROE高、PE低,但市场给予它们的长期估值一直偏低,因为行业处于成熟期甚至衰退期,缺乏增长预期。而成长股投资者宁愿买80倍PE的AI芯片公司,也不愿买8倍PE的钢铁股。这不是谁对谁错的问题,而是投资框架的差异——价值投资者赚业绩修复的钱,成长投资者赚产业扩张的钱。

我选择不主动“优化”这份名单,而是将它作为一个锚点。原因在于:如果对方不满意,他的反馈将是最有价值的修正信号——他会告诉我,他更在意成长性、产业趋势还是其他什么维度。而如果他满意,说明他的策略偏好就是深度价值+逆向投资。

这就像机器学习中的主动学习:先用初始模型给出预测,然后根据用户对预测结果的反馈,不断调整模型权重。迭代的成本远低于一次性构建完美模型的成本。
做量化这么多年我最深的感受是:好的策略从来不是设计出来的,而是迭代出来的。

2.6 第一次迭代的伏笔:为什么传统框架不够用

对方看到这份名单后的第一句话,将成为整个策略迭代的转折点。但那是第三章的内容。

在本章结束时,我需要做的是:准备好接收反馈,并预判可能的调整方向。
根据我的经验,对方最可能的反应有四种:
第一种是接受,那么策略方向就定格在深度价值+超跌反转,后续可以做更长期的回测,优化参数细节。
第二种是认为“太传统”,需要增加成长因子、产业趋势标签,把选股方向转向新兴行业。
第三种是认为“还不够超跌”,提高回撤阈值至40%以上,或加入超跌动量指标,追求更深的安全边际。
第四种是询问资金面,需要接入主力资金流向数据,验证股票是否有机构资金关注。

而根据A股个人投资者的普遍偏好,以及对方一开始就强调“超跌排名靠前”的逆向风格,我预判他大概率会偏向第二种,也就是不满意传统行业,想要更有想象空间的标的。当然,也有可能他会追加资金面的要求,毕竟“有没有资金买”是所有交易者都关心的问题。

第2章量化专家笔记

为什么坚持用前复权价格计算回撤?

不复权的价格会导致回撤计算严重失真。例如,某股10送10后价格减半,不复权回撤显示-50%,但实际复权回撤可能只有-10%。误把除权当成超跌,是新手最常见的错误之一。
这里补充一下前复权和后复权的区别:前复权是以当前价格为基准,把历史价格按除权比例调整,适合计算历史回撤和收益率;后复权是以发行价为基准,适合计算累计收益。选股和回测场景下,统一使用前复权价格是行业标准。

防御性代码模板

def safe_retrace(price_series, current_price):
    """计算年内回撤,处理空值和除权"""
    if price_series.empty:
        return None
    # 使用前复权价格(已在数据获取时处理)
    high_252 = price_series.rolling(252).max()
    if high_252.iloc[-1] == 0:
        return None
    retrace = (current_price - high_252.iloc[-1]) / high_252.iloc[-1]
    return retrace

写代码的时候一定要做异常处理,比如价格序列为空、最高价为0这些边界情况,否则程序很容易因为某只股票数据缺失就崩溃。量化系统的稳定性,90%取决于异常处理做得够不够。

回撤计算的边界情况

上市不足252个交易日的次新股:不要硬套252日高点,用实际交易日数计算高点,或者直接剔除上市不满一年的次新股,因为次新股没有足够的历史数据,超跌逻辑不适用。
近期有重大资产重组的股票:建议人工复核,因为价格连续性被破坏,之前的高点没有参考意义。
ST摘帽的股票:摘帽前后的估值逻辑完全不同,不能直接用过去一年的价格算回撤。

这些边界情况的处理逻辑,在a-sig.com的因子计算引擎中都已经内置,所有指标都会自动处理除权、次新股、重组等特殊情况,输出的结果直接可用,不用自己做数据清洗。

下一章将揭示,对方的真实反馈是第二种——“老师,我觉得太传统了”。这个反馈将引导我们进入A股的未来产业赛道:AI算力、半导体、新能源。

本章内容节选自原创量化书籍《因子会说谎》,更多数据清洗与因子计算的实操细节,可登录intoquant.com查阅完整内容。

⚠️ 风险提示:本书内容仅为量化研究与知识分享,不构成任何投资建议。套利交易存在基差、费率、流动性、平台与极端行情等风险,历史表现不代表未来收益。投资有风险,入市需谨慎,请自主决策、量力而行。

💡 键盘 ←/→ 翻章 · T 键切换目录