量化因子自动挖掘指南

用户头像sh_*056uc6
2026-11-04 发布

手动翻财报、一个个算PE/PB/ROE的时代早就过去了。量化因子挖掘的核心不是"找到一个好因子",而是建立一套能批量生产、自动检验、持续迭代的因子工厂。 这篇文章把整条流水线拆开了讲,数据从哪来、因子怎么造、有效性怎么验,全部用真实代码跑通。

为什么你写的因子总是不赚钱

写因子不难,写一个能持续赚钱的因子才难。大部分人挖因子的流程是这样的:翻研报看到一个逻辑,手动写个公式,跑一下回测,收益率曲线看着还行就上了。结果实盘一跑就崩。

问题出在哪?不是因子逻辑不对,而是你只测了一个因子在一个时间段的表现,没有做系统性的有效性检验。 单因子的IC均值、ICIR、分层回测、换手率、衰减速度这些指标一个都不能少。更要命的是,一个因子不够用,你得同时跑几十个甚至上百个因子做组合,这就需要一套自动化的流程。

手动挖因子就像用锄头挖矿,效率低还容易漏。自动挖掘的本质是把"构思因子 → 获取数据 → 计算因子值 → 有效性检验 → 入池或淘汰"这整条链路代码化,让机器帮你7×24小时跑。

量化因子挖掘的数据底座

巧妇难为无米之炊。因子挖掘第一步是搞定数据源,你需要三类数据:行情数据、基本面数据、盘口情绪数据。黑狼数据提供了14个HTTP API接口,刚好覆盖这三类需求。

接口全景

数据类别 接口路径 核心用途 更新频率
股票列表 /wolf/list 获取全市场股票池,支持9种分类 每日18:00
概念成分股 /wolf/sector 板块轮动、题材因子构建 每日18:00
财务核心指标 /wolf/financemetric ROE、毛利率、营收增速等20+因子 财报发布后同步
实时行情 /wolf/time 53个实时指标,含涨速、换手、量比 盘中每分钟
K线数据 /wolf/time/kline 8种周期(5分钟到年线),支持复权 盘中实时
涨停板/跌停板 /wolf/zt、/wolf/dt 情绪因子、极端事件因子 盘中每分钟
强势股 /wolf/qsg 动量因子、趋势跟踪 盘中每分钟
买卖五档 /wolf/five 盘口压力因子、微观结构 盘中实时
竞价数据 /wolf/jj 开盘情绪因子 09:15-09:25
逐笔交易 /wolf/deal 大单检测因子、主力行为 盘后18:00
资金流向 /wolf/money 主力净流入因子、筹码分布 盘后18:00

快速接入

所有接口的基础URL是 http://api.fxyz.site,返回JSON格式,用Python的requests库就能调用。调用时需要带token参数,在官网注册登录后获取。

import requests

BASE_URL = "//api.fxyz.site"
TOKEN = "your_token_here"  # 替换为你的token

def fetch_api(path, params=None):
    """统一请求封装"""
    if params is None:
        params = {}
    params["token"] = TOKEN
    url = f"{BASE_URL}{path}"
    resp = requests.get(url, params=params, timeout=10)
    resp.raise_for_status()
    return resp.json()

# 获取全市场A股列表
stocks = fetch_api("/wolf/list", {"flag": 0})
print(f"共获取到 {len(stocks)} 只股票")

# 获取财务核心指标
finance = fetch_api("/wolf/financemetric", {
    "code": "000001"
})

这段代码虽然简单,但它解决了一个关键问题:把所有数据获取逻辑收口到一个函数里,后面构建因子时只需要关心"用什么数据",不用操心"怎么拿数据"。

自动因子挖掘的三条路径

拿到数据只是起点,怎么从数据里"挖"出因子才是核心。当前主流的自动化因子挖掘有三条路径,各有适用场景。

路径一:因子公式库 + 批量回测

这是最经典也最实用的方法。核心思路是预定义一批因子表达式,用数据批量计算,再跑有效性检验。WorldQuant的101个Alpha因子就是这个路子的开山之作。

拿黑狼数据来说,你可以基于K线数据构建价量因子,基于财务数据构建基本面因子,基于资金流向构建资金行为因子。下面是一个实际的例子:

import pandas as pd
import numpy as np

def compute_price_volume_factors(kline_df):
    """基于K线数据批量计算价量因子"""
    df = kline_df.copy()
    df = df.sort_values("t")
  
    # 因子1:5日收益率动量
    df["ret_5d"] = df["c"].pct_change(5)
  
    # 因子2:成交量波动率(20日)
    df["vol_std_20"] = df["v"].rolling(20).std() / df["v"].rolling(20).mean()
  
    # 因子3:价格偏离度(收盘价偏离20日均线的程度)
    df["ma20"] = df["c"].rolling(20).mean()
    df["bias_20"] = (df["c"] - df["ma20"]) / df["ma20"]
  
    # 因子4:振幅因子(近5日平均振幅)
    df["amplitude_5d"] = ((df["h"] - df["l"]) / df["pc"]).rolling(5).mean()
  
    # 因子5:量价背离(价涨量缩的天数占比)
    price_up = df["c"] > df["c"].shift(1)
    vol_down = df["v"] < df["v"].shift(1)
    df["pv_diverge_10"] = (price_up & vol_down).rolling(10).sum() / 10
  
    return df

