手动翻财报、一个个算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