这段代码一次性算了5个因子,每个因子背后都有明确的经济学逻辑。 动量因子捕捉趋势延续,波动率因子衡量交易活跃度变化,偏离度因子反映均值回归力度,振幅因子度量价格弹性,量价背离因子检测上涨动能衰竭。

路径二:遗传算法 / 强化学习自动生成因子表达式

这条路径更激进——让机器自己"发明"因子公式。核心思想是把因子表达式编码成一棵树,用遗传算法或强化学习在表达式空间里搜索。

表达式空间通常由这些基本元素构成:

类别 元素示例
数据字段 open, close, high, low, volume, amount
时序算子 ts_mean, ts_std, ts_rank, ts_max, ts_min, delay, delta
截面算子 rank, zscore, scale
数学算子 log, abs, sign, power, corr, cov
常数 1, 2, 5, 10, 20, 60

一个典型的因子表达式长这样:-1 * corr(rank(delta(log(volume), 1)), rank(delta(close, 1)), 5)。翻译成人话就是"过去5天里,成交量变化排名和价格变化排名的相关性的负值"——本质上是一个量价同步性因子。

用黑狼数据的K线接口拿到全市场的OHLCV数据后,可以批量计算这类因子表达式,再用IC检验筛选。

路径三:LLM辅助因子发现

这是2025年以来最前沿的方向。arXiv上已经有好几篇论文(Hubble、FactorEngine、QuantEvolver)在探索用大语言模型自动生成因子。核心流程是:LLM根据金融知识提出因子假说 → 自动生成Python代码 → 回测验证 → 反馈给LLM迭代优化。

这条路径目前还偏研究阶段,但它代表了一个明确趋势:因子挖掘正在从"手艺人活"变成"流水线工程"。

因子有效性检验:IC、ICIR和分层回测

因子算出来了,怎么判断它有没有用?量化圈有一套成熟的检验体系,核心就三个指标:IC、ICIR和分层回测。

IC(信息系数)

IC是因子值与未来收益之间的秩相关系数(Spearman Rank Correlation)。取值范围在-1到1之间。

IC绝对值 因子有效性判定
> 0.05 强效因子,可直接使用
0.03 ~ 0.05 中等因子,建议与其他因子组合
< 0.03 弱效因子,单独使用价值不大
def calc_ic_series(factor_df, forward_days=5):
    """计算IC时间序列
  
    factor_df: 包含 'code', 'date', 'factor_value', 'forward_return' 的DataFrame
    """
    ic_list = []
    for date in factor_df["date"].unique():
        sub = factor_df[factor_df["date"] == date]
        ic = sub["factor_value"].corr(
            sub["forward_return"], method="spearman"
        )
        ic_list.append({"date": date, "ic": ic})
  
    ic_df = pd.DataFrame(ic_list)
    return ic_df

# 计算IC均值和ICIR
ic_mean = ic_df["ic"].mean()
ic_std = ic_df["ic"].std()
icir = ic_mean / ic_std

print(f"IC均值: {ic_mean:.4f}")
print(f"ICIR:   {icir:.4f}")

ICIR(信息比率)

ICIR = IC均值 / IC标准差。它衡量的是因子预测能力的稳定性。一个IC均值0.04但ICIR只有0.3的因子,不如一个IC均值0.03但ICIR有0.8的因子靠谱。 因为前者时灵时不灵,后者虽然预测力弱一点但非常稳定。

经验法则:ICIR绝对值大于0.5就值得重点关注,大于1.0就是非常优质的因子。

分层回测

把全市场股票按因子值从小到大分成5组或10组,分别计算每组的未来收益。如果多头组(因子值最大或最小的那组)持续跑赢空头组,说明因子有效。

def layered_backtest(factor_df, n_groups=5, forward_days=5):
    """分层回测"""
    results = []
    for date in factor_df["date"].unique():
        sub = factor_df[factor_df["date"] == date].copy()
        sub["group"] = pd.qcut(sub["factor_value"], n_groups, labels=False)
        group_ret = sub.groupby("group")["forward_return"].mean()
        group_ret["date"] = date
        results.append(group_ret)
  
    result_df = pd.DataFrame(results)
  
    # 多空收益:最大组 - 最小组
    ls_ret = result_df[n_groups - 1] - result_df[0]
    print(f"多空年化收益: {ls_ret.mean() * 252:.2%}")
    print(f"多空夏普比率: {ls_ret.mean() / ls_ret.std() * np.sqrt(252):.2f}")
  
    return result_df

完整实战流程:从API取数到因子入池

把前面的模块串起来,跑一个完整的因子挖掘流程。

第一步:批量获取K线数据

def batch_fetch_kline(stock_list, period="1d", start="2025-01-01", end="2025-12-31"):
    """批量获取K线数据"""
    all_kline = []
    for stock in stock_list:
        code = stock["code"]
        data = fetch_api("/wolf/time/kline", {
            "symbol": "stock",
            "code": code,
            "period": period,
            "cq": "2",  # 前复权
            "startDate": start,
            "endDate": end
        })
        for bar in data:
            bar["code"] = code
        all_kline.extend(data)
  
    return pd.DataFrame(all_kline)

第二步:计算因子值

# 获取数据
stocks = fetch_api("/wolf/list", {"flag": 0})
kline_df = batch_fetch_kline(stocks[:100])  # 先用100只股票测试

# 计算因子
factor_df = compute_price_volume_factors(kline_df)

# 计算未来5日收益(用于检验)
factor_df["forward_return"] = factor_df.groupby("code")["c"].transform(
    lambda x: x.shift(-5) / x - 1
)

第三步:有效性检验

# IC检验
ic_df = calc_ic_series(factor_df, forward_days=5)
ic_mean = ic_df["ic"].mean()
icir = ic_mean / ic_df["ic"].std()

print(f"因子: 5日动量")
print(f"IC均值: {ic_mean:.4f}")
print(f"ICIR:   {icir:.4f}")

# 分层回测
layered = layered_backtest(factor_df, n_groups=5, forward_days=5)

第四步:因子入池决策

指标 入池标准 你的因子
IC均值绝对值 > 0.03 看检验结果
ICIR绝对值 > 0.5 看检验结果
多空年化收益 > 10% 看回测结果
换手率 < 30% 需额外计算
因子间相关性 与已有因子相关性 < 0.7 需额外计算

五项全过的因子直接入池,过三项以上的保留观察,其余淘汰。

进阶:用资金流和盘口数据构建高阶因子

K线因子只是入门级。黑狼数据的特色在于提供了资金流向、盘口、竞价等"非传统"数据,这些数据可以构建出信息含量更高的因子。

资金流因子

# 获取资金流向数据
money_flow = fetch_api("/wolf/money", {"code": "000001"})

# 构建主力净流入因子
# 特大单 + 大单净流入 / 总成交额
def build_smart_money_factor(mf_list):
    df = pd.DataFrame(mf_list)
    df["smart_money_ratio"] = (df["ted_amount"] + df["big_amount"]) / df["total_amount"]
    df["smart_money_5d_avg"] = df["smart_money_ratio"].rolling(5).mean()
    return df

竞价情绪因子

集合竞价阶段(09:15-09:25)的数据是A股独有的信息宝库。竞价量占比、竞价价格偏离前收的程度,都可以反映当日市场情绪。

# 获取竞价数据
bid_data = fetch_api("/wolf/jj", {"code": "000001"})

# 竞价因子:竞价涨幅 * 竞价量占比
# 逻辑:高开且竞价放量的股票,当日表现往往更强
def build_bid_factor(bid_list):
    df = pd.DataFrame(bid_list)
    df["bid_return"] = (df["bid_price"] - df["pre_close"]) / df["pre_close"]
    df["bid_vol_ratio"] = df["bid_volume"] / df["total_volume"]
    df["bid_factor"] = df["bid_return"] * df["bid_vol_ratio"]
    return df

这类因子的信息含量通常远高于纯价量因子,因为它们捕捉的是市场参与者的"意图"而非"结果"。

因子挖掘的避坑清单

挖因子不难,挖到能用的因子才难。这些坑我替你踩过了:

常见陷阱 为什么会踩坑 怎么避开
过拟合 在历史数据上反复调参,因子"记住"了过去 严格分训练集/测试集,样本外验证
未来函数 用了当天数据预测当天收益 因子值只能用T日数据,收益从T+1开始算
幸存者偏差 只用了当前还在交易的股票 数据源需包含退市股票,黑狼数据flag=9可获取
忽略换手率 因子收益很高但换手率50%,实盘根本跑不了 换手率超过30%的因子谨慎使用
因子共线性 池子里10个因子有8个高度相关 入池前做因子间相关性分析,相关性>0.7只保留一个

写在最后

自动因子挖掘不是什么玄学,它就是把"数据获取 → 因子计算 → 有效性检验 → 入池管理"这条流水线代码化。黑狼数据这类API平台降低了数据获取的门槛,Python生态提供了足够的计算工具,剩下的就是你自己的因子逻辑和检验纪律。

好的因子不是被"发明"出来的,而是被"筛选"出来的。 你需要做的不是苦思冥想找到一个天才因子,而是建立一个能批量生产、严格检验、自动淘汰的系统。量变引发质变,挖100个因子能留下5个就不错了——但那5个,够你吃好几年。

GitHub开源项目:https://github.com/fxyzsite/FxyzSkill

评论